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 | #![allow (clippy::just_underscores_and_digits)] |
11 | |
12 | use super::{Angle, UnknownUnit}; |
13 | use crate::approxeq::ApproxEq; |
14 | use crate::box2d::Box2D; |
15 | use crate::num::{One, Zero}; |
16 | use crate::point::{point2, Point2D}; |
17 | use crate::rect::Rect; |
18 | use crate::transform3d::Transform3D; |
19 | use crate::trig::Trig; |
20 | use crate::vector::{vec2, Vector2D}; |
21 | use core::cmp::{Eq, PartialEq}; |
22 | use core::fmt; |
23 | use core::hash::Hash; |
24 | use core::marker::PhantomData; |
25 | use core::ops::{Add, Div, Mul, Sub}; |
26 | |
27 | #[cfg (feature = "bytemuck" )] |
28 | use bytemuck::{Pod, Zeroable}; |
29 | #[cfg (feature = "mint" )] |
30 | use mint; |
31 | use num_traits::NumCast; |
32 | #[cfg (feature = "serde" )] |
33 | use serde::{Deserialize, Serialize}; |
34 | |
35 | /// A 2d transform represented by a column-major 3 by 3 matrix, compressed down to 3 by 2. |
36 | /// |
37 | /// Transforms can be parametrized over the source and destination units, to describe a |
38 | /// transformation from a space to another. |
39 | /// For example, `Transform2D<f32, WorldSpace, ScreenSpace>::transform_point4d` |
40 | /// takes a `Point2D<f32, WorldSpace>` and returns a `Point2D<f32, ScreenSpace>`. |
41 | /// |
42 | /// Transforms expose a set of convenience methods for pre- and post-transformations. |
43 | /// Pre-transformations (`pre_*` methods) correspond to adding an operation that is |
44 | /// applied before the rest of the transformation, while post-transformations (`then_*` |
45 | /// methods) add an operation that is applied after. |
46 | /// |
47 | /// The matrix representation is conceptually equivalent to a 3 by 3 matrix transformation |
48 | /// compressed to 3 by 2 with the components that aren't needed to describe the set of 2d |
49 | /// transformations we are interested in implicitly defined: |
50 | /// |
51 | /// ```text |
52 | /// | m11 m21 m31 | |x| |x'| |
53 | /// | m12 m22 m32 | x |y| = |y'| |
54 | /// | 0 0 1 | |1| |1 | |
55 | /// ``` |
56 | /// |
57 | /// When translating `Transform2D` into general matrix representations, consider that the |
58 | /// representation follows the column-major notation with column vectors. |
59 | /// |
60 | /// The translation terms are `m31` and `m32`. |
61 | #[repr (C)] |
62 | #[cfg_attr (feature = "serde" , derive(Serialize, Deserialize))] |
63 | #[cfg_attr ( |
64 | feature = "serde" , |
65 | serde(bound(serialize = "T: Serialize" , deserialize = "T: Deserialize<'de>" )) |
66 | )] |
67 | #[rustfmt::skip] |
68 | pub struct Transform2D<T, Src, Dst> { |
69 | pub m11: T, pub m12: T, |
70 | pub m21: T, pub m22: T, |
71 | pub m31: T, pub m32: T, |
72 | #[doc (hidden)] |
73 | pub _unit: PhantomData<(Src, Dst)>, |
74 | } |
75 | |
76 | #[cfg (feature = "arbitrary" )] |
77 | impl<'a, T, Src, Dst> arbitrary::Arbitrary<'a> for Transform2D<T, Src, Dst> |
78 | where |
79 | T: arbitrary::Arbitrary<'a>, |
80 | { |
81 | fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> { |
82 | let (m11, m12, m21, m22, m31, m32) = arbitrary::Arbitrary::arbitrary(u)?; |
83 | Ok(Transform2D { |
84 | m11, |
85 | m12, |
86 | m21, |
87 | m22, |
88 | m31, |
89 | m32, |
90 | _unit: PhantomData, |
91 | }) |
92 | } |
93 | } |
94 | |
95 | #[cfg (feature = "bytemuck" )] |
96 | unsafe impl<T: Zeroable, Src, Dst> Zeroable for Transform2D<T, Src, Dst> {} |
97 | |
98 | #[cfg (feature = "bytemuck" )] |
99 | unsafe impl<T: Pod, Src: 'static, Dst: 'static> Pod for Transform2D<T, Src, Dst> {} |
100 | |
101 | impl<T: Copy, Src, Dst> Copy for Transform2D<T, Src, Dst> {} |
102 | |
103 | impl<T: Clone, Src, Dst> Clone for Transform2D<T, Src, Dst> { |
104 | fn clone(&self) -> Self { |
105 | Transform2D { |
106 | m11: self.m11.clone(), |
107 | m12: self.m12.clone(), |
108 | m21: self.m21.clone(), |
109 | m22: self.m22.clone(), |
110 | m31: self.m31.clone(), |
111 | m32: self.m32.clone(), |
112 | _unit: PhantomData, |
113 | } |
114 | } |
115 | } |
116 | |
117 | impl<T, Src, Dst> Eq for Transform2D<T, Src, Dst> where T: Eq {} |
118 | |
119 | impl<T, Src, Dst> PartialEq for Transform2D<T, Src, Dst> |
120 | where |
121 | T: PartialEq, |
122 | { |
123 | fn eq(&self, other: &Self) -> bool { |
124 | self.m11 == other.m11 |
125 | && self.m12 == other.m12 |
126 | && self.m21 == other.m21 |
127 | && self.m22 == other.m22 |
128 | && self.m31 == other.m31 |
129 | && self.m32 == other.m32 |
130 | } |
131 | } |
132 | |
133 | impl<T, Src, Dst> Hash for Transform2D<T, Src, Dst> |
134 | where |
135 | T: Hash, |
136 | { |
137 | fn hash<H: core::hash::Hasher>(&self, h: &mut H) { |
138 | self.m11.hash(state:h); |
139 | self.m12.hash(state:h); |
140 | self.m21.hash(state:h); |
141 | self.m22.hash(state:h); |
142 | self.m31.hash(state:h); |
143 | self.m32.hash(state:h); |
144 | } |
145 | } |
146 | |
147 | impl<T, Src, Dst> Transform2D<T, Src, Dst> { |
148 | /// Create a transform specifying its components in using the column-major-column-vector |
149 | /// matrix notation. |
150 | /// |
151 | /// For example, the translation terms m31 and m32 are the last two parameters parameters. |
152 | /// |
153 | /// ``` |
154 | /// use euclid::default::Transform2D; |
155 | /// let tx = 1.0; |
156 | /// let ty = 2.0; |
157 | /// let translation = Transform2D::new( |
158 | /// 1.0, 0.0, |
159 | /// 0.0, 1.0, |
160 | /// tx, ty, |
161 | /// ); |
162 | /// ``` |
163 | #[rustfmt::skip] |
164 | pub const fn new(m11: T, m12: T, m21: T, m22: T, m31: T, m32: T) -> Self { |
165 | Transform2D { |
166 | m11, m12, |
167 | m21, m22, |
168 | m31, m32, |
169 | _unit: PhantomData, |
170 | } |
171 | } |
172 | |
173 | /// Returns `true` if this transform is approximately equal to the other one, using |
174 | /// `T`'s default epsilon value. |
175 | /// |
176 | /// The same as [`ApproxEq::approx_eq`] but available without importing trait. |
177 | #[inline ] |
178 | pub fn approx_eq(&self, other: &Self) -> bool |
179 | where |
180 | T: ApproxEq<T>, |
181 | { |
182 | <Self as ApproxEq<T>>::approx_eq(&self, &other) |
183 | } |
184 | |
185 | /// Returns `true` if this transform is approximately equal to the other one, using |
186 | /// a provided epsilon value. |
187 | /// |
188 | /// The same as [`ApproxEq::approx_eq_eps`] but available without importing trait. |
189 | #[inline ] |
190 | pub fn approx_eq_eps(&self, other: &Self, eps: &T) -> bool |
191 | where |
192 | T: ApproxEq<T>, |
193 | { |
194 | <Self as ApproxEq<T>>::approx_eq_eps(&self, &other, &eps) |
195 | } |
196 | } |
197 | |
198 | impl<T: Copy, Src, Dst> Transform2D<T, Src, Dst> { |
199 | /// Returns an array containing this transform's terms. |
200 | /// |
201 | /// The terms are laid out in the same order as they are |
202 | /// specified in [`Transform2D::new`], that is following the |
203 | /// column-major-column-vector matrix notation. |
204 | /// |
205 | /// For example the translation terms are found in the |
206 | /// last two slots of the array. |
207 | #[inline ] |
208 | #[rustfmt::skip] |
209 | pub fn to_array(&self) -> [T; 6] { |
210 | [ |
211 | self.m11, self.m12, |
212 | self.m21, self.m22, |
213 | self.m31, self.m32 |
214 | ] |
215 | } |
216 | |
217 | /// Returns an array containing this transform's terms transposed. |
218 | /// |
219 | /// The terms are laid out in transposed order from the same order of |
220 | /// `Transform3D::new` and `Transform3D::to_array`, that is following |
221 | /// the row-major-column-vector matrix notation. |
222 | /// |
223 | /// For example the translation terms are found at indices 2 and 5 |
224 | /// in the array. |
225 | #[inline ] |
226 | #[rustfmt::skip] |
227 | pub fn to_array_transposed(&self) -> [T; 6] { |
228 | [ |
229 | self.m11, self.m21, self.m31, |
230 | self.m12, self.m22, self.m32 |
231 | ] |
232 | } |
233 | |
234 | /// Equivalent to `to_array` with elements packed two at a time |
235 | /// in an array of arrays. |
236 | #[inline ] |
237 | pub fn to_arrays(&self) -> [[T; 2]; 3] { |
238 | [ |
239 | [self.m11, self.m12], |
240 | [self.m21, self.m22], |
241 | [self.m31, self.m32], |
242 | ] |
243 | } |
244 | |
245 | /// Create a transform providing its components via an array |
246 | /// of 6 elements instead of as individual parameters. |
247 | /// |
248 | /// The order of the components corresponds to the |
249 | /// column-major-column-vector matrix notation (the same order |
250 | /// as `Transform2D::new`). |
251 | #[inline ] |
252 | #[rustfmt::skip] |
253 | pub fn from_array(array: [T; 6]) -> Self { |
254 | Self::new( |
255 | array[0], array[1], |
256 | array[2], array[3], |
257 | array[4], array[5], |
258 | ) |
259 | } |
260 | |
261 | /// Equivalent to `from_array` with elements packed two at a time |
262 | /// in an array of arrays. |
263 | /// |
264 | /// The order of the components corresponds to the |
265 | /// column-major-column-vector matrix notation (the same order |
266 | /// as `Transform3D::new`). |
267 | #[inline ] |
268 | #[rustfmt::skip] |
269 | pub fn from_arrays(array: [[T; 2]; 3]) -> Self { |
270 | Self::new( |
271 | array[0][0], array[0][1], |
272 | array[1][0], array[1][1], |
273 | array[2][0], array[2][1], |
274 | ) |
275 | } |
276 | |
277 | /// Drop the units, preserving only the numeric value. |
278 | #[inline ] |
279 | #[rustfmt::skip] |
280 | pub fn to_untyped(&self) -> Transform2D<T, UnknownUnit, UnknownUnit> { |
281 | Transform2D::new( |
282 | self.m11, self.m12, |
283 | self.m21, self.m22, |
284 | self.m31, self.m32 |
285 | ) |
286 | } |
287 | |
288 | /// Tag a unitless value with units. |
289 | #[inline ] |
290 | #[rustfmt::skip] |
291 | pub fn from_untyped(p: &Transform2D<T, UnknownUnit, UnknownUnit>) -> Self { |
292 | Transform2D::new( |
293 | p.m11, p.m12, |
294 | p.m21, p.m22, |
295 | p.m31, p.m32 |
296 | ) |
297 | } |
298 | |
299 | /// Returns the same transform with a different source unit. |
300 | #[inline ] |
301 | #[rustfmt::skip] |
302 | pub fn with_source<NewSrc>(&self) -> Transform2D<T, NewSrc, Dst> { |
303 | Transform2D::new( |
304 | self.m11, self.m12, |
305 | self.m21, self.m22, |
306 | self.m31, self.m32, |
307 | ) |
308 | } |
309 | |
310 | /// Returns the same transform with a different destination unit. |
311 | #[inline ] |
312 | #[rustfmt::skip] |
313 | pub fn with_destination<NewDst>(&self) -> Transform2D<T, Src, NewDst> { |
314 | Transform2D::new( |
315 | self.m11, self.m12, |
316 | self.m21, self.m22, |
317 | self.m31, self.m32, |
318 | ) |
319 | } |
320 | |
321 | /// Create a 3D transform from the current transform |
322 | pub fn to_3d(&self) -> Transform3D<T, Src, Dst> |
323 | where |
324 | T: Zero + One, |
325 | { |
326 | Transform3D::new_2d(self.m11, self.m12, self.m21, self.m22, self.m31, self.m32) |
327 | } |
328 | } |
329 | |
330 | impl<T: NumCast + Copy, Src, Dst> Transform2D<T, Src, Dst> { |
331 | /// Cast from one numeric representation to another, preserving the units. |
332 | #[inline ] |
333 | pub fn cast<NewT: NumCast>(&self) -> Transform2D<NewT, Src, Dst> { |
334 | self.try_cast().unwrap() |
335 | } |
336 | |
337 | /// Fallible cast from one numeric representation to another, preserving the units. |
338 | #[rustfmt::skip] |
339 | pub fn try_cast<NewT: NumCast>(&self) -> Option<Transform2D<NewT, Src, Dst>> { |
340 | match (NumCast::from(self.m11), NumCast::from(self.m12), |
341 | NumCast::from(self.m21), NumCast::from(self.m22), |
342 | NumCast::from(self.m31), NumCast::from(self.m32)) { |
343 | (Some(m11), Some(m12), |
344 | Some(m21), Some(m22), |
345 | Some(m31), Some(m32)) => { |
346 | Some(Transform2D::new( |
347 | m11, m12, |
348 | m21, m22, |
349 | m31, m32 |
350 | )) |
351 | }, |
352 | _ => None |
353 | } |
354 | } |
355 | } |
356 | |
357 | impl<T, Src, Dst> Transform2D<T, Src, Dst> |
358 | where |
359 | T: Zero + One, |
360 | { |
361 | /// Create an identity matrix: |
362 | /// |
363 | /// ```text |
364 | /// 1 0 |
365 | /// 0 1 |
366 | /// 0 0 |
367 | /// ``` |
368 | #[inline ] |
369 | pub fn identity() -> Self { |
370 | Self::translation(T::zero(), T::zero()) |
371 | } |
372 | |
373 | /// Intentional not public, because it checks for exact equivalence |
374 | /// while most consumers will probably want some sort of approximate |
375 | /// equivalence to deal with floating-point errors. |
376 | fn is_identity(&self) -> bool |
377 | where |
378 | T: PartialEq, |
379 | { |
380 | *self == Self::identity() |
381 | } |
382 | } |
383 | |
384 | /// Methods for combining generic transformations |
385 | impl<T, Src, Dst> Transform2D<T, Src, Dst> |
386 | where |
387 | T: Copy + Add<Output = T> + Mul<Output = T>, |
388 | { |
389 | /// Returns the multiplication of the two matrices such that mat's transformation |
390 | /// applies after self's transformation. |
391 | #[must_use ] |
392 | #[rustfmt::skip] |
393 | pub fn then<NewDst>(&self, mat: &Transform2D<T, Dst, NewDst>) -> Transform2D<T, Src, NewDst> { |
394 | Transform2D::new( |
395 | self.m11 * mat.m11 + self.m12 * mat.m21, |
396 | self.m11 * mat.m12 + self.m12 * mat.m22, |
397 | |
398 | self.m21 * mat.m11 + self.m22 * mat.m21, |
399 | self.m21 * mat.m12 + self.m22 * mat.m22, |
400 | |
401 | self.m31 * mat.m11 + self.m32 * mat.m21 + mat.m31, |
402 | self.m31 * mat.m12 + self.m32 * mat.m22 + mat.m32, |
403 | ) |
404 | } |
405 | } |
406 | |
407 | /// Methods for creating and combining translation transformations |
408 | impl<T, Src, Dst> Transform2D<T, Src, Dst> |
409 | where |
410 | T: Zero + One, |
411 | { |
412 | /// Create a 2d translation transform: |
413 | /// |
414 | /// ```text |
415 | /// 1 0 |
416 | /// 0 1 |
417 | /// x y |
418 | /// ``` |
419 | #[inline ] |
420 | #[rustfmt::skip] |
421 | pub fn translation(x: T, y: T) -> Self { |
422 | let _0 = || T::zero(); |
423 | let _1 = || T::one(); |
424 | |
425 | Self::new( |
426 | _1(), _0(), |
427 | _0(), _1(), |
428 | x, y, |
429 | ) |
430 | } |
431 | |
432 | /// Applies a translation after self's transformation and returns the resulting transform. |
433 | #[inline ] |
434 | #[must_use ] |
435 | pub fn then_translate(&self, v: Vector2D<T, Dst>) -> Self |
436 | where |
437 | T: Copy + Add<Output = T> + Mul<Output = T>, |
438 | { |
439 | self.then(&Transform2D::translation(v.x, v.y)) |
440 | } |
441 | |
442 | /// Applies a translation before self's transformation and returns the resulting transform. |
443 | #[inline ] |
444 | #[must_use ] |
445 | pub fn pre_translate(&self, v: Vector2D<T, Src>) -> Self |
446 | where |
447 | T: Copy + Add<Output = T> + Mul<Output = T>, |
448 | { |
449 | Transform2D::translation(v.x, v.y).then(self) |
450 | } |
451 | } |
452 | |
453 | /// Methods for creating and combining rotation transformations |
454 | impl<T, Src, Dst> Transform2D<T, Src, Dst> |
455 | where |
456 | T: Copy + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Zero + Trig, |
457 | { |
458 | /// Returns a rotation transform. |
459 | #[inline ] |
460 | #[rustfmt::skip] |
461 | pub fn rotation(theta: Angle<T>) -> Self { |
462 | let _0 = Zero::zero(); |
463 | let cos = theta.get().cos(); |
464 | let sin = theta.get().sin(); |
465 | Transform2D::new( |
466 | cos, sin, |
467 | _0 - sin, cos, |
468 | _0, _0 |
469 | ) |
470 | } |
471 | |
472 | /// Applies a rotation after self's transformation and returns the resulting transform. |
473 | #[inline ] |
474 | #[must_use ] |
475 | pub fn then_rotate(&self, theta: Angle<T>) -> Self { |
476 | self.then(&Transform2D::rotation(theta)) |
477 | } |
478 | |
479 | /// Applies a rotation before self's transformation and returns the resulting transform. |
480 | #[inline ] |
481 | #[must_use ] |
482 | pub fn pre_rotate(&self, theta: Angle<T>) -> Self { |
483 | Transform2D::rotation(theta).then(self) |
484 | } |
485 | } |
486 | |
487 | /// Methods for creating and combining scale transformations |
488 | impl<T, Src, Dst> Transform2D<T, Src, Dst> { |
489 | /// Create a 2d scale transform: |
490 | /// |
491 | /// ```text |
492 | /// x 0 |
493 | /// 0 y |
494 | /// 0 0 |
495 | /// ``` |
496 | #[inline ] |
497 | #[rustfmt::skip] |
498 | pub fn scale(x: T, y: T) -> Self |
499 | where |
500 | T: Zero, |
501 | { |
502 | let _0 = || Zero::zero(); |
503 | |
504 | Self::new( |
505 | x, _0(), |
506 | _0(), y, |
507 | _0(), _0(), |
508 | ) |
509 | } |
510 | |
511 | /// Applies a scale after self's transformation and returns the resulting transform. |
512 | #[inline ] |
513 | #[must_use ] |
514 | pub fn then_scale(&self, x: T, y: T) -> Self |
515 | where |
516 | T: Copy + Add<Output = T> + Mul<Output = T> + Zero, |
517 | { |
518 | self.then(&Transform2D::scale(x, y)) |
519 | } |
520 | |
521 | /// Applies a scale before self's transformation and returns the resulting transform. |
522 | #[inline ] |
523 | #[must_use ] |
524 | #[rustfmt::skip] |
525 | pub fn pre_scale(&self, x: T, y: T) -> Self |
526 | where |
527 | T: Copy + Mul<Output = T>, |
528 | { |
529 | Transform2D::new( |
530 | self.m11 * x, self.m12 * x, |
531 | self.m21 * y, self.m22 * y, |
532 | self.m31, self.m32 |
533 | ) |
534 | } |
535 | } |
536 | |
537 | /// Methods for apply transformations to objects |
538 | impl<T, Src, Dst> Transform2D<T, Src, Dst> |
539 | where |
540 | T: Copy + Add<Output = T> + Mul<Output = T>, |
541 | { |
542 | /// Returns the given point transformed by this transform. |
543 | #[inline ] |
544 | #[must_use ] |
545 | pub fn transform_point(&self, point: Point2D<T, Src>) -> Point2D<T, Dst> { |
546 | Point2D::new( |
547 | point.x * self.m11 + point.y * self.m21 + self.m31, |
548 | point.x * self.m12 + point.y * self.m22 + self.m32, |
549 | ) |
550 | } |
551 | |
552 | /// Returns the given vector transformed by this matrix. |
553 | #[inline ] |
554 | #[must_use ] |
555 | pub fn transform_vector(&self, vec: Vector2D<T, Src>) -> Vector2D<T, Dst> { |
556 | vec2( |
557 | vec.x * self.m11 + vec.y * self.m21, |
558 | vec.x * self.m12 + vec.y * self.m22, |
559 | ) |
560 | } |
561 | |
562 | /// Returns a rectangle that encompasses the result of transforming the given rectangle by this |
563 | /// transform. |
564 | #[inline ] |
565 | #[must_use ] |
566 | pub fn outer_transformed_rect(&self, rect: &Rect<T, Src>) -> Rect<T, Dst> |
567 | where |
568 | T: Sub<Output = T> + Zero + PartialOrd, |
569 | { |
570 | let min = rect.min(); |
571 | let max = rect.max(); |
572 | Rect::from_points(&[ |
573 | self.transform_point(min), |
574 | self.transform_point(max), |
575 | self.transform_point(point2(max.x, min.y)), |
576 | self.transform_point(point2(min.x, max.y)), |
577 | ]) |
578 | } |
579 | |
580 | /// Returns a box that encompasses the result of transforming the given box by this |
581 | /// transform. |
582 | #[inline ] |
583 | #[must_use ] |
584 | pub fn outer_transformed_box(&self, b: &Box2D<T, Src>) -> Box2D<T, Dst> |
585 | where |
586 | T: Sub<Output = T> + Zero + PartialOrd, |
587 | { |
588 | Box2D::from_points(&[ |
589 | self.transform_point(b.min), |
590 | self.transform_point(b.max), |
591 | self.transform_point(point2(b.max.x, b.min.y)), |
592 | self.transform_point(point2(b.min.x, b.max.y)), |
593 | ]) |
594 | } |
595 | } |
596 | |
597 | impl<T, Src, Dst> Transform2D<T, Src, Dst> |
598 | where |
599 | T: Copy + Sub<Output = T> + Mul<Output = T> + Div<Output = T> + PartialEq + Zero + One, |
600 | { |
601 | /// Computes and returns the determinant of this transform. |
602 | pub fn determinant(&self) -> T { |
603 | self.m11 * self.m22 - self.m12 * self.m21 |
604 | } |
605 | |
606 | /// Returns whether it is possible to compute the inverse transform. |
607 | #[inline ] |
608 | pub fn is_invertible(&self) -> bool { |
609 | self.determinant() != Zero::zero() |
610 | } |
611 | |
612 | /// Returns the inverse transform if possible. |
613 | #[must_use ] |
614 | pub fn inverse(&self) -> Option<Transform2D<T, Dst, Src>> { |
615 | let det = self.determinant(); |
616 | |
617 | let _0: T = Zero::zero(); |
618 | let _1: T = One::one(); |
619 | |
620 | if det == _0 { |
621 | return None; |
622 | } |
623 | |
624 | let inv_det = _1 / det; |
625 | Some(Transform2D::new( |
626 | inv_det * self.m22, |
627 | inv_det * (_0 - self.m12), |
628 | inv_det * (_0 - self.m21), |
629 | inv_det * self.m11, |
630 | inv_det * (self.m21 * self.m32 - self.m22 * self.m31), |
631 | inv_det * (self.m31 * self.m12 - self.m11 * self.m32), |
632 | )) |
633 | } |
634 | } |
635 | |
636 | impl<T, Src, Dst> Default for Transform2D<T, Src, Dst> |
637 | where |
638 | T: Zero + One, |
639 | { |
640 | /// Returns the [identity transform](Transform2D::identity). |
641 | fn default() -> Self { |
642 | Self::identity() |
643 | } |
644 | } |
645 | |
646 | impl<T: ApproxEq<T>, Src, Dst> ApproxEq<T> for Transform2D<T, Src, Dst> { |
647 | #[inline ] |
648 | fn approx_epsilon() -> T { |
649 | T::approx_epsilon() |
650 | } |
651 | |
652 | /// Returns `true` if this transform is approximately equal to the other one, using |
653 | /// a provided epsilon value. |
654 | fn approx_eq_eps(&self, other: &Self, eps: &T) -> bool { |
655 | self.m11.approx_eq_eps(&other.m11, approx_epsilon:eps) |
656 | && self.m12.approx_eq_eps(&other.m12, approx_epsilon:eps) |
657 | && self.m21.approx_eq_eps(&other.m21, approx_epsilon:eps) |
658 | && self.m22.approx_eq_eps(&other.m22, approx_epsilon:eps) |
659 | && self.m31.approx_eq_eps(&other.m31, approx_epsilon:eps) |
660 | && self.m32.approx_eq_eps(&other.m32, approx_epsilon:eps) |
661 | } |
662 | } |
663 | |
664 | impl<T, Src, Dst> fmt::Debug for Transform2D<T, Src, Dst> |
665 | where |
666 | T: Copy + fmt::Debug + PartialEq + One + Zero, |
667 | { |
668 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
669 | if self.is_identity() { |
670 | write!(f, "[I]" ) |
671 | } else { |
672 | self.to_array().fmt(f) |
673 | } |
674 | } |
675 | } |
676 | |
677 | #[cfg (feature = "mint" )] |
678 | impl<T, Src, Dst> From<mint::RowMatrix3x2<T>> for Transform2D<T, Src, Dst> { |
679 | #[rustfmt::skip] |
680 | fn from(m: mint::RowMatrix3x2<T>) -> Self { |
681 | Transform2D { |
682 | m11: m.x.x, m12: m.x.y, |
683 | m21: m.y.x, m22: m.y.y, |
684 | m31: m.z.x, m32: m.z.y, |
685 | _unit: PhantomData, |
686 | } |
687 | } |
688 | } |
689 | #[cfg (feature = "mint" )] |
690 | impl<T, Src, Dst> From<Transform2D<T, Src, Dst>> for mint::RowMatrix3x2<T> { |
691 | fn from(t: Transform2D<T, Src, Dst>) -> Self { |
692 | mint::RowMatrix3x2 { |
693 | x: mint::Vector2 { x: t.m11, y: t.m12 }, |
694 | y: mint::Vector2 { x: t.m21, y: t.m22 }, |
695 | z: mint::Vector2 { x: t.m31, y: t.m32 }, |
696 | } |
697 | } |
698 | } |
699 | |
700 | #[cfg (test)] |
701 | mod test { |
702 | use super::*; |
703 | use crate::approxeq::ApproxEq; |
704 | use crate::default; |
705 | #[cfg (feature = "mint" )] |
706 | use mint; |
707 | |
708 | use core::f32::consts::FRAC_PI_2; |
709 | |
710 | type Mat = default::Transform2D<f32>; |
711 | |
712 | fn rad(v: f32) -> Angle<f32> { |
713 | Angle::radians(v) |
714 | } |
715 | |
716 | #[test ] |
717 | pub fn test_translation() { |
718 | let t1 = Mat::translation(1.0, 2.0); |
719 | let t2 = Mat::identity().pre_translate(vec2(1.0, 2.0)); |
720 | let t3 = Mat::identity().then_translate(vec2(1.0, 2.0)); |
721 | assert_eq!(t1, t2); |
722 | assert_eq!(t1, t3); |
723 | |
724 | assert_eq!( |
725 | t1.transform_point(Point2D::new(1.0, 1.0)), |
726 | Point2D::new(2.0, 3.0) |
727 | ); |
728 | |
729 | assert_eq!(t1.then(&t1), Mat::translation(2.0, 4.0)); |
730 | } |
731 | |
732 | #[test ] |
733 | pub fn test_rotation() { |
734 | let r1 = Mat::rotation(rad(FRAC_PI_2)); |
735 | let r2 = Mat::identity().pre_rotate(rad(FRAC_PI_2)); |
736 | let r3 = Mat::identity().then_rotate(rad(FRAC_PI_2)); |
737 | assert_eq!(r1, r2); |
738 | assert_eq!(r1, r3); |
739 | |
740 | assert!(r1 |
741 | .transform_point(Point2D::new(1.0, 2.0)) |
742 | .approx_eq(&Point2D::new(-2.0, 1.0))); |
743 | |
744 | assert!(r1.then(&r1).approx_eq(&Mat::rotation(rad(FRAC_PI_2 * 2.0)))); |
745 | } |
746 | |
747 | #[test ] |
748 | pub fn test_scale() { |
749 | let s1 = Mat::scale(2.0, 3.0); |
750 | let s2 = Mat::identity().pre_scale(2.0, 3.0); |
751 | let s3 = Mat::identity().then_scale(2.0, 3.0); |
752 | assert_eq!(s1, s2); |
753 | assert_eq!(s1, s3); |
754 | |
755 | assert!(s1 |
756 | .transform_point(Point2D::new(2.0, 2.0)) |
757 | .approx_eq(&Point2D::new(4.0, 6.0))); |
758 | } |
759 | |
760 | #[test ] |
761 | pub fn test_pre_then_scale() { |
762 | let m = Mat::rotation(rad(FRAC_PI_2)).then_translate(vec2(6.0, 7.0)); |
763 | let s = Mat::scale(2.0, 3.0); |
764 | assert_eq!(m.then(&s), m.then_scale(2.0, 3.0)); |
765 | } |
766 | |
767 | #[test ] |
768 | pub fn test_inverse_simple() { |
769 | let m1 = Mat::identity(); |
770 | let m2 = m1.inverse().unwrap(); |
771 | assert!(m1.approx_eq(&m2)); |
772 | } |
773 | |
774 | #[test ] |
775 | pub fn test_inverse_scale() { |
776 | let m1 = Mat::scale(1.5, 0.3); |
777 | let m2 = m1.inverse().unwrap(); |
778 | assert!(m1.then(&m2).approx_eq(&Mat::identity())); |
779 | assert!(m2.then(&m1).approx_eq(&Mat::identity())); |
780 | } |
781 | |
782 | #[test ] |
783 | pub fn test_inverse_translate() { |
784 | let m1 = Mat::translation(-132.0, 0.3); |
785 | let m2 = m1.inverse().unwrap(); |
786 | assert!(m1.then(&m2).approx_eq(&Mat::identity())); |
787 | assert!(m2.then(&m1).approx_eq(&Mat::identity())); |
788 | } |
789 | |
790 | #[test ] |
791 | fn test_inverse_none() { |
792 | assert!(Mat::scale(2.0, 0.0).inverse().is_none()); |
793 | assert!(Mat::scale(2.0, 2.0).inverse().is_some()); |
794 | } |
795 | |
796 | #[test ] |
797 | pub fn test_pre_post() { |
798 | let m1 = default::Transform2D::identity() |
799 | .then_scale(1.0, 2.0) |
800 | .then_translate(vec2(1.0, 2.0)); |
801 | let m2 = default::Transform2D::identity() |
802 | .pre_translate(vec2(1.0, 2.0)) |
803 | .pre_scale(1.0, 2.0); |
804 | assert!(m1.approx_eq(&m2)); |
805 | |
806 | let r = Mat::rotation(rad(FRAC_PI_2)); |
807 | let t = Mat::translation(2.0, 3.0); |
808 | |
809 | let a = Point2D::new(1.0, 1.0); |
810 | |
811 | assert!(r |
812 | .then(&t) |
813 | .transform_point(a) |
814 | .approx_eq(&Point2D::new(1.0, 4.0))); |
815 | assert!(t |
816 | .then(&r) |
817 | .transform_point(a) |
818 | .approx_eq(&Point2D::new(-4.0, 3.0))); |
819 | assert!(t |
820 | .then(&r) |
821 | .transform_point(a) |
822 | .approx_eq(&r.transform_point(t.transform_point(a)))); |
823 | } |
824 | |
825 | #[test ] |
826 | fn test_size_of() { |
827 | use core::mem::size_of; |
828 | assert_eq!(size_of::<default::Transform2D<f32>>(), 6 * size_of::<f32>()); |
829 | assert_eq!(size_of::<default::Transform2D<f64>>(), 6 * size_of::<f64>()); |
830 | } |
831 | |
832 | #[test ] |
833 | pub fn test_is_identity() { |
834 | let m1 = default::Transform2D::identity(); |
835 | assert!(m1.is_identity()); |
836 | let m2 = m1.then_translate(vec2(0.1, 0.0)); |
837 | assert!(!m2.is_identity()); |
838 | } |
839 | |
840 | #[test ] |
841 | pub fn test_transform_vector() { |
842 | // Translation does not apply to vectors. |
843 | let m1 = Mat::translation(1.0, 1.0); |
844 | let v1 = vec2(10.0, -10.0); |
845 | assert_eq!(v1, m1.transform_vector(v1)); |
846 | } |
847 | |
848 | #[cfg (feature = "mint" )] |
849 | #[test ] |
850 | pub fn test_mint() { |
851 | let m1 = Mat::rotation(rad(FRAC_PI_2)); |
852 | let mm: mint::RowMatrix3x2<_> = m1.into(); |
853 | let m2 = Mat::from(mm); |
854 | |
855 | assert_eq!(m1, m2); |
856 | } |
857 | } |
858 | |