Skip to content

Fallible interpolation #20579

@viridia

Description

@viridia

What problem does this solve or what need does it fill?

The StableInterpolate trait is very useful! Unfortunately, there are some data types such as Val and Color which may or may not be possible to interpolate depending on whether the two input values have the same enum variant. In practice, these will almost always be the same variant, but they might not be, in which case it should fail gracefully.

What solution would you like?

I propose a new trait, FallibleInterpolate, which can fail:

/// A trait that indicates that a value may be interpolable via [`StableInterpolate`]. An
/// interpolation may fail if the values have different units - for example, attempting to
/// interpolate between `Val::Px` and `Val::Percent` will fail with a `MismatchedUnits` error,
/// even though they are the same Rust type.
pub trait FallibleInterpolate: Clone {
    /// Attempt to interpolate the value. This may fail if the two interpolation values have
    /// different units.
    fn try_interpolate(&self, other: &Self, t: f32) -> Result<Self, InterpolationError>;
}

Code that uses this trait can take appropriate action on failure. For example, an animated transition attempting to interpolate between a start and end point can simply jump directly to the end without interpolation.

The same macro which implements StableInterpolate for all of the common types can be extend to implement FallibileInterpolate without the possibility of failure. For something like Val we can implement the trait thusly:

impl FallibleInterpolate for Val {
    fn try_interpolate(&self, other: &Self, t: f32) -> Result<Self, InterpolationError> {
        match (self, other) {
            (Val::Px(a), Val::Px(b)) => Ok(Val::Px(a.interpolate_stable(b, t))),
            (Val::Percent(a), Val::Percent(b)) => Ok(Val::Percent(a.interpolate_stable(b, t))),
            (Val::Vw(a), Val::Vw(b)) => Ok(Val::Vw(a.interpolate_stable(b, t))),
            (Val::Vh(a), Val::Vh(b)) => Ok(Val::Vh(a.interpolate_stable(b, t))),
            (Val::VMin(a), Val::VMin(b)) => Ok(Val::VMin(a.interpolate_stable(b, t))),
            (Val::VMax(a), Val::VMax(b)) => Ok(Val::VMax(a.interpolate_stable(b, t))),
            (Val::Auto, Val::Auto) => Ok(Val::Auto),
            _ => Err(InterpolationError::MismatchedUnits),
        }
    }
}

What alternative(s) have you considered?

Previously I wrote code that hard-coded the Val variant choice into the generic type of the transition, but this is better.

Additional context

This is part of the work on animated transitions.

@alice-i-cecile @NthTensor

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-MathFundamental domain-agnostic mathematical operationsC-FeatureA new feature, making something new possibleD-StraightforwardSimple bug fixes and API improvements, docs, test and examplesS-Ready-For-ImplementationThis issue is ready for an implementation PR. Go for it!X-UncontroversialThis work is generally agreed upon

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions