Skip to main content

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}