use itertools::izip;
use num_traits::ToPrimitive;
use std::ops::{Add, Mul, Sub};
#[derive(Copy, Clone, PartialEq)]
pub struct Point<const N: usize> {
coords: [f64; N],
}
impl<const N: usize> Point<N> {
pub fn new<T: ToPrimitive>(coords: [T; N]) -> Self {
let coords = coords.map(|x| x.to_f64().expect("Coordinates must be convertible to f64."));
Self { coords }
}
pub fn coords(&self) -> &[f64; N] {
&self.coords
}
pub fn coords_mut(&mut self) -> &mut [f64; N] {
&mut self.coords
}
pub fn set_coords<T: ToPrimitive>(&mut self, coords: [T; N]) {
self.coords = coords.map(|x| x.to_f64().expect("Coordinates must be convertible to f64."));
}
}
#[derive(Copy, Clone, PartialEq)]
pub struct Vector<const N: usize> {
coords: [f64; N],
}
impl<const N: usize> Vector<N> {
pub fn new<T: ToPrimitive>(coords: [T; N]) -> Self {
let coords = coords.map(|x| x.to_f64().expect("Coordinates must be convertible to f64."));
Self { coords }
}
pub fn coords(&self) -> &[f64; N] {
&self.coords
}
pub fn coords_mut(&mut self) -> &mut [f64; N] {
&mut self.coords
}
pub fn set_coords<T: ToPrimitive>(&mut self, coords: [T; N]) {
self.coords = coords.map(|x| x.to_f64().expect("Coordinates must be convertible to f64."));
}
pub fn from_points(point1: Point<N>, point2: Point<N>) -> Self {
let mut coords = [0.0; N];
izip!(
coords.iter_mut(),
point1.coords().iter(),
point2.coords().iter()
)
.for_each(|(coord, x, y)| *coord = y - x);
Self { coords }
}
pub fn angle_between(&self, other: &Self) -> f64 {
(self.dot_product(other) / (self.magnitude() * other.magnitude())).acos()
}
pub fn dot_product(&self, other: &Self) -> f64 {
self.coords
.iter()
.zip(other.coords.iter())
.map(|(x, y)| x * y)
.sum()
}
pub fn magnitude(&self) -> f64 {
self.coords.iter().map(|x| x.powi(2)).sum::<f64>().sqrt()
}
pub fn normalize(&self) -> Self {
let magnitude = self.magnitude();
let mut coords = [0.0; N];
coords
.iter_mut()
.zip(self.coords.iter())
.for_each(|(coord, x)| *coord = x / magnitude);
Self { coords }
}
}
impl Vector<3> {
pub fn cross_product(&self, other: &Self) -> Self {
let coords = [
self.coords[1] * other.coords[2] - self.coords[2] * other.coords[1],
self.coords[2] * other.coords[0] - self.coords[0] * other.coords[2],
self.coords[0] * other.coords[1] - self.coords[1] * other.coords[0],
];
Self { coords }
}
}
impl<const N: usize> Add<Self> for Vector<N> {
type Output = Self;
fn add(self, other: Self) -> Self {
let mut coords = [0.0; N];
izip!(coords.iter_mut(), self.coords.iter(), other.coords.iter())
.for_each(|(coord, x, y)| *coord = x + y);
Self { coords }
}
}
impl<const N: usize> Sub<Self> for Vector<N> {
type Output = Self;
fn sub(self, other: Self) -> Self {
let mut coords = [0.0; N];
izip!(coords.iter_mut(), self.coords.iter(), other.coords.iter())
.for_each(|(coord, x, y)| *coord = x - y);
Self { coords }
}
}
impl<const N: usize, T: ToPrimitive> Mul<T> for Vector<N> {
type Output = Vector<N>;
fn mul(self, rhs: T) -> Self::Output {
let mut coords = self.coords;
coords
.iter_mut()
.for_each(|coord| *coord *= rhs.to_f64().expect("Scalar must be convertible to f64."));
Self { coords }
}
}