Repository: bevyengine/bevy
File: crates/bevy_math/src/common_traits.rs (Rust)

Code before:
/// The motivating case for this trait is animating UI entities with [`Val`]
/// properties, which, because they are enums, cannot be interpolated in the normal way. This same
/// concept can be extended to other types as well.
///
/// Fallible interpolation can be used for animated transitions, which can be set up to fail
/// gracefully if there's a mismatch of units. For example, the a transition could smoothly
/// go from `Val::Px(10)` to `Val::Px(20)`, but if the user attempts to go from `Val::Px(10)` to
/// `Val::Percent(10)`, the animation player can detect the failure and simply snap to the new
/// value without interpolating.
///
/// An animation clip system can incorporate fallible interpolation to support a broad set of
/// sequenced parameter values. This can include numeric types, which always interpolate,
/// enum types, which may or may not interpolate depending on the units, and non-interpolable
/// types, which always jump immediately to the new value without interpolation. This meaas, for
/// example, that you can have an animation track whose value type is a boolean or a string.
///
/// Interpolation for simple number and coordinate types will always succeed, as will any type
/// that implements [`StableInterpolate`]. Types which have different variants such as
/// [`Val`] and [`Color`] will only fail if the units are different.
/// Note that [`Color`] has its own, non-fallible mixing methods, but those entail
/// automatically converting between different color spaces, and is both expensive and complex.
/// [`TryStableInterpolate`] is more conservative, and doesn't automatically convert between
/// color spaces. This produces a color interpolation that has more predictable performance.
///
/// [`Val::Px`]: https://docs.rs/bevy/latest/bevy/ui/enum.Val.html
/// [`Val::Percent`]: https://docs.rs/bevy/latest/bevy/ui/enum.Val.html
/// [`Val`]: https://docs.rs/bevy/latest/bevy/ui/struct.enum.html
/// [`Color`]: https://docs.rs/bevy/latest/bevy/color/enum.Color.html
pub trait TryStableInterpolate: Clone {
    /// Error produced when the value cannot be interpolated.
    type Error;

    /// Attempt to interpolate the value. This may fail if the two interpolation values have
    /// different units, or if the type is not interpolable.
    fn try_interpolate_stable(&self, other: &Self, t: f32) -> Result<Self, Self::Error>;
}

impl<T: StableInterpolate> TryStableInterpolate for T {
    type Error = Infallible;
    fn try_interpolate_stable(&self, other: &Self, t: f32) -> Result<Self, Self::Error> {
        Ok(self.interpolate_stable(other, t))
    }
}

/// A type that has tangents.
pub trait HasTangent {
    /// The tangent type.
    type Tangent: VectorSpace;
}

/// A value with its derivative.

Review comment (suggestion):
I really appreciate the extensive documentation here, but now that the error type can be customized by the user, I do think it focuses a bit too much on mismatched units.

Here's how I might rewrite this documentation:

```
/// A type with a natural interpolation that provides strong subdivision guarantees, similar to
/// [`StableInterpolation`], but which cannot be guaranteed to interpolate correctly between two
/// arbitrary values.
///
/// The motivating case for this trait is animating UI entities with [`Val`] properties. Because
/// different `Val` instances may be of different incompatible variants, there is no guarantee that
/// we can be interpolate between two `Val`s. The same concept can be extended to other types as
/// well.
///
/// Bevy's default animation system handles erroring interpolations by snapping to the new value
/// without interpolating.
///
/// [`Val`]: https://docs.rs/bevy/latest/bevy/ui/enum.Val.html
```

It's not a perfect rewrite, and could probably use some work, but I hope it helps.

Code after:
/// even though they are the same Rust type.
///
/// Fallible interpolation can be used for animated transitions, which can be set up to fail
/// gracefully if the the values cannot be interpolated. For example, the a transition could smoothly
/// go from `Val::Px(10)` to `Val::Px(20)`, but if the user attempts to go from `Val::Px(10)` to
/// `Val::Percent(10)`, the animation player can detect the failure and simply snap to the new
/// value without interpolating.
///
/// An animation clip system can incorporate fallible interpolation to support a broad set of
/// sequenced parameter values. This can include numeric types, which always interpolate,
/// enum types, which may or may not interpolate depending on the units, and non-interpolable
/// types, which always jump immediately to the new value without interpolation. This meaas, for
/// example, that you can have an animation track whose value type is a boolean or a string.
///
/// Interpolation for simple number and coordinate types will always succeed, as will any type
/// that implements [`StableInterpolate`]. Types which have different variants such as
/// [`Val`] and [`Color`] will only fail if the units are different.
/// Note that [`Color`] has its own, non-fallible mixing methods, but those entail
/// automatically converting between different color spaces, and is both expensive and complex.
/// [`TryStableInterpolate`] is more conservative, and doesn't automatically convert between
/// color spaces. This produces a color interpolation that has more predictable performance.
///
/// [`Val::Px`]: https://docs.rs/bevy/latest/bevy/ui/enum.Val.html#variant.Px
/// [`Val::Percent`]: https://docs.rs/bevy/latest/bevy/ui/enum.Val.html#variant.Percent
/// [`Val`]: https://docs.rs/bevy/latest/bevy/ui/enum.Val.html
/// [`Color`]: https://docs.rs/bevy/latest/bevy/color/enum.Color.html
pub trait TryStableInterpolate: Clone {
    /// Error produced when the value cannot be interpolated.
    type Error;

    /// Attempt to interpolate the value. This may fail if the two interpolation values have
    /// different units, or if the type is not interpolable.
    fn try_interpolate_stable(&self, other: &Self, t: f32) -> Result<Self, Self::Error>;
}

impl<T: StableInterpolate> TryStableInterpolate for T {
    type Error = Infallible;
    fn try_interpolate_stable(&self, other: &Self, t: f32) -> Result<Self, Self::Error> {
        Ok(self.interpolate_stable(other, t))
    }
}

/// A type that has tangents.
pub trait HasTangent {
    /// The tangent type.
    type Tangent: VectorSpace;
}

/// A value with its derivative.
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]