pub use self::{
fb::{FourBar, NormFourBar},
mfb::{MFourBar, MNormFourBar},
sfb::{SFourBar, SNormFourBar},
stat::*,
vectorized::*,
};
#[cfg(feature = "serde")]
mod impl_serde;
pub mod fb;
pub mod mfb;
pub mod sfb;
mod stat;
mod vectorized;
#[derive(Clone, Default, Debug, PartialEq)]
pub struct Mech<UN, NM> {
pub unnorm: UN,
pub norm: NM,
}
impl<UN, NM> Mech<UN, NM> {
pub const fn new(unnorm: UN, norm: NM) -> Self {
Self { unnorm, norm }
}
pub fn from_norm<const D: usize>(norm: NM) -> Self
where
NM: Normalized<D, De = Self>,
efd::U<D>: efd::EfdDim<D>,
{
norm.denormalize()
}
pub fn with_stat(self, stat: Stat) -> Self
where
Self: Statable,
{
Statable::with_stat(self, stat)
}
pub fn stat(&self) -> Stat
where
Self: Statable,
{
Statable::stat(self)
}
pub fn to_states(self) -> Vec<Self>
where
Self: Statable,
{
Statable::to_states(self)
}
pub fn other_states(&self) -> Vec<Self>
where
Self: Statable,
{
Statable::other_states(self)
}
pub fn ty(&self) -> FourBarTy
where
Self: Statable,
{
Statable::ty(self)
}
pub fn normalize<const D: usize>(self) -> NM
where
NM: Normalized<D, De = Self>,
efd::U<D>: efd::EfdDim<D>,
{
Normalized::<D>::normalize(self)
}
pub fn curve<const D: usize>(&self, res: usize) -> Vec<[f64; D]>
where
Self: CurveGen<D>,
{
CurveGen::<D>::curve(self, res)
}
pub fn pose<const D: usize>(&self, res: usize) -> (Vec<[f64; D]>, Vec<[f64; D]>)
where
Self: PoseGen<D>,
{
PoseGen::<D>::pose(self, res)
}
pub fn pose_zipped<const D: usize>(&self, res: usize) -> Vec<([f64; D], [f64; D])>
where
Self: PoseGen<D>,
{
PoseGen::<D>::pose_zipped(self, res)
}
pub fn ext_curve<const D: usize>(
&self,
length: f64,
res: usize,
) -> (Vec<[f64; D]>, Vec<[f64; D]>)
where
Self: PoseGen<D>,
{
PoseGen::<D>::ext_curve(self, length, res)
}
pub fn is_valid(&self) -> bool
where
Self: Statable,
{
Statable::is_valid(self)
}
pub fn is_open(&self) -> bool
where
Self: Statable,
{
Statable::is_open(self)
}
pub fn angle_bound(&self) -> AngleBound
where
Self: Statable,
{
Statable::angle_bound(self)
}
}
impl<UN, NM> std::ops::Deref for Mech<UN, NM> {
type Target = NM;
fn deref(&self) -> &Self::Target {
&self.norm
}
}
impl<UN, NM> std::ops::DerefMut for Mech<UN, NM> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.norm
}
}
pub trait Normalized<const D: usize>: Sized
where
efd::U<D>: efd::EfdDim<D>,
{
type De: Transformable<D>;
fn denormalize(self) -> Self::De;
fn normalize(de: Self::De) -> Self;
fn normalize_inplace(de: &mut Self::De);
fn trans_denorm(self, geo: &efd::GeoVar<efd::Rot<D>, D>) -> Self::De {
self.denormalize().transform(geo)
}
}
pub trait Transformable<const D: usize>: Sized
where
efd::U<D>: efd::EfdDim<D>,
{
fn transform_inplace(&mut self, geo: &efd::GeoVar<efd::Rot<D>, D>);
fn transform(mut self, geo: &efd::GeoVar<efd::Rot<D>, D>) -> Self {
self.transform_inplace(geo);
self
}
}
pub trait CurveGen<const D: usize>: Statable {
fn pos_s(&self, t: f64, inv: bool) -> Option<[[f64; D]; 5]>;
fn pos(&self, t: f64) -> Option<[[f64; D]; 5]> {
self.pos_s(t, self.inv())
}
fn pos_iter<I>(&self, iter: I) -> impl Iterator<Item = [[f64; D]; 5]>
where
I: IntoIterator<Item = f64>,
{
let inv = self.inv();
iter.into_iter().filter_map(move |t| self.pos_s(t, inv))
}
fn curves_in(&self, start: f64, end: f64, res: usize) -> Vec<[[f64; D]; 3]> {
self.pos_iter(linspace(start, end, res))
.map(|[.., p3, p4, p5]| [p3, p4, p5])
.collect()
}
fn curve_in(&self, start: f64, end: f64, res: usize) -> Vec<[f64; D]> {
self.pos_iter(linspace(start, end, res))
.map(|[.., p5]| p5)
.collect()
}
fn curves(&self, res: usize) -> Vec<[[f64; D]; 3]> {
self.angle_bound()
.to_value()
.map(|[start, end]| self.curves_in(start, end, res))
.unwrap_or_default()
}
fn curve(&self, res: usize) -> Vec<[f64; D]> {
self.angle_bound()
.to_value()
.map(|[start, end]| self.curve_in(start, end, res))
.unwrap_or_default()
}
fn curve_by(&self, t: &[f64]) -> Vec<[f64; D]> {
self.pos_iter(t.iter().copied())
.map(|[.., p5]| p5)
.collect()
}
}
impl<N, const D: usize> CurveGen<D> for N
where
N: Normalized<D> + Statable + Clone,
N::De: CurveGen<D>,
efd::U<D>: efd::EfdDim<D>,
{
fn pos_s(&self, t: f64, inv: bool) -> Option<[[f64; D]; 5]> {
self.clone().denormalize().pos_s(t, inv)
}
fn pos_iter<I>(&self, iter: I) -> impl Iterator<Item = [[f64; D]; 5]>
where
I: IntoIterator<Item = f64>,
{
let de = self.clone().denormalize();
let inv = de.inv();
iter.into_iter().filter_map(move |t| de.pos_s(t, inv))
}
}
fn linspace(start: f64, end: f64, res: usize) -> impl Iterator<Item = f64> {
use std::f64::consts::TAU;
let end = if end > start { end } else { end + TAU };
let step = (end - start) / res as f64;
(0..res).map(move |n| start + n as f64 * step)
}
pub trait PoseGen<const D: usize>: CurveGen<D> {
fn uvec(&self, pos: &[[f64; D]; 3]) -> [f64; D];
fn uvec_iter<I>(&self, iter: I) -> impl Iterator<Item = ([f64; D], [f64; D])>
where
I: IntoIterator<Item = f64>,
{
self.pos_iter(iter)
.map(|[.., p3, p4, p5]| (p5, self.uvec(&[p3, p4, p5])))
}
fn pose_in(&self, start: f64, end: f64, res: usize) -> (Vec<[f64; D]>, Vec<[f64; D]>) {
self.uvec_iter(linspace(start, end, res)).unzip()
}
fn pose(&self, res: usize) -> (Vec<[f64; D]>, Vec<[f64; D]>) {
self.angle_bound()
.to_value()
.map(|[start, end]| self.pose_in(start, end, res))
.unwrap_or_default()
}
fn pose_by(&self, t: &[f64]) -> (Vec<[f64; D]>, Vec<[f64; D]>) {
self.uvec_iter(t.iter().copied()).unzip()
}
fn pose_zipped_in(&self, start: f64, end: f64, res: usize) -> Vec<([f64; D], [f64; D])> {
self.uvec_iter(linspace(start, end, res)).collect()
}
fn pose_zipped(&self, res: usize) -> Vec<([f64; D], [f64; D])> {
self.angle_bound()
.to_value()
.map(|[start, end]| self.pose_zipped_in(start, end, res))
.unwrap_or_default()
}
fn pose_zipped_by(&self, t: &[f64]) -> Vec<([f64; D], [f64; D])> {
self.uvec_iter(t.iter().copied()).collect()
}
fn pose_from_curves(&self, curves: &[[[f64; D]; 3]]) -> Vec<[f64; D]> {
curves.iter().map(|pos| self.uvec(pos)).collect()
}
fn ext_iter<I>(&self, length: f64, iter: I) -> impl Iterator<Item = ([f64; D], [f64; D])>
where
I: IntoIterator<Item = f64>,
{
self.uvec_iter(iter)
.map(move |(p, v)| (p, std::array::from_fn(|i| p[i] + length * v[i])))
}
fn ext_curve_in(
&self,
length: f64,
start: f64,
end: f64,
res: usize,
) -> (Vec<[f64; D]>, Vec<[f64; D]>) {
self.ext_iter(length, linspace(start, end, res)).unzip()
}
fn ext_curve(&self, length: f64, res: usize) -> (Vec<[f64; D]>, Vec<[f64; D]>) {
self.angle_bound()
.to_value()
.map(|[start, end]| self.ext_curve_in(length, start, end, res))
.unwrap_or_default()
}
fn ext_curve_by(&self, length: f64, t: &[f64]) -> (Vec<[f64; D]>, Vec<[f64; D]>) {
self.ext_iter(length, t.iter().copied()).unzip()
}
}
impl<N, const D: usize> PoseGen<D> for N
where
N: Normalized<D> + Statable + Clone,
N::De: PoseGen<D>,
efd::U<D>: efd::EfdDim<D>,
{
fn uvec(&self, pos: &[[f64; D]; 3]) -> [f64; D] {
self.clone().denormalize().uvec(pos)
}
fn uvec_iter<I>(&self, iter: I) -> impl Iterator<Item = ([f64; D], [f64; D])>
where
I: IntoIterator<Item = f64>,
{
let de = self.clone().denormalize();
let inv = de.inv();
iter.into_iter().filter_map(move |t| {
let [.., p3, p4, p5] = de.pos_s(t, inv)?;
Some((p5, de.uvec(&[p3, p4, p5])))
})
}
}