figures/
traits.rs

1use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, RemAssign, Sub, SubAssign};
2use std::time::Duration;
3
4use intentional::{Cast, CastInto};
5
6use crate::units::{Lp, Px, UPx, ARBITRARY_SCALE};
7use crate::Fraction;
8
9/// Converts a type to its floating point representation.
10///
11/// This trait exists because there is no trait in Rust to peform `x as f32`.
12pub trait FloatConversion {
13    /// The type that represents this type in floating point form.
14    type Float;
15
16    /// Returns this value in floating point form.
17    fn into_float(self) -> Self::Float;
18    /// Converts from floating point to this form.
19    fn from_float(float: Self::Float) -> Self;
20}
21
22impl FloatConversion for u32 {
23    type Float = f32;
24
25    #[allow(clippy::cast_precision_loss)] // precision loss desired to best approximate the value
26    fn into_float(self) -> Self::Float {
27        self as f32
28    }
29
30    #[allow(clippy::cast_possible_truncation)] // truncation desired
31    #[allow(clippy::cast_sign_loss)] // sign loss is asserted
32    fn from_float(float: Self::Float) -> Self {
33        assert!(float.is_sign_positive());
34        float as u32
35    }
36}
37
38impl FloatConversion for i32 {
39    type Float = f32;
40
41    #[allow(clippy::cast_precision_loss)] // precision loss desired to best approximate the value
42    fn into_float(self) -> Self::Float {
43        self as f32
44    }
45
46    #[allow(clippy::cast_possible_truncation)] // truncation desired
47    #[allow(clippy::cast_sign_loss)] // sign loss is asserted
48    fn from_float(float: Self::Float) -> Self {
49        float as i32
50    }
51}
52
53/// A type that can represent a zero-value.
54pub trait Zero {
55    /// The zero value for this type.
56    const ZERO: Self;
57
58    /// Returns true if `self` represents `0`.
59    fn is_zero(&self) -> bool;
60}
61
62macro_rules! impl_int_zero {
63    ($type:ident) => {
64        impl Zero for $type {
65            const ZERO: Self = 0;
66
67            fn is_zero(&self) -> bool {
68                *self == 0
69            }
70        }
71    };
72}
73
74impl_int_zero!(i8);
75impl_int_zero!(i16);
76impl_int_zero!(i32);
77impl_int_zero!(i64);
78impl_int_zero!(i128);
79impl_int_zero!(isize);
80impl_int_zero!(u8);
81impl_int_zero!(u16);
82impl_int_zero!(u32);
83impl_int_zero!(u64);
84impl_int_zero!(u128);
85impl_int_zero!(usize);
86
87/// A type that can have its absolute difference from zero calculated.
88pub trait Abs {
89    /// Returns the positive difference between this value and 0.
90    ///
91    /// This function should never panic and always perform a saturating
92    /// absolute value calculation.
93    #[must_use]
94    fn abs(&self) -> Self;
95}
96
97macro_rules! impl_int_abs {
98    ($type:ident) => {
99        impl Abs for $type {
100            fn abs(&self) -> Self {
101                self.saturating_abs()
102            }
103        }
104    };
105}
106
107impl_int_abs!(i8);
108impl_int_abs!(i16);
109impl_int_abs!(i32);
110impl_int_abs!(i64);
111impl_int_abs!(i128);
112impl_int_abs!(isize);
113
114impl Abs for f32 {
115    fn abs(&self) -> Self {
116        (*self).abs()
117    }
118}
119
120/// Raises a value to an exponent.
121pub trait Pow {
122    /// Returns the saturating result of raising `self` to the `exp` power.
123    #[must_use]
124    fn pow(&self, exp: u32) -> Self;
125}
126
127macro_rules! impl_int_pow {
128    ($type:ident) => {
129        impl Pow for $type {
130            fn pow(&self, exp: u32) -> Self {
131                self.saturating_pow(exp)
132            }
133        }
134    };
135}
136
137impl_int_pow!(i8);
138impl_int_pow!(i16);
139impl_int_pow!(i32);
140impl_int_pow!(i64);
141impl_int_pow!(i128);
142impl_int_pow!(isize);
143impl_int_pow!(u8);
144impl_int_pow!(u16);
145impl_int_pow!(u32);
146impl_int_pow!(u64);
147impl_int_pow!(u128);
148impl_int_pow!(usize);
149
150impl Pow for f32 {
151    fn pow(&self, exp: u32) -> Self {
152        self.powf(exp.cast())
153    }
154}
155
156/// Converts from a 2d vector in tuple form
157pub trait FromComponents<Unit>: Sized {
158    /// Returns a new instance from the 2d vector components provided.
159    fn from_components(components: (Unit, Unit)) -> Self;
160
161    /// Converts this type to another type using [`FromComponents`] and
162    /// [`IntoComponents`].
163    fn from_vec<Type>(other: Type) -> Self
164    where
165        Type: IntoComponents<Unit>,
166    {
167        Self::from_components(other.into_components())
168    }
169}
170
171/// Constructors for types that are composed of two [`Px`] components.
172pub trait Px2D: FromComponents<Px> {
173    /// Returns a new value containing the x and y components converted into
174    /// [`Px`].
175    fn px(x: impl Into<Px>, y: impl Into<Px>) -> Self {
176        Self::from_components((x.into(), y.into()))
177    }
178}
179
180impl<T> Px2D for T where T: FromComponents<Px> {}
181
182/// Constructors for types that are composed of two [`UPx`] components.
183pub trait UPx2D: FromComponents<UPx> {
184    /// Returns a new value containing the x and y components converted into
185    /// [`UPx`].
186    fn upx(x: impl Into<UPx>, y: impl Into<UPx>) -> Self {
187        Self::from_components((x.into(), y.into()))
188    }
189}
190
191impl<T> UPx2D for T where T: FromComponents<UPx> {}
192
193/// Constructors for types that are composed of two [`Lp`] components.
194pub trait Lp2D: FromComponents<Lp> {
195    /// Returns a new value containing the x and y components converted into
196    /// [`Lp`] using [`Lp::points`]/[`Lp::points_f`].
197    fn points(x: impl Into<FloatOrInt>, y: impl Into<FloatOrInt>) -> Self {
198        Self::from_components((x.into().into_points(), y.into().into_points()))
199    }
200
201    /// Returns a new value containing the x and y components converted into
202    /// [`Lp`] using [`Lp::cm`]/[`Lp::cm_f`].
203    fn cm(x: impl Into<FloatOrInt>, y: impl Into<FloatOrInt>) -> Self {
204        Self::from_components((x.into().into_cm(), y.into().into_cm()))
205    }
206
207    /// Returns a new value containing the x and y components converted into
208    /// [`Lp`] using [`Lp::mm`]/[`Lp::mm_f`].
209    fn mm(x: impl Into<FloatOrInt>, y: impl Into<FloatOrInt>) -> Self {
210        Self::from_components((x.into().into_mm(), y.into().into_mm()))
211    }
212
213    /// Returns a new value containing the x and y components converted into
214    /// [`Lp`] using [`Lp::inches`]/[`Lp::inches_f`].
215    fn inches(x: impl Into<FloatOrInt>, y: impl Into<FloatOrInt>) -> Self {
216        Self::from_components((x.into().into_inches(), y.into().into_inches()))
217    }
218}
219
220impl<T> Lp2D for T where T: FromComponents<Lp> {}
221
222/// A type representing either an [`i32`] or an [`f32`].
223#[derive(Clone, Copy)]
224pub enum FloatOrInt {
225    /// An integer value.
226    Int(i32),
227    /// A floating point value.
228    Float(f32),
229}
230
231impl FloatOrInt {
232    fn map<R>(self, float: impl FnOnce(f32) -> R, int: impl FnOnce(i32) -> R) -> R {
233        match self {
234            FloatOrInt::Int(value) => int(value),
235            FloatOrInt::Float(value) => float(value),
236        }
237    }
238
239    /// Returns this number as [`Lp`] using [`Lp::points`]/[`Lp::points_f`].
240    pub fn into_points(self) -> Lp {
241        self.map(Lp::points_f, Lp::points)
242    }
243
244    /// Returns this number as [`Lp`] using [`Lp::cm`]/[`Lp::cm_f`].
245    #[must_use]
246    pub fn into_cm(self) -> Lp {
247        self.map(Lp::cm_f, Lp::cm)
248    }
249
250    /// Returns this number as [`Lp`] using [`Lp::mm`]/[`Lp::mm_f`].
251    #[must_use]
252    pub fn into_mm(self) -> Lp {
253        self.map(Lp::mm_f, Lp::mm)
254    }
255
256    /// Returns this number as [`Lp`] using [`Lp::inches`]/[`Lp::inches_f`].
257    #[must_use]
258    pub fn into_inches(self) -> Lp {
259        self.map(Lp::inches_f, Lp::inches)
260    }
261}
262
263impl From<i32> for FloatOrInt {
264    fn from(value: i32) -> Self {
265        Self::Int(value)
266    }
267}
268
269impl From<f32> for FloatOrInt {
270    fn from(value: f32) -> Self {
271        Self::Float(value)
272    }
273}
274
275/// Converts to a 2d vector in tuple form
276pub trait IntoComponents<Unit>: Sized {
277    /// Extracts this type's 2d vector components.
278    fn into_components(self) -> (Unit, Unit);
279
280    /// Converts this type to another type using [`FromComponents`] and
281    /// [`IntoComponents`].
282    fn to_vec<Type>(self) -> Type
283    where
284        Type: FromComponents<Unit>,
285    {
286        Type::from_vec(self)
287    }
288}
289
290impl<Unit> FromComponents<Unit> for (Unit, Unit) {
291    fn from_components(components: Self) -> Self {
292        components
293    }
294}
295
296impl<Unit> IntoComponents<Unit> for (Unit, Unit) {
297    fn into_components(self) -> Self {
298        self
299    }
300}
301
302impl<Unit> IntoComponents<Unit> for Unit
303where
304    Unit: Copy,
305{
306    fn into_components(self) -> (Unit, Unit) {
307        (self, self)
308    }
309}
310
311/// Converts this type into its measurement in [`Px`](crate::units::Px) and [`Lp`](crate::units::Lp).
312pub trait ScreenScale {
313    /// This type when measuring with [`Px`](crate::units::Px).
314    type Px;
315    /// This type when measuring with [`UPx`](crate::units::UPx).
316    type UPx;
317    /// This type when measuring with [`Lp`](crate::units::Lp).
318    type Lp;
319
320    /// Converts this value from its current unit into device pixels ([`Px`](crate::units::Px))
321    /// using the provided `scale` factor.
322    fn into_px(self, scale: Fraction) -> Self::Px;
323    /// Converts from pixels into this type, using the provided `scale` factor.
324    fn from_px(px: Self::Px, scale: Fraction) -> Self;
325
326    /// Converts this value from its current unit into device pixels
327    /// ([`UPx`](crate::units::UPx)) using the provided `scale` factor.
328    fn into_upx(self, scale: Fraction) -> Self::UPx;
329    /// Converts from unsigned pixels into this type, using the provided `scale` factor.
330    fn from_upx(px: Self::UPx, scale: Fraction) -> Self;
331
332    /// Converts this value from its current unit into device independent pixels
333    /// ([`Lp`](crate::units::Lp)) using the provided `scale` factor.
334    fn into_lp(self, scale: Fraction) -> Self::Lp;
335    /// Converts from Lp into this type, using the provided `scale` factor.
336    fn from_lp(lp: Self::Lp, scale: Fraction) -> Self;
337}
338
339/// Converts a value into its signed representation, clamping negative numbers
340/// to `i32::MAX`.
341pub trait IntoSigned {
342    /// The signed representation of this type.
343    type Signed;
344    /// Returns this value as an unsigned value. Values that are larger than can
345    /// fit in an `i32` are converted to `i32::MAX`.
346    #[must_use]
347    fn into_signed(self) -> Self::Signed;
348}
349
350impl IntoSigned for u32 {
351    type Signed = i32;
352
353    fn into_signed(self) -> Self::Signed {
354        self.try_into().unwrap_or(i32::MAX)
355    }
356}
357
358impl IntoSigned for i32 {
359    type Signed = Self;
360
361    fn into_signed(self) -> Self::Signed {
362        self
363    }
364}
365
366impl IntoSigned for f32 {
367    type Signed = Self;
368
369    fn into_signed(self) -> Self::Signed {
370        self
371    }
372}
373
374/// Converts a value into its signed representation, clamping negative numbers
375/// to 0.
376pub trait IntoUnsigned {
377    /// The unsigned representation of this type.
378    type Unsigned;
379    /// Returns this value as an unsigned value. Negative values will be
380    /// converted to 0.
381    #[must_use]
382    fn into_unsigned(self) -> Self::Unsigned;
383}
384
385impl IntoUnsigned for i32 {
386    type Unsigned = u32;
387
388    fn into_unsigned(self) -> Self::Unsigned {
389        self.try_into().unwrap_or(0)
390    }
391}
392
393impl IntoUnsigned for u32 {
394    type Unsigned = Self;
395
396    fn into_unsigned(self) -> Self::Unsigned {
397        self
398    }
399}
400
401/// A type that can be used as a `Unit` in figures.
402pub trait Unit:
403    FloatConversion<Float = f32>
404    + Add<Output = Self>
405    + Sub<Output = Self>
406    + Div<Output = Self>
407    + Mul<Output = Self>
408    + Rem<Output = Self>
409    + AddAssign
410    + SubAssign
411    + DivAssign
412    + MulAssign
413    + RemAssign
414    + Zero
415    + Ord
416    + Eq
417    + Copy
418    + Default
419    + std::fmt::Debug
420    + IntoSigned
421    + TryInto<i32>
422    + 'static
423{
424}
425
426/// Common number operations available on number types in Rust that aren't
427/// available as traits.
428pub trait StdNumOps {
429    /// Adds `self` and `other`, saturating instead of overflowing.
430    fn saturating_add(self, other: Self) -> Self;
431    /// Multiplies `self` and `other`, saturating instead of overflowing.
432    fn saturating_mul(self, other: Self) -> Self;
433    /// Divides `self` by `other`, saturating instead of overflowing.
434    fn saturating_div(self, other: Self) -> Self;
435    /// Subtracts `other` from `self`, saturating instead of overflowing.
436    fn saturating_sub(self, other: Self) -> Self;
437}
438
439macro_rules! impl_std_num_ops {
440    ($type:ident) => {
441        impl StdNumOps for $type {
442            fn saturating_add(self, other: Self) -> Self {
443                self.saturating_add(other)
444            }
445
446            fn saturating_mul(self, other: Self) -> Self {
447                self.saturating_mul(other)
448            }
449
450            fn saturating_div(self, other: Self) -> Self {
451                self.saturating_div(other)
452            }
453
454            fn saturating_sub(self, other: Self) -> Self {
455                self.saturating_sub(other)
456            }
457        }
458    };
459}
460
461impl_std_num_ops!(u8);
462
463impl<T> Unit for T where
464    T: FloatConversion<Float = f32>
465        + Add<Output = Self>
466        + Sub<Output = Self>
467        + Div<Output = Self>
468        + Mul<Output = Self>
469        + Rem<Output = Self>
470        + AddAssign
471        + SubAssign
472        + DivAssign
473        + MulAssign
474        + RemAssign
475        + Zero
476        + Ord
477        + Eq
478        + Copy
479        + Default
480        + std::fmt::Debug
481        + IntoSigned
482        + TryInto<i32>
483        + 'static
484{
485}
486
487/// A type that can be used as a `Unit` in figures that knows how to convert to
488/// [`Lp`] or [`Px`].
489pub trait ScreenUnit: UnscaledUnit + ScreenScale<Px = Px, Lp = Lp, UPx = UPx> + Unit {}
490
491impl<T> ScreenUnit for T where T: UnscaledUnit + ScreenScale<Px = Px, Lp = Lp, UPx = UPx> + Unit {}
492
493/// A type that has a minimum and a maximum.
494pub trait Ranged: Sized {
495    /// The minimum value for this type.
496    const MIN: Self;
497    /// The maximum value for this type.
498    const MAX: Self;
499}
500
501macro_rules! impl_int_ranged {
502    ($type:ident) => {
503        impl Ranged for $type {
504            const MAX: Self = $type::MAX;
505            const MIN: Self = $type::MIN;
506        }
507    };
508}
509
510impl_int_ranged!(i8);
511impl_int_ranged!(i16);
512impl_int_ranged!(i32);
513impl_int_ranged!(i64);
514impl_int_ranged!(i128);
515impl_int_ranged!(isize);
516impl_int_ranged!(u8);
517impl_int_ranged!(u16);
518impl_int_ranged!(u32);
519impl_int_ranged!(u64);
520impl_int_ranged!(u128);
521impl_int_ranged!(usize);
522impl_int_ranged!(f32);
523impl_int_ranged!(f64);
524impl_int_ranged!(Px);
525impl_int_ranged!(UPx);
526impl_int_ranged!(Lp);
527
528impl Ranged for Duration {
529    const MAX: Self = Duration::MAX;
530    const MIN: Self = Duration::ZERO;
531}
532
533impl Ranged for bool {
534    const MAX: Self = true;
535    const MIN: Self = false;
536}
537
538/// A type that has a scaling factor when converting to pixels.
539pub trait PixelScaling {
540    /// The scaling factor to apply when converting to pixels, in addition to
541    /// any spatial scaling already being applied.
542    const PX_SCALING_FACTOR: u16;
543}
544
545impl PixelScaling for Px {
546    const PX_SCALING_FACTOR: u16 = 1;
547}
548
549impl PixelScaling for UPx {
550    const PX_SCALING_FACTOR: u16 = 1;
551}
552
553impl PixelScaling for Lp {
554    const PX_SCALING_FACTOR: u16 = ARBITRARY_SCALE; // ARBITRARY_SCALE / 96
555}
556
557/// Information about scaling for a numerical unit type.
558pub trait UnscaledUnit {
559    /// The internal reprsentation used by this type.
560    type Representation: CastInto<i32>;
561
562    /// Returns a new instance using the unscaled representation.
563    fn from_unscaled(unscaled: Self::Representation) -> Self;
564    /// Returns the inner, unscaled representation of this value.
565    fn into_unscaled(self) -> Self::Representation;
566}
567
568/// Functionality for rounding values to whole numbers.
569pub trait Round {
570    /// Returns `self` rounded to the nearest whole number.
571    #[must_use]
572    fn round(self) -> Self;
573    /// Returns `self` raised to the next whole number further away from 0.
574    #[must_use]
575    fn ceil(self) -> Self;
576    /// Returns `self` lowered to the next whole number closer to 0.
577    #[must_use]
578    fn floor(self) -> Self;
579}
580
581impl Round for f32 {
582    fn round(self) -> Self {
583        self.round()
584    }
585
586    fn ceil(self) -> Self {
587        self.ceil()
588    }
589
590    fn floor(self) -> Self {
591        self.floor()
592    }
593}
594
595/// Functionality for getting the root of a number.
596pub trait Roots {
597    /// Returns the square root of `self`.
598    #[must_use]
599    fn sqrt(self) -> Self;
600
601    /// Returns the cube root of `self`.
602    #[must_use]
603    fn cbrt(self) -> Self;
604}
605
606impl Roots for f32 {
607    fn sqrt(self) -> Self {
608        self.sqrt()
609    }
610
611    fn cbrt(self) -> Self {
612        self.cbrt()
613    }
614}