// nih-plug: plugins, but rewritten in Rust // Copyright (C) 2022 Robbert van der Helm // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . //! Different ranges for numeric parameters. /// A distribution for a parameter's range. Probably need to add some forms of skewed ranges and /// maybe a callback based implementation at some point. #[derive(Debug)] pub enum Range { Linear { min: T, max: T }, } /// A normalizable range for type `T`, where `self` is expected to be a type `R`. Higher kinded /// types would have made this trait definition a lot clearer. pub(crate) trait NormalizebleRange { /// Normalize a plain, unnormalized value. Will be clamped to the bounds of the range if the /// normalized value exceeds `[0, 1]`. fn normalize(&self, plain: T) -> f32; /// Unnormalize a normalized value. Will be clamped to `[0, 1]` if the plain, unnormalized value /// would exceed that range. fn unnormalize(&self, normalized: f32) -> T; } impl Default for Range { fn default() -> Self { Self::Linear { min: 0.0, max: 1.0 } } } impl Default for Range { fn default() -> Self { Self::Linear { min: 0, max: 1 } } } impl NormalizebleRange for Range { fn normalize(&self, plain: f32) -> f32 { match &self { Range::Linear { min, max } => (plain - min) / (max - min), } .clamp(0.0, 1.0) } fn unnormalize(&self, normalized: f32) -> f32 { let normalized = normalized.clamp(0.0, 1.0); match &self { Range::Linear { min, max } => (normalized * (max - min)) + min, } } } impl NormalizebleRange for Range { fn normalize(&self, plain: i32) -> f32 { match &self { Range::Linear { min, max } => (plain - min) as f32 / (max - min) as f32, } .clamp(0.0, 1.0) } fn unnormalize(&self, normalized: f32) -> i32 { let normalized = normalized.clamp(0.0, 1.0); match &self { Range::Linear { min, max } => (normalized * (max - min) as f32).round() as i32 + min, } } } #[cfg(test)] mod tests { use super::*; fn make_linear_float_range() -> Range { Range::Linear { min: 10.0, max: 20.0, } } fn make_linear_int_range() -> Range { Range::Linear { min: -10, max: 10 } } #[test] fn range_normalize_linear_float() { let range = make_linear_float_range(); assert_eq!(range.normalize(17.5), 0.75); } #[test] fn range_normalize_linear_int() { let range = make_linear_int_range(); assert_eq!(range.normalize(-5), 0.25); } #[test] fn range_unnormalize_linear_float() { let range = make_linear_float_range(); assert_eq!(range.unnormalize(0.25), 12.5); } #[test] fn range_unnormalize_linear_int() { let range = make_linear_int_range(); assert_eq!(range.unnormalize(0.75), 5); } #[test] fn range_unnormalize_linear_int_rounding() { let range = make_linear_int_range(); assert_eq!(range.unnormalize(0.73), 5); } }