use crate::*;
use alloc::vec::Vec;
use core::{array, iter::zip};
#[cfg(not(feature = "std"))]
#[allow(unused_imports)]
use num_traits::*;
pub type PosedEfd1 = PosedEfd<1>;
pub type PosedEfd2 = PosedEfd<2>;
pub type PosedEfd3 = PosedEfd<3>;
pub fn ang2vec(angles: &[f64]) -> Vec<[f64; 2]> {
angles.iter().map(|a| [a.cos(), a.sin()]).collect()
}
pub fn guide_from_curve<C, V, const D: usize>(curve: C, vectors: V, length: f64) -> Vec<[f64; D]>
where
C: Curve<D>,
V: Curve<D>,
{
zip(curve.as_curve(), vectors.as_curve())
.map(|(p, v)| core::array::from_fn(|i| p[i] + length * v[i]))
.collect()
}
#[derive(Clone)]
pub struct MotionSig<const D: usize>
where
U<D>: EfdDim<D>,
{
pub curve: Vec<[f64; D]>,
pub vectors: Vec<[f64; D]>,
pub t: Vec<f64>,
pub geo: GeoVar<Rot<D>, D>,
}
impl<const D: usize> MotionSig<D>
where
U<D>: EfdDim<D>,
{
pub fn new<C, V>(curve: C, vectors: V, is_open: bool) -> Self
where
C: Curve<D>,
V: Curve<D>,
{
let PathSig { curve, t, geo } = PathSig::new(curve.as_curve(), is_open);
let mut vectors = geo.inverse().only_rot().transform(vectors);
for (p, v) in zip(&curve, &mut vectors) {
*v = array::from_fn(|i| p[i] + v[i]);
}
Self { curve, vectors, t, geo }
}
pub fn as_t(&self) -> &[f64] {
&self.t
}
pub fn as_geo(&self) -> &GeoVar<Rot<D>, D> {
&self.geo
}
}
#[derive(Clone)]
pub struct PosedEfd<const D: usize>
where
U<D>: EfdDim<D>,
{
curve: Efd<D>,
pose: Efd<D>,
}
impl PosedEfd2 {
pub fn from_angles<C>(curve: C, angles: &[f64], is_open: bool) -> Self
where
C: Curve<2>,
{
let harmonic = harmonic(false, curve.len());
Self::from_angles_harmonic(curve, angles, is_open, harmonic).fourier_power_anaysis(None)
}
pub fn from_angles_harmonic<C>(curve: C, angles: &[f64], is_open: bool, harmonic: usize) -> Self
where
C: Curve<2>,
{
Self::from_uvec_harmonic(curve, ang2vec(angles), is_open, harmonic)
}
}
impl<const D: usize> PosedEfd<D>
where
U<D>: EfdDim<D>,
{
pub const fn from_parts_unchecked(curve: Efd<D>, pose: Efd<D>) -> Self {
Self { curve, pose }
}
pub fn from_series<C1, C2>(curve_p: C1, curve_q: C2, is_open: bool) -> Self
where
C1: Curve<D>,
C2: Curve<D>,
{
let harmonic = harmonic(true, curve_p.len());
Self::from_series_harmonic(curve_p, curve_q, is_open, harmonic).fourier_power_anaysis(None)
}
pub fn from_series_harmonic<C1, C2>(
curve_p: C1,
curve_q: C2,
is_open: bool,
harmonic: usize,
) -> Self
where
C1: Curve<D>,
C2: Curve<D>,
{
let vectors = zip(curve_p.as_curve(), curve_q.as_curve())
.map(|(p, q)| array::from_fn(|i| q[i] - p[i]))
.collect::<Vec<_>>();
Self::from_uvec_harmonic(curve_p, vectors, is_open, harmonic)
}
pub fn from_uvec<C, V>(curve: C, vectors: V, is_open: bool) -> Self
where
C: Curve<D>,
V: Curve<D>,
{
let harmonic = harmonic(true, curve.len());
Self::from_uvec_harmonic(curve, vectors, is_open, harmonic).fourier_power_anaysis(None)
}
pub fn from_uvec_harmonic<C, V>(curve: C, vectors: V, is_open: bool, harmonic: usize) -> Self
where
C: Curve<D>,
V: Curve<D>,
{
debug_assert!(harmonic != 0, "harmonic must not be 0");
debug_assert!(curve.len() > 2, "the curve length must greater than 2");
debug_assert!(
curve.len() == vectors.len(),
"the curve length must be equal to the vectors length"
);
let curve = curve.as_curve();
let guide = {
let dxyz = util::diff(if !is_open && curve.first() != curve.last() {
to_mat(curve.closed_lin())
} else {
to_mat(curve)
});
dxyz.map(util::pow2).row_sum().map(f64::sqrt)
};
let (_, mut coeffs, geo) = U::get_coeff(curve, is_open, harmonic, Some(guide.as_slice()));
let geo = geo * U::norm_coeff(&mut coeffs, None);
let geo_inv = geo.inverse();
let p_norm = geo_inv.transform(curve);
let mut q_norm = geo_inv.only_rot().transform(vectors);
for (p, q) in zip(p_norm, &mut q_norm) {
*q = array::from_fn(|i| p[i] + q[i]);
}
let curve = Efd::from_parts_unchecked(coeffs, geo);
let (_, mut coeffs, q_trans) =
U::get_coeff(&q_norm, is_open, harmonic, Some(guide.as_slice()));
U::norm_zeta(&mut coeffs, None);
let pose = Efd::from_parts_unchecked(coeffs, q_trans);
Self { curve, pose }
}
pub fn fourier_power_anaysis(mut self, threshold: impl Into<Option<f64>>) -> Self {
self.fpa_inplace(threshold);
self
}
pub fn fpa_inplace(&mut self, threshold: impl Into<Option<f64>>) {
let threshold = threshold.into();
let [harmonic1, harmonic2] = [&self.curve, &self.pose].map(|efd| {
let lut = efd.coeffs_iter().map(|m| m.map(util::pow2).sum()).collect();
fourier_power_anaysis(lut, threshold)
});
self.set_harmonic(harmonic1.max(harmonic2));
}
pub fn set_harmonic(&mut self, harmonic: usize) {
self.curve.set_harmonic(harmonic);
self.pose.set_harmonic(harmonic);
}
pub fn into_inner(self) -> (Efd<D>, Efd<D>) {
(self.curve, self.pose)
}
pub fn as_curve(&self) -> &Efd<D> {
&self.curve
}
pub fn as_pose(&self) -> &Efd<D> {
&self.pose
}
pub fn is_open(&self) -> bool {
self.curve.is_open()
}
#[inline]
pub fn harmonic(&self) -> usize {
self.curve.harmonic()
}
pub fn err(&self, rhs: &Self) -> f64 {
(2. * self.curve.err(&rhs.curve))
.max(self.pose.err(&rhs.pose))
.max((self.pose.as_geo().trans()).l2_err(rhs.pose.as_geo().trans()))
}
pub fn err_sig(&self, sig: &MotionSig<D>) -> f64 {
let curve =
zip(self.curve.recon_norm_by(&sig.t), &sig.curve).map(|(a, b)| 2. * a.l2_err(b));
let pose = zip(self.pose.recon_by(&sig.t), &sig.vectors).map(|(a, b)| a.l2_err(b));
curve.chain(pose).fold(0., f64::max)
}
}