-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Implement TryStableInterpolate
.
#21633
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
base: main
Are you sure you want to change the base?
Conversation
@LikeLakers2 Tried to add you as a reviewer but could not for some reason. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No worries. I hope I didn't frustrate you too much in the issue you made!
impl<T: StableInterpolate> TryStableInterpolate for T { | ||
fn try_interpolate_stable(&self, other: &Self, t: f32) -> Result<Self, InterpolationError> { | ||
Ok(self.interpolate_stable(other, t)) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Huh, so this ended up working? I'm curious what was different about how you initially tested it, that caused the error you were reporting.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it has to do with the fact that the impls are in the same module as the target types instead of being in the same module as the trait definition. But to be honest, I'm not really sure why it works.
pub trait TryStableInterpolate: Clone { | ||
/// 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, InterpolationError>; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Me thinks it's best to let the user specify an error type, since interpolation can fail for reasons specific to the type being interpolated.
pub trait TryStableInterpolate: Clone { | |
/// 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, InterpolationError>; | |
} | |
pub trait TryStableInterpolate: Clone { | |
/// The type returned in the event a stable interpolation cannot be performed. | |
type Error; | |
/// Attempt to interpolate the value. | |
fn try_interpolate_stable(&self, other: &Self, t: f32) -> Result<Self, Self::Error>; | |
} |
This is similar to what TryFrom
does, since conversion can fail for a variety of reasons, usually reasons specific to the types being converted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Making the error type depend on the value type would defeat one of my purposes: to have a single uniform animation clip system that can mix both interpolable and non-interpolable types. Basically something like AnimatableProperty
which can work for Val
.
To be perfectly honest, we don't actually need an error at all, we could instead just make it an Option
and return None
. We never actually look at the error code, it's only there for documentation purposes and because the try_
name prefix suggest a Result
return type. The animation only cares that the interpolation succeeded, it's not like it actually logs an error or anything.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you elaborate? I'm not sure how an error type that can be specified by each impl would defeat that purpose.
Also, I would recommend keeping it as a Result
, even if our error type is ()
. This is because Option
implies that trying will always succeed but may sometimes return nothing, whereas Result
tells the user that the operation may fail.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess that it could work, since the error value is ignored, but it seems a bit dodgy. Here's the way the animation code looks now:
match self.start.try_interpolate(&self.end, t) {
Ok(value) => value,
Err(_) => self.end, // If we can't interpolate, then just skip to the end
}
Where start
and end
are generics of type TransitionProperty::ValueType
:
/// Represents an animatable property such as `BackgroundColor` or `Width`.
pub trait TransitionProperty {
/// The data type of the animated property.
type ValueType: Copy + Send + Sync + PartialEq + 'static + TryStableInterpolate;
/// The type of component that contains the animated property.
type ComponentType: Component<Mutability = Mutable>;
/// Read the value of the animatable property from the component.
fn get(component: &Self::ComponentType) -> Self::ValueType;
/// Update the value of the animatable property in the component.
fn set(component: &mut Mut<Self::ComponentType>, value: Self::ValueType);
}
Adding an extra generic parameter for error type would mean that I wouldn't be able to treat different value types quite as uniformly as before.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding an extra generic parameter for error type would mean that I wouldn't be able to treat different value types quite as uniformly as before.
I'm not sure what's meant by this. Can you give me an example of what is meant here?
/// Boolean values can never be interpolated, but they can be animatable parameters (for things | ||
/// like enabling and disabling lighting). | ||
impl TryStableInterpolate for bool { | ||
fn try_interpolate_stable(&self, _other: &Self, _t: f32) -> Result<Self, InterpolationError> { | ||
Err(InterpolationError::NonContiguous) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this will always return an error, then I don't think it's an interpolate-able type.
/// Boolean values can never be interpolated, but they can be animatable parameters (for things | |
/// like enabling and disabling lighting). | |
impl TryStableInterpolate for bool { | |
fn try_interpolate_stable(&self, _other: &Self, _t: f32) -> Result<Self, InterpolationError> { | |
Err(InterpolationError::NonContiguous) | |
} | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding this was a bit of a social experiment to see what other people thought.
The use case here is to be able to have an AnimatableProperty
that is a boolean rather than a continuous value - so for example you could use this to enable or disable an entity or component as part of an animation clip.
Fixes: #20579
Testing
Note: Because docs.rs was down, I could not validate the doc link urls.