1use fixed::types::{I16F16, I32F32, U16F16};
7
8#[derive(Copy, Clone, Debug, Eq, PartialEq)]
10pub enum Rounding {
11 Floor,
13 Ceil,
15 Nearest,
17}
18
19pub trait UnitValue: Copy + 'static {
29 fn zero() -> Self;
31 fn one() -> Self;
33
34 fn to_index(self) -> usize;
36
37 fn from_time_frac(elapsed_ms: u32, duration_ms: u32) -> Self;
42
43 fn to_time_offset(self, duration_ms: u32) -> u32;
46
47 fn lerp_u16(self, a: u16, b: u16) -> u16;
50
51 fn inv_lerp_u16(a: u16, b: u16, target: u16) -> Self;
54}
55
56impl UnitValue for u8 {
61 fn zero() -> Self {
62 0
63 }
64
65 fn one() -> Self {
66 255
67 }
68
69 fn to_index(self) -> usize {
70 self as usize
71 }
72
73 fn from_time_frac(elapsed_ms: u32, duration_ms: u32) -> Self {
74 if duration_ms == 0 || elapsed_ms >= duration_ms {
75 return 255;
76 }
77 if elapsed_ms == 0 {
78 return 0;
79 }
80 let frac = U16F16::from_num(elapsed_ms) / U16F16::from_num(duration_ms);
81 let u = frac * U16F16::from_num(255u16);
82 u.to_num::<u32>().min(255) as u8
83 }
84
85 fn to_time_offset(self, duration_ms: u32) -> u32 {
86 if duration_ms == 0 {
87 return 0;
88 }
89 let frac = U16F16::from_num(self) / U16F16::from_num(255u16);
90 (frac * U16F16::from_num(duration_ms))
91 .ceil()
92 .to_num::<u32>()
93 }
94
95 fn lerp_u16(self, a: u16, b: u16) -> u16 {
96 let a_fix = I32F32::from_num(a);
97 let b_fix = I32F32::from_num(b);
98 let t = I32F32::from_num(self) / I32F32::from_num(255);
99 let result = a_fix + t * (b_fix - a_fix);
100 result.to_num::<i64>().clamp(0, u16::MAX as i64) as u16
101 }
102
103 fn inv_lerp_u16(a: u16, b: u16, target: u16) -> Self {
104 if a == b {
105 return 255;
106 }
107 let delta = I32F32::from_num(b) - I32F32::from_num(a);
108 let numer = I32F32::from_num(target) - I32F32::from_num(a);
109 let frac = numer / delta;
110 let w = (frac * I32F32::from_num(255)).ceil();
111 w.to_num::<i32>().clamp(0, 255) as u8
112 }
113}
114
115impl UnitValue for u16 {
120 fn zero() -> Self {
121 0
122 }
123
124 fn one() -> Self {
125 65535
126 }
127
128 fn to_index(self) -> usize {
129 self as usize
130 }
131
132 fn from_time_frac(elapsed_ms: u32, duration_ms: u32) -> Self {
133 if duration_ms == 0 || elapsed_ms >= duration_ms {
134 return 65535;
135 }
136 if elapsed_ms == 0 {
137 return 0;
138 }
139 let frac = I32F32::from_num(elapsed_ms) / I32F32::from_num(duration_ms);
140 let u = frac * I32F32::from_num(65535u32);
141 u.to_num::<u32>().min(65535) as u16
142 }
143
144 fn to_time_offset(self, duration_ms: u32) -> u32 {
145 if duration_ms == 0 {
146 return 0;
147 }
148 let frac = I32F32::from_num(self) / I32F32::from_num(65535u32);
149 (frac * I32F32::from_num(duration_ms))
150 .ceil()
151 .to_num::<u32>()
152 }
153
154 fn lerp_u16(self, a: u16, b: u16) -> u16 {
155 let a_fix = I32F32::from_num(a);
156 let b_fix = I32F32::from_num(b);
157 let t = I32F32::from_num(self) / I32F32::from_num(65535);
158 let result = a_fix + t * (b_fix - a_fix);
159 result.to_num::<i64>().clamp(0, u16::MAX as i64) as u16
160 }
161
162 fn inv_lerp_u16(a: u16, b: u16, target: u16) -> Self {
163 if a == b {
164 return 65535;
165 }
166 let delta = I32F32::from_num(b) - I32F32::from_num(a);
167 let numer = I32F32::from_num(target) - I32F32::from_num(a);
168 let frac = numer / delta;
169 let w = (frac * I32F32::from_num(65535)).ceil();
170 w.to_num::<i64>().clamp(0, 65535) as u16
171 }
172}
173
174fn weight_frac(w: u8) -> I16F16 {
180 I16F16::from_num(w) / I16F16::from_num(255)
181}
182
183pub fn lerp_u8(a: u8, b: u8, w: u8) -> u8 {
188 let a_fix = I16F16::from_num(a);
189 let b_fix = I16F16::from_num(b);
190 let t = weight_frac(w);
191 let result = a_fix + t * (b_fix - a_fix);
192 result.to_num::<i32>().clamp(0, 255) as u8
193}
194
195pub fn lerp_u16(a: u16, b: u16, w: u8) -> u16 {
200 let a_fix = I32F32::from_num(a);
201 let b_fix = I32F32::from_num(b);
202 let t = I32F32::from_num(w) / I32F32::from_num(255);
203 let result = a_fix + t * (b_fix - a_fix);
204 result.to_num::<i64>().clamp(0, u16::MAX as i64) as u16
205}
206
207pub fn map_u8_to_u16(w: u8, max: u16) -> u16 {
211 let t = U16F16::from_num(w) / U16F16::from_num(255);
212 let result = t * U16F16::from_num(max);
213 result.to_num::<u32>().min(u16::MAX as u32) as u16
214}
215
216pub fn quantize(value: u16, step: u16, rounding: Rounding) -> u16 {
226 let v = u32::from(value);
227 let s = u32::from(step);
228 let result = match rounding {
229 Rounding::Floor => (v / s) * s,
230 Rounding::Ceil => v.div_ceil(s) * s,
231 Rounding::Nearest => ((v + s / 2) / s) * s,
232 };
233 result.min(u32::from(u16::MAX)) as u16
234}
235
236pub fn next_target_value(current: u16, end: u16, step: u16, increasing: bool) -> u16 {
241 if increasing {
242 let next = current.saturating_add(step);
243 next.min(end)
244 } else {
245 let next = current.saturating_sub(step);
246 next.max(end)
247 }
248}