pub trait StableInterpolate: Clone {
// Required method
fn interpolate_stable(&self, other: &Self, t: f32) -> Self;
// Provided methods
fn interpolate_stable_assign(&mut self, other: &Self, t: f32) { ... }
fn smooth_nudge(&mut self, target: &Self, decay_rate: f32, delta: f32) { ... }
}Expand description
A type with a natural interpolation that provides strong subdivision guarantees.
Although the only required method is interpolate_stable, many things are expected of it:
-
The notion of interpolation should follow naturally from the semantics of the type, so that inferring the interpolation mode from the type alone is sensible.
-
The interpolation recovers something equivalent to the starting value at
t = 0.0and likewise with the ending value att = 1.0. They do not have to be data-identical, but they should be semantically identical. For example,Quat::slerpdoesn’t always yield its second rotation input exactly att = 1.0, but it always returns an equivalent rotation. -
Importantly, the interpolation must be subdivision-stable: for any interpolation curve between two (unnamed) values and any parameter-value pairs
(t0, p)and(t1, q), the interpolation curve betweenpandqmust be the linear reparameterization of the original interpolation curve restricted to the interval[t0, t1].
The last of these conditions is very strong and indicates something like constant speed. It is called “subdivision stability” because it guarantees that breaking up the interpolation into segments and joining them back together has no effect.
Here is a diagram depicting it:
top curve = u.interpolate_stable(v, t)
t0 => p t1 => q
|-------------|---------|-------------|
0 => u / \ 1 => v
/ \
/ \
/ linear \
/ reparameterization \
/ t = t0 * (1 - s) + t1 * s \
/ \
|-------------------------------------|
0 => p 1 => q
bottom curve = p.interpolate_stable(q, s)Note that some common forms of interpolation do not satisfy this criterion. For example,
Quat::lerp and Rot2::nlerp are not subdivision-stable.
Furthermore, this is not to be used as a general trait for abstract interpolation. Consumers rely on the strong guarantees in order for behavior based on this trait to be well-behaved.
Required Methods§
Sourcefn interpolate_stable(&self, other: &Self, t: f32) -> Self
fn interpolate_stable(&self, other: &Self, t: f32) -> Self
Interpolate between this value and the other given value using the parameter t. At
t = 0.0, a value equivalent to self is recovered, while t = 1.0 recovers a value
equivalent to other, with intermediate values interpolating between the two.
See the trait-level documentation for details.
Provided Methods§
Sourcefn interpolate_stable_assign(&mut self, other: &Self, t: f32)
fn interpolate_stable_assign(&mut self, other: &Self, t: f32)
A version of interpolate_stable that assigns the result to self for convenience.
A manual implementation can be provided to optimize in-place mutation, such as avoiding memory allocations.
Sourcefn smooth_nudge(&mut self, target: &Self, decay_rate: f32, delta: f32)
fn smooth_nudge(&mut self, target: &Self, decay_rate: f32, delta: f32)
Smoothly nudge this value towards the target at a given decay rate. The decay_rate
parameter controls how fast the distance between self and target decays relative to
the units of delta; the intended usage is for decay_rate to generally remain fixed,
while delta is something like delta_time from an updating system. This produces a
smooth following of the target that is independent of framerate.
More specifically, when this is called repeatedly, the result is that the distance between
self and a fixed target attenuates exponentially, with the rate of this exponential
decay given by decay_rate.
For example, at decay_rate = 0.0, this has no effect.
At decay_rate = f32::INFINITY, self immediately snaps to target.
In general, higher rates mean that self moves more quickly towards target.
§Example
let mut object_position: Vec3 = Vec3::ZERO;
let target_position: Vec3 = Vec3::new(2.0, 3.0, 5.0);
// Decay rate of ln(10) => after 1 second, remaining distance is 1/10th
let decay_rate = f32::ln(10.0);
// Calling this repeatedly will move `object_position` towards `target_position`:
object_position.smooth_nudge(&target_position, decay_rate, delta_time);Examples found in repository?
140fn rotate_ship(ship: Single<(&mut Ship, &mut Transform)>, time: Res<Time>) {
141 let (mut ship, mut ship_transform) = ship.into_inner();
142
143 if !ship.in_motion {
144 return;
145 }
146
147 let target_rotation = ship.target_transform.rotation;
148
149 ship_transform
150 .rotation
151 .smooth_nudge(&target_rotation, 3.0, time.delta_secs());
152
153 if ship_transform.rotation.angle_between(target_rotation) <= f32::EPSILON {
154 ship.in_motion = false;
155 }
156}More examples
119fn move_follower(
120 mut following: Single<&mut Transform, With<FollowingSphere>>,
121 target: Single<&Transform, (With<TargetSphere>, Without<FollowingSphere>)>,
122 decay_rate: Res<DecayRate>,
123 time: Res<Time>,
124) {
125 let decay_rate = decay_rate.0;
126 let delta_time = time.delta_secs();
127
128 // Calling `smooth_nudge` is what moves the following sphere smoothly toward the target.
129 following
130 .translation
131 .smooth_nudge(&target.translation, decay_rate, delta_time);
132}68fn update_camera(
69 mut camera: Single<&mut Transform, (With<Camera2d>, Without<Player>)>,
70 player: Single<&Transform, (With<Player>, Without<Camera2d>)>,
71 time: Res<Time>,
72) {
73 let Vec3 { x, y, .. } = player.translation;
74 let direction = Vec3::new(x, y, camera.translation.z);
75
76 // Applies a smooth effect to camera movement using stable interpolation
77 // between the camera position and the player position on the x and y axes.
78 camera
79 .translation
80 .smooth_nudge(&direction, CAMERA_DECAY_RATE, time.delta_secs());
81}194fn simulate_particles(
195 mut commands: Commands,
196 mut query: Query<(Entity, &mut Transform, &mut Particle)>,
197 time: Res<Time>,
198) {
199 for (entity, mut transform, mut particle) in &mut query {
200 if particle.lifetime_timer.tick(time.delta()).just_finished() {
201 commands.entity(entity).despawn();
202 return;
203 }
204
205 transform.translation += particle.velocity * time.delta_secs();
206 transform.scale = Vec3::splat(particle.size.lerp(0.0, particle.lifetime_timer.fraction()));
207 particle
208 .velocity
209 .smooth_nudge(&Vec3::ZERO, 4.0, time.delta_secs());
210 }
211}283fn simulate_particles(
284 mut commands: Commands,
285 mut query: Query<(Entity, &mut Transform, &mut Particle)>,
286 time: Res<Time>,
287) {
288 for (entity, mut transform, mut particle) in &mut query {
289 if particle.lifetime_timer.tick(time.delta()).just_finished() {
290 commands.entity(entity).despawn();
291 return;
292 }
293
294 transform.translation += particle.velocity * time.delta_secs();
295 transform.scale = Vec3::splat(particle.size.lerp(0.0, particle.lifetime_timer.fraction()));
296 particle
297 .velocity
298 .smooth_nudge(&Vec3::ZERO, 4.0, time.delta_secs());
299 }
300}Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".
Implementations on Foreign Types§
Source§impl<T, const LEN: usize> StableInterpolate for [T; LEN]where
T: StableInterpolate,
impl<T, const LEN: usize> StableInterpolate for [T; LEN]where
T: StableInterpolate,
Source§impl<T> StableInterpolate for (T₁, T₂, …, Tₙ)where
T: StableInterpolate,
This trait is implemented for tuples down to 1 up to 11 items long.
impl<T> StableInterpolate for (T₁, T₂, …, Tₙ)where
T: StableInterpolate,
This trait is implemented for tuples down to 1 up to 11 items long.