float_pigment_css/
length_num.rs

1//! General length type utilities.
2
3use az::SaturatingCast;
4use fixed::types::extra::*;
5use num_traits::{bounds::Bounded, sign::Signed, NumAssign, Zero};
6
7/// A generic length trait.
8///
9/// A more specific number type is needed for CSS handling.
10/// In most cases `f32` is what you need, but sometimes fixed-pointer types are preferred.
11pub trait LengthNum:
12    NumAssign + Bounded + Signed + PartialEq + PartialOrd + Clone + Copy + core::fmt::Debug + Zero
13{
14    /// A target type used for hashing.
15    type Hashable: Eq + core::hash::Hash + core::fmt::Debug;
16
17    /// Convert to the hashable type.
18    fn to_hashable(&self) -> Self::Hashable;
19
20    /// The number is normal (a.k.a. `f32::is_normal`) or not.
21    fn is_normal(&self) -> bool;
22
23    /// Convert from `f32`.
24    fn from_f32(v: f32) -> Self;
25
26    /// Convert to `f32`.
27    fn to_f32(self) -> f32;
28
29    /// Multiply `f32`.
30    fn mul_f32(self, v: f32) -> Self {
31        self * Self::from_f32(v)
32    }
33
34    /// Divide `f32`.
35    fn div_f32(self, v: f32) -> Self {
36        self / Self::from_f32(v)
37    }
38
39    /// Convert from `i32`.
40    fn from_i32(v: i32) -> Self;
41
42    /// Multiply `i32`.
43    fn mul_i32(self, v: i32) -> Self {
44        self * Self::from_i32(v)
45    }
46
47    /// Divide `i32`.
48    fn div_i32(self, v: i32) -> Self {
49        self / Self::from_i32(v)
50    }
51
52    /// Limit the number with an upper bound.
53    fn upper_bound(self, v: Self) -> Self {
54        if self <= v {
55            self
56        } else {
57            v
58        }
59    }
60
61    /// Limit the number with an lower bound.
62    fn lower_bound(self, v: Self) -> Self {
63        if self >= v {
64            self
65        } else {
66            v
67        }
68    }
69}
70
71/// Get the sum of a series of `LengthNum`s.
72pub fn length_sum<L: LengthNum>(iter: impl Iterator<Item = L>) -> L {
73    let mut ret = L::zero();
74    for x in iter {
75        ret += x;
76    }
77    ret
78}
79
80impl LengthNum for f32 {
81    type Hashable = u32;
82
83    fn to_hashable(&self) -> Self::Hashable {
84        if self.is_normal() {
85            self.to_bits()
86        } else {
87            f32::NEG_INFINITY.to_bits()
88        }
89    }
90
91    fn is_normal(&self) -> bool {
92        f32::is_normal(*self)
93    }
94
95    fn from_f32(v: f32) -> Self {
96        v
97    }
98
99    fn to_f32(self) -> f32 {
100        self
101    }
102
103    fn from_i32(v: i32) -> Self {
104        v as f32
105    }
106}
107
108impl LengthNum for f64 {
109    type Hashable = u64;
110
111    fn to_hashable(&self) -> Self::Hashable {
112        if self.is_normal() {
113            self.to_bits()
114        } else {
115            f64::NEG_INFINITY.to_bits()
116        }
117    }
118
119    fn is_normal(&self) -> bool {
120        f64::is_normal(*self)
121    }
122
123    fn from_f32(v: f32) -> Self {
124        v as f64
125    }
126
127    fn to_f32(self) -> f32 {
128        self as f32
129    }
130
131    fn from_i32(v: i32) -> Self {
132        v as f64
133    }
134}
135
136macro_rules! raw_impl {
137    ($base:ty) => {
138        type Hashable = Self;
139
140        fn to_hashable(&self) -> Self::Hashable {
141            *self
142        }
143
144        fn is_normal(&self) -> bool {
145            true
146        }
147        fn from_f32(v: f32) -> Self {
148            let base = Self::from_num(1).to_bits() as f32;
149            Self::from_bits((v * base) as $base)
150        }
151
152        fn to_f32(self) -> f32 {
153            self.saturating_cast()
154        }
155
156        fn from_i32(v: i32) -> Self {
157            v.saturating_cast()
158        }
159    };
160}
161
162impl<Frac> LengthNum for fixed::FixedI32<Frac>
163where
164    Frac: LeEqU32 + IsLessOrEqual<U30, Output = True>,
165{
166    raw_impl!(i32);
167}
168
169impl<Frac> LengthNum for fixed::FixedI64<Frac>
170where
171    Frac: LeEqU64 + IsLessOrEqual<U62, Output = True>,
172{
173    raw_impl!(i64);
174}