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 gain_range(min_db: f32, max_db: f32) -> Self {
65 Self::Skewed {
66 min: util::db_to_gain(min_db),
67 max: util::db_to_gain(max_db),
68 factor: Self::gain_skew_factor(min_db, max_db),
69 }
70 }
71
72 pub fn normalize(&self, plain: f32) -> f32 {
75 match self {
76 FloatRange::Linear { min, max } => (plain.clamp(*min, *max) - min) / (max - min),
77 FloatRange::Skewed { min, max, factor } => {
78 ((plain.clamp(*min, *max) - min) / (max - min)).powf(*factor)
79 }
80 FloatRange::SymmetricalSkewed {
81 min,
82 max,
83 factor,
84 center,
85 } => {
86 let unscaled_proportion = (plain.clamp(*min, *max) - min) / (max - min);
89 let center_proportion = (center - min) / (max - min);
90 if unscaled_proportion > center_proportion {
91 let scaled_proportion = (unscaled_proportion - center_proportion)
94 * (1.0 - center_proportion).recip();
95 (scaled_proportion.powf(*factor) * 0.5) + 0.5
96 } else {
97 let inverted_scaled_proportion =
102 (center_proportion - unscaled_proportion) * (center_proportion).recip();
103 (1.0 - inverted_scaled_proportion.powf(*factor)) * 0.5
104 }
105 }
106 FloatRange::Reversed(range) => 1.0 - range.normalize(plain),
107 }
108 }
109
110 pub fn unnormalize(&self, normalized: f32) -> f32 {
113 let normalized = normalized.clamp(0.0, 1.0);
114 match self {
115 FloatRange::Linear { min, max } => (normalized * (max - min)) + min,
116 FloatRange::Skewed { min, max, factor } => {
117 (normalized.powf(factor.recip()) * (max - min)) + min
118 }
119 FloatRange::SymmetricalSkewed {
120 min,
121 max,
122 factor,
123 center,
124 } => {
125 let center_proportion = (center - min) / (max - min);
127 let skewed_proportion = if normalized > 0.5 {
128 let scaled_proportion = (normalized - 0.5) * 2.0;
129 (scaled_proportion.powf(factor.recip()) * (1.0 - center_proportion))
130 + center_proportion
131 } else {
132 let inverted_scaled_proportion = (0.5 - normalized) * 2.0;
133 (1.0 - inverted_scaled_proportion.powf(factor.recip())) * center_proportion
134 };
135
136 (skewed_proportion * (max - min)) + min
137 }
138 FloatRange::Reversed(range) => range.unnormalize(1.0 - normalized),
139 }
140 }
141
142 pub fn previous_step(&self, from: f32, step_size: Option<f32>, finer: bool) -> f32 {
146 match self {
150 FloatRange::Linear { min, max }
151 | FloatRange::Skewed { min, max, .. }
152 | FloatRange::SymmetricalSkewed { min, max, .. } => {
153 let normalized_naive_step_size = if finer { 0.005 } else { 0.02 };
154 let naive_step =
155 self.unnormalize(self.normalize(from) - normalized_naive_step_size);
156
157 match step_size {
158 Some(step_size) if (naive_step - from).abs() > step_size => {
160 self.snap_to_step(naive_step, step_size)
161 }
162 Some(step_size) => from - step_size,
163 None => naive_step,
164 }
165 .clamp(*min, *max)
166 }
167 FloatRange::Reversed(range) => range.next_step(from, step_size, finer),
168 }
169 }
170
171 pub fn next_step(&self, from: f32, step_size: Option<f32>, finer: bool) -> f32 {
174 match self {
176 FloatRange::Linear { min, max }
177 | FloatRange::Skewed { min, max, .. }
178 | FloatRange::SymmetricalSkewed { min, max, .. } => {
179 let normalized_naive_step_size = if finer { 0.005 } else { 0.02 };
180 let naive_step =
181 self.unnormalize(self.normalize(from) + normalized_naive_step_size);
182
183 match step_size {
184 Some(step_size) if (naive_step - from).abs() > step_size => {
185 self.snap_to_step(naive_step, step_size)
186 }
187 Some(step_size) => from + step_size,
188 None => naive_step,
189 }
190 .clamp(*min, *max)
191 }
192 FloatRange::Reversed(range) => range.previous_step(from, step_size, finer),
193 }
194 }
195
196 pub fn snap_to_step(&self, value: f32, step_size: f32) -> f32 {
198 match self {
199 FloatRange::Linear { min, max }
200 | FloatRange::Skewed { min, max, .. }
201 | FloatRange::SymmetricalSkewed { min, max, .. } => {
202 ((value / step_size).round() * step_size).clamp(*min, *max)
203 }
204 FloatRange::Reversed(range) => range.snap_to_step(value, step_size),
205 }
206 }
207
208 pub(super) fn assert_validity(&self) {
211 match self {
212 FloatRange::Linear { min, max }
213 | FloatRange::Skewed { min, max, .. }
214 | FloatRange::SymmetricalSkewed { min, max, .. } => {
215 nice_debug_assert!(
216 min < max,
217 "The range minimum ({}) needs to be less than the range maximum ({}) and they \
218 cannot be equal",
219 min,
220 max
221 );
222 }
223 FloatRange::Reversed(range) => range.assert_validity(),
224 }
225 }
226}
227
228impl IntRange {
229 pub fn normalize(&self, plain: i32) -> f32 {
232 match self {
233 IntRange::Linear { min, max } => (plain - min) as f32 / (max - min) as f32,
234 IntRange::Reversed(range) => 1.0 - range.normalize(plain),
235 }
236 .clamp(0.0, 1.0)
237 }
238
239 pub fn unnormalize(&self, normalized: f32) -> i32 {
242 let normalized = normalized.clamp(0.0, 1.0);
243 match self {
244 IntRange::Linear { min, max } => (normalized * (max - min) as f32).round() as i32 + min,
245 IntRange::Reversed(range) => range.unnormalize(1.0 - normalized),
246 }
247 }
248
249 pub fn previous_step(&self, from: i32) -> i32 {
251 match self {
252 IntRange::Linear { min, max } => (from - 1).clamp(*min, *max),
253 IntRange::Reversed(range) => range.next_step(from),
254 }
255 }
256
257 pub fn next_step(&self, from: i32) -> i32 {
259 match self {
260 IntRange::Linear { min, max } => (from + 1).clamp(*min, *max),
261 IntRange::Reversed(range) => range.previous_step(from),
262 }
263 }
264
265 pub fn step_count(&self) -> usize {
267 match self {
268 IntRange::Linear { min, max } => (max - min) as usize,
269 IntRange::Reversed(range) => range.step_count(),
270 }
271 }
272
273 pub fn inner_range(&self) -> Self {
275 match self {
276 IntRange::Linear { .. } => *self,
277 IntRange::Reversed(range) => range.inner_range(),
278 }
279 }
280
281 pub(super) fn assert_validity(&self) {
284 match self {
285 IntRange::Linear { min, max } => {
286 nice_debug_assert!(
287 min < max,
288 "The range minimum ({}) needs to be less than the range maximum ({}) and they \
289 cannot be equal",
290 min,
291 max
292 );
293 }
294 IntRange::Reversed(range) => range.assert_validity(),
295 }
296 }
297}
298
299#[cfg(test)]
300mod tests {
301 use super::*;
302
303 const fn make_linear_float_range() -> FloatRange {
304 FloatRange::Linear {
305 min: 10.0,
306 max: 20.0,
307 }
308 }
309
310 const fn make_linear_int_range() -> IntRange {
311 IntRange::Linear { min: -10, max: 10 }
312 }
313
314 const fn make_skewed_float_range(factor: f32) -> FloatRange {
315 FloatRange::Skewed {
316 min: 10.0,
317 max: 20.0,
318 factor,
319 }
320 }
321
322 const fn make_symmetrical_skewed_float_range(factor: f32) -> FloatRange {
323 FloatRange::SymmetricalSkewed {
324 min: 10.0,
325 max: 20.0,
326 factor,
327 center: 12.5,
328 }
329 }
330
331 #[test]
332 fn step_size() {
333 let range = make_linear_float_range();
335 assert_eq!(range.snap_to_step(13.0, 4.73), 14.190001);
337 }
338
339 #[test]
340 fn step_size_clamping() {
341 let range = make_linear_float_range();
342 assert_eq!(range.snap_to_step(10.0, 4.73), 10.0);
343 assert_eq!(range.snap_to_step(20.0, 6.73), 20.0);
344 }
345
346 mod linear {
347 use super::*;
348
349 #[test]
350 fn range_normalize_float() {
351 let range = make_linear_float_range();
352 assert_eq!(range.normalize(17.5), 0.75);
353 }
354
355 #[test]
356 fn range_normalize_int() {
357 let range = make_linear_int_range();
358 assert_eq!(range.normalize(-5), 0.25);
359 }
360
361 #[test]
362 fn range_unnormalize_float() {
363 let range = make_linear_float_range();
364 assert_eq!(range.unnormalize(0.25), 12.5);
365 }
366
367 #[test]
368 fn range_unnormalize_int() {
369 let range = make_linear_int_range();
370 assert_eq!(range.unnormalize(0.75), 5);
371 }
372
373 #[test]
374 fn range_unnormalize_int_rounding() {
375 let range = make_linear_int_range();
376 assert_eq!(range.unnormalize(0.73), 5);
377 }
378 }
379
380 mod skewed {
381 use super::*;
382
383 #[test]
384 fn range_normalize_float() {
385 let range = make_skewed_float_range(FloatRange::skew_factor(-2.0));
386 assert_eq!(range.normalize(17.5), 0.9306049);
387 }
388
389 #[test]
390 fn range_unnormalize_float() {
391 let range = make_skewed_float_range(FloatRange::skew_factor(-2.0));
392 assert_eq!(range.unnormalize(0.9306049), 17.5);
393 }
394
395 #[test]
396 fn range_normalize_linear_equiv_float() {
397 let linear_range = make_linear_float_range();
398 let skewed_range = make_skewed_float_range(1.0);
399 assert_eq!(linear_range.normalize(17.5), skewed_range.normalize(17.5));
400 }
401
402 #[test]
403 fn range_unnormalize_linear_equiv_float() {
404 let linear_range = make_linear_float_range();
405 let skewed_range = make_skewed_float_range(1.0);
406 assert_eq!(
407 linear_range.unnormalize(0.25),
408 skewed_range.unnormalize(0.25)
409 );
410 }
411 }
412
413 mod symmetrical_skewed {
414 use super::*;
415
416 #[test]
417 fn range_normalize_float() {
418 let range = make_symmetrical_skewed_float_range(FloatRange::skew_factor(-2.0));
419 assert_eq!(range.normalize(17.5), 0.951801);
420 }
421
422 #[test]
423 fn range_unnormalize_float() {
424 let range = make_symmetrical_skewed_float_range(FloatRange::skew_factor(-2.0));
425 assert_eq!(range.unnormalize(0.951801), 17.5);
426 }
427 }
428
429 mod reversed_linear {
430 use super::*;
431
432 #[test]
433 fn range_normalize_int() {
434 const WRAPPED_RANGE: IntRange = make_linear_int_range();
435 let range = IntRange::Reversed(&WRAPPED_RANGE);
436 assert_eq!(range.normalize(-5), 1.0 - 0.25);
437 }
438
439 #[test]
440 fn range_unnormalize_int() {
441 const WRAPPED_RANGE: IntRange = make_linear_int_range();
442 let range = IntRange::Reversed(&WRAPPED_RANGE);
443 assert_eq!(range.unnormalize(1.0 - 0.75), 5);
444 }
445
446 #[test]
447 fn range_unnormalize_int_rounding() {
448 const WRAPPED_RANGE: IntRange = make_linear_int_range();
449 let range = IntRange::Reversed(&WRAPPED_RANGE);
450 assert_eq!(range.unnormalize(1.0 - 0.73), 5);
451 }
452 }
453
454 mod reversed_skewed {
455 use super::*;
456
457 #[test]
458 fn range_normalize_float() {
459 const WRAPPED_RANGE: FloatRange = make_skewed_float_range(0.25);
460 let range = FloatRange::Reversed(&WRAPPED_RANGE);
461 assert_eq!(range.normalize(17.5), 1.0 - 0.9306049);
462 }
463
464 #[test]
465 fn range_unnormalize_float() {
466 const WRAPPED_RANGE: FloatRange = make_skewed_float_range(0.25);
467 let range = FloatRange::Reversed(&WRAPPED_RANGE);
468 assert_eq!(range.unnormalize(1.0 - 0.9306049), 17.5);
469 }
470
471 #[test]
472 fn range_normalize_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!(linear_range.normalize(17.5), skewed_range.normalize(17.5));
478 }
479
480 #[test]
481 fn range_unnormalize_linear_equiv_float() {
482 const WRAPPED_LINEAR_RANGE: FloatRange = make_linear_float_range();
483 const WRAPPED_SKEWED_RANGE: FloatRange = make_skewed_float_range(1.0);
484 let linear_range = FloatRange::Reversed(&WRAPPED_LINEAR_RANGE);
485 let skewed_range = FloatRange::Reversed(&WRAPPED_SKEWED_RANGE);
486 assert_eq!(
487 linear_range.unnormalize(0.25),
488 skewed_range.unnormalize(0.25)
489 );
490 }
491 }
492}