pix_engine/
ops.rs

1//! Common [`Engine`] trait implementations for types.
2
3use crate::prelude::*;
4use num_traits::AsPrimitive;
5use std::{
6    array::IntoIter,
7    iter::{FromIterator, Product, Sum},
8    ops::{
9        Add, AddAssign, Deref, DerefMut, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub,
10        SubAssign,
11    },
12};
13
14#[inline]
15pub(crate) fn clamp_size(val: u32) -> i32 {
16    val.clamp(0, i32::MAX as u32 / 2) as i32
17}
18
19#[inline]
20pub(crate) fn clamp_dimensions(width: u32, height: u32) -> (i32, i32) {
21    (clamp_size(width), clamp_size(height))
22}
23
24/// Helper macro to create From conversion traits for arrays into generic shape types.
25macro_rules! impl_from_array {
26    ($($Type:ident<T$(, $N:ident)?>),* => [$T:ty; $M:expr]) => {$(
27        impl<T$(, const $N: usize)?> From<[$T; $M]> for $Type<T$(, $N)?> {
28            #[doc = concat!("Converts `[T; M]` to ", stringify!($Type<T$(, $N)?>), ".")]
29            #[inline]
30            fn from(arr: [$T; $M]) -> Self {
31                Self(arr)
32            }
33        }
34        impl<T: Copy$(, const $N: usize)?> From<&[$T; $M]> for $Type<T$(, $N)?> {
35            #[doc = concat!("Converts `&[T; M]` to ", stringify!($Type<T$(, $N)?>), ".")]
36            #[inline]
37            fn from(&arr: &[$T; $M]) -> Self {
38                Self(arr)
39            }
40        }
41        impl<T$(, const $N: usize)?> From<$Type<T$(, $N)?>> for [$T; $M] {
42            #[doc = concat!("Converts ", stringify!($Type<T$(, $N)?>), " to `[T; M]`.")]
43            #[inline]
44            fn from(t: $Type<T$(, $N)?>) -> Self {
45                t.0
46            }
47        }
48        impl<T: Copy$(, const $N: usize)?> From<&$Type<T$(, $N)?>> for [$T; $M] {
49            #[doc = concat!("Converts ", stringify!($Type<T$(, $N)?>), " to `&[T; M]`.")]
50            #[inline]
51            fn from(t: &$Type<T$(, $N)?>) -> Self {
52                t.0
53            }
54        }
55    )*};
56}
57
58impl_from_array!(Ellipse<T>, Rect<T>, Sphere<T> => [T; 4]);
59impl_from_array!(Point<T, N>, Vector<T, N> => [T; N]);
60impl_from_array!(Line<T, N> => [Point<T, N>; 2]);
61impl_from_array!(Tri<T, N> => [Point<T, N>; 3]);
62impl_from_array!(Quad<T, N> => [Point<T, N>; 4]);
63
64/// Implement an `as_` methods for types for lossy conversions using the `as` operator.
65macro_rules! impl_as {
66    ($($Type:ident<T$(, $N:ident)?>),*) => {$(
67        impl<T$(, const $N: usize)?> $Type<T$(, $N)?> {
68            #[doc = concat!("Converts ", stringify!($Type<T$(, $N)?>),
69                " to ", stringify!($Type<U$(, $N)?>), ".")]
70            #[inline]
71            pub fn as_<U>(&self) -> $Type<U$(, $N)?>
72            where
73                U: 'static + Copy,
74                T: AsPrimitive<U>
75            {
76                $Type(self.map(AsPrimitive::as_))
77            }
78        }
79    )*};
80    ($($Type:ident<T$(, $N:ident)?>),* from $U:ident) => {$(
81        impl<T$(, const $N: usize)?> $Type<T$(, $N)?> {
82            /// Returns `Self` with the numbers cast using `as` operator.
83            #[doc = concat!("Converts ", stringify!($Type<T$(, $N)?>),
84                " to ", stringify!($Type<U$(, $N)?>), ".")]
85            #[inline]
86            pub fn as_<U>(&self) -> $Type<U$(, $N)?>
87            where
88                U: 'static + Copy,
89                T: AsPrimitive<U>
90            {
91                $Type(self.map(|p| $U(p.map(AsPrimitive::as_))))
92            }
93        }
94    )*};
95}
96
97impl_as!(Point<T, N>, Vector<T, N>, Ellipse<T>, Rect<T>, Sphere<T>);
98impl_as!(Line<T, N>, Tri<T, N>, Quad<T, N> from Point);
99
100/// Helper functions for types that contain floats giving similar element-size methods as [f32] and
101/// [f64].
102macro_rules! impl_float_conversion {
103    ($($Type:ident<T$(, $N:ident)?>),*) => {$(
104        impl<T: Float$(, const $N: usize)?> $Type<T$(, $N)?> {
105            #[doc = concat!("Returns ", stringify!($Type<T$(, $N)?>),
106                " with the nearest integers to the numbers. Round half-way cases away from 0.0.")]
107            #[inline]
108            pub fn round(&self) -> Self {
109                Self(self.map(num_traits::Float::round))
110            }
111            #[doc = concat!("Returns ", stringify!($Type<T$(, $N)?>),
112                " with the largest integers less than or equal to the numbers.")]
113            #[inline]
114            pub fn floor(&self) -> Self {
115                Self(self.map(num_traits::Float::floor))
116            }
117            #[doc = concat!("Returns ", stringify!($Type<T$(, $N)?>),
118                " with the smallest integers greater than or equal to the numbers.")]
119            #[inline]
120            pub fn ceil(&self) -> Self {
121                Self(self.map(num_traits::Float::ceil))
122            }
123        }
124    )*};
125    ($($Type:ident<T$(, $N:ident)?>),* from $U:ident) => {$(
126        impl<T: Float$(, const $N: usize)?> $Type<T$(, $N)?> {
127            #[doc = concat!("Returns ", stringify!($Type<T$(, $N)?>),
128                " with the nearest integers to the numbers. Round half-way cases away from 0.0.")]
129            #[inline]
130            pub fn round(&self) -> Self {
131                Self(self.map(|p| $U(p.map(num_traits::Float::round))))
132            }
133            #[doc = concat!("Returns ", stringify!($Type<T$(, $N)?>),
134                "with the largest integers less than or equal to the numbers.")]
135            #[inline]
136            pub fn floor(&self) -> Self {
137                Self(self.map(|p| $U(p.map(num_traits::Float::floor))))
138            }
139            #[doc = concat!("Returns ", stringify!($Type<T$(, $N)?>),
140                " with the smallest integers greater than or equal to the numbers.")]
141            #[inline]
142            pub fn ceil(&self) -> Self {
143                Self(self.map(|p| $U(p.map(num_traits::Float::ceil))))
144            }
145        }
146    )*};
147}
148
149impl_float_conversion!(Point<T, N>, Vector<T, N>, Ellipse<T>, Rect<T>, Sphere<T>);
150impl_float_conversion!(Line<T, N>, Tri<T, N>, Quad<T, N> from Point);
151
152/// Helper macro to generate standard ops for generic shape types.
153macro_rules! impl_wrapper_traits {
154    ($($Type:ident<T$(, $N:ident)?>),* => [$T:ty; $M:expr]) => {
155        $(
156            impl<T$(, const $N: usize)?> Deref for $Type<T$(, $N)?> {
157                type Target = [$T; $M];
158                #[inline]
159                fn deref(&self) -> &Self::Target {
160                    &self.0
161                }
162            }
163            impl<T$(, const $N: usize)?> DerefMut for $Type<T$(, $N)?> {
164                #[inline]
165                fn deref_mut(&mut self) -> &mut Self::Target {
166                    &mut self.0
167                }
168            }
169
170            impl<T$(, const $N: usize)?> AsRef<[$T; $M]> for $Type<T$(, $N)?> {
171                #[inline]
172                fn as_ref(&self) -> &[$T; $M] {
173                    &self.0
174                }
175            }
176            impl<T$(, const $N: usize)?> AsMut<[$T; $M]> for $Type<T$(, $N)?> {
177                #[inline]
178                fn as_mut(&mut self) -> &mut [$T; $M] {
179                    &mut self.0
180                }
181            }
182
183            impl<T$(, const $N: usize)?> Index<usize> for $Type<T$(, $N)?> {
184                type Output = $T;
185                #[inline]
186                fn index(&self, idx: usize) -> &Self::Output {
187                    &self.0[idx]
188                }
189            }
190            impl<T$(, const $N: usize)?> IndexMut<usize> for $Type<T$(, $N)?> {
191                #[inline]
192                fn index_mut(&mut self, idx: usize) -> &mut Self::Output {
193                    &mut self.0[idx]
194                }
195            }
196
197            impl<T$(, const $N: usize)?> IntoIterator for $Type<T$(, $N)?> {
198                type Item = $T;
199                type IntoIter = IntoIter<Self::Item, $M>;
200                #[inline]
201                fn into_iter(self) -> Self::IntoIter {
202                    self.0.into_iter()
203                }
204            }
205            impl<'a, T$(, const $N: usize)?> IntoIterator for &'a $Type<T$(, $N)?> {
206                type Item = &'a $T;
207                type IntoIter = std::slice::Iter<'a, $T>;
208                #[inline]
209                fn into_iter(self) -> Self::IntoIter {
210                    self.0.iter()
211                }
212            }
213            impl<'a, T$(, const $N: usize)?> IntoIterator for &'a mut $Type<T$(, $N)?> {
214                type Item = &'a mut $T;
215                type IntoIter = std::slice::IterMut<'a, $T>;
216                #[inline]
217                fn into_iter(self) -> Self::IntoIter {
218                    self.0.iter_mut()
219                }
220            }
221            impl <T: Default$(, const $N: usize)?> FromIterator<$T> for $Type<T$(, $N)?> {
222                #[inline]
223                fn from_iter<I>(iter: I) -> Self
224                where
225                    I: IntoIterator<Item = $T>
226                {
227                    let mut iter = iter.into_iter();
228                    let arr = [(); $M].map(|_| iter.next().unwrap_or_else(<$T>::default));
229                    Self(arr)
230                }
231            }
232        )*
233    };
234}
235
236impl_wrapper_traits!(Ellipse<T>, Rect<T>, Sphere<T> => [T; 4]);
237impl_wrapper_traits!(Point<T, N>, Vector<T, N> => [T; N]);
238impl_wrapper_traits!(Line<T, N> => [Point<T, N>; 2]);
239impl_wrapper_traits!(Tri<T, N> => [Point<T, N>; 3]);
240impl_wrapper_traits!(Quad<T, N> => [Point<T, N>; 4]);
241
242/// Multiply `T` * `Type<T, N>` = `Type<T, N>`. Required because of orphan rule: Cannot implement
243/// foreign traits on foreign generic types, thus we use concrete primitive types.
244macro_rules! impl_primitive_mul {
245    ($Type:ident => $($target:ty),*) => {
246        $(
247            impl<const N: usize> Mul<$Type<$target, N>> for $target {
248                type Output = $Type<$target, N>;
249                /// T * [Point].
250                fn mul(self, t: $Type<$target, N>) -> Self::Output {
251                    $Type(t.map(|v| self * v))
252                }
253            }
254        )*
255    };
256}
257
258impl_primitive_mul!(Point => i8, u8, i16, u16, i32, u32, i64, u64, i128, u128, isize, usize, f32, f64);
259impl_primitive_mul!(Vector => i8, u8, i16, u16, i32, u32, i64, u64, i128, u128, isize, usize, f32, f64);
260
261macro_rules! impl_num_assign_op {
262    (@ $OpTrait:ident, $func:ident, $op:tt, $Type:ty) => {
263        impl<T, U, const N: usize> $OpTrait<U> for $Type
264        where
265            T: Num + $OpTrait<U>,
266            U: Num,
267        {
268            fn $func(&mut self, val: U) {
269                for v in self.iter_mut() {
270                    *v $op val;
271                }
272            }
273        }
274    };
275    ($OpTrait:ident, $func:ident, $Lhs:ty, $op:tt, $Rhs:ty = $Output:ty) => {
276        impl<T: Num, const N: usize> $OpTrait<$Rhs> for $Lhs {
277            fn $func(&mut self, other: $Rhs) {
278                for (v, o) in self.iter_mut().zip(other) {
279                    *v $op o;
280                }
281            }
282        }
283    };
284}
285
286impl_num_assign_op!(AddAssign, add_assign, Point<T, N>, +=, Vector<T, N> = Point<T, N>);
287impl_num_assign_op!(AddAssign, add_assign, Point<T, N>, +=, Point<T, N> = Point<T, N>);
288impl_num_assign_op!(AddAssign, add_assign, Vector<T, N>, +=, Vector<T, N> = Vector<T, N>);
289impl_num_assign_op!(SubAssign, sub_assign, Point<T, N>, -=, Vector<T, N> = Point<T, N>);
290impl_num_assign_op!(SubAssign, sub_assign, Point<T, N>, -=, Point<T, N> = Point<T, N>);
291impl_num_assign_op!(SubAssign, sub_assign, Vector<T, N>, -=, Vector<T, N> = Vector<T, N>);
292
293macro_rules! impl_num_op {
294    (@ $IterTrait:ident, $func:ident, $Bound:ident, $op:tt, $Type:ty) => {
295        impl<T, const N: usize> $IterTrait for $Type
296        where
297            Self: Default + $Bound<Output = Self>,
298            T: Num,
299        {
300            fn $func<I>(iter: I) -> Self
301            where
302                I: Iterator<Item = Self>,
303            {
304                let t = <$Type>::default();
305                iter.fold(t, |a, b| a $op b)
306            }
307        }
308        impl<'a, T, const N: usize> $IterTrait<&'a $Type> for $Type
309        where
310            Self: Default + $Bound<Output = Self>,
311            T: Num,
312        {
313            fn $func<I>(iter: I) -> Self
314            where
315                I: Iterator<Item = &'a Self>,
316            {
317                let t = <$Type>::default();
318                iter.fold(t, |a, b| a $op *b)
319            }
320        }
321    };
322    (@ $OpTrait:ident, $func:ident, $op:tt, $Type:ty) => {
323        impl<T, U, const N: usize> $OpTrait<U> for $Type
324        where
325            T: Num + $OpTrait<U, Output = T>,
326            U: Num,
327        {
328            type Output = Self;
329            fn $func(self, val: U) -> Self::Output {
330                let mut t = <$Type>::default();
331                for (v, s) in t.iter_mut().zip(self) {
332                    *v = s $op val;
333                }
334                t
335            }
336        }
337    };
338    ($($Type:ty),*) => {
339        $(
340            impl_num_op!(@ Sum, sum, Add, +, $Type);
341            impl_num_op!(@ Product, product, Mul, *, $Type);
342            impl_num_op!(@ Add, add, +, $Type);
343            impl_num_op!(@ Sub, sub, -, $Type);
344            impl_num_op!(@ Mul, mul, *, $Type);
345            impl_num_op!(@ Div, div, /, $Type);
346            impl_num_assign_op!(@ AddAssign, add_assign, +=, $Type);
347            impl_num_assign_op!(@ SubAssign, sub_assign, -=, $Type);
348            impl_num_assign_op!(@ MulAssign, mul_assign, *=, $Type);
349            impl_num_assign_op!(@ DivAssign, div_assign, /=, $Type);
350            impl<T, const N: usize> Neg for $Type
351            where
352                T: Num + Neg<Output = T>,
353            {
354                type Output = Self;
355                fn neg(self) -> Self::Output {
356                    let mut t = <$Type>::default();
357                    for (v, s) in t.iter_mut().zip(self) {
358                        *v = s.neg();
359                    }
360                    t
361                }
362            }
363
364        )*
365    };
366    ($OpTrait:ident, $func:ident, $Lhs:ty, $op:tt, $Rhs:ty = $Output:ty) => {
367        impl<T, const N: usize> $OpTrait<$Rhs> for $Lhs
368        where
369            T: Num + $OpTrait,
370        {
371            type Output = $Output;
372            fn $func(self, other: $Rhs) -> Self::Output {
373                let mut t = <$Output>::default();
374                for ((v, s), o) in t.iter_mut().zip(self).zip(other) {
375                    *v = s $op o;
376                }
377                t
378            }
379        }
380    };
381}
382
383impl_num_op!(Point<T, N>, Vector<T, N>);
384impl_num_op!(Add, add, Point<T, N>, +, Point<T, N> = Vector<T, N>);
385impl_num_op!(Add, add, Point<T, N>, +, Vector<T, N> = Point<T, N>);
386impl_num_op!(Add, add, Vector<T, N>, +, Point<T, N> = Point<T, N>);
387impl_num_op!(Add, add, Vector<T, N>, +, Vector<T, N> = Vector<T, N>);
388impl_num_op!(Sub, sub, Point<T, N>, -, Point<T, N> = Vector<T, N>);
389impl_num_op!(Sub, sub, Point<T, N>, -, Vector<T, N> = Point<T, N>);
390impl_num_op!(Sub, sub, Vector<T, N>, -, Point<T, N> = Point<T, N>);
391impl_num_op!(Sub, sub, Vector<T, N>, -, Vector<T, N> = Vector<T, N>);
392
393#[cfg(test)]
394mod tests {
395    use super::*;
396
397    #[test]
398    fn iter_ops() {
399        let p = point!(1, 2, 3);
400        assert_eq!(p.deref(), &[1, 2, 3]);
401        let mut p = point!(1, 2, 3);
402        assert_eq!(p.deref_mut(), &mut [1, 2, 3]);
403
404        let p = point!(1, 2, 3);
405        assert_eq!(p[0], 1);
406        assert_eq!(p[1], 2);
407        assert_eq!(p[2], 3);
408        assert_eq!(p.get(3), None);
409
410        let p = point!(1, 2, 3);
411        for (i, v) in p.into_iter().enumerate() {
412            assert_eq!(v, p[i]);
413        }
414        let i = IntoIterator::into_iter([1, 2, 3]);
415        let p = Point::from_iter(i);
416        assert_eq!(p, point!(1, 2, 3));
417    }
418
419    #[test]
420    fn from_array() {
421        macro_rules! test {
422            ($Type:ident, $N:expr; $($T:ty),* => $arr:expr) => {$(
423                let t: $Type<$T, $N> = $arr.into();
424                assert_eq!(t.deref(), &$arr);
425                let v: [$T; $N] = <$Type<$T, $N>>::new($arr).into();
426                assert_eq!(v, $arr);
427            )*};
428        }
429        test!(Point, 1; i8, u8, i16, u16, i32, u32, i64, u64, i128, u128 => [1]);
430        test!(Point, 2; i8, u8, i16, u16, i32, u32, i64, u64, i128, u128 => [1, 2]);
431        test!(Point, 3; i8, u8, i16, u16, i32, u32, i64, u64, i128, u128 => [1, 2, 3]);
432        test!(Vector, 1; i8, u8, i16, u16, i32, u32, i64, u64, i128, u128 => [1]);
433        test!(Vector, 2; i8, u8, i16, u16, i32, u32, i64, u64, i128, u128 => [1, 2]);
434        test!(Vector, 3; i8, u8, i16, u16, i32, u32, i64, u64, i128, u128 => [1, 2, 3]);
435    }
436}