euclid/
transform3d.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};
13use approxeq::ApproxEq;
14use homogen::HomogeneousVector;
15#[cfg(feature = "mint")]
16use mint;
17use trig::Trig;
18use point::{TypedPoint2D, TypedPoint3D};
19use vector::{TypedVector2D, TypedVector3D, vec2, vec3};
20use rect::TypedRect;
21use transform2d::TypedTransform2D;
22use scale::TypedScale;
23use num::{One, Zero};
24use core::ops::{Add, Mul, Sub, Div, Neg};
25use core::marker::PhantomData;
26use core::fmt;
27use num_traits::NumCast;
28
29/// A 3d transform stored as a 4 by 4 matrix in row-major order in memory.
30///
31/// Transforms can be parametrized over the source and destination units, to describe a
32/// transformation from a space to another.
33/// For example, `TypedTransform3D<f32, WorldSpace, ScreenSpace>::transform_point3d`
34/// takes a `TypedPoint3D<f32, WorldSpace>` and returns a `TypedPoint3D<f32, ScreenSpace>`.
35///
36/// Transforms expose a set of convenience methods for pre- and post-transformations.
37/// A pre-transformation corresponds to adding an operation that is applied before
38/// the rest of the transformation, while a post-transformation adds an operation
39/// that is applied after.
40///
41/// These transforms are for working with _row vectors_, so the matrix math for transforming
42/// a vector is `v * T`. If your library is using column vectors, use `row_major` functions when you
43/// are asked for `column_major` representations and vice versa.
44#[derive(EuclidMatrix)]
45#[repr(C)]
46pub struct TypedTransform3D<T, Src, Dst> {
47    pub m11: T, pub m12: T, pub m13: T, pub m14: T,
48    pub m21: T, pub m22: T, pub m23: T, pub m24: T,
49    pub m31: T, pub m32: T, pub m33: T, pub m34: T,
50    pub m41: T, pub m42: T, pub m43: T, pub m44: T,
51    #[doc(hidden)]
52    pub _unit: PhantomData<(Src, Dst)>,
53}
54
55/// The default 3d transform type with no units.
56pub type Transform3D<T> = TypedTransform3D<T, UnknownUnit, UnknownUnit>;
57
58impl<T, Src, Dst> TypedTransform3D<T, Src, Dst> {
59    /// Create a transform specifying its components in row-major order.
60    ///
61    /// For example, the translation terms m41, m42, m43 on the last row with the
62    /// row-major convention) are the 13rd, 14th and 15th parameters.
63    ///
64    /// Beware: This library is written with the assumption that row vectors
65    /// are being used. If your matrices use column vectors (i.e. transforming a vector
66    /// is `T * v`), then please use `column_major`
67    #[inline]
68    #[cfg_attr(feature = "cargo-clippy", allow(too_many_arguments))]
69    pub fn row_major(
70            m11: T, m12: T, m13: T, m14: T,
71            m21: T, m22: T, m23: T, m24: T,
72            m31: T, m32: T, m33: T, m34: T,
73            m41: T, m42: T, m43: T, m44: T)
74         -> Self {
75        TypedTransform3D {
76            m11, m12, m13, m14,
77            m21, m22, m23, m24,
78            m31, m32, m33, m34,
79            m41, m42, m43, m44,
80            _unit: PhantomData,
81        }
82    }
83
84    /// Create a transform specifying its components in column-major order.
85    ///
86    /// For example, the translation terms m41, m42, m43 on the last column with the
87    /// column-major convention) are the 4th, 8th and 12nd parameters.
88    ///
89    /// Beware: This library is written with the assumption that row vectors
90    /// are being used. If your matrices use column vectors (i.e. transforming a vector
91    /// is `T * v`), then please use `row_major`
92    #[inline]
93    #[cfg_attr(feature = "cargo-clippy", allow(too_many_arguments))]
94    pub fn column_major(
95            m11: T, m21: T, m31: T, m41: T,
96            m12: T, m22: T, m32: T, m42: T,
97            m13: T, m23: T, m33: T, m43: T,
98            m14: T, m24: T, m34: T, m44: T)
99         -> Self {
100        TypedTransform3D {
101            m11, m12, m13, m14,
102            m21, m22, m23, m24,
103            m31, m32, m33, m34,
104            m41, m42, m43, m44,
105            _unit: PhantomData,
106        }
107    }
108}
109
110impl <T, Src, Dst> TypedTransform3D<T, Src, Dst>
111where T: Copy + Clone +
112         PartialEq +
113         One + Zero {
114    #[inline]
115    pub fn identity() -> Self {
116        let (_0, _1): (T, T) = (Zero::zero(), One::one());
117        TypedTransform3D::row_major(
118            _1, _0, _0, _0,
119            _0, _1, _0, _0,
120            _0, _0, _1, _0,
121            _0, _0, _0, _1
122        )
123    }
124
125    // Intentional not public, because it checks for exact equivalence
126    // while most consumers will probably want some sort of approximate
127    // equivalence to deal with floating-point errors.
128    #[inline]
129    fn is_identity(&self) -> bool {
130        *self == TypedTransform3D::identity()
131    }
132}
133
134impl <T, Src, Dst> TypedTransform3D<T, Src, Dst>
135where T: Copy + Clone +
136         Add<T, Output=T> +
137         Sub<T, Output=T> +
138         Mul<T, Output=T> +
139         Div<T, Output=T> +
140         Neg<Output=T> +
141         PartialOrd +
142         Trig +
143         One + Zero {
144
145    /// Create a 4 by 4 transform representing a 2d transformation, specifying its components
146    /// in row-major order.
147    #[inline]
148    pub fn row_major_2d(m11: T, m12: T, m21: T, m22: T, m41: T, m42: T) -> Self {
149        let (_0, _1): (T, T) = (Zero::zero(), One::one());
150        TypedTransform3D::row_major(
151            m11, m12, _0, _0,
152            m21, m22, _0, _0,
153             _0,  _0, _1, _0,
154            m41, m42, _0, _1
155       )
156    }
157
158    /// Create an orthogonal projection transform.
159    pub fn ortho(left: T, right: T,
160                 bottom: T, top: T,
161                 near: T, far: T) -> Self {
162        let tx = -((right + left) / (right - left));
163        let ty = -((top + bottom) / (top - bottom));
164        let tz = -((far + near) / (far - near));
165
166        let (_0, _1): (T, T) = (Zero::zero(), One::one());
167        let _2 = _1 + _1;
168        TypedTransform3D::row_major(
169            _2 / (right - left), _0                 , _0                , _0,
170            _0                 , _2 / (top - bottom), _0                , _0,
171            _0                 , _0                 , -_2 / (far - near), _0,
172            tx                 , ty                 , tz                , _1
173        )
174    }
175
176    /// Returns true if this transform can be represented with a `TypedTransform2D`.
177    ///
178    /// See <https://drafts.csswg.org/css-transforms/#2d-transform>
179    #[inline]
180    pub fn is_2d(&self) -> bool {
181        let (_0, _1): (T, T) = (Zero::zero(), One::one());
182        self.m31 == _0 && self.m32 == _0 &&
183        self.m13 == _0 && self.m23 == _0 &&
184        self.m43 == _0 && self.m14 == _0 &&
185        self.m24 == _0 && self.m34 == _0 &&
186        self.m33 == _1 && self.m44 == _1
187    }
188
189    /// Create a 2D transform picking the relevant terms from this transform.
190    ///
191    /// This method assumes that self represents a 2d transformation, callers
192    /// should check that self.is_2d() returns true beforehand.
193    pub fn to_2d(&self) -> TypedTransform2D<T, Src, Dst> {
194        TypedTransform2D::row_major(
195            self.m11, self.m12,
196            self.m21, self.m22,
197            self.m41, self.m42
198        )
199    }
200
201    /// Check whether shapes on the XY plane with Z pointing towards the
202    /// screen transformed by this matrix would be facing back.
203    pub fn is_backface_visible(&self) -> bool {
204        // inverse().m33 < 0;
205        let det = self.determinant();
206        let m33 = self.m12 * self.m24 * self.m41 - self.m14 * self.m22 * self.m41 +
207                  self.m14 * self.m21 * self.m42 - self.m11 * self.m24 * self.m42 -
208                  self.m12 * self.m21 * self.m44 + self.m11 * self.m22 * self.m44;
209        let _0: T = Zero::zero();
210        (m33 * det) < _0
211    }
212
213    pub fn approx_eq(&self, other: &Self) -> bool
214    where T : ApproxEq<T> {
215        self.m11.approx_eq(&other.m11) && self.m12.approx_eq(&other.m12) &&
216        self.m13.approx_eq(&other.m13) && self.m14.approx_eq(&other.m14) &&
217        self.m21.approx_eq(&other.m21) && self.m22.approx_eq(&other.m22) &&
218        self.m23.approx_eq(&other.m23) && self.m24.approx_eq(&other.m24) &&
219        self.m31.approx_eq(&other.m31) && self.m32.approx_eq(&other.m32) &&
220        self.m33.approx_eq(&other.m33) && self.m34.approx_eq(&other.m34) &&
221        self.m41.approx_eq(&other.m41) && self.m42.approx_eq(&other.m42) &&
222        self.m43.approx_eq(&other.m43) && self.m44.approx_eq(&other.m44)
223    }
224
225    /// Returns the same transform with a different destination unit.
226    #[inline]
227    pub fn with_destination<NewDst>(&self) -> TypedTransform3D<T, Src, NewDst> {
228        TypedTransform3D::row_major(
229            self.m11, self.m12, self.m13, self.m14,
230            self.m21, self.m22, self.m23, self.m24,
231            self.m31, self.m32, self.m33, self.m34,
232            self.m41, self.m42, self.m43, self.m44,
233        )
234    }
235
236    /// Returns the same transform with a different source unit.
237    #[inline]
238    pub fn with_source<NewSrc>(&self) -> TypedTransform3D<T, NewSrc, Dst> {
239        TypedTransform3D::row_major(
240            self.m11, self.m12, self.m13, self.m14,
241            self.m21, self.m22, self.m23, self.m24,
242            self.m31, self.m32, self.m33, self.m34,
243            self.m41, self.m42, self.m43, self.m44,
244        )
245    }
246
247    /// Drop the units, preserving only the numeric value.
248    #[inline]
249    pub fn to_untyped(&self) -> Transform3D<T> {
250        Transform3D::row_major(
251            self.m11, self.m12, self.m13, self.m14,
252            self.m21, self.m22, self.m23, self.m24,
253            self.m31, self.m32, self.m33, self.m34,
254            self.m41, self.m42, self.m43, self.m44,
255        )
256    }
257
258    /// Tag a unitless value with units.
259    #[inline]
260    pub fn from_untyped(m: &Transform3D<T>) -> Self {
261        TypedTransform3D::row_major(
262            m.m11, m.m12, m.m13, m.m14,
263            m.m21, m.m22, m.m23, m.m24,
264            m.m31, m.m32, m.m33, m.m34,
265            m.m41, m.m42, m.m43, m.m44,
266        )
267    }
268
269    /// Returns the multiplication of the two matrices such that mat's transformation
270    /// applies after self's transformation.
271    ///
272    /// Assuming row vectors, this is equivalent to self * mat
273    pub fn post_mul<NewDst>(&self, mat: &TypedTransform3D<T, Dst, NewDst>) -> TypedTransform3D<T, Src, NewDst> {
274        TypedTransform3D::row_major(
275            self.m11 * mat.m11  +  self.m12 * mat.m21  +  self.m13 * mat.m31  +  self.m14 * mat.m41,
276            self.m11 * mat.m12  +  self.m12 * mat.m22  +  self.m13 * mat.m32  +  self.m14 * mat.m42,
277            self.m11 * mat.m13  +  self.m12 * mat.m23  +  self.m13 * mat.m33  +  self.m14 * mat.m43,
278            self.m11 * mat.m14  +  self.m12 * mat.m24  +  self.m13 * mat.m34  +  self.m14 * mat.m44,
279            self.m21 * mat.m11  +  self.m22 * mat.m21  +  self.m23 * mat.m31  +  self.m24 * mat.m41,
280            self.m21 * mat.m12  +  self.m22 * mat.m22  +  self.m23 * mat.m32  +  self.m24 * mat.m42,
281            self.m21 * mat.m13  +  self.m22 * mat.m23  +  self.m23 * mat.m33  +  self.m24 * mat.m43,
282            self.m21 * mat.m14  +  self.m22 * mat.m24  +  self.m23 * mat.m34  +  self.m24 * mat.m44,
283            self.m31 * mat.m11  +  self.m32 * mat.m21  +  self.m33 * mat.m31  +  self.m34 * mat.m41,
284            self.m31 * mat.m12  +  self.m32 * mat.m22  +  self.m33 * mat.m32  +  self.m34 * mat.m42,
285            self.m31 * mat.m13  +  self.m32 * mat.m23  +  self.m33 * mat.m33  +  self.m34 * mat.m43,
286            self.m31 * mat.m14  +  self.m32 * mat.m24  +  self.m33 * mat.m34  +  self.m34 * mat.m44,
287            self.m41 * mat.m11  +  self.m42 * mat.m21  +  self.m43 * mat.m31  +  self.m44 * mat.m41,
288            self.m41 * mat.m12  +  self.m42 * mat.m22  +  self.m43 * mat.m32  +  self.m44 * mat.m42,
289            self.m41 * mat.m13  +  self.m42 * mat.m23  +  self.m43 * mat.m33  +  self.m44 * mat.m43,
290            self.m41 * mat.m14  +  self.m42 * mat.m24  +  self.m43 * mat.m34  +  self.m44 * mat.m44,
291        )
292    }
293
294    /// Returns the multiplication of the two matrices such that mat's transformation
295    /// applies before self's transformation.
296    ///
297    /// Assuming row vectors, this is equivalent to mat * self
298    pub fn pre_mul<NewSrc>(&self, mat: &TypedTransform3D<T, NewSrc, Src>) -> TypedTransform3D<T, NewSrc, Dst> {
299        mat.post_mul(self)
300    }
301
302    /// Returns the inverse transform if possible.
303    pub fn inverse(&self) -> Option<TypedTransform3D<T, Dst, Src>> {
304        let det = self.determinant();
305
306        if det == Zero::zero() {
307            return None;
308        }
309
310        // todo(gw): this could be made faster by special casing
311        // for simpler transform types.
312        let m = TypedTransform3D::row_major(
313            self.m23*self.m34*self.m42 - self.m24*self.m33*self.m42 +
314            self.m24*self.m32*self.m43 - self.m22*self.m34*self.m43 -
315            self.m23*self.m32*self.m44 + self.m22*self.m33*self.m44,
316
317            self.m14*self.m33*self.m42 - self.m13*self.m34*self.m42 -
318            self.m14*self.m32*self.m43 + self.m12*self.m34*self.m43 +
319            self.m13*self.m32*self.m44 - self.m12*self.m33*self.m44,
320
321            self.m13*self.m24*self.m42 - self.m14*self.m23*self.m42 +
322            self.m14*self.m22*self.m43 - self.m12*self.m24*self.m43 -
323            self.m13*self.m22*self.m44 + self.m12*self.m23*self.m44,
324
325            self.m14*self.m23*self.m32 - self.m13*self.m24*self.m32 -
326            self.m14*self.m22*self.m33 + self.m12*self.m24*self.m33 +
327            self.m13*self.m22*self.m34 - self.m12*self.m23*self.m34,
328
329            self.m24*self.m33*self.m41 - self.m23*self.m34*self.m41 -
330            self.m24*self.m31*self.m43 + self.m21*self.m34*self.m43 +
331            self.m23*self.m31*self.m44 - self.m21*self.m33*self.m44,
332
333            self.m13*self.m34*self.m41 - self.m14*self.m33*self.m41 +
334            self.m14*self.m31*self.m43 - self.m11*self.m34*self.m43 -
335            self.m13*self.m31*self.m44 + self.m11*self.m33*self.m44,
336
337            self.m14*self.m23*self.m41 - self.m13*self.m24*self.m41 -
338            self.m14*self.m21*self.m43 + self.m11*self.m24*self.m43 +
339            self.m13*self.m21*self.m44 - self.m11*self.m23*self.m44,
340
341            self.m13*self.m24*self.m31 - self.m14*self.m23*self.m31 +
342            self.m14*self.m21*self.m33 - self.m11*self.m24*self.m33 -
343            self.m13*self.m21*self.m34 + self.m11*self.m23*self.m34,
344
345            self.m22*self.m34*self.m41 - self.m24*self.m32*self.m41 +
346            self.m24*self.m31*self.m42 - self.m21*self.m34*self.m42 -
347            self.m22*self.m31*self.m44 + self.m21*self.m32*self.m44,
348
349            self.m14*self.m32*self.m41 - self.m12*self.m34*self.m41 -
350            self.m14*self.m31*self.m42 + self.m11*self.m34*self.m42 +
351            self.m12*self.m31*self.m44 - self.m11*self.m32*self.m44,
352
353            self.m12*self.m24*self.m41 - self.m14*self.m22*self.m41 +
354            self.m14*self.m21*self.m42 - self.m11*self.m24*self.m42 -
355            self.m12*self.m21*self.m44 + self.m11*self.m22*self.m44,
356
357            self.m14*self.m22*self.m31 - self.m12*self.m24*self.m31 -
358            self.m14*self.m21*self.m32 + self.m11*self.m24*self.m32 +
359            self.m12*self.m21*self.m34 - self.m11*self.m22*self.m34,
360
361            self.m23*self.m32*self.m41 - self.m22*self.m33*self.m41 -
362            self.m23*self.m31*self.m42 + self.m21*self.m33*self.m42 +
363            self.m22*self.m31*self.m43 - self.m21*self.m32*self.m43,
364
365            self.m12*self.m33*self.m41 - self.m13*self.m32*self.m41 +
366            self.m13*self.m31*self.m42 - self.m11*self.m33*self.m42 -
367            self.m12*self.m31*self.m43 + self.m11*self.m32*self.m43,
368
369            self.m13*self.m22*self.m41 - self.m12*self.m23*self.m41 -
370            self.m13*self.m21*self.m42 + self.m11*self.m23*self.m42 +
371            self.m12*self.m21*self.m43 - self.m11*self.m22*self.m43,
372
373            self.m12*self.m23*self.m31 - self.m13*self.m22*self.m31 +
374            self.m13*self.m21*self.m32 - self.m11*self.m23*self.m32 -
375            self.m12*self.m21*self.m33 + self.m11*self.m22*self.m33
376        );
377
378        let _1: T = One::one();
379        Some(m.mul_s(_1 / det))
380    }
381
382    /// Compute the determinant of the transform.
383    pub fn determinant(&self) -> T {
384        self.m14 * self.m23 * self.m32 * self.m41 -
385        self.m13 * self.m24 * self.m32 * self.m41 -
386        self.m14 * self.m22 * self.m33 * self.m41 +
387        self.m12 * self.m24 * self.m33 * self.m41 +
388        self.m13 * self.m22 * self.m34 * self.m41 -
389        self.m12 * self.m23 * self.m34 * self.m41 -
390        self.m14 * self.m23 * self.m31 * self.m42 +
391        self.m13 * self.m24 * self.m31 * self.m42 +
392        self.m14 * self.m21 * self.m33 * self.m42 -
393        self.m11 * self.m24 * self.m33 * self.m42 -
394        self.m13 * self.m21 * self.m34 * self.m42 +
395        self.m11 * self.m23 * self.m34 * self.m42 +
396        self.m14 * self.m22 * self.m31 * self.m43 -
397        self.m12 * self.m24 * self.m31 * self.m43 -
398        self.m14 * self.m21 * self.m32 * self.m43 +
399        self.m11 * self.m24 * self.m32 * self.m43 +
400        self.m12 * self.m21 * self.m34 * self.m43 -
401        self.m11 * self.m22 * self.m34 * self.m43 -
402        self.m13 * self.m22 * self.m31 * self.m44 +
403        self.m12 * self.m23 * self.m31 * self.m44 +
404        self.m13 * self.m21 * self.m32 * self.m44 -
405        self.m11 * self.m23 * self.m32 * self.m44 -
406        self.m12 * self.m21 * self.m33 * self.m44 +
407        self.m11 * self.m22 * self.m33 * self.m44
408    }
409
410    /// Multiplies all of the transform's component by a scalar and returns the result.
411    #[cfg_attr(feature = "unstable", must_use)]
412    pub fn mul_s(&self, x: T) -> Self {
413        TypedTransform3D::row_major(
414            self.m11 * x, self.m12 * x, self.m13 * x, self.m14 * x,
415            self.m21 * x, self.m22 * x, self.m23 * x, self.m24 * x,
416            self.m31 * x, self.m32 * x, self.m33 * x, self.m34 * x,
417            self.m41 * x, self.m42 * x, self.m43 * x, self.m44 * x
418        )
419    }
420
421    /// Convenience function to create a scale transform from a `TypedScale`.
422    pub fn from_scale(scale: TypedScale<T, Src, Dst>) -> Self {
423        TypedTransform3D::create_scale(scale.get(), scale.get(), scale.get())
424    }
425
426    /// Returns the homogeneous vector corresponding to the transformed 2d point.
427    ///
428    /// The input point must be use the unit Src, and the returned point has the unit Dst.
429    ///
430    /// Assuming row vectors, this is equivalent to `p * self`
431    #[inline]
432    pub fn transform_point2d_homogeneous(
433        &self, p: &TypedPoint2D<T, Src>
434    ) -> HomogeneousVector<T, Dst> {
435        let x = p.x * self.m11 + p.y * self.m21 + self.m41;
436        let y = p.x * self.m12 + p.y * self.m22 + self.m42;
437        let z = p.x * self.m13 + p.y * self.m23 + self.m43;
438        let w = p.x * self.m14 + p.y * self.m24 + self.m44;
439
440        HomogeneousVector::new(x, y, z, w)
441    }
442
443    /// Returns the given 2d point transformed by this transform, if the transform makes sense,
444    /// or `None` otherwise.
445    ///
446    /// The input point must be use the unit Src, and the returned point has the unit Dst.
447    ///
448    /// Assuming row vectors, this is equivalent to `p * self`
449    #[inline]
450    pub fn transform_point2d(&self, p: &TypedPoint2D<T, Src>) -> Option<TypedPoint2D<T, Dst>> {
451        //Note: could use `transform_point2d_homogeneous()` but it would waste the calculus of `z`
452        let w = p.x * self.m14 + p.y * self.m24 + self.m44;
453        if w > T::zero() {
454            let x = p.x * self.m11 + p.y * self.m21 + self.m41;
455            let y = p.x * self.m12 + p.y * self.m22 + self.m42;
456
457            Some(TypedPoint2D::new(x / w, y / w))
458        } else {
459            None
460        }
461    }
462
463    /// Returns the given 2d vector transformed by this matrix.
464    ///
465    /// The input point must be use the unit Src, and the returned point has the unit Dst.
466    ///
467    /// Assuming row vectors, this is equivalent to `v * self`
468    #[inline]
469    pub fn transform_vector2d(&self, v: &TypedVector2D<T, Src>) -> TypedVector2D<T, Dst> {
470        vec2(
471            v.x * self.m11 + v.y * self.m21,
472            v.x * self.m12 + v.y * self.m22,
473        )
474    }
475
476    /// Returns the homogeneous vector corresponding to the transformed 3d point.
477    ///
478    /// The input point must be use the unit Src, and the returned point has the unit Dst.
479    ///
480    /// Assuming row vectors, this is equivalent to `p * self`
481    #[inline]
482    pub fn transform_point3d_homogeneous(
483        &self, p: &TypedPoint3D<T, Src>
484    ) -> HomogeneousVector<T, Dst> {
485        let x = p.x * self.m11 + p.y * self.m21 + p.z * self.m31 + self.m41;
486        let y = p.x * self.m12 + p.y * self.m22 + p.z * self.m32 + self.m42;
487        let z = p.x * self.m13 + p.y * self.m23 + p.z * self.m33 + self.m43;
488        let w = p.x * self.m14 + p.y * self.m24 + p.z * self.m34 + self.m44;
489
490        HomogeneousVector::new(x, y, z, w)
491    }
492
493    /// Returns the given 3d point transformed by this transform, if the transform makes sense,
494    /// or `None` otherwise.
495    ///
496    /// The input point must be use the unit Src, and the returned point has the unit Dst.
497    ///
498    /// Assuming row vectors, this is equivalent to `p * self`
499    #[inline]
500    pub fn transform_point3d(&self, p: &TypedPoint3D<T, Src>) -> Option<TypedPoint3D<T, Dst>> {
501        self.transform_point3d_homogeneous(p).to_point3d()
502    }
503
504    /// Returns the given 3d vector transformed by this matrix.
505    ///
506    /// The input point must be use the unit Src, and the returned point has the unit Dst.
507    ///
508    /// Assuming row vectors, this is equivalent to `v * self`
509    #[inline]
510    pub fn transform_vector3d(&self, v: &TypedVector3D<T, Src>) -> TypedVector3D<T, Dst> {
511        vec3(
512            v.x * self.m11 + v.y * self.m21 + v.z * self.m31,
513            v.x * self.m12 + v.y * self.m22 + v.z * self.m32,
514            v.x * self.m13 + v.y * self.m23 + v.z * self.m33,
515        )
516    }
517
518    /// Returns a rectangle that encompasses the result of transforming the given rectangle by this
519    /// transform, if the transform makes sense for it, or `None` otherwise.
520    pub fn transform_rect(&self, rect: &TypedRect<T, Src>) -> Option<TypedRect<T, Dst>> {
521        Some(TypedRect::from_points(&[
522            self.transform_point2d(&rect.origin)?,
523            self.transform_point2d(&rect.top_right())?,
524            self.transform_point2d(&rect.bottom_left())?,
525            self.transform_point2d(&rect.bottom_right())?,
526        ]))
527    }
528
529    /// Create a 3d translation transform
530    pub fn create_translation(x: T, y: T, z: T) -> Self {
531        let (_0, _1): (T, T) = (Zero::zero(), One::one());
532        TypedTransform3D::row_major(
533            _1, _0, _0, _0,
534            _0, _1, _0, _0,
535            _0, _0, _1, _0,
536             x,  y,  z, _1
537        )
538    }
539
540    /// Returns a transform with a translation applied before self's transformation.
541    #[cfg_attr(feature = "unstable", must_use)]
542    pub fn pre_translate(&self, v: TypedVector3D<T, Src>) -> Self {
543        self.pre_mul(&TypedTransform3D::create_translation(v.x, v.y, v.z))
544    }
545
546    /// Returns a transform with a translation applied after self's transformation.
547    #[cfg_attr(feature = "unstable", must_use)]
548    pub fn post_translate(&self, v: TypedVector3D<T, Dst>) -> Self {
549        self.post_mul(&TypedTransform3D::create_translation(v.x, v.y, v.z))
550    }
551
552    /// Returns a projection of this transform in 2d space.
553    pub fn project_to_2d(&self) -> Self {
554        let (_0, _1): (T, T) = (Zero::zero(), One::one());
555
556        let mut result = self.clone();
557
558        result.m31 = _0;
559        result.m32 = _0;
560        result.m13 = _0;
561        result.m23 = _0;
562        result.m33 = _1;
563        result.m43 = _0;
564        result.m34 = _0;
565
566        // Try to normalize perspective when possible to convert to a 2d matrix.
567        // Some matrices, such as those derived from perspective transforms, can
568        // modify m44 from 1, while leaving the rest of the fourth column
569        // (m14, m24) at 0. In this case, after resetting the third row and
570        // third column above, the value of m44 functions only to scale the
571        // coordinate transform divide by W. The matrix can be converted to
572        // a true 2D matrix by normalizing out the scaling effect of m44 on
573        // the remaining components ahead of time.
574        if self.m14 == _0 && self.m24 == _0 && self.m44 != _0 && self.m44 != _1 {
575           let scale = _1 / self.m44;
576           result.m11 = result.m11 * scale;
577           result.m12 = result.m12 * scale;
578           result.m21 = result.m21 * scale;
579           result.m22 = result.m22 * scale;
580           result.m41 = result.m41 * scale;
581           result.m42 = result.m42 * scale;
582           result.m44 = _1;
583        }
584
585        result
586    }
587
588    /// Create a 3d scale transform
589    pub fn create_scale(x: T, y: T, z: T) -> Self {
590        let (_0, _1): (T, T) = (Zero::zero(), One::one());
591        TypedTransform3D::row_major(
592             x, _0, _0, _0,
593            _0,  y, _0, _0,
594            _0, _0,  z, _0,
595            _0, _0, _0, _1
596        )
597    }
598
599    /// Returns a transform with a scale applied before self's transformation.
600    #[cfg_attr(feature = "unstable", must_use)]
601    pub fn pre_scale(&self, x: T, y: T, z: T) -> Self {
602        TypedTransform3D::row_major(
603            self.m11 * x, self.m12,     self.m13,     self.m14,
604            self.m21    , self.m22 * y, self.m23,     self.m24,
605            self.m31    , self.m32,     self.m33 * z, self.m34,
606            self.m41    , self.m42,     self.m43,     self.m44
607        )
608    }
609
610    /// Returns a transform with a scale applied after self's transformation.
611    #[cfg_attr(feature = "unstable", must_use)]
612    pub fn post_scale(&self, x: T, y: T, z: T) -> Self {
613        self.post_mul(&TypedTransform3D::create_scale(x, y, z))
614    }
615
616    /// Create a 3d rotation transform from an angle / axis.
617    /// The supplied axis must be normalized.
618    pub fn create_rotation(x: T, y: T, z: T, theta: Angle<T>) -> Self {
619        let (_0, _1): (T, T) = (Zero::zero(), One::one());
620        let _2 = _1 + _1;
621
622        let xx = x * x;
623        let yy = y * y;
624        let zz = z * z;
625
626        let half_theta = theta.get() / _2;
627        let sc = half_theta.sin() * half_theta.cos();
628        let sq = half_theta.sin() * half_theta.sin();
629
630        TypedTransform3D::row_major(
631            _1 - _2 * (yy + zz) * sq,
632            _2 * (x * y * sq - z * sc),
633            _2 * (x * z * sq + y * sc),
634            _0,
635
636            _2 * (x * y * sq + z * sc),
637            _1 - _2 * (xx + zz) * sq,
638            _2 * (y * z * sq - x * sc),
639            _0,
640
641            _2 * (x * z * sq - y * sc),
642            _2 * (y * z * sq + x * sc),
643            _1 - _2 * (xx + yy) * sq,
644            _0,
645
646            _0,
647            _0,
648            _0,
649            _1
650        )
651    }
652
653    /// Returns a transform with a rotation applied after self's transformation.
654    #[cfg_attr(feature = "unstable", must_use)]
655    pub fn post_rotate(&self, x: T, y: T, z: T, theta: Angle<T>) -> Self {
656        self.post_mul(&TypedTransform3D::create_rotation(x, y, z, theta))
657    }
658
659    /// Returns a transform with a rotation applied before self's transformation.
660    #[cfg_attr(feature = "unstable", must_use)]
661    pub fn pre_rotate(&self, x: T, y: T, z: T, theta: Angle<T>) -> Self {
662        self.pre_mul(&TypedTransform3D::create_rotation(x, y, z, theta))
663    }
664
665    /// Create a 2d skew transform.
666    ///
667    /// See <https://drafts.csswg.org/css-transforms/#funcdef-skew>
668    pub fn create_skew(alpha: Angle<T>, beta: Angle<T>) -> Self {
669        let (_0, _1): (T, T) = (Zero::zero(), One::one());
670        let (sx, sy) = (beta.get().tan(), alpha.get().tan());
671        TypedTransform3D::row_major(
672            _1, sx, _0, _0,
673            sy, _1, _0, _0,
674            _0, _0, _1, _0,
675            _0, _0, _0, _1
676        )
677    }
678
679    /// Create a simple perspective projection transform
680    pub fn create_perspective(d: T) -> Self {
681        let (_0, _1): (T, T) = (Zero::zero(), One::one());
682        TypedTransform3D::row_major(
683            _1, _0, _0, _0,
684            _0, _1, _0, _0,
685            _0, _0, _1, -_1 / d,
686            _0, _0, _0, _1
687        )
688    }
689}
690
691impl<T: Copy, Src, Dst> TypedTransform3D<T, Src, Dst> {
692    /// Returns an array containing this transform's terms in row-major order (the order
693    /// in which the transform is actually laid out in memory).
694    ///
695    /// Beware: This library is written with the assumption that row vectors
696    /// are being used. If your matrices use column vectors (i.e. transforming a vector
697    /// is `T * v`), then please use `to_column_major_array`
698    pub fn to_row_major_array(&self) -> [T; 16] {
699        [
700            self.m11, self.m12, self.m13, self.m14,
701            self.m21, self.m22, self.m23, self.m24,
702            self.m31, self.m32, self.m33, self.m34,
703            self.m41, self.m42, self.m43, self.m44
704        ]
705    }
706
707    /// Returns an array containing this transform's terms in column-major order.
708    ///
709    /// Beware: This library is written with the assumption that row vectors
710    /// are being used. If your matrices use column vectors (i.e. transforming a vector
711    /// is `T * v`), then please use `to_row_major_array`
712    pub fn to_column_major_array(&self) -> [T; 16] {
713        [
714            self.m11, self.m21, self.m31, self.m41,
715            self.m12, self.m22, self.m32, self.m42,
716            self.m13, self.m23, self.m33, self.m43,
717            self.m14, self.m24, self.m34, self.m44
718        ]
719    }
720
721    /// Returns an array containing this transform's 4 rows in (in row-major order)
722    /// as arrays.
723    ///
724    /// This is a convenience method to interface with other libraries like glium.
725    ///
726    /// Beware: This library is written with the assumption that row vectors
727    /// are being used. If your matrices use column vectors (i.e. transforming a vector
728    /// is `T * v`), then please use `to_column_arrays`
729    pub fn to_row_arrays(&self) -> [[T; 4]; 4] {
730        [
731            [self.m11, self.m12, self.m13, self.m14],
732            [self.m21, self.m22, self.m23, self.m24],
733            [self.m31, self.m32, self.m33, self.m34],
734            [self.m41, self.m42, self.m43, self.m44]
735        ]
736    }
737
738    /// Returns an array containing this transform's 4 columns in (in row-major order,
739    /// or 4 rows in column-major order) as arrays.
740    ///
741    /// This is a convenience method to interface with other libraries like glium.
742    ///
743    /// Beware: This library is written with the assumption that row vectors
744    /// are being used. If your matrices use column vectors (i.e. transforming a vector
745    /// is `T * v`), then please use `to_row_arrays`
746    pub fn to_column_arrays(&self) -> [[T; 4]; 4] {
747        [
748            [self.m11, self.m21, self.m31, self.m41],
749            [self.m12, self.m22, self.m32, self.m42],
750            [self.m13, self.m23, self.m33, self.m43],
751            [self.m14, self.m24, self.m34, self.m44]
752        ]
753    }
754
755    /// Creates a transform from an array of 16 elements in row-major order.
756    ///
757    /// Beware: This library is written with the assumption that row vectors
758    /// are being used. If your matrices use column vectors (i.e. transforming a vector
759    /// is `T * v`), please provide column-major data to this function.
760    pub fn from_array(array: [T; 16]) -> Self {
761        Self::row_major(
762            array[0],  array[1],  array[2],  array[3],
763            array[4],  array[5],  array[6],  array[7],
764            array[8],  array[9],  array[10], array[11],
765            array[12], array[13], array[14], array[15],
766        )
767    }
768
769    /// Creates a transform from 4 rows of 4 elements (row-major order).
770    ///
771    /// Beware: This library is written with the assumption that row vectors
772    /// are being used. If your matrices use column vectors (i.e. transforming a vector
773    /// is `T * v`), please provide column-major data to tis function.
774    pub fn from_row_arrays(array: [[T; 4]; 4]) -> Self {
775        Self::row_major(
776            array[0][0], array[0][1], array[0][2], array[0][3],
777            array[1][0], array[1][1], array[1][2], array[1][3],
778            array[2][0], array[2][1], array[2][2], array[2][3],
779            array[3][0], array[3][1], array[3][2], array[3][3],
780        )
781    }
782}
783
784impl<T0: NumCast + Copy, Src, Dst> TypedTransform3D<T0, Src, Dst> {
785    /// Cast from one numeric representation to another, preserving the units.
786    pub fn cast<T1: NumCast + Copy>(&self) -> TypedTransform3D<T1, Src, Dst> {
787        self.try_cast().unwrap()
788    }
789
790    /// Fallible cast from one numeric representation to another, preserving the units.
791    pub fn try_cast<T1: NumCast + Copy>(&self) -> Option<TypedTransform3D<T1, Src, Dst>> {
792        match (NumCast::from(self.m11), NumCast::from(self.m12),
793               NumCast::from(self.m13), NumCast::from(self.m14),
794               NumCast::from(self.m21), NumCast::from(self.m22),
795               NumCast::from(self.m23), NumCast::from(self.m24),
796               NumCast::from(self.m31), NumCast::from(self.m32),
797               NumCast::from(self.m33), NumCast::from(self.m34),
798               NumCast::from(self.m41), NumCast::from(self.m42),
799               NumCast::from(self.m43), NumCast::from(self.m44)) {
800            (Some(m11), Some(m12), Some(m13), Some(m14),
801             Some(m21), Some(m22), Some(m23), Some(m24),
802             Some(m31), Some(m32), Some(m33), Some(m34),
803             Some(m41), Some(m42), Some(m43), Some(m44)) => {
804                Some(TypedTransform3D::row_major(m11, m12, m13, m14,
805                                                 m21, m22, m23, m24,
806                                                 m31, m32, m33, m34,
807                                                 m41, m42, m43, m44))
808            },
809            _ => None
810        }
811    }
812}
813
814impl <T, Src, Dst> Default for TypedTransform3D<T, Src, Dst>
815    where T: Copy + PartialEq + One + Zero
816{
817    fn default() -> Self {
818        Self::identity()
819    }
820}
821
822impl<T, Src, Dst> fmt::Debug for TypedTransform3D<T, Src, Dst>
823where T: Copy + fmt::Debug +
824         PartialEq +
825         One + Zero {
826    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
827        if self.is_identity() {
828            write!(f, "[I]")
829        } else {
830            self.to_row_major_array().fmt(f)
831        }
832    }
833}
834
835#[cfg(feature = "mint")]
836impl<T, Src, Dst> From<mint::RowMatrix4<T>> for TypedTransform3D<T, Src, Dst> {
837    fn from(m: mint::RowMatrix4<T>) -> Self {
838        TypedTransform3D {
839            m11: m.x.x, m12: m.x.y, m13: m.x.z, m14: m.x.w,
840            m21: m.y.x, m22: m.y.y, m23: m.y.z, m24: m.y.w,
841            m31: m.z.x, m32: m.z.y, m33: m.z.z, m34: m.z.w,
842            m41: m.w.x, m42: m.w.y, m43: m.w.z, m44: m.w.w,
843            _unit: PhantomData,
844        }
845    }
846}
847#[cfg(feature = "mint")]
848impl<T, Src, Dst> Into<mint::RowMatrix4<T>> for TypedTransform3D<T, Src, Dst> {
849    fn into(self) -> mint::RowMatrix4<T> {
850        mint::RowMatrix4 {
851            x: mint::Vector4 { x: self.m11, y: self.m12, z: self.m13, w: self.m14 },
852            y: mint::Vector4 { x: self.m21, y: self.m22, z: self.m23, w: self.m24 },
853            z: mint::Vector4 { x: self.m31, y: self.m32, z: self.m33, w: self.m34 },
854            w: mint::Vector4 { x: self.m41, y: self.m42, z: self.m43, w: self.m44 },
855        }
856    }
857}
858
859
860#[cfg(test)]
861mod tests {
862    use approxeq::ApproxEq;
863    use transform2d::Transform2D;
864    use point::{point2, point3};
865    use Angle;
866    use super::*;
867
868    use core::f32::consts::{FRAC_PI_2, PI};
869
870    type Mf32 = Transform3D<f32>;
871
872    // For convenience.
873    fn rad(v: f32) -> Angle<f32> { Angle::radians(v) }
874
875    #[test]
876    pub fn test_translation() {
877        let t1 = Mf32::create_translation(1.0, 2.0, 3.0);
878        let t2 = Mf32::identity().pre_translate(vec3(1.0, 2.0, 3.0));
879        let t3 = Mf32::identity().post_translate(vec3(1.0, 2.0, 3.0));
880        assert_eq!(t1, t2);
881        assert_eq!(t1, t3);
882
883        assert_eq!(t1.transform_point3d(&point3(1.0, 1.0, 1.0)), Some(point3(2.0, 3.0, 4.0)));
884        assert_eq!(t1.transform_point2d(&point2(1.0, 1.0)), Some(point2(2.0, 3.0)));
885
886        assert_eq!(t1.post_mul(&t1), Mf32::create_translation(2.0, 4.0, 6.0));
887
888        assert!(!t1.is_2d());
889        assert_eq!(Mf32::create_translation(1.0, 2.0, 3.0).to_2d(), Transform2D::create_translation(1.0, 2.0));
890    }
891
892    #[test]
893    pub fn test_rotation() {
894        let r1 = Mf32::create_rotation(0.0, 0.0, 1.0, rad(FRAC_PI_2));
895        let r2 = Mf32::identity().pre_rotate(0.0, 0.0, 1.0, rad(FRAC_PI_2));
896        let r3 = Mf32::identity().post_rotate(0.0, 0.0, 1.0, rad(FRAC_PI_2));
897        assert_eq!(r1, r2);
898        assert_eq!(r1, r3);
899
900        assert!(r1.transform_point3d(&point3(1.0, 2.0, 3.0)).unwrap().approx_eq(&point3(2.0, -1.0, 3.0)));
901        assert!(r1.transform_point2d(&point2(1.0, 2.0)).unwrap().approx_eq(&point2(2.0, -1.0)));
902
903        assert!(r1.post_mul(&r1).approx_eq(&Mf32::create_rotation(0.0, 0.0, 1.0, rad(FRAC_PI_2*2.0))));
904
905        assert!(r1.is_2d());
906        assert!(r1.to_2d().approx_eq(&Transform2D::create_rotation(rad(FRAC_PI_2))));
907    }
908
909    #[test]
910    pub fn test_scale() {
911        let s1 = Mf32::create_scale(2.0, 3.0, 4.0);
912        let s2 = Mf32::identity().pre_scale(2.0, 3.0, 4.0);
913        let s3 = Mf32::identity().post_scale(2.0, 3.0, 4.0);
914        assert_eq!(s1, s2);
915        assert_eq!(s1, s3);
916
917        assert!(s1.transform_point3d(&point3(2.0, 2.0, 2.0)).unwrap().approx_eq(&point3(4.0, 6.0, 8.0)));
918        assert!(s1.transform_point2d(&point2(2.0, 2.0)).unwrap().approx_eq(&point2(4.0, 6.0)));
919
920        assert_eq!(s1.post_mul(&s1), Mf32::create_scale(4.0, 9.0, 16.0));
921
922        assert!(!s1.is_2d());
923        assert_eq!(Mf32::create_scale(2.0, 3.0, 0.0).to_2d(), Transform2D::create_scale(2.0, 3.0));
924    }
925
926    #[test]
927    pub fn test_ortho() {
928        let (left, right, bottom, top) = (0.0f32, 1.0f32, 0.1f32, 1.0f32);
929        let (near, far) = (-1.0f32, 1.0f32);
930        let result = Mf32::ortho(left, right, bottom, top, near, far);
931        let expected = Mf32::row_major(
932             2.0,  0.0,         0.0, 0.0,
933             0.0,  2.22222222,  0.0, 0.0,
934             0.0,  0.0,        -1.0, 0.0,
935            -1.0, -1.22222222, -0.0, 1.0
936        );
937        assert!(result.approx_eq(&expected));
938    }
939
940    #[test]
941    pub fn test_is_2d() {
942        assert!(Mf32::identity().is_2d());
943        assert!(Mf32::create_rotation(0.0, 0.0, 1.0, rad(0.7854)).is_2d());
944        assert!(!Mf32::create_rotation(0.0, 1.0, 0.0, rad(0.7854)).is_2d());
945    }
946
947    #[test]
948    pub fn test_row_major_2d() {
949        let m1 = Mf32::row_major_2d(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);
950        let m2 = Mf32::row_major(
951            1.0, 2.0, 0.0, 0.0,
952            3.0, 4.0, 0.0, 0.0,
953            0.0, 0.0, 1.0, 0.0,
954            5.0, 6.0, 0.0, 1.0
955        );
956        assert_eq!(m1, m2);
957    }
958
959    #[test]
960    fn test_column_major() {
961        assert_eq!(
962            Mf32::row_major(
963                1.0,  2.0,  3.0,  4.0,
964                5.0,  6.0,  7.0,  8.0,
965                9.0,  10.0, 11.0, 12.0,
966                13.0, 14.0, 15.0, 16.0,
967            ),
968            Mf32::column_major(
969                1.0,  5.0,  9.0,  13.0,
970                2.0,  6.0,  10.0, 14.0,
971                3.0,  7.0,  11.0, 15.0,
972                4.0,  8.0,  12.0, 16.0,
973            )
974        );
975    }
976
977    #[test]
978    pub fn test_inverse_simple() {
979        let m1 = Mf32::identity();
980        let m2 = m1.inverse().unwrap();
981        assert!(m1.approx_eq(&m2));
982    }
983
984    #[test]
985    pub fn test_inverse_scale() {
986        let m1 = Mf32::create_scale(1.5, 0.3, 2.1);
987        let m2 = m1.inverse().unwrap();
988        assert!(m1.pre_mul(&m2).approx_eq(&Mf32::identity()));
989    }
990
991    #[test]
992    pub fn test_inverse_translate() {
993        let m1 = Mf32::create_translation(-132.0, 0.3, 493.0);
994        let m2 = m1.inverse().unwrap();
995        assert!(m1.pre_mul(&m2).approx_eq(&Mf32::identity()));
996    }
997
998    #[test]
999    pub fn test_inverse_rotate() {
1000        let m1 = Mf32::create_rotation(0.0, 1.0, 0.0, rad(1.57));
1001        let m2 = m1.inverse().unwrap();
1002        assert!(m1.pre_mul(&m2).approx_eq(&Mf32::identity()));
1003    }
1004
1005    #[test]
1006    pub fn test_inverse_transform_point_2d() {
1007        let m1 = Mf32::create_translation(100.0, 200.0, 0.0);
1008        let m2 = m1.inverse().unwrap();
1009        assert!(m1.pre_mul(&m2).approx_eq(&Mf32::identity()));
1010
1011        let p1 = point2(1000.0, 2000.0);
1012        let p2 = m1.transform_point2d(&p1);
1013        assert_eq!(p2, Some(point2(1100.0, 2200.0)));
1014
1015        let p3 = m2.transform_point2d(&p2.unwrap());
1016        assert_eq!(p3, Some(p1));
1017    }
1018
1019    #[test]
1020    fn test_inverse_none() {
1021        assert!(Mf32::create_scale(2.0, 0.0, 2.0).inverse().is_none());
1022        assert!(Mf32::create_scale(2.0, 2.0, 2.0).inverse().is_some());
1023    }
1024
1025    #[test]
1026    pub fn test_pre_post() {
1027        let m1 = Transform3D::identity().post_scale(1.0, 2.0, 3.0).post_translate(vec3(1.0, 2.0, 3.0));
1028        let m2 = Transform3D::identity().pre_translate(vec3(1.0, 2.0, 3.0)).pre_scale(1.0, 2.0, 3.0);
1029        assert!(m1.approx_eq(&m2));
1030
1031        let r = Mf32::create_rotation(0.0, 0.0, 1.0, rad(FRAC_PI_2));
1032        let t = Mf32::create_translation(2.0, 3.0, 0.0);
1033
1034        let a = point3(1.0, 1.0, 1.0);
1035
1036        assert!(r.post_mul(&t).transform_point3d(&a).unwrap().approx_eq(&point3(3.0, 2.0, 1.0)));
1037        assert!(t.post_mul(&r).transform_point3d(&a).unwrap().approx_eq(&point3(4.0, -3.0, 1.0)));
1038        assert!(t.post_mul(&r).transform_point3d(&a).unwrap().approx_eq(&r.transform_point3d(&t.transform_point3d(&a).unwrap()).unwrap()));
1039
1040        assert!(r.pre_mul(&t).transform_point3d(&a).unwrap().approx_eq(&point3(4.0, -3.0, 1.0)));
1041        assert!(t.pre_mul(&r).transform_point3d(&a).unwrap().approx_eq(&point3(3.0, 2.0, 1.0)));
1042        assert!(t.pre_mul(&r).transform_point3d(&a).unwrap().approx_eq(&t.transform_point3d(&r.transform_point3d(&a).unwrap()).unwrap()));
1043    }
1044
1045    #[test]
1046    fn test_size_of() {
1047        use core::mem::size_of;
1048        assert_eq!(size_of::<Transform3D<f32>>(), 16*size_of::<f32>());
1049        assert_eq!(size_of::<Transform3D<f64>>(), 16*size_of::<f64>());
1050    }
1051
1052    #[test]
1053    pub fn test_transform_associativity() {
1054        let m1 = Mf32::row_major(3.0, 2.0, 1.5, 1.0,
1055                                 0.0, 4.5, -1.0, -4.0,
1056                                 0.0, 3.5, 2.5, 40.0,
1057                                 0.0, 3.0, 0.0, 1.0);
1058        let m2 = Mf32::row_major(1.0, -1.0, 3.0, 0.0,
1059                                 -1.0, 0.5, 0.0, 2.0,
1060                                 1.5, -2.0, 6.0, 0.0,
1061                                 -2.5, 6.0, 1.0, 1.0);
1062
1063        let p = point3(1.0, 3.0, 5.0);
1064        let p1 = m2.pre_mul(&m1).transform_point3d(&p).unwrap();
1065        let p2 = m2.transform_point3d(&m1.transform_point3d(&p).unwrap()).unwrap();
1066        assert!(p1.approx_eq(&p2));
1067    }
1068
1069    #[test]
1070    pub fn test_is_identity() {
1071        let m1 = Transform3D::identity();
1072        assert!(m1.is_identity());
1073        let m2 = m1.post_translate(vec3(0.1, 0.0, 0.0));
1074        assert!(!m2.is_identity());
1075    }
1076
1077    #[test]
1078    pub fn test_transform_vector() {
1079        // Translation does not apply to vectors.
1080        let m = Mf32::create_translation(1.0, 2.0, 3.0);
1081        let v1 = vec3(10.0, -10.0, 3.0);
1082        assert_eq!(v1, m.transform_vector3d(&v1));
1083        // While it does apply to points.
1084        assert_ne!(Some(v1.to_point()), m.transform_point3d(&v1.to_point()));
1085
1086        // same thing with 2d vectors/points
1087        let v2 = vec2(10.0, -5.0);
1088        assert_eq!(v2, m.transform_vector2d(&v2));
1089        assert_ne!(Some(v2.to_point()), m.transform_point2d(&v2.to_point()));
1090    }
1091
1092    #[test]
1093    pub fn test_is_backface_visible() {
1094        // backface is not visible for rotate-x 0 degree.
1095        let r1 = Mf32::create_rotation(1.0, 0.0, 0.0, rad(0.0));
1096        assert!(!r1.is_backface_visible());
1097        // backface is not visible for rotate-x 45 degree.
1098        let r1 = Mf32::create_rotation(1.0, 0.0, 0.0, rad(PI * 0.25));
1099        assert!(!r1.is_backface_visible());
1100        // backface is visible for rotate-x 180 degree.
1101        let r1 = Mf32::create_rotation(1.0, 0.0, 0.0, rad(PI));
1102        assert!(r1.is_backface_visible());
1103        // backface is visible for rotate-x 225 degree.
1104        let r1 = Mf32::create_rotation(1.0, 0.0, 0.0, rad(PI * 1.25));
1105        assert!(r1.is_backface_visible());
1106        // backface is not visible for non-inverseable matrix
1107        let r1 = Mf32::create_scale(2.0, 0.0, 2.0);
1108        assert!(!r1.is_backface_visible());
1109    }
1110
1111    #[test]
1112    pub fn test_homogeneous() {
1113        let m = Mf32::row_major(
1114            1.0, 2.0, 0.5, 5.0,
1115            3.0, 4.0, 0.25, 6.0,
1116            0.5, -1.0, 1.0, -1.0,
1117            -1.0, 1.0, -1.0, 2.0,
1118        );
1119        assert_eq!(
1120            m.transform_point2d_homogeneous(&point2(1.0, 2.0)),
1121            HomogeneousVector::new(6.0, 11.0, 0.0, 19.0),
1122        );
1123        assert_eq!(
1124            m.transform_point3d_homogeneous(&point3(1.0, 2.0, 4.0)),
1125            HomogeneousVector::new(8.0, 7.0, 4.0, 15.0),
1126        );
1127    }
1128
1129    #[test]
1130    pub fn test_perspective_division() {
1131        let p = point2(1.0, 2.0);
1132        let mut m = Mf32::identity();
1133        assert!(m.transform_point2d(&p).is_some());
1134        m.m44 = 0.0;
1135        assert_eq!(None, m.transform_point2d(&p));
1136        m.m44 = 1.0;
1137        m.m24 = -1.0;
1138        assert_eq!(None, m.transform_point2d(&p));
1139    }
1140
1141    #[cfg(feature = "mint")]
1142    #[test]
1143    pub fn test_mint() {
1144        let m1 = Mf32::create_rotation(0.0, 0.0, 1.0, rad(FRAC_PI_2));
1145        let mm: mint::RowMatrix4<_> = m1.into();
1146        let m2 = Mf32::from(mm);
1147
1148        assert_eq!(m1, m2);
1149    }
1150}