euclid/
transform2d.rs

1// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
2// file at the top-level directory of this distribution.
3//
4// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7// option. This file may not be copied, modified, or distributed
8// except according to those terms.
9
10#![cfg_attr(feature = "cargo-clippy", allow(just_underscores_and_digits))]
11
12use super::{UnknownUnit, Angle};
13#[cfg(feature = "mint")]
14use mint;
15use num::{One, Zero};
16use point::TypedPoint2D;
17use vector::{TypedVector2D, vec2};
18use rect::TypedRect;
19use transform3d::TypedTransform3D;
20use core::ops::{Add, Mul, Div, Sub, Neg};
21use core::marker::PhantomData;
22use approxeq::ApproxEq;
23use trig::Trig;
24use core::fmt;
25use num_traits::NumCast;
26
27/// A 2d transform stored as a 3 by 2 matrix in row-major order in memory.
28///
29/// Transforms can be parametrized over the source and destination units, to describe a
30/// transformation from a space to another.
31/// For example, `TypedTransform2D<f32, WorldSpace, ScreenSpace>::transform_point4d`
32/// takes a `TypedPoint2D<f32, WorldSpace>` and returns a `TypedPoint2D<f32, ScreenSpace>`.
33///
34/// Transforms expose a set of convenience methods for pre- and post-transformations.
35/// A pre-transformation corresponds to adding an operation that is applied before
36/// the rest of the transformation, while a post-transformation adds an operation
37/// that is applied after.
38///
39/// These transforms are for working with _row vectors_, so the matrix math for transforming
40/// a vector is `v * T`. If your library is using column vectors, use `row_major` functions when you
41/// are asked for `column_major` representations and vice versa.
42#[repr(C)]
43#[derive(EuclidMatrix)]
44pub struct TypedTransform2D<T, Src, Dst> {
45    pub m11: T, pub m12: T,
46    pub m21: T, pub m22: T,
47    pub m31: T, pub m32: T,
48    #[doc(hidden)]
49    pub _unit: PhantomData<(Src, Dst)>,
50}
51
52/// The default 2d transform type with no units.
53pub type Transform2D<T> = TypedTransform2D<T, UnknownUnit, UnknownUnit>;
54
55impl<T: Copy, Src, Dst> TypedTransform2D<T, Src, Dst> {
56    /// Create a transform specifying its matrix elements in row-major order.
57    ///
58    /// Beware: This library is written with the assumption that row vectors
59    /// are being used. If your matrices use column vectors (i.e. transforming a vector
60    /// is `T * v`), then please use `column_major`
61    pub fn row_major(m11: T, m12: T, m21: T, m22: T, m31: T, m32: T) -> Self {
62        TypedTransform2D {
63            m11, m12,
64            m21, m22,
65            m31, m32,
66            _unit: PhantomData,
67        }
68    }
69
70    /// Create a transform specifying its matrix elements in column-major order.
71    ///
72    /// Beware: This library is written with the assumption that row vectors
73    /// are being used. If your matrices use column vectors (i.e. transforming a vector
74    /// is `T * v`), then please use `row_major`
75    pub fn column_major(m11: T, m21: T, m31: T, m12: T, m22: T, m32: T) -> Self {
76        TypedTransform2D {
77            m11, m12,
78            m21, m22,
79            m31, m32,
80            _unit: PhantomData,
81        }
82    }
83
84    /// Returns an array containing this transform's terms in row-major order (the order
85    /// in which the transform is actually laid out in memory).
86    ///
87    /// Beware: This library is written with the assumption that row vectors
88    /// are being used. If your matrices use column vectors (i.e. transforming a vector
89    /// is `T * v`), then please use `to_column_major_array`
90    pub fn to_row_major_array(&self) -> [T; 6] {
91        [
92            self.m11, self.m12,
93            self.m21, self.m22,
94            self.m31, self.m32
95        ]
96    }
97
98    /// Returns an array containing this transform's terms in column-major order.
99    ///
100    /// Beware: This library is written with the assumption that row vectors
101    /// are being used. If your matrices use column vectors (i.e. transforming a vector
102    /// is `T * v`), then please use `to_row_major_array`
103    pub fn to_column_major_array(&self) -> [T; 6] {
104        [
105            self.m11, self.m21, self.m31,
106            self.m12, self.m22, self.m32
107        ]
108    }
109
110    /// Returns an array containing this transform's 3 rows in (in row-major order)
111    /// as arrays.
112    ///
113    /// This is a convenience method to interface with other libraries like glium.
114    ///
115    /// Beware: This library is written with the assumption that row vectors
116    /// are being used. If your matrices use column vectors (i.e. transforming a vector
117    /// is `T * v`), this will return column major arrays.
118    pub fn to_row_arrays(&self) -> [[T; 2]; 3] {
119        [
120            [self.m11, self.m12],
121            [self.m21, self.m22],
122            [self.m31, self.m32],
123        ]
124    }
125
126    /// Creates a transform from an array of 6 elements in row-major order.
127    ///
128    /// Beware: This library is written with the assumption that row vectors
129    /// are being used. If your matrices use column vectors (i.e. transforming a vector
130    /// is `T * v`), please provide a column major array.
131    pub fn from_row_major_array(array: [T; 6]) -> Self {
132        Self::row_major(
133            array[0], array[1],
134            array[2], array[3],
135            array[4], array[5],
136        )
137    }
138
139    /// Creates a transform from 3 rows of 2 elements (row-major order).
140    ///
141    /// Beware: This library is written with the assumption that row vectors
142    /// are being used. If your matrices use column vectors (i.e. transforming a vector
143    /// is `T * v`), please provide a column major array.
144    pub fn from_row_arrays(array: [[T; 2]; 3]) -> Self {
145        Self::row_major(
146            array[0][0], array[0][1],
147            array[1][0], array[1][1],
148            array[2][0], array[2][1],
149        )
150    }
151
152    /// Drop the units, preserving only the numeric value.
153    pub fn to_untyped(&self) -> Transform2D<T> {
154        Transform2D::row_major(
155            self.m11, self.m12,
156            self.m21, self.m22,
157            self.m31, self.m32
158        )
159    }
160
161    /// Tag a unitless value with units.
162    pub fn from_untyped(p: &Transform2D<T>) -> Self {
163        TypedTransform2D::row_major(
164            p.m11, p.m12,
165            p.m21, p.m22,
166            p.m31, p.m32
167        )
168    }
169}
170
171impl<T0: NumCast + Copy, Src, Dst> TypedTransform2D<T0, Src, Dst> {
172    /// Cast from one numeric representation to another, preserving the units.
173    pub fn cast<T1: NumCast + Copy>(&self) -> TypedTransform2D<T1, Src, Dst> {
174        self.try_cast().unwrap()
175    }
176
177    /// Fallible cast from one numeric representation to another, preserving the units.
178    pub fn try_cast<T1: NumCast + Copy>(&self) -> Option<TypedTransform2D<T1, Src, Dst>> {
179        match (NumCast::from(self.m11), NumCast::from(self.m12),
180               NumCast::from(self.m21), NumCast::from(self.m22),
181               NumCast::from(self.m31), NumCast::from(self.m32)) {
182            (Some(m11), Some(m12),
183             Some(m21), Some(m22),
184             Some(m31), Some(m32)) => {
185                Some(TypedTransform2D::row_major(
186                    m11, m12,
187                    m21, m22,
188                    m31, m32
189                ))
190            },
191            _ => None
192        }
193    }
194}
195
196impl<T, Src, Dst> TypedTransform2D<T, Src, Dst>
197where T: Copy +
198         PartialEq +
199         One + Zero {
200    pub fn identity() -> Self {
201        let (_0, _1) = (Zero::zero(), One::one());
202        TypedTransform2D::row_major(
203           _1, _0,
204           _0, _1,
205           _0, _0
206        )
207    }
208
209    // Intentional not public, because it checks for exact equivalence
210    // while most consumers will probably want some sort of approximate
211    // equivalence to deal with floating-point errors.
212    fn is_identity(&self) -> bool {
213        *self == TypedTransform2D::identity()
214    }
215}
216
217impl<T, Src, Dst> TypedTransform2D<T, Src, Dst>
218where T: Copy + Clone +
219         Add<T, Output=T> +
220         Mul<T, Output=T> +
221         Div<T, Output=T> +
222         Sub<T, Output=T> +
223         Trig +
224         PartialOrd +
225         One + Zero  {
226
227    /// Returns the multiplication of the two matrices such that mat's transformation
228    /// applies after self's transformation.
229    ///
230    /// Assuming row vectors, this is equivalent to self * mat
231    #[cfg_attr(feature = "unstable", must_use)]
232    pub fn post_mul<NewDst>(&self, mat: &TypedTransform2D<T, Dst, NewDst>) -> TypedTransform2D<T, Src, NewDst> {
233        TypedTransform2D::row_major(
234            self.m11 * mat.m11 + self.m12 * mat.m21,
235            self.m11 * mat.m12 + self.m12 * mat.m22,
236            self.m21 * mat.m11 + self.m22 * mat.m21,
237            self.m21 * mat.m12 + self.m22 * mat.m22,
238            self.m31 * mat.m11 + self.m32 * mat.m21 + mat.m31,
239            self.m31 * mat.m12 + self.m32 * mat.m22 + mat.m32,
240        )
241    }
242
243    /// Returns the multiplication of the two matrices such that mat's transformation
244    /// applies before self's transformation.
245    ///
246    /// Assuming row vectors, this is equivalent to mat * self
247    #[cfg_attr(feature = "unstable", must_use)]
248    pub fn pre_mul<NewSrc>(&self, mat: &TypedTransform2D<T, NewSrc, Src>) -> TypedTransform2D<T, NewSrc, Dst> {
249        mat.post_mul(self)
250    }
251
252    /// Returns a translation transform.
253    pub fn create_translation(x: T, y: T) -> Self {
254         let (_0, _1): (T, T) = (Zero::zero(), One::one());
255         TypedTransform2D::row_major(
256            _1, _0,
257            _0, _1,
258             x,  y
259        )
260    }
261
262    /// Applies a translation after self's transformation and returns the resulting transform.
263    #[cfg_attr(feature = "unstable", must_use)]
264    pub fn post_translate(&self, v: TypedVector2D<T, Dst>) -> Self {
265        self.post_mul(&TypedTransform2D::create_translation(v.x, v.y))
266    }
267
268    /// Applies a translation before self's transformation and returns the resulting transform.
269    #[cfg_attr(feature = "unstable", must_use)]
270    pub fn pre_translate(&self, v: TypedVector2D<T, Src>) -> Self {
271        self.pre_mul(&TypedTransform2D::create_translation(v.x, v.y))
272    }
273
274    /// Returns a scale transform.
275    pub fn create_scale(x: T, y: T) -> Self {
276        let _0 = Zero::zero();
277        TypedTransform2D::row_major(
278             x, _0,
279            _0,  y,
280            _0, _0
281        )
282    }
283
284    /// Applies a scale after self's transformation and returns the resulting transform.
285    #[cfg_attr(feature = "unstable", must_use)]
286    pub fn post_scale(&self, x: T, y: T) -> Self {
287        self.post_mul(&TypedTransform2D::create_scale(x, y))
288    }
289
290    /// Applies a scale before self's transformation and returns the resulting transform.
291    #[cfg_attr(feature = "unstable", must_use)]
292    pub fn pre_scale(&self, x: T, y: T) -> Self {
293        TypedTransform2D::row_major(
294            self.m11 * x, self.m12,
295            self.m21,     self.m22 * y,
296            self.m31,     self.m32
297        )
298    }
299
300    /// Returns a rotation transform.
301    pub fn create_rotation(theta: Angle<T>) -> Self {
302        let _0 = Zero::zero();
303        let cos = theta.get().cos();
304        let sin = theta.get().sin();
305        TypedTransform2D::row_major(
306            cos, _0 - sin,
307            sin, cos,
308             _0, _0
309        )
310    }
311
312    /// Applies a rotation after self's transformation and returns the resulting transform.
313    #[cfg_attr(feature = "unstable", must_use)]
314    pub fn post_rotate(&self, theta: Angle<T>) -> Self {
315        self.post_mul(&TypedTransform2D::create_rotation(theta))
316    }
317
318    /// Applies a rotation after self's transformation and returns the resulting transform.
319    #[cfg_attr(feature = "unstable", must_use)]
320    pub fn pre_rotate(&self, theta: Angle<T>) -> Self {
321        self.pre_mul(&TypedTransform2D::create_rotation(theta))
322    }
323
324    /// Returns the given point transformed by this transform.
325    ///
326    /// Assuming row vectors, this is equivalent to `p * self`
327    #[inline]
328    #[cfg_attr(feature = "unstable", must_use)]
329    pub fn transform_point(&self, point: &TypedPoint2D<T, Src>) -> TypedPoint2D<T, Dst> {
330        TypedPoint2D::new(point.x * self.m11 + point.y * self.m21 + self.m31,
331                          point.x * self.m12 + point.y * self.m22 + self.m32)
332    }
333
334    /// Returns the given vector transformed by this matrix.
335    ///
336    /// Assuming row vectors, this is equivalent to `v * self`
337    #[inline]
338    #[cfg_attr(feature = "unstable", must_use)]
339    pub fn transform_vector(&self, vec: &TypedVector2D<T, Src>) -> TypedVector2D<T, Dst> {
340        vec2(vec.x * self.m11 + vec.y * self.m21,
341             vec.x * self.m12 + vec.y * self.m22)
342    }
343
344    /// Returns a rectangle that encompasses the result of transforming the given rectangle by this
345    /// transform.
346    #[inline]
347    #[cfg_attr(feature = "unstable", must_use)]
348    pub fn transform_rect(&self, rect: &TypedRect<T, Src>) -> TypedRect<T, Dst> {
349        TypedRect::from_points(&[
350            self.transform_point(&rect.origin),
351            self.transform_point(&rect.top_right()),
352            self.transform_point(&rect.bottom_left()),
353            self.transform_point(&rect.bottom_right()),
354        ])
355    }
356
357    /// Computes and returns the determinant of this transform.
358    pub fn determinant(&self) -> T {
359        self.m11 * self.m22 - self.m12 * self.m21
360    }
361
362    /// Returns the inverse transform if possible.
363    #[cfg_attr(feature = "unstable", must_use)]
364    pub fn inverse(&self) -> Option<TypedTransform2D<T, Dst, Src>> {
365        let det = self.determinant();
366
367        let _0: T = Zero::zero();
368        let _1: T = One::one();
369
370        if det == _0 {
371          return None;
372        }
373
374        let inv_det = _1 / det;
375        Some(TypedTransform2D::row_major(
376            inv_det * self.m22,
377            inv_det * (_0 - self.m12),
378            inv_det * (_0 - self.m21),
379            inv_det * self.m11,
380            inv_det * (self.m21 * self.m32 - self.m22 * self.m31),
381            inv_det * (self.m31 * self.m12 - self.m11 * self.m32),
382        ))
383    }
384
385    /// Returns the same transform with a different destination unit.
386    #[inline]
387    pub fn with_destination<NewDst>(&self) -> TypedTransform2D<T, Src, NewDst> {
388        TypedTransform2D::row_major(
389            self.m11, self.m12,
390            self.m21, self.m22,
391            self.m31, self.m32,
392        )
393    }
394
395    /// Returns the same transform with a different source unit.
396    #[inline]
397    pub fn with_source<NewSrc>(&self) -> TypedTransform2D<T, NewSrc, Dst> {
398        TypedTransform2D::row_major(
399            self.m11, self.m12,
400            self.m21, self.m22,
401            self.m31, self.m32,
402        )
403    }
404}
405
406impl <T, Src, Dst> TypedTransform2D<T, Src, Dst>
407where T: Copy + Clone +
408         Add<T, Output=T> +
409         Sub<T, Output=T> +
410         Mul<T, Output=T> +
411         Div<T, Output=T> +
412         Neg<Output=T> +
413         PartialOrd +
414         Trig +
415         One + Zero {
416    /// Create a 3D transform from the current transform
417    pub fn to_3d(&self) -> TypedTransform3D<T, Src, Dst> {
418        TypedTransform3D::row_major_2d(self.m11, self.m12, self.m21, self.m22, self.m31, self.m32)
419    }
420
421}
422
423impl <T, Src, Dst> Default for TypedTransform2D<T, Src, Dst>
424    where T: Copy + PartialEq + One + Zero
425{
426    fn default() -> Self {
427        Self::identity()
428    }
429}
430
431impl<T: ApproxEq<T>, Src, Dst> TypedTransform2D<T, Src, Dst> {
432    pub fn approx_eq(&self, other: &Self) -> bool {
433        self.m11.approx_eq(&other.m11) && self.m12.approx_eq(&other.m12) &&
434        self.m21.approx_eq(&other.m21) && self.m22.approx_eq(&other.m22) &&
435        self.m31.approx_eq(&other.m31) && self.m32.approx_eq(&other.m32)
436    }
437}
438
439impl<T: Copy + fmt::Debug, Src, Dst> fmt::Debug for TypedTransform2D<T, Src, Dst>
440where T: Copy + fmt::Debug +
441         PartialEq +
442         One + Zero {
443    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
444        if self.is_identity() {
445            write!(f, "[I]")
446        } else {
447            self.to_row_major_array().fmt(f)
448        }
449    }
450}
451
452#[cfg(feature = "mint")]
453impl<T, Src, Dst> From<mint::RowMatrix3x2<T>> for TypedTransform2D<T, Src, Dst> {
454    fn from(m: mint::RowMatrix3x2<T>) -> Self {
455        TypedTransform2D {
456            m11: m.x.x, m12: m.x.y,
457            m21: m.y.x, m22: m.y.y,
458            m31: m.z.x, m32: m.z.y,
459            _unit: PhantomData,
460        }
461    }
462}
463#[cfg(feature = "mint")]
464impl<T, Src, Dst> Into<mint::RowMatrix3x2<T>> for TypedTransform2D<T, Src, Dst> {
465    fn into(self) -> mint::RowMatrix3x2<T> {
466        mint::RowMatrix3x2 {
467            x: mint::Vector2 { x: self.m11, y: self.m12 },
468            y: mint::Vector2 { x: self.m21, y: self.m22 },
469            z: mint::Vector2 { x: self.m31, y: self.m32 },
470        }
471    }
472}
473
474
475#[cfg(test)]
476mod test {
477    use super::*;
478    use approxeq::ApproxEq;
479    use point::Point2D;
480    use Angle;
481    #[cfg(feature = "mint")]
482    use mint;
483
484    use core::f32::consts::FRAC_PI_2;
485
486    type Mat = Transform2D<f32>;
487
488    fn rad(v: f32) -> Angle<f32> { Angle::radians(v) }
489
490    #[test]
491    pub fn test_translation() {
492        let t1 = Mat::create_translation(1.0, 2.0);
493        let t2 = Mat::identity().pre_translate(vec2(1.0, 2.0));
494        let t3 = Mat::identity().post_translate(vec2(1.0, 2.0));
495        assert_eq!(t1, t2);
496        assert_eq!(t1, t3);
497
498        assert_eq!(t1.transform_point(&Point2D::new(1.0, 1.0)), Point2D::new(2.0, 3.0));
499
500        assert_eq!(t1.post_mul(&t1), Mat::create_translation(2.0, 4.0));
501    }
502
503    #[test]
504    pub fn test_rotation() {
505        let r1 = Mat::create_rotation(rad(FRAC_PI_2));
506        let r2 = Mat::identity().pre_rotate(rad(FRAC_PI_2));
507        let r3 = Mat::identity().post_rotate(rad(FRAC_PI_2));
508        assert_eq!(r1, r2);
509        assert_eq!(r1, r3);
510
511        assert!(r1.transform_point(&Point2D::new(1.0, 2.0)).approx_eq(&Point2D::new(2.0, -1.0)));
512
513        assert!(r1.post_mul(&r1).approx_eq(&Mat::create_rotation(rad(FRAC_PI_2*2.0))));
514    }
515
516    #[test]
517    pub fn test_scale() {
518        let s1 = Mat::create_scale(2.0, 3.0);
519        let s2 = Mat::identity().pre_scale(2.0, 3.0);
520        let s3 = Mat::identity().post_scale(2.0, 3.0);
521        assert_eq!(s1, s2);
522        assert_eq!(s1, s3);
523
524        assert!(s1.transform_point(&Point2D::new(2.0, 2.0)).approx_eq(&Point2D::new(4.0, 6.0)));
525    }
526
527    #[test]
528    fn test_column_major() {
529        assert_eq!(
530            Mat::row_major(
531                1.0,  2.0,
532                3.0,  4.0,
533                5.0,  6.0
534            ),
535            Mat::column_major(
536                1.0,  3.0,  5.0,
537                2.0,  4.0,  6.0,
538            )
539        );
540    }
541
542    #[test]
543    pub fn test_inverse_simple() {
544        let m1 = Mat::identity();
545        let m2 = m1.inverse().unwrap();
546        assert!(m1.approx_eq(&m2));
547    }
548
549    #[test]
550    pub fn test_inverse_scale() {
551        let m1 = Mat::create_scale(1.5, 0.3);
552        let m2 = m1.inverse().unwrap();
553        assert!(m1.pre_mul(&m2).approx_eq(&Mat::identity()));
554    }
555
556    #[test]
557    pub fn test_inverse_translate() {
558        let m1 = Mat::create_translation(-132.0, 0.3);
559        let m2 = m1.inverse().unwrap();
560        assert!(m1.pre_mul(&m2).approx_eq(&Mat::identity()));
561    }
562
563    #[test]
564    fn test_inverse_none() {
565        assert!(Mat::create_scale(2.0, 0.0).inverse().is_none());
566        assert!(Mat::create_scale(2.0, 2.0).inverse().is_some());
567    }
568
569    #[test]
570    pub fn test_pre_post() {
571        let m1 = Transform2D::identity().post_scale(1.0, 2.0).post_translate(vec2(1.0, 2.0));
572        let m2 = Transform2D::identity().pre_translate(vec2(1.0, 2.0)).pre_scale(1.0, 2.0);
573        assert!(m1.approx_eq(&m2));
574
575        let r = Mat::create_rotation(rad(FRAC_PI_2));
576        let t = Mat::create_translation(2.0, 3.0);
577
578        let a = Point2D::new(1.0, 1.0);
579
580        assert!(r.post_mul(&t).transform_point(&a).approx_eq(&Point2D::new(3.0, 2.0)));
581        assert!(t.post_mul(&r).transform_point(&a).approx_eq(&Point2D::new(4.0, -3.0)));
582        assert!(t.post_mul(&r).transform_point(&a).approx_eq(&r.transform_point(&t.transform_point(&a))));
583
584        assert!(r.pre_mul(&t).transform_point(&a).approx_eq(&Point2D::new(4.0, -3.0)));
585        assert!(t.pre_mul(&r).transform_point(&a).approx_eq(&Point2D::new(3.0, 2.0)));
586        assert!(t.pre_mul(&r).transform_point(&a).approx_eq(&t.transform_point(&r.transform_point(&a))));
587    }
588
589    #[test]
590    fn test_size_of() {
591        use core::mem::size_of;
592        assert_eq!(size_of::<Transform2D<f32>>(), 6*size_of::<f32>());
593        assert_eq!(size_of::<Transform2D<f64>>(), 6*size_of::<f64>());
594    }
595
596    #[test]
597    pub fn test_is_identity() {
598        let m1 = Transform2D::identity();
599        assert!(m1.is_identity());
600        let m2 = m1.post_translate(vec2(0.1, 0.0));
601        assert!(!m2.is_identity());
602    }
603
604    #[test]
605    pub fn test_transform_vector() {
606        // Translation does not apply to vectors.
607        let m1 = Mat::create_translation(1.0, 1.0);
608        let v1 = vec2(10.0, -10.0);
609        assert_eq!(v1, m1.transform_vector(&v1));
610    }
611
612    #[cfg(feature = "mint")]
613    #[test]
614    pub fn test_mint() {
615        let m1 = Mat::create_rotation(rad(FRAC_PI_2));
616        let mm: mint::RowMatrix3x2<_> = m1.into();
617        let m2 = Mat::from(mm);
618
619        assert_eq!(m1, m2);
620    }
621}