Skip to main content

math_core_renderer_internal/
length.rs

1//! Lengths, both absolute and relative to font size.
2
3use alloc::string::String;
4
5use crate::attribute::MathSpacing;
6
7#[cfg(feature = "serde")]
8use serde::Serialize;
9use strum_macros::IntoStaticStr;
10
11#[derive(Debug, Clone, Copy, PartialEq, IntoStaticStr)]
12#[cfg_attr(feature = "serde", derive(Serialize))]
13pub enum LengthUnit {
14    // absolute unit
15    #[strum(serialize = "rem")]
16    Rem,
17    // relative units
18    #[strum(serialize = "em")]
19    Em,
20    #[strum(serialize = "ex")]
21    Ex,
22}
23
24#[derive(Debug, Clone, Copy, PartialEq)]
25#[cfg_attr(feature = "serde", derive(Serialize))]
26pub struct Length {
27    value: LengthValue,
28    pub(crate) unit: LengthUnit,
29}
30
31#[derive(Debug, Clone, Copy, PartialEq)]
32#[cfg_attr(feature = "serde", derive(Serialize))]
33#[repr(transparent)]
34#[cfg_attr(feature = "serde", serde(transparent))]
35pub struct LengthValue(pub(crate) f32);
36
37impl Length {
38    pub const fn new(value: f32, unit: LengthUnit) -> Self {
39        Length {
40            value: LengthValue(value),
41            unit,
42        }
43    }
44
45    pub fn push_to_string(&self, output: &mut String) {
46        let mut buffer = dtoa::Buffer::new();
47        let result = buffer.format(self.value.0);
48        // let _ = write!(output, "{}", self.value.0).is_ok();
49        output.push_str(result.strip_suffix(".0").unwrap_or(result));
50        if self.value.0 != 0.0 {
51            output.push_str(<&'static str>::from(self.unit));
52        }
53    }
54
55    pub const fn none() -> Self {
56        Length {
57            value: LengthValue(f32::NAN),
58            unit: LengthUnit::Rem,
59        }
60    }
61
62    pub const fn zero() -> Self {
63        Length {
64            value: LengthValue(0.0),
65            unit: LengthUnit::Rem,
66        }
67    }
68
69    pub const fn into_parts(self) -> (LengthValue, LengthUnit) {
70        (self.value, self.unit)
71    }
72
73    pub fn from_parts(value: LengthValue, unit: LengthUnit) -> Option<Self> {
74        if value.0.is_finite() {
75            Some(Length { value, unit })
76        } else {
77            None
78        }
79    }
80
81    pub const fn is_negative(self) -> bool {
82        self.value.0 < 0.0
83    }
84}
85
86impl From<MathSpacing> for Length {
87    fn from(spacing: MathSpacing) -> Length {
88        let value = LengthValue(match spacing {
89            MathSpacing::Zero => 0.0,
90            MathSpacing::ThreeMu => 0.1667,
91            MathSpacing::FourMu => 0.2222,
92            MathSpacing::FiveMu => 0.2778,
93        });
94        Length {
95            value,
96            unit: LengthUnit::Em,
97        }
98    }
99}
100
101#[derive(Debug, Clone, Copy, PartialEq)]
102#[cfg_attr(feature = "serde", derive(Serialize))]
103pub struct LengthSet {
104    pub rem: LengthValue,
105    pub em: LengthValue,
106    pub ex: LengthValue,
107}
108
109impl LengthSet {
110    pub fn zero() -> LengthSet {
111        LengthSet {
112            rem: LengthValue(0.0),
113            em: LengthValue(0.0),
114            ex: LengthValue(0.0),
115        }
116    }
117    pub fn iter(self) -> impl Iterator<Item = Length> {
118        struct Iter(LengthSet);
119        impl Iterator for Iter {
120            type Item = Length;
121            fn next(&mut self) -> Option<Length> {
122                if self.0.rem.0 != 0.0 {
123                    let rem = Length::from_parts(self.0.rem, LengthUnit::Rem);
124                    self.0.rem.0 = 0.0;
125                    rem
126                } else if self.0.em.0 != 0.0 {
127                    let em = Length::from_parts(self.0.em, LengthUnit::Em);
128                    self.0.em.0 = 0.0;
129                    em
130                } else if self.0.ex.0 != 0.0 {
131                    let ex = Length::from_parts(self.0.ex, LengthUnit::Ex);
132                    self.0.ex.0 = 0.0;
133                    ex
134                } else {
135                    None
136                }
137            }
138        }
139        Iter(self)
140    }
141}
142
143impl From<Length> for LengthSet {
144    fn from(value: Length) -> Self {
145        LengthSet::zero() + value
146    }
147}
148
149impl core::ops::AddAssign for LengthSet {
150    fn add_assign(&mut self, rhs: Self) {
151        *self = LengthSet {
152            rem: LengthValue(self.rem.0 + rhs.rem.0),
153            em: LengthValue(self.em.0 + rhs.em.0),
154            ex: LengthValue(self.ex.0 + rhs.ex.0),
155        };
156    }
157}
158
159impl core::ops::Add<LengthSet> for LengthSet {
160    type Output = LengthSet;
161
162    fn add(mut self, rhs: Self) -> Self::Output {
163        self += rhs;
164        self
165    }
166}
167
168impl core::ops::Add<Length> for LengthSet {
169    type Output = LengthSet;
170
171    fn add(mut self, rhs: Length) -> Self::Output {
172        match rhs.into_parts() {
173            (value, LengthUnit::Rem) => self.rem.0 += value.0,
174            (value, LengthUnit::Em) => self.em.0 += value.0,
175            (value, LengthUnit::Ex) => self.ex.0 += value.0,
176        }
177        self
178    }
179}
180
181#[cfg(test)]
182mod tests {
183    use super::*;
184    use LengthUnit::*;
185
186    #[test]
187    fn test_write() {
188        let mut output = String::new();
189        Length::new(0.0, Rem).push_to_string(&mut output);
190        assert_eq!(&output, "0");
191        output.clear();
192        Length::new(1.0, Rem).push_to_string(&mut output);
193        assert_eq!(&output, "1rem");
194        output.clear();
195        Length::new(10.0, Rem).push_to_string(&mut output);
196        assert_eq!(&output, "10rem");
197        output.clear();
198        Length::new(5965232.0, Rem).push_to_string(&mut output);
199        assert_eq!(&output, "5965232rem");
200        output.clear();
201        Length::new(-5965232.0, Rem).push_to_string(&mut output);
202        assert_eq!(&output, "-5965232rem");
203    }
204
205    #[test]
206    fn test_write_relative() {
207        let mut output = String::new();
208        Length::new(0.0, Em).push_to_string(&mut output);
209        assert_eq!(&output, "0");
210        output.clear();
211        Length::new(0.0, Ex).push_to_string(&mut output);
212        assert_eq!(&output, "0");
213        output.clear();
214        Length::new(1.0, Em).push_to_string(&mut output);
215        assert_eq!(&output, "1em");
216        output.clear();
217        Length::new(1.0, Ex).push_to_string(&mut output);
218        assert_eq!(&output, "1ex");
219        output.clear();
220        Length::new(546.0, Em).push_to_string(&mut output);
221        assert_eq!(&output, "546em");
222        output.clear();
223        Length::new(546.0, Ex).push_to_string(&mut output);
224        assert_eq!(&output, "546ex");
225        output.clear();
226        Length::new(-546.0, Em).push_to_string(&mut output);
227        assert_eq!(&output, "-546em");
228        output.clear();
229        Length::new(-546.0, Ex).push_to_string(&mut output);
230        assert_eq!(&output, "-546ex");
231        output.clear();
232    }
233}