1use super::{Angle, Length, Number, Percentage, Resolution, Time};
8use crate::derives::*;
9use crate::typed_om::{NumericBaseType, NumericType};
10use crate::values::generics::calc::{
11 self, CalcType, GenericCalcPercentageLeaf, SimplificationResult,
12};
13use crate::values::generics::Optional;
14use crate::Zero;
15use debug_unreachable::debug_unreachable;
16use serde::{Deserialize, Serialize};
17
18pub type CalcPercentageLeaf = GenericCalcPercentageLeaf<Percentage>;
20
21#[derive(
23 Clone,
24 Debug,
25 Deserialize,
26 MallocSizeOf,
27 PartialEq,
28 Serialize,
29 ToAnimatedZero,
30 ToCss,
31 ToResolvedValue,
32 ToTyped,
33)]
34#[allow(missing_docs)]
35#[repr(u8)]
36pub enum ComputedLeaf {
37 Length(Length),
38 Percentage(CalcPercentageLeaf),
39 Number(Number),
40 Angle(Angle),
41 Time(Time),
42 Resolution(Resolution),
43}
44
45impl ComputedLeaf {
46 pub(super) fn is_zero_length(&self) -> bool {
47 match *self {
48 Self::Length(ref l) => l.is_zero(),
49 Self::Percentage(..)
50 | Self::Number(..)
51 | Self::Angle(..)
52 | Self::Time(..)
53 | Self::Resolution(..) => false,
54 }
55 }
56}
57
58impl calc::CalcNodeLeaf for ComputedLeaf {
59 fn numeric_type(&self) -> NumericType {
60 match self {
61 Self::Length(_) => NumericType::length(),
62 Self::Percentage(p) => p.numeric_type(),
63 Self::Number(_) => NumericType::number(),
64 Self::Angle(_) => NumericType::angle(),
65 Self::Time(_) => NumericType::time(),
66 Self::Resolution(_) => NumericType::resolution(),
67 }
68 }
69
70 fn unitless_value(&self) -> Option<f32> {
71 Some(match *self {
72 Self::Length(ref l) => l.px(),
73 Self::Percentage(ref p) => p.get(),
74 Self::Number(n) => n,
75 Self::Angle(ref a) => a.degrees(),
76 Self::Time(ref t) => t.seconds(),
77 Self::Resolution(ref r) => r.dppx(),
78 })
79 }
80
81 fn canonical_value(&self) -> Option<f32> {
82 Some(match *self {
83 Self::Length(ref l) => l.px(),
84 Self::Percentage(ref p) => match p.hint {
85 Optional::Some(NumericBaseType::Percent) => p.get(),
89 _ => return None,
90 },
91 Self::Number(n) => n,
92 Self::Angle(ref a) => a.degrees(),
93 Self::Time(ref t) => t.seconds(),
94 Self::Resolution(ref r) => r.dppx(),
95 })
96 }
97
98 fn new_number(value: f32) -> Self {
99 Self::Number(value)
100 }
101
102 fn as_number(&self) -> Option<f32> {
103 match *self {
104 Self::Length(_)
105 | Self::Percentage(_)
106 | Self::Angle(_)
107 | Self::Time(_)
108 | Self::Resolution(_) => None,
109 Self::Number(value) => Some(value),
110 }
111 }
112
113 fn as_percentage(&self) -> Option<(f32, Optional<NumericBaseType>)> {
114 match *self {
115 Self::Percentage(p) => Some((p.get(), p.hint)),
116 _ => None,
117 }
118 }
119
120 fn as_angle_radians(&self) -> Option<f32> {
121 match *self {
122 Self::Angle(a) => Some(a.radians()),
123 _ => None,
124 }
125 }
126
127 fn new_angle_from_radians(radians: f32) -> Self {
128 Self::Angle(Angle::from_radians(radians))
129 }
130
131 fn new_from_typed_value(value: f32, numeric_type: NumericType) -> Result<Self, ()> {
132 let calc_type = numeric_type.as_calc_type()?;
133 let percent_hint = numeric_type.percent_hint();
134 Ok(match calc_type {
135 CalcType::Number => Self::new_number(value),
136 CalcType::Length => Self::Length(Length::new(value)),
137 CalcType::Angle => Self::Angle(Angle::from_degrees(value)),
138 CalcType::Time => Self::Time(Time::from_seconds(value)),
139 CalcType::Resolution => Self::Resolution(Resolution::from_dppx(value)),
140 CalcType::Percentage => Self::Percentage(CalcPercentageLeaf::new(value, percent_hint)),
141 })
142 }
143
144 fn compare(&self, other: &Self) -> Option<std::cmp::Ordering> {
145 use self::ComputedLeaf::*;
146 if std::mem::discriminant(self) != std::mem::discriminant(other) {
147 return None;
148 }
149
150 if matches!(self, Percentage(p) if p.hint != Optional::Some(NumericBaseType::Percent)) {
152 return None;
153 }
154
155 let Ok(self_negative) = self.is_negative() else {
156 return None;
157 };
158 let Ok(other_negative) = other.is_negative() else {
159 return None;
160 };
161 if self_negative != other_negative {
162 return Some(if self_negative {
163 std::cmp::Ordering::Less
164 } else {
165 std::cmp::Ordering::Greater
166 });
167 }
168
169 match (self, other) {
170 (Length(one), Length(other)) => one.partial_cmp(other),
171 (Percentage(one), Percentage(other)) => one.value.partial_cmp(&other.value),
172 (Number(one), Number(other)) => one.partial_cmp(other),
173 (Angle(one), Angle(other)) => one.partial_cmp(other),
174 (Time(one), Time(other)) => one.partial_cmp(other),
175 (Resolution(one), Resolution(other)) => one.partial_cmp(other),
176 _ => unsafe {
177 match *self {
178 Length(..) | Percentage(..) | Number(..) | Angle(..) | Time(..)
179 | Resolution(..) => {},
180 }
181 debug_unreachable!("Forgot to handle unit in compare()")
182 },
183 }
184 }
185
186 fn try_sum_in_place(&mut self, other: &Self) -> Result<(), ()> {
187 use self::ComputedLeaf::*;
188
189 if self.is_zero_length() {
191 *self = other.clone();
192 return Ok(());
193 }
194
195 if other.is_zero_length() {
196 return Ok(());
197 }
198
199 if std::mem::discriminant(self) != std::mem::discriminant(other) {
200 return Err(());
201 }
202
203 match (self, other) {
204 (&mut Length(ref mut one), Length(other)) => {
205 *one += *other;
206 },
207 (&mut Percentage(ref mut one), Percentage(other)) => {
208 *one = CalcPercentageLeaf::new(one.get() + other.get(), one.combined_hint(other));
209 },
210 (&mut Number(ref mut one), Number(other)) => {
211 *one += *other;
212 },
213 (&mut Angle(ref mut one), Angle(other)) => {
214 *one += *other;
215 },
216 (&mut Time(ref mut one), Time(other)) => {
217 *one += *other;
218 },
219 (&mut Resolution(ref mut one), Resolution(other)) => {
220 *one += *other;
221 },
222 _ => unsafe {
223 match *other {
224 Length(..) | Percentage(..) | Number(..) | Angle(..) | Time(..)
225 | Resolution(..) => {},
226 }
227 debug_unreachable!("Forgot to handle unit in try_sum_in_place()")
228 },
229 }
230
231 Ok(())
232 }
233
234 fn try_product_in_place(&mut self, other: &mut Self) -> bool {
235 if let Self::Number(ref mut left) = *self {
236 if let Self::Number(ref right) = *other {
237 *left *= *right;
239 true
240 } else {
241 if other.map(|v| v * *left).is_ok() {
244 std::mem::swap(self, other);
245 true
246 } else {
247 false
248 }
249 }
250 } else if let Self::Number(ref right) = *other {
251 self.map(|v| v * *right).is_ok()
254 } else {
255 false
257 }
258 }
259
260 fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>
261 where
262 O: Fn(f32, f32) -> f32,
263 {
264 use self::ComputedLeaf::*;
265 if std::mem::discriminant(self) != std::mem::discriminant(other) {
266 return Err(());
267 }
268 Ok(match (self, other) {
269 (Length(one), Length(other)) => Length(super::Length::new(op(one.px(), other.px()))),
270 (Percentage(one), Percentage(other)) => Self::Percentage(CalcPercentageLeaf::new(
271 op(one.get(), other.get()),
272 one.combined_hint(other),
273 )),
274 (&Number(one), &Number(other)) => Self::Number(op(one, other)),
275 (Angle(one), Angle(other)) => Self::Angle(super::Angle::from_degrees(op(
276 one.degrees(),
277 other.degrees(),
278 ))),
279 (Time(one), Time(other)) => Self::Time(super::Time::from_seconds(op(
280 one.seconds(),
281 other.seconds(),
282 ))),
283 (Resolution(one), Resolution(other)) => {
284 Self::Resolution(super::Resolution::from_dppx(op(one.dppx(), other.dppx())))
285 },
286 _ => unsafe {
287 match *self {
288 Length(..) | Percentage(..) | Number(..) | Angle(..) | Time(..)
289 | Resolution(..) => {},
290 }
291 debug_unreachable!("Forgot to handle unit in try_op()")
292 },
293 })
294 }
295
296 fn map(&mut self, mut op: impl FnMut(f32) -> f32) -> Result<(), ()> {
297 let _: () = match self {
298 Self::Length(value) => {
299 *value = Length::new(op(value.px()));
300 },
301 Self::Percentage(p) => {
302 *p = CalcPercentageLeaf::new(op(p.get()), p.hint);
303 },
304 Self::Number(value) => {
305 *value = op(*value);
306 },
307 Self::Angle(value) => {
308 *value = Angle::from_degrees(op(value.degrees()));
309 },
310 Self::Time(value) => {
311 *value = Time::from_seconds(op(value.seconds()));
312 },
313 Self::Resolution(value) => {
314 *value = Resolution::from_dppx(op(value.dppx()));
315 },
316 };
317 Ok(())
318 }
319
320 fn simplify(&mut self) -> SimplificationResult {
321 SimplificationResult::Unchanged
322 }
323
324 fn sort_key(&self) -> calc::SortKey {
325 match *self {
326 Self::Length(..) => calc::SortKey::Px,
327 Self::Percentage(..) => calc::SortKey::Percentage,
328 Self::Number(..) => calc::SortKey::Number,
329 Self::Angle(..) => calc::SortKey::Deg,
330 Self::Time(..) => calc::SortKey::S,
331 Self::Resolution(..) => calc::SortKey::Dppx,
332 }
333 }
334}