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}