Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make SampleCurve/UnevenSampleCurve succeed at reflection #15493

Merged
merged 8 commits into from
Sep 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
203 changes: 7 additions & 196 deletions crates/bevy_math/src/curve/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,19 @@
pub mod adaptors;
pub mod cores;
pub mod interval;
pub mod sample_curves;

use adaptors::*;
// bevy_math::curve re-exports all commonly-needed curve-related items.
pub use interval::{interval, Interval};
use itertools::Itertools;
pub use sample_curves::*;

use adaptors::*;
use cores::{EvenCore, UnevenCore};

use crate::{StableInterpolate, VectorSpace};
use core::{marker::PhantomData, ops::Deref};
use cores::{EvenCore, EvenCoreError, UnevenCore, UnevenCoreError};
use interval::InvalidIntervalError;
use itertools::Itertools;
use thiserror::Error;

/// A trait for a type that can represent values of type `T` parametrized over a fixed interval.
Expand Down Expand Up @@ -936,199 +940,6 @@ where
}
}

/// A curve that is defined by explicit neighbor interpolation over a set of samples.
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
pub struct SampleCurve<T, I> {
core: EvenCore<T>,
interpolation: I,
}

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_unchecked(&self, t: f32) -> T {
self.core.sample_with(t, &self.interpolation)
}
}

impl<T, I> SampleCurve<T, I> {
/// Create a new [`SampleCurve`] using the specified `interpolation` to interpolate between
/// the given `samples`. An error is returned if there are not at least 2 samples or if the
/// given `domain` is unbounded.
///
/// The interpolation takes two values by reference together with a scalar parameter and
/// produces an owned value. The expectation is that `interpolation(&x, &y, 0.0)` and
/// `interpolation(&x, &y, 1.0)` are equivalent to `x` and `y` respectively.
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,
})
}
}

/// A curve that is defined by neighbor interpolation over a set of samples.
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
pub struct SampleAutoCurve<T> {
core: EvenCore<T>,
}

impl<T> Curve<T> for SampleAutoCurve<T>
where
T: StableInterpolate,
{
#[inline]
fn domain(&self) -> Interval {
self.core.domain()
}

#[inline]
fn sample_unchecked(&self, t: f32) -> T {
self.core
.sample_with(t, <T as StableInterpolate>::interpolate_stable)
}
}

impl<T> SampleAutoCurve<T> {
/// Create a new [`SampleCurve`] using type-inferred interpolation to interpolate between
/// the given `samples`. An error is returned if there are not at least 2 samples or if the
/// given `domain` is unbounded.
pub fn new(
domain: Interval,
samples: impl IntoIterator<Item = T>,
) -> Result<Self, EvenCoreError> {
Ok(Self {
core: EvenCore::new(domain, samples)?,
})
}
}

/// A curve that is defined by interpolation over unevenly spaced samples with explicit
/// interpolation.
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
pub struct UnevenSampleCurve<T, I> {
core: UnevenCore<T>,
interpolation: I,
}

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_unchecked(&self, t: f32) -> T {
self.core.sample_with(t, &self.interpolation)
}
}

impl<T, I> UnevenSampleCurve<T, I> {
/// Create a new [`UnevenSampleCurve`] using the provided `interpolation` to interpolate
/// between adjacent `timed_samples`. The given samples are filtered to finite times and
/// sorted internally; if there are not at least 2 valid timed samples, an error will be
/// returned.
///
/// The interpolation takes two values by reference together with a scalar parameter and
/// produces an owned value. The expectation is that `interpolation(&x, &y, 0.0)` and
/// `interpolation(&x, &y, 1.0)` are equivalent to `x` and `y` respectively.
pub fn new(
timed_samples: impl IntoIterator<Item = (f32, T)>,
interpolation: I,
) -> Result<Self, UnevenCoreError> {
Ok(Self {
core: UnevenCore::new(timed_samples)?,
interpolation,
})
}

/// This [`UnevenSampleAutoCurve`], but with the sample times moved by the map `f`.
/// In principle, when `f` is monotone, this is equivalent to [`Curve::reparametrize`],
/// but the function inputs to each are inverses of one another.
///
/// The samples are re-sorted by time after mapping and deduplicated by output time, so
/// the function `f` should generally be injective over the sample times of the curve.
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,
}
}
}

/// A curve that is defined by interpolation over unevenly spaced samples.
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
pub struct UnevenSampleAutoCurve<T> {
core: UnevenCore<T>,
}

impl<T> Curve<T> for UnevenSampleAutoCurve<T>
where
T: StableInterpolate,
{
#[inline]
fn domain(&self) -> Interval {
self.core.domain()
}

#[inline]
fn sample_unchecked(&self, t: f32) -> T {
self.core
.sample_with(t, <T as StableInterpolate>::interpolate_stable)
}
}

impl<T> UnevenSampleAutoCurve<T> {
/// Create a new [`UnevenSampleAutoCurve`] from a given set of timed samples, interpolated
/// using the The samples are filtered to finite times and
/// sorted internally; if there are not at least 2 valid timed samples, an error will be
/// returned.
pub fn new(timed_samples: impl IntoIterator<Item = (f32, T)>) -> Result<Self, UnevenCoreError> {
Ok(Self {
core: UnevenCore::new(timed_samples)?,
})
}

/// This [`UnevenSampleAutoCurve`], but with the sample times moved by the map `f`.
/// In principle, when `f` is monotone, this is equivalent to [`Curve::reparametrize`],
/// but the function inputs to each are inverses of one another.
///
/// The samples are re-sorted by time after mapping and deduplicated by output time, so
/// the function `f` should generally be injective over the sample times of the curve.
pub fn map_sample_times(self, f: impl Fn(f32) -> f32) -> UnevenSampleAutoCurve<T> {
Self {
core: self.core.map_sample_times(f),
}
}
}

/// Create a [`Curve`] that constantly takes the given `value` over the given `domain`.
pub fn constant_curve<T: Clone>(domain: Interval, value: T) -> ConstantCurve<T> {
ConstantCurve { domain, value }
Expand Down
Loading
Loading