From 88bfae96084db5bd2f5ec68e036d24c71a1f37e7 Mon Sep 17 00:00:00 2001 From: numzero Date: Sat, 29 Jun 2024 00:10:17 +0300 Subject: [PATCH] Unify limiters --- src/bin/flat/fns.rs | 90 ++++++++++++------------------------- src/bin/flat/tube/metric.rs | 4 +- 2 files changed, 30 insertions(+), 64 deletions(-) diff --git a/src/bin/flat/fns.rs b/src/bin/flat/fns.rs index a4ed940..b1f7d7e 100644 --- a/src/bin/flat/fns.rs +++ b/src/bin/flat/fns.rs @@ -1,13 +1,18 @@ use crate::float_fun::FloatExt2; +pub trait Limiter { + fn value(&self, x: f32) -> f32; + fn derivative(&self, x: f32) -> f32; +} + pub struct LinearLimiter { pub min: f32, pub max: f32, } -impl LinearLimiter { - pub fn value(&self, x: f32) -> f32 { (self.min, self.max).inverse_lerp(x.abs()).clamp(0.0, 1.0) } - pub fn derivative(&self, x: f32) -> f32 { if x.abs() > self.min && x.abs() < self.max { x.signum() / (self.max - self.min) } else { 0.0 } } +impl Limiter for LinearLimiter { + fn value(&self, x: f32) -> f32 { (self.min, self.max).inverse_lerp(x.abs()).clamp(0.0, 1.0) } + fn derivative(&self, x: f32) -> f32 { if x.abs() > self.min && x.abs() < self.max { x.signum() / (self.max - self.min) } else { 0.0 } } } pub struct SmoothstepLimiter { @@ -15,12 +20,12 @@ pub struct SmoothstepLimiter { pub max: f32, } -impl SmoothstepLimiter { - pub fn value(&self, x: f32) -> f32 { +impl Limiter for SmoothstepLimiter { + fn value(&self, x: f32) -> f32 { let y = (self.min, self.max).inverse_lerp(x.abs()).clamp(0.0, 1.0); 3.0 * y * y - 2.0 * y * y * y } - pub fn derivative(&self, x: f32) -> f32 { + fn derivative(&self, x: f32) -> f32 { if x.abs() > self.min && x.abs() < self.max { let t = (self.min, self.max).inverse_lerp(x.abs()); 6.0 * x.signum() * t * (1.0 - t) / (self.max - self.min) @@ -35,12 +40,12 @@ pub struct SmootherstepLimiter { pub max: f32, } -impl SmootherstepLimiter { - pub fn value(&self, x: f32) -> f32 { +impl Limiter for SmootherstepLimiter { + fn value(&self, x: f32) -> f32 { let y = (self.min, self.max).inverse_lerp(x.abs()).clamp(0.0, 1.0); 6.0 * y.powi(5) - 15.0 * y.powi(4) + 10.0 * y.powi(3) } - pub fn derivative(&self, x: f32) -> f32 { + fn derivative(&self, x: f32) -> f32 { if x.abs() > self.min && x.abs() < self.max { let t = (self.min, self.max).inverse_lerp(x.abs()); 30.0 * (t * (1.0 - t)).powi(2) * x.signum() / (self.max - self.min) @@ -79,26 +84,24 @@ impl QuadraticAccelerator { #[cfg(test)] mod test { + use super::*; use approx::{abs_diff_eq, AbsDiffEq, assert_abs_diff_eq}; - #[test] - fn test_linear_limiter() { - let testee = super::LinearLimiter { min: 20.0, max: 30.0 }; + fn test_limiter(testee: impl Limiter, min: f32, max: f32, δ: f32) { let ε = 1.0e-4f32; - let δ = 1.0 / 8.0; // Mathematically, you want this to be small. Computationally, you don’t. let margin = 1.0 / 16.0; let mul = 1.0 + margin; - for x in itertools_num::linspace(0., testee.min, 10) { + for x in itertools_num::linspace(0., min, 10) { assert_abs_diff_eq!(testee.value(x), 0., epsilon = ε); assert_abs_diff_eq!(testee.value(-x), 0., epsilon = ε); } - for x in itertools_num::linspace(testee.max, mul * testee.max, 10) { + for x in itertools_num::linspace(max, mul * max, 10) { assert_abs_diff_eq!(testee.value(x), 1., epsilon = ε); assert_abs_diff_eq!(testee.value(-x), 1., epsilon = ε); } - for x in itertools_num::linspace(-mul * testee.max, mul * testee.max, 100) { + for x in itertools_num::linspace(-mul * max, mul * max, 100) { // Currently, the derivative is discontinuous at ±min and ±max... let’s just skip these for now. - if x.abs().abs_diff_eq(&testee.min, δ) || x.abs().abs_diff_eq(&testee.max, δ) { + if x.abs().abs_diff_eq(&min, δ) || x.abs().abs_diff_eq(&max, δ) { continue; } let df_num = (testee.value(x + δ) - testee.value(x - δ)) / (2. * δ); @@ -108,55 +111,18 @@ mod test { } #[test] - fn test_smoothsteo_limiter() { - let testee = super::SmoothstepLimiter { min: 20.0, max: 30.0 }; - let ε = 1.0e-4f32; - let δ = 1.0 / 8.0; // Mathematically, you want this to be small. Computationally, you don’t. - let margin = 1.0 / 16.0; - let mul = 1.0 + margin; - for x in itertools_num::linspace(0., testee.min, 10) { - assert_abs_diff_eq!(testee.value(x), 0., epsilon = ε); - assert_abs_diff_eq!(testee.value(-x), 0., epsilon = ε); - } - for x in itertools_num::linspace(testee.max, mul * testee.max, 10) { - assert_abs_diff_eq!(testee.value(x), 1., epsilon = ε); - assert_abs_diff_eq!(testee.value(-x), 1., epsilon = ε); - } - for x in itertools_num::linspace(-mul * testee.max, mul * testee.max, 100) { - // Currently, the derivative is discontinuous at ±min and ±max... let’s just skip these for now. - if x.abs().abs_diff_eq(&testee.min, δ) || x.abs().abs_diff_eq(&testee.max, δ) { - continue; - } - let df_num = (testee.value(x + δ) - testee.value(x - δ)) / (2. * δ); - let df_expl = testee.derivative(x); - assert!(abs_diff_eq!(df_expl, df_num, epsilon = ε), "At x={x}, df/dx:\nnumerical: {df_num}\nexplicit: {df_expl}\n"); - } + fn test_linear_limiter() { + test_limiter(LinearLimiter { min: 20.0, max: 30.0 }, 20.0, 30.0, 1.0 / 8.0); + } + + #[test] + fn test_smoothstep_limiter() { + test_limiter(SmoothstepLimiter { min: 20.0, max: 30.0 }, 20.0, 30.0, 1.0 / 32.0); } #[test] fn test_smootherstep_limiter() { - let testee = super::SmootherstepLimiter { min: 20.0, max: 30.0 }; - let ε = 1.0e-4f32; - let δ = 1.0 / 32.0; // Mathematically, you want this to be small. Computationally, you don’t. - let margin = 1.0 / 16.0; - let mul = 1.0 + margin; - for x in itertools_num::linspace(0., testee.min, 10) { - assert_abs_diff_eq!(testee.value(x), 0., epsilon = ε); - assert_abs_diff_eq!(testee.value(-x), 0., epsilon = ε); - } - for x in itertools_num::linspace(testee.max, mul * testee.max, 10) { - assert_abs_diff_eq!(testee.value(x), 1., epsilon = ε); - assert_abs_diff_eq!(testee.value(-x), 1., epsilon = ε); - } - for x in itertools_num::linspace(-mul * testee.max, mul * testee.max, 100) { - // Currently, the derivative is discontinuous at ±min and ±max... let’s just skip these for now. - if x.abs().abs_diff_eq(&testee.min, δ) || x.abs().abs_diff_eq(&testee.max, δ) { - continue; - } - let df_num = (testee.value(x + δ) - testee.value(x - δ)) / (2. * δ); - let df_expl = testee.derivative(x); - assert!(abs_diff_eq!(df_expl, df_num, epsilon = ε), "At x={x}, df/dx:\nnumerical: {df_num}\nexplicit: {df_expl}\n"); - } + test_limiter(SmootherstepLimiter { min: 20.0, max: 30.0 }, 20.0, 30.0, 1.0 / 32.0); } #[test] diff --git a/src/bin/flat/tube/metric.rs b/src/bin/flat/tube/metric.rs index 689ace5..45dee22 100644 --- a/src/bin/flat/tube/metric.rs +++ b/src/bin/flat/tube/metric.rs @@ -1,5 +1,5 @@ use glam::{f32, Mat2, Vec2, vec2}; -use crate::fns; +use crate::fns::{self, Limiter}; use crate::riemann::{Decomp2, Metric, Tens2}; #[derive(Copy, Clone, Debug)] @@ -11,7 +11,7 @@ pub struct Tube { } impl Tube { - fn fx(&self) -> fns::SmootherstepLimiter { fns::SmootherstepLimiter { min: self.inner_radius, max: self.outer_radius } } + fn fx(&self) -> impl Limiter { fns::SmootherstepLimiter { min: self.inner_radius, max: self.outer_radius } } fn fy(&self) -> fns::QuadraticAccelerator { fns::QuadraticAccelerator { internal: self.internal_halflength, external: self.external_halflength } } pub fn y(&self, v: f32) -> f32 { self.fy().x(v) }