|
1 |
| -use glam::{Vec2, Vec3, Vec3A, Vec4}; |
| 1 | +use crate::{Dir2, Dir3, Dir3A, Quat, Rot2, Vec2, Vec3, Vec3A, Vec4}; |
2 | 2 | use std::fmt::Debug;
|
3 | 3 | use std::ops::{Add, Div, Mul, Neg, Sub};
|
4 | 4 |
|
@@ -161,3 +161,147 @@ impl NormedVectorSpace for f32 {
|
161 | 161 | self * self
|
162 | 162 | }
|
163 | 163 | }
|
| 164 | + |
| 165 | +/// A type with a natural interpolation that provides strong subdivision guarantees. |
| 166 | +/// |
| 167 | +/// Although the only required method is `interpolate_stable`, many things are expected of it: |
| 168 | +/// |
| 169 | +/// 1. The notion of interpolation should follow naturally from the semantics of the type, so |
| 170 | +/// that inferring the interpolation mode from the type alone is sensible. |
| 171 | +/// |
| 172 | +/// 2. The interpolation recovers something equivalent to the starting value at `t = 0.0` |
| 173 | +/// and likewise with the ending value at `t = 1.0`. They do not have to be data-identical, but |
| 174 | +/// they should be semantically identical. For example, [`Quat::slerp`] doesn't always yield its |
| 175 | +/// second rotation input exactly at `t = 1.0`, but it always returns an equivalent rotation. |
| 176 | +/// |
| 177 | +/// 3. Importantly, the interpolation must be *subdivision-stable*: for any interpolation curve |
| 178 | +/// between two (unnamed) values and any parameter-value pairs `(t0, p)` and `(t1, q)`, the |
| 179 | +/// interpolation curve between `p` and `q` must be the *linear* reparametrization of the original |
| 180 | +/// interpolation curve restricted to the interval `[t0, t1]`. |
| 181 | +/// |
| 182 | +/// The last of these conditions is very strong and indicates something like constant speed. It |
| 183 | +/// is called "subdivision stability" because it guarantees that breaking up the interpolation |
| 184 | +/// into segments and joining them back together has no effect. |
| 185 | +/// |
| 186 | +/// Here is a diagram depicting it: |
| 187 | +/// ```text |
| 188 | +/// top curve = u.interpolate_stable(v, t) |
| 189 | +/// |
| 190 | +/// t0 => p t1 => q |
| 191 | +/// |-------------|---------|-------------| |
| 192 | +/// 0 => u / \ 1 => v |
| 193 | +/// / \ |
| 194 | +/// / \ |
| 195 | +/// / linear \ |
| 196 | +/// / reparametrization \ |
| 197 | +/// / t = t0 * (1 - s) + t1 * s \ |
| 198 | +/// / \ |
| 199 | +/// |-------------------------------------| |
| 200 | +/// 0 => p 1 => q |
| 201 | +/// |
| 202 | +/// bottom curve = p.interpolate_stable(q, s) |
| 203 | +/// ``` |
| 204 | +/// |
| 205 | +/// Note that some common forms of interpolation do not satisfy this criterion. For example, |
| 206 | +/// [`Quat::lerp`] and [`Rot2::nlerp`] are not subdivision-stable. |
| 207 | +/// |
| 208 | +/// Furthermore, this is not to be used as a general trait for abstract interpolation. |
| 209 | +/// Consumers rely on the strong guarantees in order for behavior based on this trait to be |
| 210 | +/// well-behaved. |
| 211 | +/// |
| 212 | +/// [`Quat::slerp`]: crate::Quat::slerp |
| 213 | +/// [`Quat::lerp`]: crate::Quat::lerp |
| 214 | +/// [`Rot2::nlerp`]: crate::Rot2::nlerp |
| 215 | +pub trait StableInterpolate: Clone { |
| 216 | + /// Interpolate between this value and the `other` given value using the parameter `t`. At |
| 217 | + /// `t = 0.0`, a value equivalent to `self` is recovered, while `t = 1.0` recovers a value |
| 218 | + /// equivalent to `other`, with intermediate values interpolating between the two. |
| 219 | + /// See the [trait-level documentation] for details. |
| 220 | + /// |
| 221 | + /// [trait-level documentation]: StableInterpolate |
| 222 | + fn interpolate_stable(&self, other: &Self, t: f32) -> Self; |
| 223 | + |
| 224 | + /// A version of [`interpolate_stable`] that assigns the result to `self` for convenience. |
| 225 | + /// |
| 226 | + /// [`interpolate_stable`]: StableInterpolate::interpolate_stable |
| 227 | + fn interpolate_stable_assign(&mut self, other: &Self, t: f32) { |
| 228 | + *self = self.interpolate_stable(other, t); |
| 229 | + } |
| 230 | + |
| 231 | + /// Smoothly nudge this value towards the `target` at a given decay rate. The `decay_rate` |
| 232 | + /// parameter controls how fast the distance between `self` and `target` decays relative to |
| 233 | + /// the units of `delta`; the intended usage is for `decay_rate` to generally remain fixed, |
| 234 | + /// while `delta` is something like `delta_time` from an updating system. This produces a |
| 235 | + /// smooth following of the target that is independent of framerate. |
| 236 | + /// |
| 237 | + /// More specifically, when this is called repeatedly, the result is that the distance between |
| 238 | + /// `self` and a fixed `target` attenuates exponentially, with the rate of this exponential |
| 239 | + /// decay given by `decay_rate`. |
| 240 | + /// |
| 241 | + /// For example, at `decay_rate = 0.0`, this has no effect. |
| 242 | + /// At `decay_rate = f32::INFINITY`, `self` immediately snaps to `target`. |
| 243 | + /// In general, higher rates mean that `self` moves more quickly towards `target`. |
| 244 | + /// |
| 245 | + /// # Example |
| 246 | + /// ``` |
| 247 | + /// # use bevy_math::{Vec3, StableInterpolate}; |
| 248 | + /// # let delta_time: f32 = 1.0 / 60.0; |
| 249 | + /// let mut object_position: Vec3 = Vec3::ZERO; |
| 250 | + /// let target_position: Vec3 = Vec3::new(2.0, 3.0, 5.0); |
| 251 | + /// // Decay rate of ln(10) => after 1 second, remaining distance is 1/10th |
| 252 | + /// let decay_rate = f32::ln(10.0); |
| 253 | + /// // Calling this repeatedly will move `object_position` towards `target_position`: |
| 254 | + /// object_position.smooth_nudge(&target_position, decay_rate, delta_time); |
| 255 | + /// ``` |
| 256 | + fn smooth_nudge(&mut self, target: &Self, decay_rate: f32, delta: f32) { |
| 257 | + self.interpolate_stable_assign(target, 1.0 - f32::exp(-decay_rate * delta)); |
| 258 | + } |
| 259 | +} |
| 260 | + |
| 261 | +// Conservatively, we presently only apply this for normed vector spaces, where the notion |
| 262 | +// of being constant-speed is literally true. The technical axioms are satisfied for any |
| 263 | +// VectorSpace type, but the "natural from the semantics" part is less clear in general. |
| 264 | +impl<V> StableInterpolate for V |
| 265 | +where |
| 266 | + V: NormedVectorSpace, |
| 267 | +{ |
| 268 | + #[inline] |
| 269 | + fn interpolate_stable(&self, other: &Self, t: f32) -> Self { |
| 270 | + self.lerp(*other, t) |
| 271 | + } |
| 272 | +} |
| 273 | + |
| 274 | +impl StableInterpolate for Rot2 { |
| 275 | + #[inline] |
| 276 | + fn interpolate_stable(&self, other: &Self, t: f32) -> Self { |
| 277 | + self.slerp(*other, t) |
| 278 | + } |
| 279 | +} |
| 280 | + |
| 281 | +impl StableInterpolate for Quat { |
| 282 | + #[inline] |
| 283 | + fn interpolate_stable(&self, other: &Self, t: f32) -> Self { |
| 284 | + self.slerp(*other, t) |
| 285 | + } |
| 286 | +} |
| 287 | + |
| 288 | +impl StableInterpolate for Dir2 { |
| 289 | + #[inline] |
| 290 | + fn interpolate_stable(&self, other: &Self, t: f32) -> Self { |
| 291 | + self.slerp(*other, t) |
| 292 | + } |
| 293 | +} |
| 294 | + |
| 295 | +impl StableInterpolate for Dir3 { |
| 296 | + #[inline] |
| 297 | + fn interpolate_stable(&self, other: &Self, t: f32) -> Self { |
| 298 | + self.slerp(*other, t) |
| 299 | + } |
| 300 | +} |
| 301 | + |
| 302 | +impl StableInterpolate for Dir3A { |
| 303 | + #[inline] |
| 304 | + fn interpolate_stable(&self, other: &Self, t: f32) -> Self { |
| 305 | + self.slerp(*other, t) |
| 306 | + } |
| 307 | +} |
0 commit comments