1use std::cmp::Ordering;
2use std::fmt;
3use std::num::TryFromIntError;
4use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, RemAssign, Sub, SubAssign};
5
6use intentional::{Cast, CastFrom};
7
8use crate::traits::{
9 Abs, FloatConversion, IntoComponents, IntoSigned, IntoUnsigned, Pow, Roots, Round, ScreenScale,
10 StdNumOps, UnscaledUnit, Zero,
11};
12use crate::Fraction;
13
14pub(crate) const ARBITRARY_SCALE: u16 = 1905;
15const ARBITRARY_SCALE_I32: i32 = ARBITRARY_SCALE as i32;
16const ARBITRARY_SCALE_U32: u32 = ARBITRARY_SCALE as u32;
17#[allow(clippy::cast_precision_loss)]
18const ARBITRARY_SCALE_F32: f32 = ARBITRARY_SCALE as f32;
19
20macro_rules! define_integer_type {
21 ($name:ident, $inner:ty, $docs_file:literal, $scale:literal) => {
22 #[derive(Default, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
23 #[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
24 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
25 #[doc = include_str!($docs_file)]
26 #[repr(C)]
27 pub struct $name($inner);
28
29 impl $name {
30 pub const MAX: Self = Self(<$inner>::MAX);
32 pub const MIN: Self = Self(<$inner>::MIN);
34
35 #[must_use]
37 pub const fn new(value: $inner) -> Self {
38 Self(value * $scale)
39 }
40
41 #[must_use]
43 pub const fn get(self) -> $inner {
44 if $scale > 1 {
45 (self.0 + $scale / 2) / $scale
46 } else {
47 self.0
48 }
49 }
50
51 #[must_use]
55 pub const fn saturating_sub(self, other: Self) -> Self {
56 Self(self.0.saturating_sub(other.0))
57 }
58
59 #[must_use]
63 pub const fn saturating_add(self, other: Self) -> Self {
64 Self(self.0.saturating_add(other.0))
65 }
66
67 #[must_use]
71 pub const fn saturating_mul(self, other: Self) -> Self {
72 Self(self.0.saturating_mul(other.0) / $scale)
73 }
74
75 #[must_use]
79 pub const fn saturating_div(self, other: Self) -> Self {
80 Self::new(self.0.saturating_div(other.0))
81 }
82 }
83
84 impl FloatConversion for $name {
85 type Float = f32;
86
87 #[allow(clippy::cast_precision_loss)] fn into_float(self) -> Self::Float {
89 self.0.cast::<f32>() / $scale.cast::<f32>()
90 }
91
92 fn from_float(float: Self::Float) -> Self {
93 Self((float * $scale.cast::<f32>()).round().cast())
94 }
95 }
96
97 impl From<$name> for f32 {
98 fn from(value: $name) -> Self {
99 value.into_float()
100 }
101 }
102
103 impl From<f32> for $name {
104 fn from(value: f32) -> Self {
105 Self::from_float(value)
106 }
107 }
108
109 impl From<$name> for $inner {
110 fn from(value: $name) -> Self {
111 value.get()
112 }
113 }
114
115 impl From<$inner> for $name {
116 fn from(value: $inner) -> Self {
117 Self::new(value)
118 }
119 }
120
121 impl PartialEq<$inner> for $name {
122 fn eq(&self, other: &$inner) -> bool {
123 self == &Self::new(*other)
124 }
125 }
126
127 impl PartialOrd<$inner> for $name {
128 fn partial_cmp(&self, other: &$inner) -> Option<std::cmp::Ordering> {
129 self.partial_cmp(&Self::new(*other))
130 }
131 }
132
133 impl Div for $name {
134 type Output = Self;
135
136 fn div(self, rhs: Self) -> Self::Output {
137 Self::new(self.0 / rhs.0)
138 }
139 }
140
141 impl Div<$inner> for $name {
142 type Output = Self;
143
144 fn div(self, rhs: $inner) -> Self::Output {
145 Self(self.0 / rhs)
146 }
147 }
148
149 impl Div<f32> for $name {
150 type Output = Self;
151
152 fn div(self, rhs: f32) -> Self::Output {
153 Self::from((self.into_float() / rhs))
154 }
155 }
156
157 impl Div<Fraction> for $name {
158 type Output = Self;
159
160 fn div(self, rhs: Fraction) -> Self::Output {
161 Self::from_unscaled((self.into_unscaled() / rhs))
162 }
163 }
164
165 impl DivAssign for $name {
166 fn div_assign(&mut self, rhs: Self) {
167 *self = *self / rhs
168 }
169 }
170
171 impl DivAssign<$inner> for $name {
172 fn div_assign(&mut self, rhs: $inner) {
173 self.0 /= rhs;
174 }
175 }
176
177 impl Rem for $name {
178 type Output = Self;
179
180 fn rem(self, rhs: Self) -> Self::Output {
181 Self(self.0 % rhs.0)
182 }
183 }
184
185 impl Rem<$inner> for $name {
186 type Output = Self;
187
188 fn rem(self, rhs: $inner) -> Self::Output {
189 Self(self.0 % rhs)
190 }
191 }
192
193 impl RemAssign for $name {
194 fn rem_assign(&mut self, rhs: Self) {
195 self.0 %= rhs.0;
196 }
197 }
198
199 impl RemAssign<$inner> for $name {
200 fn rem_assign(&mut self, rhs: $inner) {
201 self.0 %= rhs;
202 }
203 }
204
205 impl Rem<f32> for $name {
206 type Output = Self;
207
208 fn rem(self, rhs: f32) -> Self::Output {
209 Self::from((self.into_float() % rhs).round())
210 }
211 }
212
213 impl Mul for $name {
214 type Output = Self;
215
216 fn mul(self, rhs: Self) -> Self::Output {
217 Self(self.0 * rhs.0 / $scale)
218 }
219 }
220
221 impl Mul<$inner> for $name {
222 type Output = Self;
223
224 fn mul(self, rhs: $inner) -> Self::Output {
225 Self(self.0 * rhs)
226 }
227 }
228
229 impl Mul<f32> for $name {
230 type Output = Self;
231
232 fn mul(self, rhs: f32) -> Self::Output {
233 Self::from((self.into_float() * rhs))
234 }
235 }
236
237 impl Mul<Fraction> for $name {
238 type Output = Self;
239
240 fn mul(self, rhs: Fraction) -> Self::Output {
241 Self::from_unscaled((self.into_unscaled() * rhs))
242 }
243 }
244
245 impl MulAssign for $name {
246 fn mul_assign(&mut self, rhs: Self) {
247 self.0 = (self.0 * rhs.0) / $scale;
248 }
249 }
250
251 impl MulAssign<$inner> for $name {
252 fn mul_assign(&mut self, rhs: $inner) {
253 self.0 *= rhs;
254 }
255 }
256
257 impl Add for $name {
258 type Output = Self;
259
260 fn add(self, rhs: Self) -> Self::Output {
261 Self(self.0 + rhs.0)
262 }
263 }
264
265 impl Add<$inner> for $name {
266 type Output = Self;
267
268 fn add(self, rhs: $inner) -> Self::Output {
269 self + Self::new(rhs)
270 }
271 }
272
273 impl AddAssign for $name {
274 fn add_assign(&mut self, rhs: Self) {
275 self.0 += rhs.0;
276 }
277 }
278
279 impl AddAssign<$inner> for $name {
280 fn add_assign(&mut self, rhs: $inner) {
281 *self += Self::new(rhs);
282 }
283 }
284
285 impl Sub for $name {
286 type Output = Self;
287
288 fn sub(self, rhs: Self) -> Self::Output {
289 Self(self.0 - rhs.0)
290 }
291 }
292
293 impl Sub<$inner> for $name {
294 type Output = Self;
295
296 fn sub(self, rhs: $inner) -> Self::Output {
297 Self(self.0 - rhs * $scale)
298 }
299 }
300
301 impl SubAssign for $name {
302 fn sub_assign(&mut self, rhs: Self) {
303 self.0 -= rhs.0;
304 }
305 }
306
307 impl SubAssign<$inner> for $name {
308 fn sub_assign(&mut self, rhs: $inner) {
309 *self -= Self::new(rhs);
310 }
311 }
312
313 impl Zero for $name {
314 const ZERO: Self = Self(0);
315
316 fn is_zero(&self) -> bool {
317 self.0 == 0
318 }
319 }
320
321 impl UnscaledUnit for $name {
322 type Representation = $inner;
323
324 fn from_unscaled(unscaled: Self::Representation) -> Self {
325 Self(unscaled)
326 }
327
328 fn into_unscaled(self) -> Self::Representation {
329 self.0
330 }
331 }
332
333 impl Round for $name {
334 fn round(self) -> Self {
335 Self((self.0 + $scale / 2) / $scale * $scale)
336 }
337
338 fn ceil(self) -> Self {
339 Self((self.0 + $scale - 1) / $scale * $scale)
340 }
341
342 fn floor(self) -> Self {
343 Self(self.0 / $scale * $scale)
344 }
345 }
346
347 impl Roots for $name {
348 fn sqrt(self) -> Self {
349 Self(f64::from(self.0).sqrt().cast())
350 }
351
352 fn cbrt(self) -> Self {
353 Self(f64::from(self.0).cbrt().cast())
354 }
355 }
356
357 impl StdNumOps for $name {
358 fn saturating_add(self, other: Self) -> Self {
359 self.saturating_add(other)
360 }
361
362 fn saturating_mul(self, other: Self) -> Self {
363 self.saturating_mul(other)
364 }
365
366 fn saturating_div(self, other: Self) -> Self {
367 self.saturating_div(other)
368 }
369
370 fn saturating_sub(self, other: Self) -> Self {
371 self.saturating_sub(other)
372 }
373 }
374 };
375}
376
377impl CastFrom<f32> for Px {
378 fn from_cast(from: f32) -> Self {
379 Px::from(from)
380 }
381}
382
383impl CastFrom<Px> for f32 {
384 fn from_cast(from: Px) -> Self {
385 from.into_float()
386 }
387}
388
389define_integer_type!(Lp, i32, "docs/lp.md", 1905);
390
391impl IntoComponents<Lp> for i32 {
392 fn into_components(self) -> (Lp, Lp) {
393 (Lp(self), Lp(self))
394 }
395}
396
397impl IntoComponents<Lp> for f32 {
398 fn into_components(self) -> (Lp, Lp) {
399 let value = Lp::from_float(self);
400 (value, value)
401 }
402}
403
404impl ScreenScale for Lp {
405 type Lp = Lp;
406 type Px = Px;
407 type UPx = UPx;
408
409 fn into_px(self, scale: Fraction) -> Self::Px {
410 Px(self.0 * 4 * scale / ARBITRARY_SCALE_I32)
411 }
412
413 fn from_px(px: Self::Px, scale: Fraction) -> Self {
414 px.into_lp(scale)
415 }
416
417 fn into_lp(self, _scale: Fraction) -> Self::Lp {
418 self
419 }
420
421 fn from_lp(lp: Self::Lp, _scale: Fraction) -> Self {
422 lp
423 }
424
425 fn into_upx(self, scale: crate::Fraction) -> Self::UPx {
426 self.into_px(scale).into_unsigned()
427 }
428
429 fn from_upx(px: Self::UPx, scale: crate::Fraction) -> Self {
430 Self::from_px(px.into_signed(), scale)
431 }
432}
433
434impl std::ops::Neg for Lp {
435 type Output = Self;
436
437 fn neg(self) -> Self::Output {
438 Self(-self.0)
439 }
440}
441
442impl TryFrom<u32> for Lp {
443 type Error = TryFromIntError;
444
445 fn try_from(value: u32) -> Result<Self, Self::Error> {
446 value.try_into().map(Self)
447 }
448}
449
450impl Lp {
451 #[must_use]
455 pub const fn points(points: i32) -> Self {
456 Self(points * ARBITRARY_SCALE_I32 * 4 / 3)
457 }
458
459 #[must_use]
463 pub fn points_f(points: f32) -> Self {
464 Lp((points * ARBITRARY_SCALE_F32 * 4. / 3.).cast())
465 }
466
467 #[must_use]
469 pub const fn cm(centimeters: i32) -> Self {
470 Self::mm(centimeters * 10)
471 }
472
473 #[must_use]
475 pub fn cm_f(centimeters: f32) -> Self {
476 Lp((centimeters * ARBITRARY_SCALE_F32 * 96. / 2.54).cast())
477 }
478
479 #[must_use]
481 pub const fn mm(millimeters: i32) -> Self {
482 Self(millimeters * ARBITRARY_SCALE_I32 * 960 / 254)
483 }
484
485 #[must_use]
487 pub fn mm_f(millimeters: f32) -> Self {
488 Lp((millimeters * ARBITRARY_SCALE_F32 * 96. / 25.4).cast())
489 }
490
491 #[must_use]
493 pub const fn inches(inches: i32) -> Self {
494 Self(inches * ARBITRARY_SCALE_I32 * 96)
495 }
496
497 #[must_use]
499 pub fn inches_f(inches: f32) -> Self {
500 Self((inches * ARBITRARY_SCALE_F32 * 96.).cast())
501 }
502}
503
504impl Pow for Lp {
505 fn pow(&self, exp: u32) -> Self {
506 Self(self.0.saturating_pow(exp))
507 }
508}
509
510impl Abs for Lp {
511 fn abs(&self) -> Self {
512 Self(self.0.saturating_abs())
513 }
514}
515
516impl IntoSigned for Lp {
517 type Signed = Self;
518
519 fn into_signed(self) -> Self::Signed {
520 self
521 }
522}
523
524impl fmt::Debug for Lp {
525 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
526 let fractional = self.0 % ARBITRARY_SCALE_I32;
527 let whole = self.0 / ARBITRARY_SCALE_I32;
528 if fractional == 0 {
529 write!(f, "{whole}lp")
530 } else {
531 let as_float =
532 f64::from(whole) + f64::from(fractional) / f64::from(ARBITRARY_SCALE_F32);
533 write!(f, "{as_float}lp")
534 }
535 }
536}
537
538impl fmt::Display for Lp {
539 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
540 fmt::Debug::fmt(self, f)
541 }
542}
543
544define_integer_type!(Px, i32, "docs/px.md", 4);
545
546impl Pow for Px {
547 fn pow(&self, exp: u32) -> Self {
548 Self(self.0.saturating_pow(exp) / 4_i32.pow(exp.saturating_sub(1)))
549 }
550}
551
552impl Abs for Px {
553 fn abs(&self) -> Self {
554 Self(self.0.saturating_abs())
555 }
556}
557
558impl IntoUnsigned for Px {
559 type Unsigned = UPx;
560
561 fn into_unsigned(self) -> Self::Unsigned {
562 UPx(self.0.into_unsigned())
563 }
564}
565
566impl IntoSigned for Px {
567 type Signed = Self;
568
569 fn into_signed(self) -> Self::Signed {
570 self
571 }
572}
573
574impl ScreenScale for Px {
575 type Lp = Lp;
576 type Px = Self;
577 type UPx = UPx;
578
579 fn into_px(self, _scale: Fraction) -> Self::Px {
580 self
581 }
582
583 fn from_px(px: Self::Px, _scale: Fraction) -> Self {
584 px
585 }
586
587 fn into_lp(self, scale: Fraction) -> Self::Lp {
588 Lp(self.0 * ARBITRARY_SCALE_I32 / scale / 4)
589 }
590
591 fn from_lp(lp: Self::Lp, scale: Fraction) -> Self {
592 lp.into_px(scale)
593 }
594
595 fn into_upx(self, _scale: crate::Fraction) -> Self::UPx {
596 self.into_unsigned()
597 }
598
599 fn from_upx(px: Self::UPx, _scale: crate::Fraction) -> Self {
600 px.into_signed()
601 }
602}
603
604impl IntoComponents<Px> for i32 {
605 fn into_components(self) -> (Px, Px) {
606 (Px::new(self), Px::new(self))
607 }
608}
609
610impl IntoComponents<Px> for f32 {
611 fn into_components(self) -> (Px, Px) {
612 let value = Px::from_float(self);
613 (value, value)
614 }
615}
616
617impl fmt::Debug for Px {
618 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
619 let whole = self.0 >> 2;
620 let remainder = self.0 & 0b11;
621 match remainder {
622 1 => write!(f, "{whole}.25px",),
623 2 => write!(f, "{whole}.5px",),
624 3 => write!(f, "{whole}.75px",),
625 _ => write!(f, "{whole}px",),
626 }
627 }
628}
629
630impl fmt::Display for Px {
631 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
632 fmt::Debug::fmt(self, f)
633 }
634}
635
636impl std::ops::Neg for Px {
637 type Output = Self;
638
639 fn neg(self) -> Self::Output {
640 Self(-self.0)
641 }
642}
643
644impl TryFrom<u32> for Px {
645 type Error = TryFromIntError;
646
647 fn try_from(value: u32) -> Result<Self, Self::Error> {
648 value.try_into().map(Self::new)
649 }
650}
651
652impl PartialEq<UPx> for Px {
653 fn eq(&self, other: &UPx) -> bool {
654 if let Ok(unsigned) = UPx::try_from(*self) {
655 unsigned == *other
656 } else {
657 false
658 }
659 }
660}
661
662impl PartialOrd<UPx> for Px {
663 fn partial_cmp(&self, other: &UPx) -> Option<Ordering> {
664 if let Ok(unsigned) = UPx::try_from(*self) {
665 Some(unsigned.cmp(other))
666 } else {
667 Some(Ordering::Less)
668 }
669 }
670}
671
672define_integer_type!(UPx, u32, "docs/upx.md", 4);
673
674impl Pow for UPx {
675 fn pow(&self, exp: u32) -> Self {
676 Self(self.0.saturating_pow(exp) / 4_u32.pow(exp.saturating_sub(1)))
677 }
678}
679
680impl IntoSigned for UPx {
681 type Signed = Px;
682
683 fn into_signed(self) -> Self::Signed {
684 Px(self.0.into_signed())
685 }
686}
687
688impl IntoUnsigned for UPx {
689 type Unsigned = Self;
690
691 fn into_unsigned(self) -> Self::Unsigned {
692 self
693 }
694}
695
696impl ScreenScale for UPx {
697 type Lp = Lp;
698 type Px = Px;
699 type UPx = Self;
700
701 fn into_px(self, _scale: Fraction) -> Self::Px {
702 Px(i32::try_from(self.0).unwrap_or(i32::MAX))
703 }
704
705 fn from_px(px: Self::Px, _scale: Fraction) -> Self {
706 Self::try_from(px).unwrap_or(Self::MIN)
707 }
708
709 fn into_lp(self, scale: Fraction) -> Self::Lp {
710 (self.0 * ARBITRARY_SCALE_U32 / scale / 4)
711 .try_into()
712 .unwrap_or(Lp::MAX)
713 }
714
715 fn from_lp(lp: Self::Lp, scale: Fraction) -> Self {
716 lp.into_px(scale).try_into().unwrap_or(Self::MIN)
717 }
718
719 fn into_upx(self, _scale: crate::Fraction) -> Self::UPx {
720 self
721 }
722
723 fn from_upx(px: Self::UPx, _scale: crate::Fraction) -> Self {
724 px
725 }
726}
727
728impl IntoComponents<UPx> for u32 {
729 fn into_components(self) -> (UPx, UPx) {
730 (UPx::new(self), UPx::new(self))
731 }
732}
733
734impl IntoComponents<UPx> for f32 {
735 fn into_components(self) -> (UPx, UPx) {
736 let value = UPx::from_float(self);
737 (value, value)
738 }
739}
740
741impl fmt::Debug for UPx {
742 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
743 let whole = self.0 / 4;
744 let remainder = self.0 % 4;
745 match remainder {
746 1 => write!(f, "{whole}.25px",),
747 2 => write!(f, "{whole}.5px",),
748 3 => write!(f, "{whole}.75px",),
749 _ => write!(f, "{whole}px",),
750 }
751 }
752}
753
754impl fmt::Display for UPx {
755 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
756 fmt::Debug::fmt(self, f)
757 }
758}
759
760impl TryFrom<UPx> for i32 {
761 type Error = TryFromIntError;
762
763 fn try_from(value: UPx) -> Result<Self, Self::Error> {
764 value.get().try_into()
765 }
766}
767
768impl TryFrom<i32> for UPx {
769 type Error = TryFromIntError;
770
771 fn try_from(value: i32) -> Result<Self, Self::Error> {
772 value.try_into().map(Self::new)
773 }
774}
775
776impl TryFrom<Px> for UPx {
777 type Error = TryFromIntError;
778
779 fn try_from(value: Px) -> Result<Self, Self::Error> {
780 value.0.try_into().map(Self)
781 }
782}
783
784impl TryFrom<UPx> for Px {
785 type Error = TryFromIntError;
786
787 fn try_from(value: UPx) -> Result<Self, Self::Error> {
788 value.0.try_into().map(Self)
789 }
790}
791
792impl PartialEq<Px> for UPx {
793 fn eq(&self, other: &Px) -> bool {
794 if let Ok(unsigned) = UPx::try_from(*other) {
795 unsigned == *self
796 } else {
797 false
798 }
799 }
800}
801
802impl PartialOrd<Px> for UPx {
803 fn partial_cmp(&self, other: &Px) -> Option<Ordering> {
804 if let Ok(unsigned) = UPx::try_from(*other) {
805 Some(unsigned.cmp(self))
806 } else {
807 Some(Ordering::Less)
808 }
809 }
810}