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}