use std::{
array::{self, from_fn},
hash::{Hash, Hasher},
ops::{Add, AddAssign, Div, Index, IndexMut, Mul, Sub, SubAssign},
};
use crate::{
geometry::{
spatial_element::SpatialElement,
vector::{Vector, VectorOps},
},
numeric::scalar::Scalar,
operations::Zero,
};
#[derive(Clone, Debug)]
pub struct Point<T: Scalar, const N: usize> {
pub coords: [T; N],
}
pub trait PointOps<T: Scalar, const N: usize>: Sized {
type Vector: VectorOps<T, N>;
fn sub(&self, other: &Self) -> Self;
fn as_vector(&self) -> Self::Vector;
fn as_vector_2(&self) -> Vector<T, 2>;
fn as_vector_3(&self) -> Vector<T, 3>;
fn add_vector(&self, v: &Self::Vector) -> Self;
fn vector_to(&self, other: &Self) -> Self::Vector;
fn midpoint(&self, other: &Self) -> Self;
}
impl<T: Scalar, const N: usize> Default for Point<T, N> {
fn default() -> Point<T, N> {
Point {
coords: array::from_fn(|_| T::default()),
}
}
}
impl<T: Scalar, const N: usize> SpatialElement<T, N> for Point<T, N> {
type With<U: Scalar> = Point<U, N>;
fn new(coords: [T; N]) -> Point<T, N> {
Point { coords }
}
fn from_vals<V>(vals: [V; N]) -> Point<T, N>
where
V: Into<T>,
{
Point {
coords: vals.map(|v| v.into()),
}
}
fn coords(&self) -> &[T; N] {
&self.coords
}
fn coords_mut(&mut self) -> &mut [T; N] {
&mut self.coords
}
fn iter(&self) -> std::slice::Iter<'_, T> {
self.coords.iter()
}
fn cast<U: Scalar>(&self) -> Self::With<U>
where
U: From<T>,
{
Point::<U, N>::from_vals(from_fn(|i| U::from(self.coords[i].clone())))
}
}
impl<T: Scalar, const N: usize> Index<usize> for Point<T, N> {
type Output = T;
fn index(&self, i: usize) -> &Self::Output {
&self.coords[i]
}
}
impl<T: Scalar, const N: usize> IndexMut<usize> for Point<T, N> {
fn index_mut(&mut self, i: usize) -> &mut Self::Output {
&mut self.coords[i]
}
}
impl<'a, 'b, T, const N: usize> Add<&'b Point<T, N>> for &'a Point<T, N>
where
T: Scalar + for<'c> AddAssign<&'c T>,
{
type Output = Point<T, N>;
fn add(self, rhs: &'b Point<T, N>) -> Self::Output {
let mut out = self.clone();
for i in 0..N {
out.coords[i] += &rhs.coords[i];
}
out
}
}
impl<T, const N: usize> Add for Point<T, N>
where
T: Scalar,
for<'a, 'b> &'a Point<T, N>: Add<&'b Point<T, N>, Output = Point<T, N>>,
{
type Output = Point<T, N>;
fn add(self, rhs: Point<T, N>) -> Self::Output {
<&Point<T, N> as Add<&Point<T, N>>>::add(&self, &rhs)
}
}
impl<'a, 'b, T, const N: usize> Sub<&'b Point<T, N>> for &'a Point<T, N>
where
T: Scalar + for<'c> SubAssign<&'c T>,
{
type Output = Point<T, N>;
fn sub(self, rhs: &'b Point<T, N>) -> Self::Output {
let mut out = self.clone();
for i in 0..N {
out.coords[i] -= &rhs.coords[i];
}
out
}
}
impl<T, const N: usize> Sub for Point<T, N>
where
T: Scalar,
for<'a, 'b> &'a Point<T, N>: Sub<&'b Point<T, N>, Output = Point<T, N>>,
{
type Output = Point<T, N>;
fn sub(self, rhs: Point<T, N>) -> Self::Output {
<&Point<T, N> as Sub<&Point<T, N>>>::sub(&self, &rhs)
}
}
impl<T, const N: usize> Zero for Point<T, N>
where
T: Scalar,
{
fn zero() -> Self {
Point {
coords: array::from_fn(|_| T::zero()),
}
}
fn is_zero(&self) -> bool {
self.coords.iter().all(|coord| coord.is_zero())
}
fn is_positive(&self) -> bool {
self.coords.iter().all(|coord| coord.is_positive())
}
fn is_negative(&self) -> bool {
self.coords.iter().all(|coord| coord.is_negative())
}
fn is_positive_or_zero(&self) -> bool {
self.coords.iter().all(|coord| coord.is_positive_or_zero())
}
fn is_negative_or_zero(&self) -> bool {
self.coords.iter().all(|coord| coord.is_negative_or_zero())
}
}
impl<T> PointOps<T, 2> for Point<T, 2>
where
T: Scalar,
for<'a> &'a T: Add<&'a T, Output = T>
+ Sub<&'a T, Output = T>
+ Mul<&'a T, Output = T>
+ Div<&'a T, Output = T>,
{
type Vector = Vector<T, 2>;
fn sub(&self, other: &Self) -> Self {
let x = &self[0] - &other[0];
let y = &self[1] - &other[1];
Self { coords: [x, y] }
}
fn as_vector(&self) -> Self::Vector {
Vector(Point::<T, 2>::from_vals([self[0].clone(), self[1].clone()]))
}
fn as_vector_2(&self) -> Vector<T, 2> {
Vector(Point::<T, 2>::from_vals([self[0].clone(), self[1].clone()]))
}
fn as_vector_3(&self) -> Vector<T, 3> {
panic!("as_vector_3 is not implemented for 2D points");
}
fn add_vector(&self, v: &Self::Vector) -> Self
where
T: Scalar,
{
Point {
coords: [&self[0] + &v[0], &self[1] + &v[1]],
}
}
fn vector_to(&self, other: &Self) -> Self::Vector {
Vector(Point::<T, 2>::from_vals([
&other[0] - &self[0],
&other[1] - &self[1],
]))
}
fn midpoint(&self, other: &Self) -> Self {
let two: T = T::from(2);
Self {
coords: from_fn(|i| {
let sum = &self.coords[i] + &other.coords[i];
&sum / &two
}),
}
}
}
impl<T> PointOps<T, 3> for Point<T, 3>
where
T: Scalar,
for<'a> &'a T: Add<&'a T, Output = T>
+ Sub<&'a T, Output = T>
+ Mul<&'a T, Output = T>
+ Div<&'a T, Output = T>,
{
type Vector = Vector<T, 3>;
fn sub(&self, other: &Self) -> Self {
let x = &self[0] - &other[0];
let y = &self[1] - &other[1];
let z = &self[2] - &other[2];
Self { coords: [x, y, z] }
}
fn as_vector(&self) -> Self::Vector {
Vector(Point::<T, 3>::from_vals([
self[0].clone(),
self[1].clone(),
self[2].clone(),
]))
}
fn as_vector_2(&self) -> Vector<T, 2> {
panic!("as_vector_2 is not implemented for 3D points");
}
fn as_vector_3(&self) -> Vector<T, 3> {
Vector(Point::<T, 3>::from_vals([
self[0].clone(),
self[1].clone(),
self[2].clone(),
]))
}
fn add_vector(&self, v: &Self::Vector) -> Self
where
T: Scalar,
{
Point {
coords: [&self[0] + &v[0], &self[1] + &v[1], &self[2] + &v[2]],
}
}
fn vector_to(&self, other: &Self) -> Self::Vector {
Vector(Point::<T, 3>::from_vals([
&other[0] - &self[0],
&other[1] - &self[1],
&other[2] - &self[2],
]))
}
fn midpoint(&self, other: &Self) -> Self {
let two: T = T::from(2);
Self {
coords: from_fn(|i| {
let sum = &self.coords[i] + &other.coords[i];
&sum / &two
}),
}
}
}
impl<T: Scalar, const N: usize> Into<[T; N]> for Point<T, N> {
fn into(self) -> [T; N] {
self.coords
}
}
impl<T: Scalar, const N: usize> From<[T; N]> for Point<T, N> {
fn from(coords: [T; N]) -> Self {
Point { coords }
}
}
impl<T: Scalar, const N: usize> Hash for Point<T, N> {
fn hash<H: Hasher>(&self, state: &mut H) {
for coord in &self.coords {
coord.hash(state);
}
}
}
impl<T: Scalar, const N: usize> PartialEq for Point<T, N> {
fn eq(&self, other: &Self) -> bool {
if self.coords.len() != other.coords.len() {
return false;
}
for i in 0..N {
if self.coords[i] != other.coords[i] {
return false;
}
}
true
}
}
impl<T: Scalar, const N: usize> PartialOrd for Point<T, N> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
if self.coords.len() != other.coords.len() {
return None;
}
for i in 0..N {
match self.coords[i].partial_cmp(&other.coords[i]) {
Some(std::cmp::Ordering::Equal) => continue,
Some(ordering) => return Some(ordering),
None => return None,
}
}
Some(std::cmp::Ordering::Equal)
}
}
impl<T: Scalar, const N: usize> Eq for Point<T, N> {}
pub type Point2<T> = Point<T, 2>;
pub type Point3<T> = Point<T, 3>;