math_core_renderer_internal/
length.rs1use 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 #[strum(serialize = "rem")]
16 Rem,
17 #[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 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}