Skip to main content

unit_intervals/
lib.rs

1//! # unit-interval
2//!
3//! Small constrained float types for values in the closed intervals `[0, 1]`
4//! and `[-1, 1]`.
5//!
6//! This crate provides [`UnitInterval`] and [`SignedUnitInterval`] wrappers for
7//! normalized floating-point values. They are useful for values such as opacity,
8//! progress, blend factors, percentages represented as fractions, centered
9//! offsets, joystick axes, and other quantities where the valid range is part of
10//! the type.
11//!
12//! Constructors reject out-of-range values and `NaN`; saturating constructors
13//! clamp inputs into range. Operations that can leave the interval are available
14//! in checked and saturating forms, while operations that are mathematically
15//! closed over the interval return constrained values directly.
16//!
17//! # Examples
18//!
19//! ```
20//! use unit_intervals::UnitInterval;
21//!
22//! let opacity = UnitInterval::new(0.8).unwrap();
23//! let clamped = UnitInterval::saturating(1.2);
24//!
25//! assert_eq!(opacity.get(), 0.8);
26//! assert_eq!(clamped, UnitInterval::ONE);
27//! ```
28//!
29//! ```
30//! use unit_intervals::{SignedUnitInterval, UnitInterval};
31//!
32//! let axis = SignedUnitInterval::new(-0.5).unwrap();
33//! let scale = UnitInterval::new(0.25).unwrap();
34//!
35//! assert_eq!((axis * scale).get(), -0.125);
36//! assert_eq!(axis.saturating_add(scale).get(), -0.25);
37//! assert_eq!(
38//!     axis.checked_add(SignedUnitInterval::<f32>::ONE),
39//!     Some(SignedUnitInterval::<f32>::HALF),
40//! );
41//! ```
42//!
43//! # Crate features
44//!
45//! - `assertions`: enables internal invariant assertions in non-test builds.
46//!   Tests always enable these assertions.
47//! - `arbitrary`: enables [`arbitrary::Arbitrary`] support for generating
48//!   valid fuzz inputs.
49//! - `bytemuck`: enables [`bytemuck::Zeroable`], [`bytemuck::NoUninit`], and
50//!   [`bytemuck::CheckedBitPattern`] support. These wrappers do not implement
51//!   [`bytemuck::Pod`] because not every backing float bit pattern satisfies
52//!   their interval invariants.
53//! - `num-traits`: enables conversion and bounds traits from [`num_traits`].
54//! - `std`: enables APIs that require the Rust standard library. The crate is
55//!   otherwise `no_std`.
56//! - `rand_distr`: enables [`rand_distr`] distribution support for
57//!   [`UnitInterval`] and [`SignedUnitInterval`].
58//! - `rkyv`: enables zero-copy serialization and checked deserialization
59//!   through the inner floating-point value.
60//! - `serde`: enables transparent serialization and checked deserialization
61//!   through the inner floating-point value.
62//! - `unsafe`: allows unsafe code and enables unchecked constructors and
63//!   operations such as [`UnitInterval::new_unchecked`] and
64//!   [`SignedUnitInterval::new_unchecked`]. These APIs assume the caller has
65//!   already proven that the produced value is inside the relevant interval and
66//!   is not `NaN`.
67
68#![no_std]
69#![cfg_attr(
70    not(any(feature = "unsafe", feature = "bytemuck")),
71    forbid(unsafe_code)
72)]
73#![cfg_attr(any(feature = "unsafe", feature = "bytemuck"), allow(unsafe_code))]
74#![cfg_attr(docsrs, feature(doc_cfg))]
75
76#[cfg(any(test, feature = "std"))]
77extern crate std;
78
79#[cfg(feature = "rand_distr")]
80#[cfg_attr(docsrs, doc(cfg(feature = "rand_distr")))]
81pub mod random;
82mod signed_unit_interval;
83mod unit_interval;
84
85use core::ops::{Add, Div, Mul, Sub};
86pub use signed_unit_interval::SignedUnitInterval;
87pub use signed_unit_interval::SignedUnitIntervalError;
88pub use unit_interval::UnitInterval;
89pub use unit_interval::UnitIntervalError;
90
91mod private {
92    pub trait Sealed {}
93}
94
95/// Floating-point support required by [`UnitInterval`].
96///
97/// This trait is sealed and implemented only for `f32` and `f64`.
98pub trait UnitIntervalFloat:
99    private::Sealed
100    + Copy
101    + PartialOrd
102    + Add<Output = Self>
103    + Sub<Output = Self>
104    + Mul<Output = Self>
105    + Div<Output = Self>
106{
107    /// The additive identity, `0`.
108    const ZERO: Self;
109
110    /// The lower bound of the signed unit interval, `-1`.
111    const NEG_ONE: Self;
112
113    /// The multiplicative identity, `1`.
114    const ONE: Self;
115
116    /// The midpoint value, `0.5`.
117    const HALF: Self;
118
119    /// Clamps a value into `[0, 1]`.
120    ///
121    /// Implementations treat `NaN` as zero.
122    fn clamp_unit(self) -> Self;
123
124    /// Clamps a value into `[-1, 1]`.
125    ///
126    /// Implementations treat `NaN` as zero.
127    fn clamp_signed_unit(self) -> Self;
128}