1use crate::{nice_debug_assert, util};
4
5#[derive(Debug, Clone, Copy)]
7pub enum FloatRange {
8 Linear { min: f32, max: f32 },
10 Skewed { min: f32, max: f32, factor: f32 },
16 SymmetricalSkewed {
20 min: f32,
21 max: f32,
22 factor: f32,
23 center: f32,
24 },
25 Reversed(&'static FloatRange),
27}
28
29#[derive(Debug, Clone, Copy)]
33pub enum IntRange {
34 Linear { min: i32, max: i32 },
36 Reversed(&'static IntRange),
38}
39
40impl FloatRange {
41 pub fn skew_factor(factor: f32) -> f32 {
45 2.0f32.powf(factor)
46 }
47
48 pub fn gain_skew_factor(min_db: f32, max_db: f32) -> f32 {
51 nice_debug_assert!(min_db < max_db);
52
53 let min_gain = util::db_to_gain(min_db);
54 let max_gain = util::db_to_gain(max_db);
55 let middle_db = (max_db + min_db) / 2.0;
56 let middle_gain = util::db_to_gain(middle_db);
57
58 0.5f32.log((middle_gain - min_gain) / (max_gain - min_gain))
61 }
62
63 pub fn normalize(&self, plain: f32) -> f32 {
66 match self {
67 FloatRange::Linear { min, max } => (plain.clamp(*min, *max) - min) / (max - min),
68 FloatRange::Skewed { min, max, factor } => {
69 ((plain.clamp(*min, *max) - min) / (max - min)).powf(*factor)
70 }
71 FloatRange::SymmetricalSkewed {
72 min,
73 max,
74 factor,
75 center,
76 } => {
77 let unscaled_proportion = (plain.clamp(*min, *max) - min) / (max - min);
80 let center_proportion = (center - min) / (max - min);
81 if unscaled_proportion > center_proportion {
82 let scaled_proportion = (unscaled_proportion - center_proportion)
85 * (1.0 - center_proportion).recip();
86 (scaled_proportion.powf(*factor) * 0.5) + 0.5
87 } else {
88 let inverted_scaled_proportion =
93 (center_proportion - unscaled_proportion) * (center_proportion).recip();
94 (1.0 - inverted_scaled_proportion.powf(*factor)) * 0.5
95 }
96 }
97 FloatRange::Reversed(range) => 1.0 - range.normalize(plain),
98 }
99 }
100
101 pub fn unnormalize(&self, normalized: f32) -> f32 {
104 let normalized = normalized.clamp(0.0, 1.0);
105 match self {
106 FloatRange::Linear { min, max } => (normalized * (max - min)) + min,
107 FloatRange::Skewed { min, max, factor } => {
108 (normalized.powf(factor.recip()) * (max - min)) + min
109 }
110 FloatRange::SymmetricalSkewed {
111 min,
112 max,
113 factor,
114 center,
115 } => {
116 let center_proportion = (center - min) / (max - min);
118 let skewed_proportion = if normalized > 0.5 {
119 let scaled_proportion = (normalized - 0.5) * 2.0;
120 (scaled_proportion.powf(factor.recip()) * (1.0 - center_proportion))
121 + center_proportion
122 } else {
123 let inverted_scaled_proportion = (0.5 - normalized) * 2.0;
124 (1.0 - inverted_scaled_proportion.powf(factor.recip())) * center_proportion
125 };
126
127 (skewed_proportion * (max - min)) + min
128 }
129 FloatRange::Reversed(range) => range.unnormalize(1.0 - normalized),
130 }
131 }
132
133 pub fn previous_step(&self, from: f32, step_size: Option<f32>, finer: bool) -> f32 {
137 match self {
141 FloatRange::Linear { min, max }
142 | FloatRange::Skewed { min, max, .. }
143 | FloatRange::SymmetricalSkewed { min, max, .. } => {
144 let normalized_naive_step_size = if finer { 0.005 } else { 0.02 };
145 let naive_step =
146 self.unnormalize(self.normalize(from) - normalized_naive_step_size);
147
148 match step_size {
149 Some(step_size) if (naive_step - from).abs() > step_size => {
151 self.snap_to_step(naive_step, step_size)
152 }
153 Some(step_size) => from - step_size,
154 None => naive_step,
155 }
156 .clamp(*min, *max)
157 }
158 FloatRange::Reversed(range) => range.next_step(from, step_size, finer),
159 }
160 }
161
162 pub fn next_step(&self, from: f32, step_size: Option<f32>, finer: bool) -> f32 {
165 match self {
167 FloatRange::Linear { min, max }
168 | FloatRange::Skewed { min, max, .. }
169 | FloatRange::SymmetricalSkewed { min, max, .. } => {
170 let normalized_naive_step_size = if finer { 0.005 } else { 0.02 };
171 let naive_step =
172 self.unnormalize(self.normalize(from) + normalized_naive_step_size);
173
174 match step_size {
175 Some(step_size) if (naive_step - from).abs() > step_size => {
176 self.snap_to_step(naive_step, step_size)
177 }
178 Some(step_size) => from + step_size,
179 None => naive_step,
180 }
181 .clamp(*min, *max)
182 }
183 FloatRange::Reversed(range) => range.previous_step(from, step_size, finer),
184 }
185 }
186
187 pub fn snap_to_step(&self, value: f32, step_size: f32) -> f32 {
189 match self {
190 FloatRange::Linear { min, max }
191 | FloatRange::Skewed { min, max, .. }
192 | FloatRange::SymmetricalSkewed { min, max, .. } => {
193 ((value / step_size).round() * step_size).clamp(*min, *max)
194 }
195 FloatRange::Reversed(range) => range.snap_to_step(value, step_size),
196 }
197 }
198
199 pub(super) fn assert_validity(&self) {
202 match self {
203 FloatRange::Linear { min, max }
204 | FloatRange::Skewed { min, max, .. }
205 | FloatRange::SymmetricalSkewed { min, max, .. } => {
206 nice_debug_assert!(
207 min < max,
208 "The range minimum ({}) needs to be less than the range maximum ({}) and they \
209 cannot be equal",
210 min,
211 max
212 );
213 }
214 FloatRange::Reversed(range) => range.assert_validity(),
215 }
216 }
217}
218
219impl IntRange {
220 pub fn normalize(&self, plain: i32) -> f32 {
223 match self {
224 IntRange::Linear { min, max } => (plain - min) as f32 / (max - min) as f32,
225 IntRange::Reversed(range) => 1.0 - range.normalize(plain),
226 }
227 .clamp(0.0, 1.0)
228 }
229
230 pub fn unnormalize(&self, normalized: f32) -> i32 {
233 let normalized = normalized.clamp(0.0, 1.0);
234 match self {
235 IntRange::Linear { min, max } => (normalized * (max - min) as f32).round() as i32 + min,
236 IntRange::Reversed(range) => range.unnormalize(1.0 - normalized),
237 }
238 }
239
240 pub fn previous_step(&self, from: i32) -> i32 {
242 match self {
243 IntRange::Linear { min, max } => (from - 1).clamp(*min, *max),
244 IntRange::Reversed(range) => range.next_step(from),
245 }
246 }
247
248 pub fn next_step(&self, from: i32) -> i32 {
250 match self {
251 IntRange::Linear { min, max } => (from + 1).clamp(*min, *max),
252 IntRange::Reversed(range) => range.previous_step(from),
253 }
254 }
255
256 pub fn step_count(&self) -> usize {
258 match self {
259 IntRange::Linear { min, max } => (max - min) as usize,
260 IntRange::Reversed(range) => range.step_count(),
261 }
262 }
263
264 pub fn inner_range(&self) -> Self {
266 match self {
267 IntRange::Linear { .. } => *self,
268 IntRange::Reversed(range) => range.inner_range(),
269 }
270 }
271
272 pub(super) fn assert_validity(&self) {
275 match self {
276 IntRange::Linear { min, max } => {
277 nice_debug_assert!(
278 min < max,
279 "The range minimum ({}) needs to be less than the range maximum ({}) and they \
280 cannot be equal",
281 min,
282 max
283 );
284 }
285 IntRange::Reversed(range) => range.assert_validity(),
286 }
287 }
288}
289
290#[cfg(test)]
291mod tests {
292 use super::*;
293
294 const fn make_linear_float_range() -> FloatRange {
295 FloatRange::Linear {
296 min: 10.0,
297 max: 20.0,
298 }
299 }
300
301 const fn make_linear_int_range() -> IntRange {
302 IntRange::Linear { min: -10, max: 10 }
303 }
304
305 const fn make_skewed_float_range(factor: f32) -> FloatRange {
306 FloatRange::Skewed {
307 min: 10.0,
308 max: 20.0,
309 factor,
310 }
311 }
312
313 const fn make_symmetrical_skewed_float_range(factor: f32) -> FloatRange {
314 FloatRange::SymmetricalSkewed {
315 min: 10.0,
316 max: 20.0,
317 factor,
318 center: 12.5,
319 }
320 }
321
322 #[test]
323 fn step_size() {
324 let range = make_linear_float_range();
326 assert_eq!(range.snap_to_step(13.0, 4.73), 14.190001);
328 }
329
330 #[test]
331 fn step_size_clamping() {
332 let range = make_linear_float_range();
333 assert_eq!(range.snap_to_step(10.0, 4.73), 10.0);
334 assert_eq!(range.snap_to_step(20.0, 6.73), 20.0);
335 }
336
337 mod linear {
338 use super::*;
339
340 #[test]
341 fn range_normalize_float() {
342 let range = make_linear_float_range();
343 assert_eq!(range.normalize(17.5), 0.75);
344 }
345
346 #[test]
347 fn range_normalize_int() {
348 let range = make_linear_int_range();
349 assert_eq!(range.normalize(-5), 0.25);
350 }
351
352 #[test]
353 fn range_unnormalize_float() {
354 let range = make_linear_float_range();
355 assert_eq!(range.unnormalize(0.25), 12.5);
356 }
357
358 #[test]
359 fn range_unnormalize_int() {
360 let range = make_linear_int_range();
361 assert_eq!(range.unnormalize(0.75), 5);
362 }
363
364 #[test]
365 fn range_unnormalize_int_rounding() {
366 let range = make_linear_int_range();
367 assert_eq!(range.unnormalize(0.73), 5);
368 }
369 }
370
371 mod skewed {
372 use super::*;
373
374 #[test]
375 fn range_normalize_float() {
376 let range = make_skewed_float_range(FloatRange::skew_factor(-2.0));
377 assert_eq!(range.normalize(17.5), 0.9306049);
378 }
379
380 #[test]
381 fn range_unnormalize_float() {
382 let range = make_skewed_float_range(FloatRange::skew_factor(-2.0));
383 assert_eq!(range.unnormalize(0.9306049), 17.5);
384 }
385
386 #[test]
387 fn range_normalize_linear_equiv_float() {
388 let linear_range = make_linear_float_range();
389 let skewed_range = make_skewed_float_range(1.0);
390 assert_eq!(linear_range.normalize(17.5), skewed_range.normalize(17.5));
391 }
392
393 #[test]
394 fn range_unnormalize_linear_equiv_float() {
395 let linear_range = make_linear_float_range();
396 let skewed_range = make_skewed_float_range(1.0);
397 assert_eq!(
398 linear_range.unnormalize(0.25),
399 skewed_range.unnormalize(0.25)
400 );
401 }
402 }
403
404 mod symmetrical_skewed {
405 use super::*;
406
407 #[test]
408 fn range_normalize_float() {
409 let range = make_symmetrical_skewed_float_range(FloatRange::skew_factor(-2.0));
410 assert_eq!(range.normalize(17.5), 0.951801);
411 }
412
413 #[test]
414 fn range_unnormalize_float() {
415 let range = make_symmetrical_skewed_float_range(FloatRange::skew_factor(-2.0));
416 assert_eq!(range.unnormalize(0.951801), 17.5);
417 }
418 }
419
420 mod reversed_linear {
421 use super::*;
422
423 #[test]
424 fn range_normalize_int() {
425 const WRAPPED_RANGE: IntRange = make_linear_int_range();
426 let range = IntRange::Reversed(&WRAPPED_RANGE);
427 assert_eq!(range.normalize(-5), 1.0 - 0.25);
428 }
429
430 #[test]
431 fn range_unnormalize_int() {
432 const WRAPPED_RANGE: IntRange = make_linear_int_range();
433 let range = IntRange::Reversed(&WRAPPED_RANGE);
434 assert_eq!(range.unnormalize(1.0 - 0.75), 5);
435 }
436
437 #[test]
438 fn range_unnormalize_int_rounding() {
439 const WRAPPED_RANGE: IntRange = make_linear_int_range();
440 let range = IntRange::Reversed(&WRAPPED_RANGE);
441 assert_eq!(range.unnormalize(1.0 - 0.73), 5);
442 }
443 }
444
445 mod reversed_skewed {
446 use super::*;
447
448 #[test]
449 fn range_normalize_float() {
450 const WRAPPED_RANGE: FloatRange = make_skewed_float_range(0.25);
451 let range = FloatRange::Reversed(&WRAPPED_RANGE);
452 assert_eq!(range.normalize(17.5), 1.0 - 0.9306049);
453 }
454
455 #[test]
456 fn range_unnormalize_float() {
457 const WRAPPED_RANGE: FloatRange = make_skewed_float_range(0.25);
458 let range = FloatRange::Reversed(&WRAPPED_RANGE);
459 assert_eq!(range.unnormalize(1.0 - 0.9306049), 17.5);
460 }
461
462 #[test]
463 fn range_normalize_linear_equiv_float() {
464 const WRAPPED_LINEAR_RANGE: FloatRange = make_linear_float_range();
465 const WRAPPED_SKEWED_RANGE: FloatRange = make_skewed_float_range(1.0);
466 let linear_range = FloatRange::Reversed(&WRAPPED_LINEAR_RANGE);
467 let skewed_range = FloatRange::Reversed(&WRAPPED_SKEWED_RANGE);
468 assert_eq!(linear_range.normalize(17.5), skewed_range.normalize(17.5));
469 }
470
471 #[test]
472 fn range_unnormalize_linear_equiv_float() {
473 const WRAPPED_LINEAR_RANGE: FloatRange = make_linear_float_range();
474 const WRAPPED_SKEWED_RANGE: FloatRange = make_skewed_float_range(1.0);
475 let linear_range = FloatRange::Reversed(&WRAPPED_LINEAR_RANGE);
476 let skewed_range = FloatRange::Reversed(&WRAPPED_SKEWED_RANGE);
477 assert_eq!(
478 linear_range.unnormalize(0.25),
479 skewed_range.unnormalize(0.25)
480 );
481 }
482 }
483}