use nalgebra::SVector;
use std::ops::{Add, Sub};
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Point<const D: usize>(pub SVector<f64, D>);
impl<const D: usize> Point<D> {
pub fn origin() -> Self {
Self(SVector::zeros())
}
pub fn new(coords: [f64; D]) -> Self {
Self(SVector::from(coords))
}
#[inline]
pub fn distance(&self, other: &Self) -> f64 {
(self.0 - other.0).norm()
}
#[inline]
pub fn distance_squared(&self, other: &Self) -> f64 {
(self.0 - other.0).norm_squared()
}
#[inline]
pub fn lerp(&self, other: &Self, t: f64) -> Self {
Self(self.0 * (1.0 - t) + other.0 * t)
}
#[inline]
pub fn coord(&self, i: usize) -> f64 {
self.0[i]
}
#[inline]
pub fn coord_mut(&mut self, i: usize) -> &mut f64 {
&mut self.0[i]
}
#[inline]
pub fn to_vector(&self) -> SVector<f64, D> {
self.0
}
}
impl<const D: usize> Add<SVector<f64, D>> for Point<D> {
type Output = Point<D>;
#[inline]
fn add(self, rhs: SVector<f64, D>) -> Point<D> {
Point(self.0 + rhs)
}
}
impl<const D: usize> Sub for Point<D> {
type Output = SVector<f64, D>;
#[inline]
fn sub(self, rhs: Point<D>) -> SVector<f64, D> {
self.0 - rhs.0
}
}
impl<const D: usize> Default for Point<D> {
fn default() -> Self {
Self::origin()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn origin_is_zero() {
let p = Point::<4>::origin();
for i in 0..4 {
assert_eq!(p.coord(i), 0.0);
}
}
#[test]
fn distance_2d() {
let a = Point::new([0.0, 0.0]);
let b = Point::new([3.0, 4.0]);
assert!((a.distance(&b) - 5.0).abs() < 1e-12);
}
#[test]
fn distance_4d() {
let a = Point::new([1.0, 2.0, 3.0, 4.0]);
let b = Point::new([5.0, 6.0, 7.0, 8.0]);
assert!((a.distance(&b) - 8.0).abs() < 1e-12);
}
#[test]
fn lerp_midpoint() {
let a = Point::new([0.0, 0.0, 0.0]);
let b = Point::new([10.0, 20.0, 30.0]);
let mid = a.lerp(&b, 0.5);
assert!((mid.coord(0) - 5.0).abs() < 1e-12);
assert!((mid.coord(1) - 10.0).abs() < 1e-12);
}
#[test]
fn subtraction() {
let a = Point::new([1.0, 2.0]);
let b = Point::new([4.0, 6.0]);
let v = b - a;
assert!((v[0] - 3.0).abs() < 1e-12);
assert!((v[1] - 4.0).abs() < 1e-12);
}
#[test]
fn is_copy() {
let a = Point::new([1.0, 2.0]);
let b = a;
assert_eq!(a, b);
}
}