clampf 0.1.1

Clamped floating-point types.
Documentation
#![no_std]
//! Clamped floating-point types.
//!
//! Provides wrappers around floating-point values clamped to the range [0,1].
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;

/// A wrapper around a 32-bit floating-point value clamped to the range [0,1].
pub type Clamp32 = Clamp<f32>;
/// A wrapper around a 64-bit floating-point value clamped to the range [0,1].
pub type Clamp64 = Clamp<f64>;

/// A wrapper around a floating-point value clamped to the range [0,1].
#[derive(Clone, Copy, PartialEq, PartialOrd)]
pub struct Clamp<T: Float>(T);

impl<T: Float> Clamp<T> {
    /// Creates a new wrapper around the value without checking.
    ///
    /// # Safety
    ///
    /// The value must be in the range [0,1].
    #[inline]
    pub unsafe fn new_unchecked(value: T) -> Self {
        Clamp(value)
    }

    /// Creates a new wrapper around the value.
    #[inline]
    pub fn new(value: T) -> Self {
        unsafe { Self::new_unchecked(clamp(value)) }
    }

    /// Sets the contained value without checking.
    ///
    /// # Safety
    ///
    /// The value must be in the range [0,1].
    #[inline]
    pub unsafe fn set_unchecked(&mut self, value: T) {
        self.0 = value;
    }

    /// Sets the contained value.
    #[inline]
    pub fn set(&mut self, value: T) {
        unsafe { self.set_unchecked(clamp(value)) }
    }

    /// Updates the contained value using a function without checking.
    ///
    /// # Safety
    ///
    /// The value produced by a function must be in the range [0,1].
    #[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);
    }

    /// Updates the contained value using a function.
    #[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);
    }

    /// Returns the contained value.
    #[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()) }
    }
}

/// Constrain the value to the range [0,1].
#[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);
}