#![no_std]
extern crate num_traits;
use core::fmt::{self, Debug, Display, Formatter, LowerExp, UpperExp};
#[cfg(feature = "std")]
use num_traits::float::Float;
#[cfg(not(feature = "std"))]
use num_traits::float::FloatCore as Float;
use num_traits::Bounded;
pub type Clamp32 = Clamp<f32>;
pub type Clamp64 = Clamp<f64>;
#[derive(Clone, Copy, PartialEq, PartialOrd)]
pub struct Clamp<T: Float>(T);
impl<T: Float> Clamp<T> {
#[inline]
pub unsafe fn new_unchecked(value: T) -> Self {
Clamp(value)
}
#[inline]
pub fn new(value: T) -> Self {
unsafe { Self::new_unchecked(clamp(value)) }
}
#[inline]
pub unsafe fn set_unchecked(&mut self, value: T) {
self.0 = value;
}
#[inline]
pub fn set(&mut self, value: T) {
unsafe { self.set_unchecked(clamp(value)) }
}
#[inline]
pub unsafe fn update_unchecked<F>(&mut self, func: F)
where
F: FnOnce(T) -> T,
{
let current = self.get();
let modified = func(current);
self.set_unchecked(modified);
}
#[inline]
pub fn update<F>(&mut self, func: F)
where
F: FnOnce(T) -> T,
{
let current = self.get();
let modified = func(current);
self.set(modified);
}
#[inline]
pub fn get(&self) -> T {
self.0
}
}
impl<T: Debug + Float> Debug for Clamp<T> {
#[inline]
fn fmt(&self, fmter: &mut Formatter) -> fmt::Result {
self.get().fmt(fmter)
}
}
impl<T: Display + Float> Display for Clamp<T> {
#[inline]
fn fmt(&self, fmter: &mut Formatter) -> fmt::Result {
self.get().fmt(fmter)
}
}
impl<T: LowerExp + Float> LowerExp for Clamp<T> {
#[inline]
fn fmt(&self, fmter: &mut Formatter) -> fmt::Result {
self.get().fmt(fmter)
}
}
impl<T: UpperExp + Float> UpperExp for Clamp<T> {
#[inline]
fn fmt(&self, fmter: &mut Formatter) -> fmt::Result {
self.get().fmt(fmter)
}
}
impl<T: Float> Default for Clamp<T> {
#[inline]
fn default() -> Self {
Self::min_value()
}
}
impl<T: Float> Bounded for Clamp<T> {
#[inline]
fn min_value() -> Self {
unsafe { Self::new_unchecked(T::zero()) }
}
#[inline]
fn max_value() -> Self {
unsafe { Self::new_unchecked(T::one()) }
}
}
#[inline]
pub fn clamp<T: Float>(value: T) -> T {
num_traits::clamp(value, T::zero(), T::one())
}
#[cfg(test)]
mod tests {
use super::*;
macro_rules! def_tests {
($($float:ident),+) => {
$(
mod $float {
use core::$float;
use super::*;
#[test]
fn test_clamp_wrapper() {
assert_eq!(Clamp::<$float>::new(0.0).get(), 0.0);
assert_eq!(Clamp::<$float>::new(0.5).get(), 0.5);
assert_eq!(Clamp::<$float>::new(1.0).get(), 1.0);
assert_eq!(Clamp::<$float>::new(-0.2).get(), 0.0);
assert_eq!(Clamp::<$float>::new(1.2).get(), 1.0);
assert_eq!(Clamp::new($float::NEG_INFINITY).get(), 0.0);
assert_eq!(Clamp::new($float::INFINITY).get(), 1.0);
assert_eq!(Clamp::new($float::MIN).get(), 0.0);
assert_eq!(Clamp::new($float::MAX).get(), 1.0);
}
#[test]
fn test_clamp_func() {
assert_eq!(clamp::<$float>(0.0), 0.0);
assert_eq!(clamp::<$float>(0.5), 0.5);
assert_eq!(clamp::<$float>(1.0), 1.0);
assert_eq!(clamp::<$float>(-0.2), 0.0);
assert_eq!(clamp::<$float>(1.2), 1.0);
assert_eq!(clamp($float::NEG_INFINITY), 0.0);
assert_eq!(clamp($float::INFINITY), 1.0);
assert_eq!(clamp($float::MIN), 0.0);
assert_eq!(clamp($float::MAX), 1.0);
}
}
)+
};
}
def_tests!(f32, f64);
}