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