math2d/
matrix3x2f.rs

1//! 2D Affine Transformation Matrix.
2//!
3//! See the actual struct documentation for more information.
4
5use point2f::Point2f;
6use vector2f::Vector2f;
7
8use std::f32::EPSILON;
9use std::ops::Mul;
10
11#[cfg(all(windows, feature = "d2d"))]
12use winapi::um::dcommon::D2D_MATRIX_3X2_F;
13#[cfg(all(windows, feature = "d2d"))]
14use winapi::um::dwrite::DWRITE_MATRIX;
15
16/// The 2D affine identity matrix.
17pub const IDENTITY: Matrix3x2f = Matrix3x2f::IDENTITY;
18
19/// 2D Affine Transformation Matrix.
20///
21/// Mathematically you can think of this matrix as if it were the following:
22/// ```
23/// # let (a,b,c,d,x,y) = (0,0,0,0,0,0);
24/// # let _ =
25/// [a, b, 0]
26/// # ; let _ =
27/// [c, d, 0]
28/// # ; let _ =
29/// [x, y, 1]
30/// # ;
31/// ```
32///
33/// ### Composing matrices
34///
35/// Affine transformations are performed in "Row-Major" order. What this means,
36/// if you're familiar with linear algebra, is that when you compose multiple
37/// affine transformations together, the matrix representing the set of operations
38/// that should happen "first" must be the left hand operand of the multiplication
39/// operator.
40///
41/// This is also why points and vectors are the left-hand operand when multiplied
42/// with matrices.
43#[derive(Copy, Clone, Debug, PartialEq)]
44#[cfg_attr(feature = "serde_derive", derive(Serialize, Deserialize))]
45#[repr(C)]
46pub struct Matrix3x2f {
47    /// Horizontal scaling / cosine of rotation
48    pub a: f32,
49    /// Vertical shear / sine of rotation
50    pub b: f32,
51    /// Horizontal shear / negative sine of rotation
52    pub c: f32,
53    /// Vertical scaling / cosine of rotation
54    pub d: f32,
55    /// Horizontal translation (always orthogonal regardless of rotation)
56    pub x: f32,
57    /// Vertical translation (always orthogonal regardless of rotation)
58    pub y: f32,
59}
60
61impl Matrix3x2f {
62    /// The 2D affine identity matrix.
63    pub const IDENTITY: Matrix3x2f = Matrix3x2f {
64        a: 1.0,
65        b: 0.0,
66        c: 0.0,
67        d: 1.0,
68        x: 0.0,
69        y: 0.0,
70    };
71
72    /// Construct the matrix from an array of the row values.
73    #[inline]
74    pub fn new(parts: [[f32; 2]; 3]) -> Matrix3x2f {
75        Matrix3x2f {
76            a: parts[0][0],
77            b: parts[0][1],
78            c: parts[1][0],
79            d: parts[1][1],
80            x: parts[2][0],
81            y: parts[2][1],
82        }
83    }
84
85    /// Constructs the matrix from a slice of 6 values as
86    /// `[a, b, c, d, x, y]`.
87    ///
88    /// Panics if `values` does not contain exactly 6 elements.
89    #[inline]
90    pub fn from_slice(values: &[f32]) -> Matrix3x2f {
91        assert_eq!(values.len(), 6);
92        Matrix3x2f {
93            a: values[0],
94            b: values[1],
95            c: values[2],
96            d: values[3],
97            x: values[4],
98            y: values[5],
99        }
100    }
101
102    /// Constructs the matrix from a tuple of 6 values as
103    /// `(a, b, c, d, x, y)`.
104    #[inline]
105    pub fn from_tuple(values: (f32, f32, f32, f32, f32, f32)) -> Matrix3x2f {
106        let (a, b, c, d, x, y) = values;
107        Matrix3x2f { a, b, c, d, x, y }
108    }
109
110    /// Creates an affine translation matrix that translates points by the passed
111    /// vector. The linear part of the matrix is the identity.
112    ///
113    /// ![Example Translation][1]
114    ///
115    /// [Read More][2]
116    ///
117    /// [1]: https://docs.microsoft.com/en-us/windows/desktop/Direct2D/images/translation-ovw.png
118    /// [2]: https://docs.microsoft.com/en-us/windows/desktop/Direct2D/how-to-translate
119    #[inline]
120    pub fn translation(trans: impl Into<Vector2f>) -> Matrix3x2f {
121        let trans = trans.into();
122
123        Matrix3x2f {
124            a: 1.0,
125            b: 0.0,
126            c: 0.0,
127            d: 1.0,
128            x: trans.x,
129            y: trans.y,
130        }
131    }
132
133    /// Creates a scaling matrix that performs scaling around a specified point
134    /// of origin. This is equivalent to translating the center point back to
135    /// the origin, scaling around the origin by the scaling value, and then
136    /// translating the center point back to its original location.
137    ///
138    /// ![Example Scaling][1]
139    ///
140    /// [Read More][2]
141    ///
142    /// [1]: https://docs.microsoft.com/en-us/windows/desktop/Direct2D/images/scale-ovw.png
143    /// [2]: https://docs.microsoft.com/en-us/windows/desktop/Direct2D/how-to-scale
144    #[inline]
145    pub fn scaling(scale: impl Into<Vector2f>, center: impl Into<Point2f>) -> Matrix3x2f {
146        let scale = scale.into();
147        let center = center.into();
148
149        Matrix3x2f {
150            a: scale.x,
151            b: 0.0,
152            c: 0.0,
153            d: scale.y,
154            x: center.x - scale.x * center.x,
155            y: center.y - scale.y * center.y,
156        }
157    }
158
159    /// Creates a rotation matrix that performs rotation around a specified point
160    /// of origin. This is equivalent to translating the center point back to the
161    /// origin, rotating around the origin by the specified angle, and then
162    /// translating the center point back to its original location.
163    ///
164    /// ![Example Rotation][1]
165    ///
166    /// [Read More][2]
167    ///
168    /// [1]: https://docs.microsoft.com/en-us/windows/desktop/Direct2D/images/rotate-ovw.png
169    /// [2]: https://docs.microsoft.com/en-us/windows/desktop/Direct2D/how-to-rotate
170    #[inline]
171    pub fn rotation(angle: f32, center: impl Into<Point2f>) -> Matrix3x2f {
172        let center = center.into();
173        let cos = angle.cos();
174        let sin = angle.sin();
175
176        Matrix3x2f {
177            a: cos,
178            b: sin,
179            c: -sin,
180            d: cos,
181            x: center.x - cos * center.x - sin * center.y,
182            y: center.y - cos * center.y - sin * center.x,
183        }
184    }
185
186    /// Creates a matrix that skews an object by a tangent angle around the center point.
187    ///
188    /// ![Example Effect of Skewing][1]
189    ///
190    /// [Read More][2]
191    ///
192    /// [1]: https://docs.microsoft.com/en-us/windows/desktop/Direct2D/images/skew-ovw.png
193    /// [2]: https://docs.microsoft.com/en-us/windows/desktop/Direct2D/how-to-skew
194    #[inline]
195    pub fn skew(angle_x: f32, angle_y: f32, center: impl Into<Point2f>) -> Matrix3x2f {
196        let center = center.into();
197        let tanx = angle_x.tan();
198        let tany = angle_y.tan();
199
200        Matrix3x2f {
201            a: 1.0,
202            b: tany,
203            c: tanx,
204            d: 1.0,
205            x: -center.y * tany,
206            y: -center.x * tanx,
207        }
208    }
209
210    #[inline]
211    /// Computes the transpose of the linear part of this matrix i.e. swap(b, c).
212    pub fn linear_transpose(&self) -> Matrix3x2f {
213        Matrix3x2f {
214            a: self.a,
215            b: self.c,
216            c: self.b,
217            d: self.d,
218            x: self.x,
219            y: self.y,
220        }
221    }
222
223    /// Returns the determinant of the matrix. Since this matrix is conceptually 3x3, and the
224    /// bottom-right element is always 1, this value works out to be `a * d - b * c`.
225    #[inline]
226    pub fn determinant(&self) -> f32 {
227        self.a * self.d - self.b * self.c
228    }
229
230    /// Determines if the `inverse` or `try_inverse` functions would succeed if called. A
231    /// matrix is invertible if its determinant is nonzero. Since we're dealing with floats,
232    /// we check that the absolute value of the determinant is greater than f32::EPSILON.
233    #[inline]
234    pub fn is_invertible(&self) -> bool {
235        Matrix3x2f::det_shows_invertible(self.determinant())
236    }
237
238    /// Calculates the inverse of this matrix. Panics if the matrix is not invertible (see above).
239    #[inline]
240    pub fn inverse(&self) -> Matrix3x2f {
241        let det = self.determinant();
242        assert!(Matrix3x2f::det_shows_invertible(det));
243
244        self.unchecked_inverse(det)
245    }
246
247    /// Calculates the inverse of the matrix. Returns None if the determinant is less than
248    /// f32::EPSILON.
249    #[inline]
250    pub fn try_inverse(&self) -> Option<Matrix3x2f> {
251        let det = self.determinant();
252        if Matrix3x2f::det_shows_invertible(det) {
253            Some(self.unchecked_inverse(det))
254        } else {
255            None
256        }
257    }
258
259    /// Performs the inverse of the matrix without checking for invertibility.
260    ///
261    /// *WARNING: If this matrix is not invertible, you may get NaN or INF!*
262    #[inline]
263    pub fn unchecked_inverse(&self, det: f32) -> Matrix3x2f {
264        Matrix3x2f {
265            a: self.d / det,
266            b: self.b / -det,
267            c: self.c / -det,
268            d: self.a / det,
269            x: (self.d * self.x - self.c * self.y) / -det,
270            y: (self.b * self.x - self.a * self.y) / det,
271        }
272    }
273
274    /// Compose a matrix from a scaling, rotation, and translation value
275    /// (combined in that order).
276    #[inline]
277    pub fn compose(
278        scaling: impl Into<Vector2f>,
279        rotation: f32,
280        translation: impl Into<Vector2f>,
281    ) -> Matrix3x2f {
282        let s = scaling.into();
283        let cos = rotation.cos();
284        let sin = rotation.sin();
285        let trans = translation.into();
286
287        Matrix3x2f {
288            a: s.x * cos,
289            b: s.y * sin,
290            c: s.x * -sin,
291            d: s.y * cos,
292            x: trans.x,
293            y: trans.y,
294        }
295    }
296
297    /// Decomposes a simple affine transformation into its scaling, rotation, and
298    /// translation parts.
299    #[inline]
300    pub fn decompose(&self) -> Decomposition {
301        Decomposition {
302            translation: [self.x, self.y].into(),
303            scaling: Vector2f {
304                x: (self.a * self.a + self.c * self.c).sqrt(),
305                y: (self.b * self.b + self.d * self.d).sqrt(),
306            },
307            rotation: self.b.atan2(self.d),
308        }
309    }
310
311    /// A more explicit way to do `point * matrix`, while also allowing any type
312    /// that may be converted into a Point2F with a From/Into impl.
313    #[inline]
314    pub fn transform_point(&self, point: impl Into<Point2f>) -> Point2f {
315        point.into() * *self
316    }
317
318    /// A more explicit way to do `vec * matrix`, while also allowing any type
319    /// that may be converted into a Vector2F with a From/Into impl.
320    #[inline]
321    pub fn transform_vector(&self, vec: impl Into<Vector2f>) -> Vector2f {
322        vec.into() * *self
323    }
324
325    /// Returns this matrix as a 3x3 float array using the mathematical form
326    /// described above.
327    #[inline]
328    pub fn to_row_major(&self) -> [[f32; 3]; 3] {
329        [
330            [self.a, self.b, 0.0],
331            [self.c, self.d, 0.0],
332            [self.x, self.y, 1.0],
333        ]
334    }
335
336    /// Returns the matrix as a 3x3 float array in column major form, i.e.
337    /// the transpose of the row-major version.
338    #[inline]
339    pub fn to_column_major(&self) -> [[f32; 3]; 3] {
340        [
341            [self.a, self.c, self.x],
342            [self.b, self.d, self.y],
343            [0.0, 0.0, 1.0],
344        ]
345    }
346
347    /// Checks if two matrices are approximately equal given an epsilon value.
348    #[inline]
349    pub fn is_approx_eq(&self, other: &Matrix3x2f, epsilon: f32) -> bool {
350        return (self.a - other.a).abs() < epsilon
351            && (self.b - other.b).abs() < epsilon
352            && (self.c - other.c).abs() < epsilon
353            && (self.d - other.d).abs() < epsilon
354            && (self.x - other.x).abs() < epsilon
355            && (self.y - other.y).abs() < epsilon;
356    }
357
358    /// Checks if this matrix is equal to the identity matrix within 1e-5
359    #[inline]
360    pub fn is_identity(&self) -> bool {
361        self.is_approx_eq(&Matrix3x2f::IDENTITY, 1e-5)
362    }
363
364    #[inline]
365    fn det_shows_invertible(det: f32) -> bool {
366        det.abs() > EPSILON
367    }
368}
369
370impl Mul for Matrix3x2f {
371    type Output = Matrix3x2f;
372
373    #[inline]
374    fn mul(self, rhs: Matrix3x2f) -> Matrix3x2f {
375        let lhs = self;
376
377        Matrix3x2f {
378            a: lhs.a * rhs.a + lhs.b * rhs.c,
379            b: lhs.a * rhs.b + lhs.b * rhs.d,
380            c: lhs.c * rhs.a + lhs.d * rhs.c,
381            d: lhs.c * rhs.b + lhs.d * rhs.d,
382            x: lhs.x * rhs.a + lhs.y * rhs.c + rhs.x,
383            y: lhs.x * rhs.b + lhs.y * rhs.d + rhs.y,
384        }
385    }
386}
387
388impl Mul<Matrix3x2f> for Point2f {
389    type Output = Point2f;
390
391    #[inline]
392    fn mul(self, m: Matrix3x2f) -> Point2f {
393        Point2f {
394            x: self.x * m.a + self.y * m.c + m.x,
395            y: self.x * m.b + self.y * m.d + m.y,
396        }
397    }
398}
399
400impl Mul<Matrix3x2f> for Vector2f {
401    type Output = Vector2f;
402
403    #[inline]
404    fn mul(self, m: Matrix3x2f) -> Vector2f {
405        Vector2f {
406            x: self.x * m.a + self.y * m.c,
407            y: self.x * m.b + self.y * m.d,
408        }
409    }
410}
411
412impl From<[[f32; 2]; 3]> for Matrix3x2f {
413    #[inline]
414    fn from(parts: [[f32; 2]; 3]) -> Matrix3x2f {
415        Matrix3x2f::new(parts)
416    }
417}
418
419impl From<Matrix3x2f> for [[f32; 2]; 3] {
420    #[inline]
421    fn from(m: Matrix3x2f) -> [[f32; 2]; 3] {
422        [[m.a, m.b], [m.c, m.d], [m.x, m.y]]
423    }
424}
425
426impl From<Matrix3x2f> for [[f32; 3]; 3] {
427    #[inline]
428    fn from(m: Matrix3x2f) -> [[f32; 3]; 3] {
429        m.to_row_major()
430    }
431}
432
433#[cfg(all(windows, feature = "d2d"))]
434impl From<Matrix3x2f> for D2D_MATRIX_3X2_F {
435    #[inline]
436    fn from(m: Matrix3x2f) -> D2D_MATRIX_3X2_F {
437        let matrix: [[f32; 2]; 3] = m.into();
438        D2D_MATRIX_3X2_F { matrix }
439    }
440}
441
442#[cfg(all(windows, feature = "d2d"))]
443impl From<D2D_MATRIX_3X2_F> for Matrix3x2f {
444    #[inline]
445    fn from(m: D2D_MATRIX_3X2_F) -> Matrix3x2f {
446        Matrix3x2f::new(m.matrix)
447    }
448}
449
450#[cfg(all(windows, feature = "d2d"))]
451impl From<Matrix3x2f> for DWRITE_MATRIX {
452    #[inline]
453    fn from(m: Matrix3x2f) -> DWRITE_MATRIX {
454        DWRITE_MATRIX {
455            m11: m.a,
456            m12: m.b,
457            m21: m.c,
458            m22: m.d,
459            dx: m.x,
460            dy: m.y,
461        }
462    }
463}
464
465#[cfg(all(windows, feature = "d2d"))]
466impl From<DWRITE_MATRIX> for Matrix3x2f {
467    #[inline]
468    fn from(m: DWRITE_MATRIX) -> Matrix3x2f {
469        Matrix3x2f {
470            a: m.m11,
471            b: m.m12,
472            c: m.m21,
473            d: m.m22,
474            x: m.dx,
475            y: m.dy,
476        }
477    }
478}
479
480impl Default for Matrix3x2f {
481    #[inline]
482    fn default() -> Self {
483        Matrix3x2f::IDENTITY
484    }
485}
486
487/// Represents a decomposition of a non-skewing matrix i.e. one made up of
488/// only rotations, translations, and scalings.
489pub struct Decomposition {
490    /// Total scaling applied in the transformation. This operation is applied
491    /// first if the decomposition is recomposed.
492    pub scaling: Vector2f,
493    /// Total rotation applied in the transformation. This operation is applied
494    /// second if the decomposition is recomposed.
495    pub rotation: f32,
496    /// Total translation applied in the transformation. This operation is
497    /// applied last if the decomposition is recomposed.
498    pub translation: Vector2f,
499}
500
501impl From<Decomposition> for Matrix3x2f {
502    #[inline]
503    fn from(decomp: Decomposition) -> Matrix3x2f {
504        Matrix3x2f::compose(decomp.scaling, decomp.rotation, decomp.translation)
505    }
506}
507
508#[cfg(feature = "mint")]
509impl From<Matrix3x2f> for mint::RowMatrix3x2<f32> {
510    #[inline]
511    fn from(mat: Matrix3x2f) -> mint::RowMatrix3x2<f32> {
512        mint::RowMatrix3x2 {
513            x: [mat.a, mat.b].into(),
514            y: [mat.c, mat.d].into(),
515            z: [mat.x, mat.y].into(),
516        }
517    }
518}
519
520#[cfg(all(test, windows, feature = "d2d"))]
521#[test]
522fn mat32_d2d_bin_compat() {
523    use std::mem::size_of_val;
524
525    fn ptr_eq<T>(a: &T, b: &T) -> bool {
526        (a as *const T) == (b as *const T)
527    }
528
529    let mat = Matrix3x2f::IDENTITY;
530    let d2d = unsafe { &*((&mat) as *const _ as *const D2D_MATRIX_3X2_F) };
531
532    assert!(ptr_eq(&mat.a, &d2d.matrix[0][0]));
533    assert!(ptr_eq(&mat.b, &d2d.matrix[0][1]));
534    assert!(ptr_eq(&mat.c, &d2d.matrix[1][0]));
535    assert!(ptr_eq(&mat.d, &d2d.matrix[1][1]));
536    assert!(ptr_eq(&mat.x, &d2d.matrix[2][0]));
537    assert!(ptr_eq(&mat.y, &d2d.matrix[2][1]));
538    assert_eq!(size_of_val(&mat), size_of_val(d2d));
539}