use crate::*;
use alloc::{format, vec::Vec};
pub type GeoVar1 = GeoVar<na::Rotation<f64, 1>, 1>;
pub type GeoVar2 = GeoVar<na::UnitComplex<f64>, 2>;
pub type GeoVar3 = GeoVar<na::UnitQuaternion<f64>, 3>;
type Sim<R, const D: usize> = na::Similarity<f64, R, D>;
pub trait RotHint<const D: usize>: na::AbstractRotation<f64, D> + core::fmt::Debug {
fn matrix(self) -> na::SMatrix<f64, D, D>;
}
impl<const D: usize> RotHint<D> for na::Rotation<f64, D> {
fn matrix(self) -> na::SMatrix<f64, D, D> {
self.into_inner()
}
}
impl RotHint<2> for na::UnitComplex<f64> {
fn matrix(self) -> na::SMatrix<f64, 2, 2> {
self.to_rotation_matrix().into_inner()
}
}
impl RotHint<3> for na::UnitQuaternion<f64> {
fn matrix(self) -> na::SMatrix<f64, 3, 3> {
self.to_rotation_matrix().into_inner()
}
}
#[derive(Clone)]
pub struct GeoVar<R: RotHint<D>, const D: usize> {
inner: Sim<R, D>,
}
impl<R: RotHint<D>, const D: usize> Default for GeoVar<R, D> {
fn default() -> Self {
Self { inner: Sim::identity() }
}
}
impl<R, const D: usize> core::fmt::Debug for GeoVar<R, D>
where
R: RotHint<D>,
{
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct(&format!("GeoVar{D}"))
.field("translation", &self.trans())
.field("rotation", self.rot())
.field("scale", &self.scale())
.finish()
}
}
impl<R, const D: usize> GeoVar<R, D>
where
R: RotHint<D>,
{
#[must_use]
pub fn new(trans: Coord<D>, rot: R, scale: f64) -> Self {
Self { inner: Sim::from_parts(trans.into(), rot, scale) }
}
#[must_use]
pub fn identity() -> Self {
Self::default()
}
#[must_use]
pub fn transform_pt(&self, p: Coord<D>) -> Coord<D> {
self.inner.transform_point(&na::Point::from(p)).into()
}
#[must_use]
pub fn transform<C>(&self, curve: C) -> Vec<Coord<D>>
where
C: AsRef<[Coord<D>]>,
{
curve
.as_ref()
.iter()
.map(|c| self.transform_pt(*c))
.collect()
}
pub fn transform_inplace<C>(&self, mut curve: C)
where
C: AsMut<[Coord<D>]>,
{
curve
.as_mut()
.iter_mut()
.for_each(|c| *c = self.transform_pt(*c));
}
pub fn transform_iter<'a, C>(&'a self, curve: C) -> impl Iterator<Item = Coord<D>> + 'a
where
C: IntoIterator<Item = Coord<D>> + 'a,
{
curve.into_iter().map(|c| self.transform_pt(c))
}
#[must_use]
pub fn to(&self, rhs: &Self) -> Self {
self.inverse().apply(rhs)
}
#[must_use]
pub fn apply(&self, rhs: &Self) -> Self {
Self { inner: &rhs.inner * &self.inner }
}
#[must_use]
pub fn inverse(&self) -> Self {
Self { inner: self.inner.inverse() }
}
#[must_use]
pub fn trans(&self) -> Coord<D> {
self.inner.isometry.translation.vector.data.0[0]
}
#[must_use]
pub fn rot(&self) -> &R {
&self.inner.isometry.rotation
}
#[must_use]
pub fn scale(&self) -> f64 {
self.inner.scaling()
}
}
macro_rules! impl_mul {
($ty1:ty, $ty2:ty) => {
impl<R, const D: usize> core::ops::Mul<$ty2> for $ty1
where
R: RotHint<D>,
{
type Output = GeoVar<R, D>;
#[must_use]
fn mul(self, rhs: $ty2) -> Self::Output {
GeoVar { inner: &self.inner * &rhs.inner }
}
}
};
}
impl_mul!(GeoVar<R, D>, Self);
impl_mul!(&GeoVar<R, D>, Self);
impl_mul!(GeoVar<R, D>, &Self);
impl_mul!(&GeoVar<R, D>, GeoVar<R, D>);