1#[cfg(feature = "malloc_size_of")]
6use malloc_size_of::malloc_size_of_is_0;
7#[cfg(feature = "num_traits")]
8use num_traits::Zero;
9#[cfg(feature = "serde_serialization")]
10use serde::{Deserialize, Deserializer, Serialize};
11
12use std::iter::Sum;
13use std::{
14 default::Default,
15 fmt,
16 ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, Sub, SubAssign},
17};
18
19pub const AU_PER_PX: i32 = 60;
21pub const MIN_AU: Au = Au(-((1 << 30) - 1));
23pub const MAX_AU: Au = Au((1 << 30) - 1);
27
28#[repr(transparent)]
29#[derive(Clone, Copy, Hash, PartialEq, PartialOrd, Eq, Ord, Default)]
30#[cfg_attr(feature = "serde_serialization", derive(Serialize), serde(transparent))]
31pub struct Au(pub i32);
38
39impl fmt::Debug for Au {
40 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
41 write!(f, "{}px", self.to_f64_px())
42 }
43}
44
45#[cfg(feature = "malloc_size_of")]
46malloc_size_of_is_0!(Au);
47
48#[cfg(feature = "serde_serialization")]
49impl<'de> Deserialize<'de> for Au {
50 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Au, D::Error> {
51 Ok(Au(i32::deserialize(deserializer)?).clamp())
52 }
53}
54
55#[cfg(feature = "num_traits")]
56impl Zero for Au {
57 #[inline]
58 fn zero() -> Au {
59 Au(0)
60 }
61
62 #[inline]
63 fn is_zero(&self) -> bool {
64 self.0 == 0
65 }
66}
67
68impl Add for Au {
69 type Output = Au;
70
71 #[inline]
72 fn add(self, other: Au) -> Au {
73 Au(self.0 + other.0).clamp()
74 }
75}
76
77impl Sub for Au {
78 type Output = Au;
79
80 #[inline]
81 fn sub(self, other: Au) -> Au {
82 Au(self.0 - other.0).clamp()
83 }
84}
85
86impl Mul<Au> for i32 {
87 type Output = Au;
88
89 #[inline]
90 fn mul(self, other: Au) -> Au {
91 if let Some(new) = other.0.checked_mul(self) {
92 Au(new).clamp()
93 } else if (self > 0) ^ (other.0 > 0) {
94 MIN_AU
95 } else {
96 MAX_AU
97 }
98 }
99}
100
101impl Mul<i32> for Au {
102 type Output = Au;
103
104 #[inline]
105 fn mul(self, other: i32) -> Au {
106 if let Some(new) = self.0.checked_mul(other) {
107 Au(new).clamp()
108 } else if (self.0 > 0) ^ (other > 0) {
109 MIN_AU
110 } else {
111 MAX_AU
112 }
113 }
114}
115
116impl Div for Au {
117 type Output = i32;
118
119 #[inline]
120 fn div(self, other: Au) -> i32 {
121 self.0 / other.0
122 }
123}
124
125impl Div<i32> for Au {
126 type Output = Au;
127
128 #[inline]
129 fn div(self, other: i32) -> Au {
130 Au(self.0 / other)
131 }
132}
133
134impl Rem for Au {
135 type Output = Au;
136
137 #[inline]
138 fn rem(self, other: Au) -> Au {
139 Au(self.0 % other.0)
140 }
141}
142
143impl Rem<i32> for Au {
144 type Output = Au;
145
146 #[inline]
147 fn rem(self, other: i32) -> Au {
148 Au(self.0 % other)
149 }
150}
151
152impl Neg for Au {
153 type Output = Au;
154
155 #[inline]
156 fn neg(self) -> Au {
157 Au(-self.0)
158 }
159}
160
161impl AddAssign for Au {
162 #[inline]
163 fn add_assign(&mut self, other: Au) {
164 *self = (*self + other).clamp();
165 }
166}
167
168impl SubAssign for Au {
169 #[inline]
170 fn sub_assign(&mut self, other: Au) {
171 *self = (*self - other).clamp();
172 }
173}
174
175impl MulAssign<i32> for Au {
176 #[inline]
177 fn mul_assign(&mut self, other: i32) {
178 *self = (*self * other).clamp();
179 }
180}
181
182impl DivAssign<i32> for Au {
183 #[inline]
184 fn div_assign(&mut self, other: i32) {
185 *self = (*self / other).clamp();
186 }
187}
188
189impl<'a> Sum<&'a Self> for Au {
190 fn sum<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
191 iter.fold(Self::zero(), |a, b| a + *b)
192 }
193}
194
195impl Sum<Self> for Au {
196 fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
197 iter.fold(Self::zero(), |a, b| a + b)
198 }
199}
200
201impl Au {
202 #[inline]
204 pub fn new(value: i32) -> Au {
205 Au(value).clamp()
206 }
207
208 #[inline]
209 fn clamp(self) -> Self {
210 Ord::clamp(self, MIN_AU, MAX_AU)
211 }
212
213 #[inline]
214 pub fn scale_by(self, factor: f32) -> Au {
215 let new_float = ((self.0 as f64) * factor as f64).round();
216 Au::from_f64_au(new_float)
217 }
218
219 #[inline]
220 pub fn scale_by_trunc(self, factor: f32) -> Au {
222 let new_float = ((self.0 as f64) * factor as f64).trunc();
223 Au::from_f64_au(new_float)
224 }
225
226 #[inline]
227 pub fn from_f64_au(float: f64) -> Self {
228 Au(float.clamp(MIN_AU.0 as f64, MAX_AU.0 as f64) as i32)
230 }
231
232 #[inline]
233 pub fn from_px(px: i32) -> Au {
234 Au(px) * AU_PER_PX
235 }
236
237 #[inline]
239 pub fn to_px(self) -> i32 {
240 self.0 / AU_PER_PX
241 }
242
243 #[inline]
245 pub fn ceil_to_px(self) -> i32 {
246 ((self.0 as f64) / (AU_PER_PX as f64)).ceil() as i32
247 }
248
249 #[inline]
250 pub fn to_nearest_px(self) -> i32 {
251 ((self.0 as f64) / (AU_PER_PX as f64)).round() as i32
252 }
253
254 #[inline]
255 pub fn to_nearest_pixel(self, pixels_per_px: f32) -> f32 {
256 ((self.0 as f32) / (AU_PER_PX as f32) * pixels_per_px).round() / pixels_per_px
257 }
258
259 #[inline]
260 pub fn to_f32_px(self) -> f32 {
261 (self.0 as f32) / (AU_PER_PX as f32)
262 }
263
264 #[inline]
265 pub fn to_f64_px(self) -> f64 {
266 (self.0 as f64) / (AU_PER_PX as f64)
267 }
268
269 #[inline]
270 pub fn from_f32_px(px: f32) -> Au {
271 let float = (px * AU_PER_PX as f32).round();
272 Au::from_f64_au(float as f64)
273 }
274
275 #[inline]
276 pub fn from_f64_px(px: f64) -> Au {
277 let float = (px * AU_PER_PX as f64).round();
278 Au::from_f64_au(float)
279 }
280
281 #[inline]
282 pub fn from_f32_px_trunc(px: f32) -> Au {
283 let float = (px * AU_PER_PX as f32).trunc();
284 Au::from_f64_au(float as f64)
285 }
286
287 #[inline]
288 pub fn from_f64_px_trunc(px: f64) -> Au {
289 let float = (px * AU_PER_PX as f64).trunc();
290 Au::from_f64_au(float)
291 }
292
293 #[inline]
294 pub fn abs(self) -> Self {
295 Au(self.0.abs())
296 }
297
298 #[inline]
299 pub fn max_assign(&mut self, other: Self) {
300 *self = (*self).max(other);
301 }
302
303 #[inline]
304 pub fn min_assign(&mut self, other: Self) {
305 *self = (*self).min(other);
306 }
307}
308
309#[test]
310fn create() {
311 assert_eq!(Au::zero(), Au(0));
312 assert_eq!(Au::default(), Au(0));
313 assert_eq!(Au::new(7), Au(7));
314}
315
316#[test]
317fn operations() {
318 assert_eq!(Au(7) + Au(5), Au(12));
319 assert_eq!(MAX_AU + Au(1), MAX_AU);
320
321 assert_eq!(Au(7) - Au(5), Au(2));
322 assert_eq!(MIN_AU - Au(1), MIN_AU);
323
324 assert_eq!(Au(7) * 5, Au(35));
325 assert_eq!(5 * Au(7), Au(35));
326 assert_eq!(MAX_AU * -1, MIN_AU);
327 assert_eq!(MIN_AU * -1, MAX_AU);
328 assert_eq!(-1 * MAX_AU, MIN_AU);
329 assert_eq!(-1 * MIN_AU, MAX_AU);
330
331 assert_eq!((Au(14) / 5) * 5 + Au(14) % 5, Au(14));
332 assert_eq!((Au(14) / Au(5)) * Au(5) + Au(14) % Au(5), Au(14));
333
334 assert_eq!(Au(35) / 5, Au(7));
335 assert_eq!(Au(35) % 6, Au(5));
336
337 assert_eq!(Au(35) / Au(5), 7);
338 assert_eq!(Au(35) / Au(5), 7);
339
340 assert_eq!(-Au(7), Au(-7));
341}
342
343#[test]
344fn saturate() {
345 let half = MAX_AU / 2;
346 assert_eq!(half + half + half + half + half, MAX_AU);
347 assert_eq!(-half - half - half - half - half, MIN_AU);
348 assert_eq!(half * -10, MIN_AU);
349 assert_eq!(-half * 10, MIN_AU);
350 assert_eq!(half * 10, MAX_AU);
351 assert_eq!(-half * -10, MAX_AU);
352}
353
354#[test]
355fn scale() {
356 assert_eq!(Au(12).scale_by(1.5), Au(18));
357 assert_eq!(Au(12).scale_by(1.7), Au(20));
358 assert_eq!(Au(12).scale_by(1.8), Au(22));
359 assert_eq!(Au(12).scale_by_trunc(1.8), Au(21));
360}
361
362#[test]
363fn abs() {
364 assert_eq!(Au(-10).abs(), Au(10));
365}
366
367#[test]
368fn convert() {
369 assert_eq!(Au::from_px(5), Au(300));
370
371 assert_eq!(Au(300).to_px(), 5);
372 assert_eq!(Au(330).to_px(), 5);
373 assert_eq!(Au(350).to_px(), 5);
374 assert_eq!(Au(360).to_px(), 6);
375
376 assert_eq!(Au(300).ceil_to_px(), 5);
377 assert_eq!(Au(310).ceil_to_px(), 6);
378 assert_eq!(Au(330).ceil_to_px(), 6);
379 assert_eq!(Au(350).ceil_to_px(), 6);
380 assert_eq!(Au(360).ceil_to_px(), 6);
381
382 assert_eq!(Au(300).to_nearest_px(), 5);
383 assert_eq!(Au(310).to_nearest_px(), 5);
384 assert_eq!(Au(330).to_nearest_px(), 6);
385 assert_eq!(Au(350).to_nearest_px(), 6);
386 assert_eq!(Au(360).to_nearest_px(), 6);
387
388 assert_eq!(Au(60).to_nearest_pixel(2.), 1.);
389 assert_eq!(Au(70).to_nearest_pixel(2.), 1.);
390 assert_eq!(Au(80).to_nearest_pixel(2.), 1.5);
391 assert_eq!(Au(90).to_nearest_pixel(2.), 1.5);
392 assert_eq!(Au(100).to_nearest_pixel(2.), 1.5);
393 assert_eq!(Au(110).to_nearest_pixel(2.), 2.);
394 assert_eq!(Au(120).to_nearest_pixel(2.), 2.);
395
396 assert_eq!(Au(300).to_f32_px(), 5.);
397 assert_eq!(Au(312).to_f32_px(), 5.2);
398 assert_eq!(Au(330).to_f32_px(), 5.5);
399 assert_eq!(Au(348).to_f32_px(), 5.8);
400 assert_eq!(Au(360).to_f32_px(), 6.);
401 assert_eq!((Au(367).to_f32_px() * 1000.).round(), 6_117.);
402 assert_eq!((Au(368).to_f32_px() * 1000.).round(), 6_133.);
403
404 assert_eq!(Au(300).to_f64_px(), 5.);
405 assert_eq!(Au(312).to_f64_px(), 5.2);
406 assert_eq!(Au(330).to_f64_px(), 5.5);
407 assert_eq!(Au(348).to_f64_px(), 5.8);
408 assert_eq!(Au(360).to_f64_px(), 6.);
409 assert_eq!((Au(367).to_f64_px() * 1000.).round(), 6_117.);
410 assert_eq!((Au(368).to_f64_px() * 1000.).round(), 6_133.);
411
412 assert_eq!(Au::from_f32_px(5.), Au(300));
413 assert_eq!(Au::from_f32_px(5.2), Au(312));
414 assert_eq!(Au::from_f32_px(5.5), Au(330));
415 assert_eq!(Au::from_f32_px(5.8), Au(348));
416 assert_eq!(Au::from_f32_px(6.), Au(360));
417 assert_eq!(Au::from_f32_px(6.12), Au(367));
418 assert_eq!(Au::from_f32_px(6.13), Au(368));
419
420 assert_eq!(Au::from_f64_px(5.), Au(300));
421 assert_eq!(Au::from_f64_px(5.2), Au(312));
422 assert_eq!(Au::from_f64_px(5.5), Au(330));
423 assert_eq!(Au::from_f64_px(5.8), Au(348));
424 assert_eq!(Au::from_f64_px(6.), Au(360));
425 assert_eq!(Au::from_f64_px(6.12), Au(367));
426 assert_eq!(Au::from_f64_px(6.13), Au(368));
427
428 assert_eq!(Au::from_f32_px_trunc(5.0), Au(300));
429 assert_eq!(Au::from_f32_px_trunc(5.2), Au(312));
430 assert_eq!(Au::from_f32_px_trunc(5.5), Au(330));
431 assert_eq!(Au::from_f32_px_trunc(5.8), Au(348));
432 assert_eq!(Au::from_f32_px_trunc(6.), Au(360));
433 assert_eq!(Au::from_f32_px_trunc(6.12), Au(367));
434 assert_eq!(Au::from_f32_px_trunc(6.13), Au(367));
435
436 assert_eq!(Au::from_f64_px_trunc(5.), Au(300));
437 assert_eq!(Au::from_f64_px_trunc(5.2), Au(312));
438 assert_eq!(Au::from_f64_px_trunc(5.5), Au(330));
439 assert_eq!(Au::from_f64_px_trunc(5.8), Au(348));
440 assert_eq!(Au::from_f64_px_trunc(6.), Au(360));
441 assert_eq!(Au::from_f64_px_trunc(6.12), Au(367));
442 assert_eq!(Au::from_f64_px_trunc(6.13), Au(367));
443}
444
445#[test]
446fn max_assign() {
447 let mut au = Au(5);
448 au.max_assign(Au(10));
449 assert_eq!(au, Au(10));
450
451 let mut au = Au(5);
452 au.max_assign(Au(-10));
453 assert_eq!(au, Au(5));
454
455 let mut au = Au(100);
456 au.max_assign(MAX_AU);
457 assert_eq!(au, MAX_AU);
458
459 let mut au = Au(-100);
460 au.max_assign(MAX_AU);
461 assert_eq!(au, MAX_AU);
462}
463
464#[test]
465fn min_assign() {
466 let mut au = Au(5);
467 au.min_assign(Au(10));
468 assert_eq!(au, Au(5));
469
470 let mut au = Au(5);
471 au.min_assign(Au(-10));
472 assert_eq!(au, Au(-10));
473
474 let mut au = Au(100);
475 au.min_assign(MAX_AU);
476 assert_eq!(au, Au(100));
477
478 let mut au = Au(-100);
479 au.min_assign(MAX_AU);
480 assert_eq!(au, Au(-100));
481}
482
483#[cfg(feature = "serde_serialization")]
484#[test]
485fn serialize() {
486 let serialized = ron::to_string(&Au(42)).unwrap();
487 assert_eq!(ron::from_str(&serialized), Ok(Au(42)));
488}