xen_animation/value.rs
1// SPDX-License-Identifier: Apache-2.0
2
3/// Generic animatable payload; every property (color, scale, opacity...)
4/// is flattened into up to 4 floats so the manager can interpolate
5/// anything without knowing what it represents.
6#[derive(Clone, Copy, Debug, PartialEq)]
7pub struct AnimValue(pub [f32; 4]);
8
9impl AnimValue {
10 /// Linearly interpolates component-wise between `self` and `other`.
11 ///
12 /// `t` is expected to lie in `[0.0, 1.0]` but is not clamped here, so
13 /// callers relying on overshoot easings can pass values outside that
14 /// range and get correct extrapolation.
15 pub fn lerp(self, other: Self, t: f32) -> Self {
16 let mut out = [0.0; 4];
17
18 for ((out, a), b) in out.iter_mut().zip(self.0.iter()).zip(other.0.iter()) {
19 *out = *a + (*b - *a) * t;
20 }
21
22 Self(out)
23 }
24
25 /// Interpolates two RGBA colors (packed as [r, g, b, a]) using
26 /// premultiplied alpha. A straight per-channel lerp drags the RGB
27 /// toward whichever endpoint has near-zero alpha (its RGB is otherwise
28 /// meaningless), which shows up as a dark flash mid-fade whenever one
29 /// endpoint is transparent.
30 pub fn lerp_premultiplied(self, other: Self, t: f32) -> Self {
31 let a_alpha = self.0[3];
32 let b_alpha = other.0[3];
33 let out_alpha = a_alpha + (b_alpha - a_alpha) * t;
34
35 let premultiply = |v: [f32; 4]| [v[0] * v[3], v[1] * v[3], v[2] * v[3]];
36 let pa = premultiply(self.0);
37 let pb = premultiply(other.0);
38
39 if out_alpha <= 0.0001 {
40 return Self([0.0, 0.0, 0.0, 0.0]);
41 }
42
43 Self([
44 (pa[0] + (pb[0] - pa[0]) * t) / out_alpha,
45 (pa[1] + (pb[1] - pa[1]) * t) / out_alpha,
46 (pa[2] + (pb[2] - pa[2]) * t) / out_alpha,
47 out_alpha,
48 ])
49 }
50}