use super::cores::{EvenCore, EvenCoreError, UnevenCore, UnevenCoreError};
use super::{Curve, Interval};
use crate::StableInterpolate;
use core::any::type_name;
use core::fmt::{self, Debug};
#[cfg(feature = "bevy_reflect")]
use bevy_reflect::{utility::GenericTypePathCell, Reflect, TypePath};
#[cfg(feature = "bevy_reflect")]
mod paths {
pub(super) const THIS_MODULE: &str = "bevy_math::curve::sample_curves";
pub(super) const THIS_CRATE: &str = "bevy_math";
}
#[derive(Clone)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(where T: TypePath),
reflect(from_reflect = false, type_path = false),
)]
pub struct SampleCurve<T, I> {
pub(crate) core: EvenCore<T>,
#[cfg_attr(feature = "bevy_reflect", reflect(ignore))]
pub(crate) interpolation: I,
}
impl<T, I> Debug for SampleCurve<T, I>
where
EvenCore<T>: Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SampleCurve")
.field("core", &self.core)
.field("interpolation", &type_name::<I>())
.finish()
}
}
#[cfg(feature = "bevy_reflect")]
impl<T, I> TypePath for SampleCurve<T, I>
where
T: TypePath,
I: 'static,
{
fn type_path() -> &'static str {
static CELL: GenericTypePathCell = GenericTypePathCell::new();
CELL.get_or_insert::<Self, _>(|| {
format!(
"{}::SampleCurve<{},{}>",
paths::THIS_MODULE,
T::type_path(),
type_name::<I>()
)
})
}
fn short_type_path() -> &'static str {
static CELL: GenericTypePathCell = GenericTypePathCell::new();
CELL.get_or_insert::<Self, _>(|| {
format!("SampleCurve<{},{}>", T::type_path(), type_name::<I>())
})
}
fn type_ident() -> Option<&'static str> {
Some("SampleCurve")
}
fn crate_name() -> Option<&'static str> {
Some(paths::THIS_CRATE)
}
fn module_path() -> Option<&'static str> {
Some(paths::THIS_MODULE)
}
}
impl<T, I> Curve<T> for SampleCurve<T, I>
where
T: Clone,
I: Fn(&T, &T, f32) -> T,
{
#[inline]
fn domain(&self) -> Interval {
self.core.domain()
}
#[inline]
fn sample_clamped(&self, t: f32) -> T {
self.core.sample_with(t, &self.interpolation)
}
#[inline]
fn sample_unchecked(&self, t: f32) -> T {
self.sample_clamped(t)
}
}
impl<T, I> SampleCurve<T, I> {
pub fn new(
domain: Interval,
samples: impl IntoIterator<Item = T>,
interpolation: I,
) -> Result<Self, EvenCoreError>
where
I: Fn(&T, &T, f32) -> T,
{
Ok(Self {
core: EvenCore::new(domain, samples)?,
interpolation,
})
}
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
pub struct SampleAutoCurve<T> {
pub(crate) core: EvenCore<T>,
}
impl<T> Curve<T> for SampleAutoCurve<T>
where
T: StableInterpolate,
{
#[inline]
fn domain(&self) -> Interval {
self.core.domain()
}
#[inline]
fn sample_clamped(&self, t: f32) -> T {
self.core
.sample_with(t, <T as StableInterpolate>::interpolate_stable)
}
#[inline]
fn sample_unchecked(&self, t: f32) -> T {
self.sample_clamped(t)
}
}
impl<T> SampleAutoCurve<T> {
pub fn new(
domain: Interval,
samples: impl IntoIterator<Item = T>,
) -> Result<Self, EvenCoreError> {
Ok(Self {
core: EvenCore::new(domain, samples)?,
})
}
}
#[derive(Clone)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(where T: TypePath),
reflect(from_reflect = false, type_path = false),
)]
pub struct UnevenSampleCurve<T, I> {
pub(crate) core: UnevenCore<T>,
#[cfg_attr(feature = "bevy_reflect", reflect(ignore))]
pub(crate) interpolation: I,
}
impl<T, I> Debug for UnevenSampleCurve<T, I>
where
UnevenCore<T>: Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SampleCurve")
.field("core", &self.core)
.field("interpolation", &type_name::<I>())
.finish()
}
}
#[cfg(feature = "bevy_reflect")]
impl<T, I> TypePath for UnevenSampleCurve<T, I>
where
T: TypePath,
I: 'static,
{
fn type_path() -> &'static str {
static CELL: GenericTypePathCell = GenericTypePathCell::new();
CELL.get_or_insert::<Self, _>(|| {
format!(
"{}::UnevenSampleCurve<{},{}>",
paths::THIS_MODULE,
T::type_path(),
type_name::<I>()
)
})
}
fn short_type_path() -> &'static str {
static CELL: GenericTypePathCell = GenericTypePathCell::new();
CELL.get_or_insert::<Self, _>(|| {
format!("UnevenSampleCurve<{},{}>", T::type_path(), type_name::<I>())
})
}
fn type_ident() -> Option<&'static str> {
Some("UnevenSampleCurve")
}
fn crate_name() -> Option<&'static str> {
Some(paths::THIS_CRATE)
}
fn module_path() -> Option<&'static str> {
Some(paths::THIS_MODULE)
}
}
impl<T, I> Curve<T> for UnevenSampleCurve<T, I>
where
T: Clone,
I: Fn(&T, &T, f32) -> T,
{
#[inline]
fn domain(&self) -> Interval {
self.core.domain()
}
#[inline]
fn sample_clamped(&self, t: f32) -> T {
self.core.sample_with(t, &self.interpolation)
}
#[inline]
fn sample_unchecked(&self, t: f32) -> T {
self.sample_clamped(t)
}
}
impl<T, I> UnevenSampleCurve<T, I> {
pub fn new(
timed_samples: impl IntoIterator<Item = (f32, T)>,
interpolation: I,
) -> Result<Self, UnevenCoreError> {
Ok(Self {
core: UnevenCore::new(timed_samples)?,
interpolation,
})
}
pub fn map_sample_times(self, f: impl Fn(f32) -> f32) -> UnevenSampleCurve<T, I> {
Self {
core: self.core.map_sample_times(f),
interpolation: self.interpolation,
}
}
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
pub struct UnevenSampleAutoCurve<T> {
pub(crate) core: UnevenCore<T>,
}
impl<T> Curve<T> for UnevenSampleAutoCurve<T>
where
T: StableInterpolate,
{
#[inline]
fn domain(&self) -> Interval {
self.core.domain()
}
#[inline]
fn sample_clamped(&self, t: f32) -> T {
self.core
.sample_with(t, <T as StableInterpolate>::interpolate_stable)
}
#[inline]
fn sample_unchecked(&self, t: f32) -> T {
self.sample_clamped(t)
}
}
impl<T> UnevenSampleAutoCurve<T> {
pub fn new(timed_samples: impl IntoIterator<Item = (f32, T)>) -> Result<Self, UnevenCoreError> {
Ok(Self {
core: UnevenCore::new(timed_samples)?,
})
}
pub fn map_sample_times(self, f: impl Fn(f32) -> f32) -> UnevenSampleAutoCurve<T> {
Self {
core: self.core.map_sample_times(f),
}
}
}
#[cfg(test)]
mod tests {
use super::{SampleCurve, UnevenSampleCurve};
use crate::{curve::Interval, VectorSpace};
use bevy_reflect::Reflect;
#[test]
fn reflect_sample_curve() {
fn foo(x: &f32, y: &f32, t: f32) -> f32 {
x.lerp(*y, t)
}
let bar = |x: &f32, y: &f32, t: f32| x.lerp(*y, t);
let baz: fn(&f32, &f32, f32) -> f32 = bar;
let samples = [0.0, 1.0, 2.0];
let _: Box<dyn Reflect> = Box::new(SampleCurve::new(Interval::UNIT, samples, foo).unwrap());
let _: Box<dyn Reflect> = Box::new(SampleCurve::new(Interval::UNIT, samples, bar).unwrap());
let _: Box<dyn Reflect> = Box::new(SampleCurve::new(Interval::UNIT, samples, baz).unwrap());
}
#[test]
fn reflect_uneven_sample_curve() {
fn foo(x: &f32, y: &f32, t: f32) -> f32 {
x.lerp(*y, t)
}
let bar = |x: &f32, y: &f32, t: f32| x.lerp(*y, t);
let baz: fn(&f32, &f32, f32) -> f32 = bar;
let keyframes = [(0.0, 1.0), (1.0, 0.0), (2.0, -1.0)];
let _: Box<dyn Reflect> = Box::new(UnevenSampleCurve::new(keyframes, foo).unwrap());
let _: Box<dyn Reflect> = Box::new(UnevenSampleCurve::new(keyframes, bar).unwrap());
let _: Box<dyn Reflect> = Box::new(UnevenSampleCurve::new(keyframes, baz).unwrap());
}
}