To animate custom data structures, implement the Animatable trait. Your type must also implement Copy, Default, and the standard arithmetic operator traits: Add, Sub, and Mul<f32>.
Required Methods:
interpolate(&self, target: &Self, t: f32) -> Self: Defines how to transition between two values at time t (0.0 to 1.0).magnitude(&self) -> f32: Returns the magnitude of the value (e.g., the length of a vector).
Best Practices:
- Circular Values (Angles): In
interpolate, ensure you calculate the shortest path between angles to avoid spinning the long way around. - Normalized Values (Colors): Use
.clamp(0.0, 1.0) in your scaling logic to keep values within valid bounds. - Precision: You can override the default epsilon (0.01) using
.with_epsilon().
#[derive(Debug, Copy, Clone, PartialEq, Default)]
struct Position {
x: f32,
y: f32,
}
// 1. Implement standard operators
impl std::ops::Add for Position {
type Output = Self;
fn add(self, other: Self) -> Self { Self { x: self.x + other.x, y: self.y + other.y } }
}
impl std::ops::Sub for Position {
type Output = Self;
fn sub(self, other: Self) -> Self { Self { x: self.x - other.x, y: self.y - other.y } }
}
impl std::ops::Mul<f32> for Position {
type Output = Self;
fn mul(self, factor: f32) -> Self { Self { x: self.x * factor, y: self.y * factor } }
}
// 2. Implement Animatable
impl Animatable for Position {
fn interpolate(&self, target: &Self, t: f32) -> Self {
*self + (*target - *self) * t
}
fn magnitude(&self) -> f32 {
(self.x * self.x + self.y * self.y).sqrt()
}
}