use derive_more::{Add, AddAssign, Deref, DerefMut, From, Into, Neg, Sub, SubAssign};
use num_traits::{Bounded, Num, CheckedMul, CheckedAdd};
use std::array;
use std::fmt::{Display, Formatter};
macro_rules! derive_universal_traits {
($struct_def: item) => {
#[derive(Copy, Clone, Default)]
#[derive(Neg)]
#[derive(Add, AddAssign, Sub, SubAssign)]
#[derive(Eq,PartialEq)]
#[derive(Debug)]
$struct_def
};
}
macro_rules! impl_hypot_sq{
($struct_id: ident, $first_operand: ident $(, $other: ident)*)=> {
impl<'l,N> $struct_id<N> where N: Num +CheckedMul +CheckedAdd {
#[doc= concat!(
"```\n",
"use siiir_points::", stringify!($struct_id), ";\n",
"\n",
"let p= ", stringify!($struct_id), "::from( std::array::from_fn(|idx| idx) );\n",
"assert_eq!( ",
"p.hypot_sq(), ",
"Some( ",
stringify!(p.$first_operand), "*", stringify!(p.$first_operand),
$( " + ", stringify!(p.$other), "*", stringify!(p.$other), )*
" )",
" );\n",
"```\n",
)]
pub fn hypot_sq(&'l self)-> Option<N>{
let mut sum: N= self.$first_operand.checked_mul( &self.$first_operand )?;
$(
sum= sum.checked_add( &self.$other.checked_mul( &self.$other )? )?;
)*
Some(sum)
}
}
};
}
macro_rules! impl_bounds {
($id: ident) => {
impl<N: Num + Bounded> Bounded for $id<N> {
fn min_value() -> Self {
array::from_fn(|_| N::min_value()).into()
}
fn max_value() -> Self {
array::from_fn(|_| N::max_value()).into()
}
}
};
}
derive_universal_traits! {
#[derive(From, Into)]
pub struct Point2D<N: Num>{
pub x: N,
pub y: N,
}
}
impl_hypot_sq!(Point2D, x, y);
impl<N: Num> From<[N; 2]> for Point2D<N> {
fn from([x, y]: [N; 2]) -> Self {
Self { x, y }
}
}
impl<N: Num> From<Point2D<N>> for [N; 2] {
fn from(Point2D { x, y }: Point2D<N>) -> Self {
[x, y]
}
}
impl<N: Num + Display> Display for Point2D<N> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "( {}, {} )", self.x, self.y)
}
}
impl_bounds!(Point2D);
derive_universal_traits! {
#[derive(Deref,DerefMut)]
pub struct Point3D<N: Num>{
#[deref]
#[deref_mut]
pub xy: Point2D<N>,
pub z: N,
}
}
impl_hypot_sq!(Point3D, x, y, z);
impl<N: Num> From<[N; 3]> for Point3D<N> {
fn from([x, y, z]: [N; 3]) -> Self {
Self {
xy: [x, y].into(),
z,
}
}
}
impl<N: Num> From<Point3D<N>> for [N; 3] {
fn from(value: Point3D<N>) -> Self {
let Point3D {
xy: Point2D { x, y },
z,
} = value;
[x, y, z]
}
}
impl<N: Num> From<(N, N, N)> for Point3D<N> {
fn from((x, y, z): (N, N, N)) -> Self {
[x, y, z].into()
}
}
impl<N: Num> From<Point3D<N>> for (N, N, N) {
fn from(value: Point3D<N>) -> Self {
let [x, y, z]: [N; 3] = value.into();
(x, y, z)
}
}
impl<N: Num + Display> Display for Point3D<N> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "( {}, {}, {} )", self.x, self.y, self.z)
}
}
impl_bounds!(Point3D);
#[cfg(test)]
mod test;