measures/inner/exact/
linear_map_2d.rs

1#[macro_export] // Don't add nor remove the first three lines and the last two lines.
2macro_rules! inner_define_linear_map_2d {
3    {} => {
4        /// Linear transformation of `Measure2d` objects.
5        pub struct LinearMap2d<Number = f64>
6        where
7            Number: ArithmeticOps,
8        {
9            pub c: [[Number; 2]; 2],
10        }
11
12        impl<Number: ArithmeticOps> LinearMap2d<Number> {
13            /// Create a LinearMap2d from its 4 coefficients.
14            pub const fn new(coefficients: [[Number; 2]; 2]) -> Self {
15                Self { c: coefficients }
16            }
17
18            // Linear maps have no translations.
19
20            //// Rotations
21
22            /// Create a rotation by an angle measure.
23            pub fn rotation<AngleUnit>(angle: Measure<AngleUnit, Number>) -> Self
24            where
25                AngleUnit: AngleMeasurementUnit,
26            {
27                Self::rotation_by_radians(angle.convert::<Radian>().value)
28            }
29
30            /// Create a rotation to the right.
31            pub fn right_rotation() -> Self {
32                Self {
33                    c: [[Number::ZERO, Number::ONE], [-Number::ONE, Number::ZERO]],
34                }
35            }
36
37            /// Create a rotation to the left.
38            pub fn left_rotation() -> Self {
39                Self {
40                    c: [[Number::ZERO, -Number::ONE], [Number::ONE, Number::ZERO]],
41                }
42            }
43
44            //// Projections
45
46            // Projection onto a line identified by a measure point angle.
47            pub fn projection_by_angle<AngleUnit: AngleMeasurementUnit>(
48                angle: impl Into<MeasurePoint<AngleUnit, Number>>,
49            ) -> Self {
50                Self::projection_by_radians(angle.into().convert::<Radian>().value)
51            }
52
53            // Projection onto a line identified by a unit plane vector.
54            // Precondition: unit_v.squared_norm().value == 1
55            pub fn projection_by_unit_vector<Unit>(v: Measure2d<Unit, Number>) -> Self
56            where
57                Unit: MeasurementUnit<Property: VectorProperty>,
58            {
59                Self::projection_by_cos_sin(v.values[0], v.values[1])
60            }
61
62            //// Reflections
63
64            // Reflection over a line identified by a point angle.
65            pub fn reflection_by_angle<AngleUnit>(angle: impl Into<MeasurePoint<AngleUnit, Number>>) -> Self
66            where
67                AngleUnit: AngleMeasurementUnit,
68            {
69                Self::reflection_by_radians(angle.into().convert::<Radian>().value)
70            }
71
72            // Reflection over a line identified by a unit plane vector.
73            // Precondition: v.squared_norm() == 1
74            pub fn reflection_by_unit_vector<Unit>(v: Measure2d<Unit, Number>) -> Self
75            where
76                Unit: MeasurementUnit<Property: VectorProperty>,
77            {
78                Self::reflection_by_cos_sin(v.values[0], v.values[1])
79            }
80
81            //// Scaling by two factors.
82
83            pub fn scaling(factors: [Number; 2]) -> Self {
84                Self {
85                    c: [[factors[0], Number::ZERO], [Number::ZERO, factors[1]]],
86                }
87            }
88
89            //// Inversion
90
91            pub fn inverted(&self) -> Self {
92                let inv_determinant =
93                    Number::ONE / (self.c[0][0] * self.c[1][1] - self.c[0][1] * self.c[1][0]);
94                Self {
95                    c: [
96                        [
97                            self.c[1][1] * inv_determinant,
98                            self.c[0][1] * -inv_determinant,
99                        ],
100                        [
101                            self.c[1][0] * -inv_determinant,
102                            self.c[0][0] * inv_determinant,
103                        ],
104                    ],
105                }
106            }
107
108            // Composition of two plane linear transformations.
109            // Applying the resulting transformation is equivalent to apply first
110            // `other` and then `self`.
111            pub fn combined_with(&self, other: &LinearMap2d<Number>) -> Self {
112                Self {
113                    c: [
114                        [
115                            other.c[0][0] * self.c[0][0] + other.c[0][1] * self.c[1][0],
116                            other.c[0][0] * self.c[0][1] + other.c[0][1] * self.c[1][1],
117                        ],
118                        [
119                            other.c[1][0] * self.c[0][0] + other.c[1][1] * self.c[1][0],
120                            other.c[1][0] * self.c[0][1] + other.c[1][1] * self.c[1][1],
121                        ],
122                    ],
123                }
124            }
125
126            pub fn apply_to<Unit>(&self, m: Measure2d<Unit, Number>) -> Measure2d<Unit, Number>
127            where
128                Unit: MeasurementUnit<Property: VectorProperty>,
129            {
130                Measure2d::<Unit, Number>::new([
131                    self.c[0][0] * m.values[0] + self.c[0][1] * m.values[1],
132                    self.c[1][0] * m.values[0] + self.c[1][1] * m.values[1],
133                ])
134            }
135
136            fn rotation_by_radians(a: Number) -> Self {
137                let (sin_a, cos_a) = a.sin_cos();
138                Self {
139                    c: [[cos_a, -sin_a], [sin_a, cos_a]],
140                }
141            }
142
143            fn projection_by_cos_sin(cos_a: Number, sin_a: Number) -> Self {
144                Self {
145                    c: [
146                        [cos_a * cos_a, cos_a * sin_a],
147                        [sin_a * cos_a, sin_a * sin_a],
148                    ],
149                }
150            }
151
152            fn projection_by_radians(a: Number) -> Self {
153                let (sin_a, cos_a) = a.sin_cos();
154                Self::projection_by_cos_sin(cos_a, sin_a)
155            }
156
157            fn reflection_by_cos_sin(cos_a: Number, sin_a: Number) -> Self {
158                let one = Number::ONE;
159                let two = Number::ONE + Number::ONE;
160                Self {
161                    c: [
162                        [two * cos_a * cos_a - one, two * cos_a * sin_a],
163                        [two * cos_a * sin_a, two * sin_a * sin_a - one],
164                    ],
165                }
166            }
167
168            fn reflection_by_radians(radians: Number) -> Self {
169                let (sin_a, cos_a) = radians.sin_cos();
170                Self::reflection_by_cos_sin(cos_a, sin_a)
171            }
172        }
173
174        impl<Number> Default for LinearMap2d<Number>
175        where
176            Number: ArithmeticOps,
177        {
178            /// LinearMap2d::default() -> LinearMap2d
179            /// It returns the identity transformation.
180            fn default() -> Self {
181                Self::new([[Number::ONE, Number::ZERO], [Number::ZERO, Number::ONE]])
182            }
183        }
184
185        // LinearMap2d == LinearMap2d -> bool
186        impl<Number> PartialEq<LinearMap2d<Number>> for LinearMap2d<Number>
187        where
188            Number: ArithmeticOps,
189        {
190            fn eq(&self, other: &LinearMap2d<Number>) -> bool {
191                self.c == other.c
192            }
193        }
194
195        // LinearMap2d.clone() -> LinearMap2d
196        impl<Number> Clone for LinearMap2d<Number>
197        where
198            Number: ArithmeticOps,
199        {
200            fn clone(&self) -> Self {
201                Self { c: self.c.clone() }
202            }
203        }
204
205        impl From<LinearMap2d<f32>> for LinearMap2d<f64> {
206            fn from(m: LinearMap2d<f32>) -> Self {
207                Self::new([
208                    [m.c[0][0] as f64, m.c[0][1] as f64],
209                    [m.c[1][0] as f64, m.c[1][1] as f64],
210                ])
211            }
212        }
213
214        /// format!("{}", LinearMap2d) -> String
215        /// LinearMap2d.to_string() -> String
216        impl<Number: ArithmeticOps> fmt::Display for LinearMap2d<Number> {
217            fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
218                write!(
219                    formatter,
220                    "{}",
221                    measures::matrix_utils::format_matrix::<2, 2, Number>(&self.c, "", 1)
222                )
223            }
224        }
225
226        // format!("{:?}", LinearMap2d)
227        impl<Number: ArithmeticOps> fmt::Debug for LinearMap2d<Number> {
228            fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
229                write!(
230                    formatter,
231                    "{}",
232                    measures::matrix_utils::format_matrix::<2, 2, Number>(&self.c, "", 1)
233                )
234            }
235        }
236    };
237}