retrofire_core/math/
vary.rs

1//! Types that can be interpolated across a face when rendering.
2//!
3//! Common varying types include colors, texture coordinates,
4//! and vertex normals.
5
6use core::mem;
7
8/// A trait for types that can be linearly interpolated and distributed
9/// between two endpoints.
10///
11/// This trait is designed particularly for *varyings:* types that are
12/// meant to be interpolated across the face of a polygon when rendering,
13/// but the methods are useful for various purposes.
14pub trait Vary: Sized + Clone {
15    /// The iterator returned by the [vary][Self::vary] method.
16    type Iter: Iterator<Item = Self>;
17    /// The difference type of `Self`.
18    type Diff: Clone;
19
20    /// Returns an iterator that yields values such that the first value
21    /// equals `self`, and each subsequent value is offset by `step` from its
22    /// predecessor using the [step][Self::step] method. If `max` is `Some(n)`,
23    /// stops after `n` steps, otherwise infinite.
24    ///
25    /// # Examples
26    /// ```
27    /// # use retrofire_core::math::vary::Vary;
28    /// let mut iter = 0.0f32.vary(0.2, Some(5));
29    /// assert_eq!(iter.next(), Some(0.0));
30    /// assert_eq!(iter.next(), Some(0.2));
31    /// assert_eq!(iter.next(), Some(0.4));
32    /// assert_eq!(iter.next(), Some(0.6));
33    /// assert_eq!(iter.next(), Some(0.8));
34    /// assert_eq!(iter.next(), None);
35    /// ```
36    fn vary(self, step: Self::Diff, max: Option<u32>) -> Self::Iter;
37
38    /// Linearly distributes values between `self` and `other` *inclusive*.
39    #[inline]
40    fn vary_to(self, other: Self, n: u32) -> Self::Iter {
41        let step = self.dv_dt(&other, 1.0 / n as f32);
42        self.vary(step, Some(n + 1))
43    }
44
45    /// Returns, conceptually, `(other - self) / dt`.
46    fn dv_dt(&self, other: &Self, recip_dt: f32) -> Self::Diff;
47
48    /// Returns the result of offsetting `self` by `delta`, conceptually
49    /// `self + delta`.
50    #[must_use]
51    fn step(&self, delta: &Self::Diff) -> Self;
52
53    /// Performs perspective division.
54    #[must_use]
55    fn z_div(&self, z: f32) -> Self;
56
57    /// Linearly interpolates between `self` and `other`.
58    ///
59    /// This method does not panic if `t < 0.0` or `t > 1.0`, or if `t`
60    /// is a `NaN`, but the return value in those cases is unspecified.
61    /// Individual implementations may offer stronger guarantees.
62    ///
63    /// # Examples
64    /// ```
65    /// # use retrofire_core::math::vary::Vary;
66    /// assert_eq!(2.0.lerp(&5.0, 0.0), 2.0);
67    /// assert_eq!(2.0.lerp(&5.0, 0.5), 3.5);
68    /// assert_eq!(2.0.lerp(&5.0, 1.0), 5.0);
69    /// ```
70    #[inline]
71    fn lerp(&self, other: &Self, t: f32) -> Self {
72        self.step(&self.dv_dt(other, t))
73    }
74}
75
76#[derive(Copy, Clone, Debug)]
77pub struct Iter<T: Vary> {
78    pub val: T,
79    pub step: T::Diff,
80    pub n: Option<u32>,
81}
82
83#[inline]
84pub fn lerp<V: Vary>(t: f32, from: V, to: V) -> V {
85    from.lerp(&to, t)
86}
87
88impl Vary for () {
89    type Iter = Iter<()>;
90    type Diff = ();
91
92    fn vary(self, _: Self::Diff, n: Option<u32>) -> Self::Iter {
93        Iter { val: (), step: (), n }
94    }
95    fn dv_dt(&self, _: &Self, _: f32) {}
96    fn step(&self, _: &Self::Diff) {}
97
98    fn z_div(&self, _: f32) {}
99}
100
101impl<T: Vary, U: Vary> Vary for (T, U) {
102    type Iter = Iter<Self>;
103    type Diff = (T::Diff, U::Diff);
104
105    fn vary(self, step: Self::Diff, n: Option<u32>) -> Self::Iter {
106        Iter { val: self, step, n }
107    }
108    fn dv_dt(&self, other: &Self, recip_dt: f32) -> Self::Diff {
109        (
110            self.0.dv_dt(&other.0, recip_dt),
111            self.1.dv_dt(&other.1, recip_dt),
112        )
113    }
114    fn step(&self, (d0, d1): &Self::Diff) -> Self {
115        (self.0.step(d0), self.1.step(d1))
116    }
117
118    fn z_div(&self, z: f32) -> Self {
119        (self.0.z_div(z), self.1.z_div(z))
120    }
121}
122
123impl<T: Vary> Iterator for Iter<T> {
124    type Item = T;
125
126    #[inline]
127    fn next(&mut self) -> Option<T> {
128        match &mut self.n {
129            Some(0) => return None,
130            Some(n) => *n -= 1,
131            None => (),
132        }
133        let new = self.val.step(&self.step);
134        Some(mem::replace(&mut self.val, new))
135    }
136}
137
138#[cfg(test)]
139mod tests {
140    use crate::assert_approx_eq;
141
142    use super::*;
143
144    #[test]
145    fn vary_f32() {
146        use alloc::vec::Vec;
147        let varying = (-6.0f32).vary(1.2, Some(10));
148        assert_approx_eq!(
149            varying.collect::<Vec<_>>()[..],
150            [-6.0, -4.8, -3.6, -2.4, -1.2, 0.0, 1.2, 2.4, 3.6, 4.8]
151        );
152    }
153}