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