1use glam::Vec2;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4pub enum InterpolateMode {
5 Nearest,
7 Linear,
9 }
11
12#[derive(Debug, Clone, thiserror::Error)]
13#[error("Unknown interpolate mode {0:?}")]
14pub struct UnknownInterpolateModeError(String);
15
16impl TryFrom<&str> for InterpolateMode {
17 type Error = UnknownInterpolateModeError;
18
19 fn try_from(value: &str) -> Result<Self, Self::Error> {
20 match value {
21 "Linear" => Ok(InterpolateMode::Linear),
22 unknown => Err(UnknownInterpolateModeError(unknown.to_owned())),
23 }
24 }
25}
26
27#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
28pub struct InterpRange<T> {
29 pub beg: T,
30 pub end: T,
31}
32
33impl<T> InterpRange<T> {
34 #[inline]
35 pub fn new(beg: T, end: T) -> Self {
36 Self { beg, end }
37 }
38}
39
40impl InterpRange<Vec2> {
41 #[inline]
42 pub fn to_x(self) -> InterpRange<f32> {
43 InterpRange {
44 beg: self.beg.x,
45 end: self.end.x,
46 }
47 }
48
49 #[inline]
50 pub fn to_y(self) -> InterpRange<f32> {
51 InterpRange {
52 beg: self.beg.y,
53 end: self.end.y,
54 }
55 }
56}
57
58#[inline]
59fn interpolate_nearest(t: f32, range_in: InterpRange<f32>, range_out: InterpRange<f32>) -> f32 {
60 debug_assert!(
61 range_in.beg <= t && t <= range_in.end,
62 "{} <= {} <= {}",
63 range_in.beg,
64 t,
65 range_in.end
66 );
67
68 if (range_in.end - t) < (t - range_in.beg) {
69 range_out.end
70 } else {
71 range_out.beg
72 }
73}
74
75#[inline]
76fn interpolate_linear(t: f32, range_in: InterpRange<f32>, range_out: InterpRange<f32>) -> f32 {
77 debug_assert!(
78 range_in.beg <= t && t <= range_in.end,
79 "{} <= {} <= {}",
80 range_in.beg,
81 t,
82 range_in.end
83 );
84
85 (t - range_in.beg) * (range_out.end - range_out.beg) / (range_in.end - range_in.beg) + range_out.beg
86}
87
88#[inline]
89pub fn interpolate_f32(t: f32, range_in: InterpRange<f32>, range_out: InterpRange<f32>, mode: InterpolateMode) -> f32 {
90 match mode {
91 InterpolateMode::Nearest => interpolate_nearest(t, range_in, range_out),
92 InterpolateMode::Linear => interpolate_linear(t, range_in, range_out),
93 }
94}
95
96#[inline]
97pub fn interpolate_vec2(
98 t: f32,
99 range_in: InterpRange<f32>,
100 range_out: InterpRange<Vec2>,
101 mode: InterpolateMode,
102) -> Vec2 {
103 let x = interpolate_f32(t, range_in, range_out.to_x(), mode);
104 let y = interpolate_f32(t, range_in, range_out.to_y(), mode);
105 Vec2 { x, y }
106}
107
108pub fn interpolate_f32s_additive(
109 t: f32,
110 range_in: InterpRange<f32>,
111 range_out: InterpRange<&[f32]>,
112 mode: InterpolateMode,
113 out: &mut [f32],
114) {
115 for ((&ob, &oe), o) in range_out.beg.iter().zip(range_out.end).zip(out) {
116 *o += interpolate_f32(t, range_in, InterpRange::new(ob, oe), mode);
117 }
118}
119
120pub fn interpolate_vec2s_additive(
121 t: f32,
122 range_in: InterpRange<f32>,
123 range_out: InterpRange<&[Vec2]>,
124 mode: InterpolateMode,
125 out: &mut [Vec2],
126) {
127 for ((&ob, &oe), o) in range_out.beg.iter().zip(range_out.end).zip(out) {
128 *o += interpolate_vec2(t, range_in, InterpRange::new(ob, oe), mode);
129 }
130}
131
132#[inline]
133pub fn bi_interpolate_f32(
134 t: Vec2,
135 range_in: InterpRange<Vec2>,
136 out_top: InterpRange<f32>,
137 out_bottom: InterpRange<f32>,
138 mode: InterpolateMode,
139) -> f32 {
140 let beg = interpolate_f32(t.x, range_in.to_x(), out_top, mode);
141 let end = interpolate_f32(t.x, range_in.to_x(), out_bottom, mode);
142 interpolate_f32(t.y, range_in.to_y(), InterpRange::new(beg, end), mode)
143}
144
145#[inline]
146pub fn bi_interpolate_vec2(
147 t: Vec2,
148 range_in: InterpRange<Vec2>,
149 out_top: InterpRange<Vec2>,
150 out_bottom: InterpRange<Vec2>,
151 mode: InterpolateMode,
152) -> Vec2 {
153 let beg = interpolate_vec2(t.x, range_in.to_x(), out_top, mode);
154 let end = interpolate_vec2(t.x, range_in.to_x(), out_bottom, mode);
155 interpolate_vec2(t.y, range_in.to_y(), InterpRange::new(beg, end), mode)
156}
157
158pub fn bi_interpolate_f32s_additive(
159 t: Vec2,
160 range_in: InterpRange<Vec2>,
161 out_top: InterpRange<&[f32]>,
162 out_bottom: InterpRange<&[f32]>,
163 mode: InterpolateMode,
164 out: &mut [f32],
165) {
166 for (((&otb, &ote), (&obb, &obe)), o) in (out_top.beg.iter().zip(out_top.end))
167 .zip(out_bottom.beg.iter().zip(out_bottom.end))
168 .zip(out)
169 {
170 *o += bi_interpolate_f32(
171 t,
172 range_in,
173 InterpRange::new(otb, ote),
174 InterpRange::new(obb, obe),
175 mode,
176 )
177 }
178}
179
180pub fn bi_interpolate_vec2s_additive(
181 t: Vec2,
182 range_in: InterpRange<Vec2>,
183 out_top: InterpRange<&[Vec2]>,
184 out_bottom: InterpRange<&[Vec2]>,
185 mode: InterpolateMode,
186 out: &mut [Vec2],
187) {
188 for (((&otb, &ote), (&obb, &obe)), o) in (out_top.beg.iter().zip(out_top.end))
189 .zip(out_bottom.beg.iter().zip(out_bottom.end))
190 .zip(out)
191 {
192 *o += bi_interpolate_vec2(
193 t,
194 range_in,
195 InterpRange::new(otb, ote),
196 InterpRange::new(obb, obe),
197 mode,
198 )
199 }
200}
201
202#[cfg(test)]
203mod tests {
204 use super::*;
205
206 #[test]
207 fn test_linear_interpolation() {
208 assert_eq!(
209 interpolate_linear(0.0, InterpRange::new(0.0, 1.0), InterpRange::new(-5.0, 5.0)),
210 -5.0
211 );
212 assert_eq!(
213 interpolate_linear(1.0, InterpRange::new(0.0, 1.0), InterpRange::new(-5.0, 5.0)),
214 5.0
215 );
216 assert_eq!(
217 interpolate_linear(0.5, InterpRange::new(0.0, 1.0), InterpRange::new(-5.0, 5.0)),
218 0.0
219 );
220 assert_eq!(
221 interpolate_linear(0.0, InterpRange::new(0.5, 0.0), InterpRange::new(-5.0, 5.0)),
222 5.0
223 );
224 }
225}