1#![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#[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
52pub type Transform2D<T> = TypedTransform2D<T, UnknownUnit, UnknownUnit>;
54
55impl<T: Copy, Src, Dst> TypedTransform2D<T, Src, Dst> {
56 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 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 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 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 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 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 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 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 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 pub fn cast<T1: NumCast + Copy>(&self) -> TypedTransform2D<T1, Src, Dst> {
174 self.try_cast().unwrap()
175 }
176
177 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 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 #[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 #[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 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 #[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 #[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 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 #[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 #[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 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 #[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 #[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 #[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 #[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 #[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 pub fn determinant(&self) -> T {
359 self.m11 * self.m22 - self.m12 * self.m21
360 }
361
362 #[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 #[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 #[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 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 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}