vecmat/transform/
affine.rs

1#[cfg(feature = "rand")]
2use crate::distr::{Invertible, Normal};
3use crate::{Vector, transform::{Chain, Linear, Shift}};
4#[cfg(feature = "rand")]
5use rand_::{distributions::Distribution, Rng};
6
7/// Affine transformation.
8pub type Affine<T, const N: usize> = Chain<Shift<T, N>, Linear<T, N>, Vector<T, N>>;
9
10pub type Affine2<T> = Affine<T, 2>;
11pub type Affine3<T> = Affine<T, 3>;
12pub type Affine4<T> = Affine<T, 4>;
13
14impl<T, const N: usize> Affine<T, N>
15where
16    T: Copy,
17{
18    /// Linear component of the transformation.
19    pub fn linear(&self) -> Linear<T, N> {
20        *self.inner()
21    }
22
23    /// Shift component of the transformation.
24    pub fn shift(&self) -> Shift<T, N> {
25        *self.outer()
26    }
27}
28
29#[cfg(feature = "rand")]
30impl<T, const N: usize> Distribution<Affine<T, N>> for Normal
31where
32    Normal: Distribution<Linear<T, N>> + Distribution<Shift<T, N>>,
33{
34    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Affine<T, N> {
35        Affine::new(self.sample(rng), self.sample(rng))
36    }
37}
38#[cfg(feature = "rand")]
39impl<T, const N: usize> Distribution<Affine<T, N>> for Invertible
40where
41    Invertible: Distribution<Linear<T, N>>,
42    Normal: Distribution<Shift<T, N>>,
43{
44    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Affine<T, N> {
45        Affine::new(rng.sample(&Normal), rng.sample(&Self))
46    }
47}
48
49#[cfg(all(test, feature = "approx"))]
50mod tests {
51    mod base {
52        use super::super::*;
53        use crate::{matrix::*, vector::*, Transform};
54        use approx::*;
55        use num_traits::{One, Zero};
56
57        macro_rules! identity_test {
58            ($X:ident, $M:ident, $V:ident) => {
59                let m = $X::<f64>::identity();
60                assert_abs_diff_eq!(Into::<$M<_>>::into(m.linear()), $M::one());
61                assert_abs_diff_eq!(Into::<$V<_>>::into(m.shift()), $V::zero());
62                let v = $V::fill(1.0);
63                assert_abs_diff_eq!(v, m.apply(v));
64            };
65        }
66        #[test]
67        fn identity() {
68            identity_test!(Affine2, Matrix2x2, Vector2);
69            identity_test!(Affine3, Matrix3x3, Vector3);
70            identity_test!(Affine4, Matrix4x4, Vector4);
71        }
72
73        macro_rules! inverse_test {
74            ($X:ident, $M:ident, $V:ident) => {
75                let m = $X::new($V::fill(1.0).into(), ($M::fill(1.0) + $M::one()).into());
76                let v = $V::fill(1.0);
77                let eps = 1e-12;
78                assert_abs_diff_eq!(v, m.inv().apply(m.apply(v)), epsilon = eps);
79                assert_abs_diff_eq!(v, m.apply(m.inv().apply(v)), epsilon = eps);
80            };
81        }
82        #[test]
83        fn inverse() {
84            inverse_test!(Affine2, Matrix2x2, Vector2);
85            inverse_test!(Affine3, Matrix3x3, Vector3);
86            inverse_test!(Affine4, Matrix4x4, Vector4);
87        }
88
89        macro_rules! chain_test {
90            ($X:ident, $M:ident, $V:ident) => {
91                let m0 = $X::new($V::fill(1.0).into(), ($M::fill(1.0) + $M::one()).into());
92                let m1 = $X::new(
93                    $V::indices().map(|i| i as f64).into(),
94                    ($M::fill(1.0) - $M::one()).into(),
95                );
96                let v = $V::fill(1.0);
97                assert_abs_diff_eq!(m0.apply(m1.apply(v)), m0.chain(m1).apply(v));
98                assert_abs_diff_eq!(m1.apply(m0.apply(v)), m1.chain(m0).apply(v));
99            };
100        }
101        #[test]
102        fn chain() {
103            chain_test!(Affine2, Matrix2x2, Vector2);
104            chain_test!(Affine3, Matrix3x3, Vector3);
105            chain_test!(Affine4, Matrix4x4, Vector4);
106        }
107    }
108
109    #[cfg(feature = "rand")]
110    mod random {
111        use super::super::*;
112        use crate::{vector::*, Transform};
113        use approx::assert_abs_diff_eq;
114        use num_traits::Zero;
115        use rand_::prelude::*;
116        use rand_xorshift::XorShiftRng;
117
118        const SAMPLE_ATTEMPTS: usize = 256;
119
120        #[test]
121        fn chaining() {
122            const EPS: f64 = 1e-14;
123            let mut rng = XorShiftRng::seed_from_u64(0xCEE);
124
125            for _ in 0..SAMPLE_ATTEMPTS {
126                let a: Affine3<f64> = rng.sample(&Normal);
127                let b: Affine3<f64> = rng.sample(&Normal);
128                let c: Vector3<f64> = rng.sample(&Normal);
129                let z = Vector3::<f64>::zero();
130
131                assert_abs_diff_eq!(a.chain(b).apply(c), a.apply(b.apply(c)), epsilon = EPS);
132                assert_abs_diff_eq!(
133                    a.chain(b).deriv(z, c),
134                    a.deriv(z, b.deriv(z, c)),
135                    epsilon = EPS
136                );
137            }
138        }
139
140        #[test]
141        fn inversion() {
142            const EPS: f64 = 1e-12;
143            let mut rng = XorShiftRng::seed_from_u64(0xDEE);
144
145            for _ in 0..SAMPLE_ATTEMPTS {
146                let a: Affine3<f64> = rng.sample(&Invertible);
147                let x: Vector3<f64> = rng.sample(&Normal);
148                let z = Vector3::<f64>::zero();
149
150                assert_abs_diff_eq!(a.inv().apply(a.apply(x)), x, epsilon = EPS);
151                assert_abs_diff_eq!(a.apply(a.inv().apply(x)), x, epsilon = EPS);
152                assert_abs_diff_eq!(a.inv().deriv(z, a.deriv(z, x)), x, epsilon = EPS);
153                assert_abs_diff_eq!(a.deriv(z, a.inv().deriv(z, x)), x, epsilon = EPS);
154            }
155        }
156    }
157}