use bevy_math::{
curve::{cores::*, iterable::IterableCurve, *},
vec4, Quat, Vec4, VectorSpace,
};
use bevy_reflect::Reflect;
use either::Either;
use thiserror::Error;
#[derive(Debug, Clone, Reflect)]
pub struct SteppedKeyframeCurve<T> {
core: UnevenCore<T>,
}
impl<T> Curve<T> for SteppedKeyframeCurve<T>
where
T: Clone,
{
#[inline]
fn domain(&self) -> Interval {
self.core.domain()
}
#[inline]
fn sample_clamped(&self, t: f32) -> T {
self.core
.sample_with(t, |x, y, t| if t >= 1.0 { y.clone() } else { x.clone() })
}
#[inline]
fn sample_unchecked(&self, t: f32) -> T {
self.sample_clamped(t)
}
}
impl<T> SteppedKeyframeCurve<T> {
#[inline]
pub fn new(timed_samples: impl IntoIterator<Item = (f32, T)>) -> Result<Self, UnevenCoreError> {
Ok(Self {
core: UnevenCore::new(timed_samples)?,
})
}
}
#[derive(Debug, Clone, Reflect)]
pub struct CubicKeyframeCurve<T> {
core: ChunkedUnevenCore<T>,
}
impl<V> Curve<V> for CubicKeyframeCurve<V>
where
V: VectorSpace<Scalar = f32>,
{
#[inline]
fn domain(&self) -> Interval {
self.core.domain()
}
#[inline]
fn sample_clamped(&self, t: f32) -> V {
match self.core.sample_interp_timed(t) {
InterpolationDatum::Exact((_, v))
| InterpolationDatum::LeftTail((_, v))
| InterpolationDatum::RightTail((_, v)) => v[1],
InterpolationDatum::Between((t0, u), (t1, v), s) => {
cubic_spline_interpolation(u[1], u[2], v[0], v[1], s, t1 - t0)
}
}
}
#[inline]
fn sample_unchecked(&self, t: f32) -> V {
self.sample_clamped(t)
}
}
impl<T> CubicKeyframeCurve<T> {
#[inline]
pub fn new(
times: impl IntoIterator<Item = f32>,
values: impl IntoIterator<Item = T>,
) -> Result<Self, ChunkedUnevenCoreError> {
Ok(Self {
core: ChunkedUnevenCore::new(times, values, 3)?,
})
}
}
#[derive(Debug, Clone, Reflect)]
#[reflect(Clone)]
pub struct CubicRotationCurve {
core: ChunkedUnevenCore<Vec4>,
}
impl Curve<Quat> for CubicRotationCurve {
#[inline]
fn domain(&self) -> Interval {
self.core.domain()
}
#[inline]
fn sample_clamped(&self, t: f32) -> Quat {
let vec = match self.core.sample_interp_timed(t) {
InterpolationDatum::Exact((_, v))
| InterpolationDatum::LeftTail((_, v))
| InterpolationDatum::RightTail((_, v)) => v[1],
InterpolationDatum::Between((t0, u), (t1, v), s) => {
cubic_spline_interpolation(u[1], u[2], v[0], v[1], s, t1 - t0)
}
};
Quat::from_vec4(vec.normalize())
}
#[inline]
fn sample_unchecked(&self, t: f32) -> Quat {
self.sample_clamped(t)
}
}
impl CubicRotationCurve {
pub fn new(
times: impl IntoIterator<Item = f32>,
values: impl IntoIterator<Item = Vec4>,
) -> Result<Self, ChunkedUnevenCoreError> {
Ok(Self {
core: ChunkedUnevenCore::new(times, values, 3)?,
})
}
}
#[derive(Debug, Clone, Reflect)]
pub struct WideLinearKeyframeCurve<T> {
core: ChunkedUnevenCore<T>,
}
impl<T> IterableCurve<T> for WideLinearKeyframeCurve<T>
where
T: VectorSpace<Scalar = f32>,
{
#[inline]
fn domain(&self) -> Interval {
self.core.domain()
}
#[inline]
fn sample_iter_clamped(&self, t: f32) -> impl Iterator<Item = T> {
match self.core.sample_interp(t) {
InterpolationDatum::Exact(v)
| InterpolationDatum::LeftTail(v)
| InterpolationDatum::RightTail(v) => Either::Left(v.iter().copied()),
InterpolationDatum::Between(u, v, s) => {
let interpolated = u.iter().zip(v.iter()).map(move |(x, y)| x.lerp(*y, s));
Either::Right(interpolated)
}
}
}
#[inline]
fn sample_iter_unchecked(&self, t: f32) -> impl Iterator<Item = T> {
self.sample_iter_clamped(t)
}
}
impl<T> WideLinearKeyframeCurve<T> {
#[inline]
pub fn new(
times: impl IntoIterator<Item = f32>,
values: impl IntoIterator<Item = T>,
) -> Result<Self, WideKeyframeCurveError> {
Ok(Self {
core: ChunkedUnevenCore::new_width_inferred(times, values)?,
})
}
}
#[derive(Debug, Clone, Reflect)]
pub struct WideSteppedKeyframeCurve<T> {
core: ChunkedUnevenCore<T>,
}
impl<T> IterableCurve<T> for WideSteppedKeyframeCurve<T>
where
T: Clone,
{
#[inline]
fn domain(&self) -> Interval {
self.core.domain()
}
#[inline]
fn sample_iter_clamped(&self, t: f32) -> impl Iterator<Item = T> {
match self.core.sample_interp(t) {
InterpolationDatum::Exact(v)
| InterpolationDatum::LeftTail(v)
| InterpolationDatum::RightTail(v) => Either::Left(v.iter().cloned()),
InterpolationDatum::Between(u, v, s) => {
let interpolated =
u.iter()
.zip(v.iter())
.map(move |(x, y)| if s >= 1.0 { y.clone() } else { x.clone() });
Either::Right(interpolated)
}
}
}
#[inline]
fn sample_iter_unchecked(&self, t: f32) -> impl Iterator<Item = T> {
self.sample_iter_clamped(t)
}
}
impl<T> WideSteppedKeyframeCurve<T> {
#[inline]
pub fn new(
times: impl IntoIterator<Item = f32>,
values: impl IntoIterator<Item = T>,
) -> Result<Self, WideKeyframeCurveError> {
Ok(Self {
core: ChunkedUnevenCore::new_width_inferred(times, values)?,
})
}
}
#[derive(Debug, Clone, Reflect)]
pub struct WideCubicKeyframeCurve<T> {
core: ChunkedUnevenCore<T>,
}
impl<T> IterableCurve<T> for WideCubicKeyframeCurve<T>
where
T: VectorSpace<Scalar = f32>,
{
#[inline]
fn domain(&self) -> Interval {
self.core.domain()
}
fn sample_iter_clamped(&self, t: f32) -> impl Iterator<Item = T> {
match self.core.sample_interp_timed(t) {
InterpolationDatum::Exact((_, v))
| InterpolationDatum::LeftTail((_, v))
| InterpolationDatum::RightTail((_, v)) => {
let width = self.core.width();
Either::Left(v[width..(width * 2)].iter().copied())
}
InterpolationDatum::Between((t0, u), (t1, v), s) => Either::Right(
cubic_spline_interpolate_slices(self.core.width() / 3, u, v, s, t1 - t0),
),
}
}
#[inline]
fn sample_iter_unchecked(&self, t: f32) -> impl Iterator<Item = T> {
self.sample_iter_clamped(t)
}
}
#[derive(Debug, Error)]
#[error("unable to construct a curve using this data")]
pub enum WideKeyframeCurveError {
#[error("number of values ({values_given}) is not divisible by {divisor}")]
LengthMismatch {
values_given: usize,
divisor: usize,
},
#[error(transparent)]
CoreError(#[from] ChunkedUnevenCoreError),
}
impl<T> WideCubicKeyframeCurve<T> {
#[inline]
pub fn new(
times: impl IntoIterator<Item = f32>,
values: impl IntoIterator<Item = T>,
) -> Result<Self, WideKeyframeCurveError> {
let times: Vec<f32> = times.into_iter().collect();
let values: Vec<T> = values.into_iter().collect();
let divisor = times.len() * 3;
if !values.len().is_multiple_of(divisor) {
return Err(WideKeyframeCurveError::LengthMismatch {
values_given: values.len(),
divisor,
});
}
Ok(Self {
core: ChunkedUnevenCore::new_width_inferred(times, values)?,
})
}
}
#[derive(Debug, Clone, Reflect)]
#[reflect(Clone)]
pub enum WeightsCurve {
Constant(ConstantCurve<Vec<f32>>),
Linear(WideLinearKeyframeCurve<f32>),
Step(WideSteppedKeyframeCurve<f32>),
CubicSpline(WideCubicKeyframeCurve<f32>),
}
fn cubic_spline_interpolation<T>(
value_start: T,
tangent_out_start: T,
tangent_in_end: T,
value_end: T,
lerp: f32,
step_duration: f32,
) -> T
where
T: VectorSpace<Scalar = f32>,
{
let coeffs = (vec4(2.0, 1.0, -2.0, 1.0) * lerp + vec4(-3.0, -2.0, 3.0, -1.0)) * lerp;
value_start * (coeffs.x * lerp + 1.0)
+ tangent_out_start * step_duration * lerp * (coeffs.y + 1.0)
+ value_end * lerp * coeffs.z
+ tangent_in_end * step_duration * lerp * coeffs.w
}
fn cubic_spline_interpolate_slices<'a, T: VectorSpace<Scalar = f32>>(
width: usize,
first: &'a [T],
second: &'a [T],
s: f32,
step_between: f32,
) -> impl Iterator<Item = T> + 'a {
(0..width).map(move |idx| {
cubic_spline_interpolation(
first[idx + width],
first[idx + (width * 2)],
second[idx + width],
second[idx],
s,
step_between,
)
})
}