euclid/
translation.rs

1// Copyright 2018 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
10use crate::num::*;
11use crate::UnknownUnit;
12use crate::{point2, point3, vec2, vec3, Box2D, Box3D, Rect, Size2D};
13use crate::{Point2D, Point3D, Transform2D, Transform3D, Vector2D, Vector3D};
14use core::cmp::{Eq, PartialEq};
15use core::fmt;
16use core::hash::Hash;
17use core::marker::PhantomData;
18use core::ops::{Add, AddAssign, Neg, Sub, SubAssign};
19#[cfg(feature = "serde")]
20use serde::{Deserialize, Serialize};
21
22/// A 2d transformation from a space to another that can only express translations.
23///
24/// The main benefit of this type over a Vector2D is the ability to cast
25/// between a source and a destination spaces.
26///
27/// Example:
28///
29/// ```
30/// use euclid::{Translation2D, Point2D, point2};
31/// struct ParentSpace;
32/// struct ChildSpace;
33/// type ScrollOffset = Translation2D<i32, ParentSpace, ChildSpace>;
34/// type ParentPoint = Point2D<i32, ParentSpace>;
35/// type ChildPoint = Point2D<i32, ChildSpace>;
36///
37/// let scrolling = ScrollOffset::new(0, 100);
38/// let p1: ParentPoint = point2(0, 0);
39/// let p2: ChildPoint = scrolling.transform_point(p1);
40/// ```
41///
42#[repr(C)]
43#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
44#[cfg_attr(
45    feature = "serde",
46    serde(bound(
47        serialize = "T: serde::Serialize",
48        deserialize = "T: serde::Deserialize<'de>"
49    ))
50)]
51pub struct Translation2D<T, Src, Dst> {
52    pub x: T,
53    pub y: T,
54    #[doc(hidden)]
55    pub _unit: PhantomData<(Src, Dst)>,
56}
57
58impl<T: Copy, Src, Dst> Copy for Translation2D<T, Src, Dst> {}
59
60impl<T: Clone, Src, Dst> Clone for Translation2D<T, Src, Dst> {
61    fn clone(&self) -> Self {
62        Translation2D {
63            x: self.x.clone(),
64            y: self.y.clone(),
65            _unit: PhantomData,
66        }
67    }
68}
69
70impl<T, Src, Dst> Eq for Translation2D<T, Src, Dst> where T: Eq {}
71
72impl<T, Src, Dst> PartialEq for Translation2D<T, Src, Dst>
73where
74    T: PartialEq,
75{
76    fn eq(&self, other: &Self) -> bool {
77        self.x == other.x && self.y == other.y
78    }
79}
80
81impl<T, Src, Dst> Hash for Translation2D<T, Src, Dst>
82where
83    T: Hash,
84{
85    fn hash<H: core::hash::Hasher>(&self, h: &mut H) {
86        self.x.hash(h);
87        self.y.hash(h);
88    }
89}
90
91impl<T, Src, Dst> Translation2D<T, Src, Dst> {
92    #[inline]
93    pub const fn new(x: T, y: T) -> Self {
94        Translation2D {
95            x,
96            y,
97            _unit: PhantomData,
98        }
99    }
100
101    /// Creates no-op translation (both `x` and `y` is `zero()`).
102    #[inline]
103    pub fn identity() -> Self
104    where
105        T: Zero,
106    {
107        Self::new(T::zero(), T::zero())
108    }
109
110    /// Check if translation does nothing (both x and y is `zero()`).
111    ///
112    /// ```rust
113    /// use euclid::default::Translation2D;
114    ///
115    /// assert_eq!(Translation2D::<f32>::identity().is_identity(), true);
116    /// assert_eq!(Translation2D::new(0, 0).is_identity(), true);
117    /// assert_eq!(Translation2D::new(1, 0).is_identity(), false);
118    /// assert_eq!(Translation2D::new(0, 1).is_identity(), false);
119    /// ```
120    #[inline]
121    pub fn is_identity(&self) -> bool
122    where
123        T: Zero + PartialEq,
124    {
125        let _0 = T::zero();
126        self.x == _0 && self.y == _0
127    }
128
129    /// No-op, just cast the unit.
130    #[inline]
131    pub fn transform_size(&self, s: Size2D<T, Src>) -> Size2D<T, Dst> {
132        Size2D::new(s.width, s.height)
133    }
134}
135
136impl<T: Copy, Src, Dst> Translation2D<T, Src, Dst> {
137    /// Cast into a 2D vector.
138    #[inline]
139    pub fn to_vector(&self) -> Vector2D<T, Src> {
140        vec2(self.x, self.y)
141    }
142
143    /// Cast into an array with x and y.
144    #[inline]
145    pub fn to_array(&self) -> [T; 2] {
146        [self.x, self.y]
147    }
148
149    /// Cast into a tuple with x and y.
150    #[inline]
151    pub fn to_tuple(&self) -> (T, T) {
152        (self.x, self.y)
153    }
154
155    /// Drop the units, preserving only the numeric value.
156    #[inline]
157    pub fn to_untyped(&self) -> Translation2D<T, UnknownUnit, UnknownUnit> {
158        Translation2D {
159            x: self.x,
160            y: self.y,
161            _unit: PhantomData,
162        }
163    }
164
165    /// Tag a unitless value with units.
166    #[inline]
167    pub fn from_untyped(t: &Translation2D<T, UnknownUnit, UnknownUnit>) -> Self {
168        Translation2D {
169            x: t.x,
170            y: t.y,
171            _unit: PhantomData,
172        }
173    }
174
175    /// Returns the matrix representation of this translation.
176    #[inline]
177    pub fn to_transform(&self) -> Transform2D<T, Src, Dst>
178    where
179        T: Zero + One,
180    {
181        (*self).into()
182    }
183
184    /// Translate a point and cast its unit.
185    #[inline]
186    pub fn transform_point(&self, p: Point2D<T, Src>) -> Point2D<T::Output, Dst>
187    where
188        T: Add,
189    {
190        point2(p.x + self.x, p.y + self.y)
191    }
192
193    /// Translate a rectangle and cast its unit.
194    #[inline]
195    pub fn transform_rect(&self, r: &Rect<T, Src>) -> Rect<T::Output, Dst>
196    where
197        T: Add<Output = T>,
198    {
199        Rect {
200            origin: self.transform_point(r.origin),
201            size: self.transform_size(r.size),
202        }
203    }
204
205    /// Translate a 2D box and cast its unit.
206    #[inline]
207    pub fn transform_box(&self, r: &Box2D<T, Src>) -> Box2D<T::Output, Dst>
208    where
209        T: Add,
210    {
211        Box2D {
212            min: self.transform_point(r.min),
213            max: self.transform_point(r.max),
214        }
215    }
216
217    /// Return the inverse transformation.
218    #[inline]
219    pub fn inverse(&self) -> Translation2D<T::Output, Dst, Src>
220    where
221        T: Neg,
222    {
223        Translation2D::new(-self.x, -self.y)
224    }
225}
226
227impl<T: Add, Src, Dst1, Dst2> Add<Translation2D<T, Dst1, Dst2>> for Translation2D<T, Src, Dst1> {
228    type Output = Translation2D<T::Output, Src, Dst2>;
229
230    fn add(self, other: Translation2D<T, Dst1, Dst2>) -> Self::Output {
231        Translation2D::new(self.x + other.x, self.y + other.y)
232    }
233}
234
235impl<T: AddAssign, Src, Dst> AddAssign<Translation2D<T, Dst, Dst>> for Translation2D<T, Src, Dst> {
236    fn add_assign(&mut self, other: Translation2D<T, Dst, Dst>) {
237        self.x += other.x;
238        self.y += other.y;
239    }
240}
241
242impl<T: Sub, Src, Dst1, Dst2> Sub<Translation2D<T, Dst1, Dst2>> for Translation2D<T, Src, Dst2> {
243    type Output = Translation2D<T::Output, Src, Dst1>;
244
245    fn sub(self, other: Translation2D<T, Dst1, Dst2>) -> Self::Output {
246        Translation2D::new(self.x - other.x, self.y - other.y)
247    }
248}
249
250impl<T: SubAssign, Src, Dst> SubAssign<Translation2D<T, Dst, Dst>> for Translation2D<T, Src, Dst> {
251    fn sub_assign(&mut self, other: Translation2D<T, Dst, Dst>) {
252        self.x -= other.x;
253        self.y -= other.y;
254    }
255}
256
257impl<T, Src, Dst> From<Vector2D<T, Src>> for Translation2D<T, Src, Dst> {
258    fn from(v: Vector2D<T, Src>) -> Self {
259        Translation2D::new(v.x, v.y)
260    }
261}
262
263impl<T, Src, Dst> Into<Vector2D<T, Src>> for Translation2D<T, Src, Dst> {
264    fn into(self) -> Vector2D<T, Src> {
265        vec2(self.x, self.y)
266    }
267}
268
269impl<T, Src, Dst> Into<Transform2D<T, Src, Dst>> for Translation2D<T, Src, Dst>
270where
271    T: Zero + One,
272{
273    fn into(self) -> Transform2D<T, Src, Dst> {
274        Transform2D::translation(self.x, self.y)
275    }
276}
277
278impl<T, Src, Dst> Default for Translation2D<T, Src, Dst>
279where
280    T: Zero,
281{
282    fn default() -> Self {
283        Self::identity()
284    }
285}
286
287impl<T: fmt::Debug, Src, Dst> fmt::Debug for Translation2D<T, Src, Dst> {
288    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
289        write!(f, "Translation({:?},{:?})", self.x, self.y)
290    }
291}
292
293/// A 3d transformation from a space to another that can only express translations.
294///
295/// The main benefit of this type over a Vector3D is the ability to cast
296/// between a source and a destination spaces.
297#[repr(C)]
298pub struct Translation3D<T, Src, Dst> {
299    pub x: T,
300    pub y: T,
301    pub z: T,
302    #[doc(hidden)]
303    pub _unit: PhantomData<(Src, Dst)>,
304}
305
306impl<T: Copy, Src, Dst> Copy for Translation3D<T, Src, Dst> {}
307
308impl<T: Clone, Src, Dst> Clone for Translation3D<T, Src, Dst> {
309    fn clone(&self) -> Self {
310        Translation3D {
311            x: self.x.clone(),
312            y: self.y.clone(),
313            z: self.z.clone(),
314            _unit: PhantomData,
315        }
316    }
317}
318
319#[cfg(feature = "serde")]
320impl<'de, T, Src, Dst> serde::Deserialize<'de> for Translation3D<T, Src, Dst>
321where
322    T: serde::Deserialize<'de>,
323{
324    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
325    where
326        D: serde::Deserializer<'de>,
327    {
328        let (x, y, z) = serde::Deserialize::deserialize(deserializer)?;
329        Ok(Translation3D {
330            x,
331            y,
332            z,
333            _unit: PhantomData,
334        })
335    }
336}
337
338#[cfg(feature = "serde")]
339impl<T, Src, Dst> serde::Serialize for Translation3D<T, Src, Dst>
340where
341    T: serde::Serialize,
342{
343    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
344    where
345        S: serde::Serializer,
346    {
347        (&self.x, &self.y, &self.z).serialize(serializer)
348    }
349}
350
351impl<T, Src, Dst> Eq for Translation3D<T, Src, Dst> where T: Eq {}
352
353impl<T, Src, Dst> PartialEq for Translation3D<T, Src, Dst>
354where
355    T: PartialEq,
356{
357    fn eq(&self, other: &Self) -> bool {
358        self.x == other.x && self.y == other.y && self.z == other.z
359    }
360}
361
362impl<T, Src, Dst> Hash for Translation3D<T, Src, Dst>
363where
364    T: Hash,
365{
366    fn hash<H: core::hash::Hasher>(&self, h: &mut H) {
367        self.x.hash(h);
368        self.y.hash(h);
369        self.z.hash(h);
370    }
371}
372
373impl<T, Src, Dst> Translation3D<T, Src, Dst> {
374    #[inline]
375    pub const fn new(x: T, y: T, z: T) -> Self {
376        Translation3D {
377            x,
378            y,
379            z,
380            _unit: PhantomData,
381        }
382    }
383
384    /// Creates no-op translation (`x`, `y` and `z` is `zero()`).
385    #[inline]
386    pub fn identity() -> Self
387    where
388        T: Zero,
389    {
390        Translation3D::new(T::zero(), T::zero(), T::zero())
391    }
392
393    /// Check if translation does nothing (`x`, `y` and `z` is `zero()`).
394    ///
395    /// ```rust
396    /// use euclid::default::Translation3D;
397    ///
398    /// assert_eq!(Translation3D::<f32>::identity().is_identity(), true);
399    /// assert_eq!(Translation3D::new(0, 0, 0).is_identity(), true);
400    /// assert_eq!(Translation3D::new(1, 0, 0).is_identity(), false);
401    /// assert_eq!(Translation3D::new(0, 1, 0).is_identity(), false);
402    /// assert_eq!(Translation3D::new(0, 0, 1).is_identity(), false);
403    /// ```
404    #[inline]
405    pub fn is_identity(&self) -> bool
406    where
407        T: Zero + PartialEq,
408    {
409        let _0 = T::zero();
410        self.x == _0 && self.y == _0 && self.z == _0
411    }
412
413    /// No-op, just cast the unit.
414    #[inline]
415    pub fn transform_size(self, s: Size2D<T, Src>) -> Size2D<T, Dst> {
416        Size2D::new(s.width, s.height)
417    }
418}
419
420impl<T: Copy, Src, Dst> Translation3D<T, Src, Dst> {
421    /// Cast into a 3D vector.
422    #[inline]
423    pub fn to_vector(&self) -> Vector3D<T, Src> {
424        vec3(self.x, self.y, self.z)
425    }
426
427    /// Cast into an array with x, y and z.
428    #[inline]
429    pub fn to_array(&self) -> [T; 3] {
430        [self.x, self.y, self.z]
431    }
432
433    /// Cast into a tuple with x, y and z.
434    #[inline]
435    pub fn to_tuple(&self) -> (T, T, T) {
436        (self.x, self.y, self.z)
437    }
438
439    /// Drop the units, preserving only the numeric value.
440    #[inline]
441    pub fn to_untyped(&self) -> Translation3D<T, UnknownUnit, UnknownUnit> {
442        Translation3D {
443            x: self.x,
444            y: self.y,
445            z: self.z,
446            _unit: PhantomData,
447        }
448    }
449
450    /// Tag a unitless value with units.
451    #[inline]
452    pub fn from_untyped(t: &Translation3D<T, UnknownUnit, UnknownUnit>) -> Self {
453        Translation3D {
454            x: t.x,
455            y: t.y,
456            z: t.z,
457            _unit: PhantomData,
458        }
459    }
460
461    /// Returns the matrix representation of this translation.
462    #[inline]
463    pub fn to_transform(&self) -> Transform3D<T, Src, Dst>
464    where
465        T: Zero + One,
466    {
467        (*self).into()
468    }
469
470    /// Translate a point and cast its unit.
471    #[inline]
472    pub fn transform_point3d(&self, p: &Point3D<T, Src>) -> Point3D<T::Output, Dst>
473    where
474        T: Add,
475    {
476        point3(p.x + self.x, p.y + self.y, p.z + self.z)
477    }
478
479    /// Translate a point and cast its unit.
480    #[inline]
481    pub fn transform_point2d(&self, p: &Point2D<T, Src>) -> Point2D<T::Output, Dst>
482    where
483        T: Add,
484    {
485        point2(p.x + self.x, p.y + self.y)
486    }
487
488    /// Translate a 2D box and cast its unit.
489    #[inline]
490    pub fn transform_box2d(&self, b: &Box2D<T, Src>) -> Box2D<T::Output, Dst>
491    where
492        T: Add,
493    {
494        Box2D {
495            min: self.transform_point2d(&b.min),
496            max: self.transform_point2d(&b.max),
497        }
498    }
499
500    /// Translate a 3D box and cast its unit.
501    #[inline]
502    pub fn transform_box3d(&self, b: &Box3D<T, Src>) -> Box3D<T::Output, Dst>
503    where
504        T: Add,
505    {
506        Box3D {
507            min: self.transform_point3d(&b.min),
508            max: self.transform_point3d(&b.max),
509        }
510    }
511
512    /// Translate a rectangle and cast its unit.
513    #[inline]
514    pub fn transform_rect(&self, r: &Rect<T, Src>) -> Rect<T, Dst>
515    where
516        T: Add<Output = T>,
517    {
518        Rect {
519            origin: self.transform_point2d(&r.origin),
520            size: self.transform_size(r.size),
521        }
522    }
523
524    /// Return the inverse transformation.
525    #[inline]
526    pub fn inverse(&self) -> Translation3D<T::Output, Dst, Src>
527    where
528        T: Neg,
529    {
530        Translation3D::new(-self.x, -self.y, -self.z)
531    }
532}
533
534impl<T: Add, Src, Dst1, Dst2> Add<Translation3D<T, Dst1, Dst2>> for Translation3D<T, Src, Dst1> {
535    type Output = Translation3D<T::Output, Src, Dst2>;
536
537    fn add(self, other: Translation3D<T, Dst1, Dst2>) -> Self::Output {
538        Translation3D::new(self.x + other.x, self.y + other.y, self.z + other.z)
539    }
540}
541
542impl<T: AddAssign, Src, Dst> AddAssign<Translation3D<T, Dst, Dst>> for Translation3D<T, Src, Dst> {
543    fn add_assign(&mut self, other: Translation3D<T, Dst, Dst>) {
544        self.x += other.x;
545        self.y += other.y;
546        self.z += other.z;
547    }
548}
549
550impl<T: Sub, Src, Dst1, Dst2> Sub<Translation3D<T, Dst1, Dst2>> for Translation3D<T, Src, Dst2> {
551    type Output = Translation3D<T::Output, Src, Dst1>;
552
553    fn sub(self, other: Translation3D<T, Dst1, Dst2>) -> Self::Output {
554        Translation3D::new(self.x - other.x, self.y - other.y, self.z - other.z)
555    }
556}
557
558impl<T: SubAssign, Src, Dst> SubAssign<Translation3D<T, Dst, Dst>> for Translation3D<T, Src, Dst> {
559    fn sub_assign(&mut self, other: Translation3D<T, Dst, Dst>) {
560        self.x -= other.x;
561        self.y -= other.y;
562        self.z -= other.z;
563    }
564}
565
566impl<T, Src, Dst> From<Vector3D<T, Src>> for Translation3D<T, Src, Dst> {
567    fn from(v: Vector3D<T, Src>) -> Self {
568        Translation3D::new(v.x, v.y, v.z)
569    }
570}
571
572impl<T, Src, Dst> Into<Vector3D<T, Src>> for Translation3D<T, Src, Dst> {
573    fn into(self) -> Vector3D<T, Src> {
574        vec3(self.x, self.y, self.z)
575    }
576}
577
578impl<T, Src, Dst> Into<Transform3D<T, Src, Dst>> for Translation3D<T, Src, Dst>
579where
580    T: Zero + One,
581{
582    fn into(self) -> Transform3D<T, Src, Dst> {
583        Transform3D::translation(self.x, self.y, self.z)
584    }
585}
586
587impl<T, Src, Dst> Default for Translation3D<T, Src, Dst>
588where
589    T: Zero,
590{
591    fn default() -> Self {
592        Self::identity()
593    }
594}
595
596impl<T: fmt::Debug, Src, Dst> fmt::Debug for Translation3D<T, Src, Dst> {
597    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
598        write!(f, "Translation({:?},{:?},{:?})", self.x, self.y, self.z)
599    }
600}
601
602#[cfg(test)]
603mod _2d {
604    #[test]
605    fn simple() {
606        use crate::{rect, Rect, Translation2D};
607
608        struct A;
609        struct B;
610
611        type Translation = Translation2D<i32, A, B>;
612        type SrcRect = Rect<i32, A>;
613        type DstRect = Rect<i32, B>;
614
615        let tx = Translation::new(10, -10);
616        let r1: SrcRect = rect(10, 20, 30, 40);
617        let r2: DstRect = tx.transform_rect(&r1);
618        assert_eq!(r2, rect(20, 10, 30, 40));
619
620        let inv_tx = tx.inverse();
621        assert_eq!(inv_tx.transform_rect(&r2), r1);
622
623        assert!((tx + inv_tx).is_identity());
624    }
625
626    /// Operation tests
627    mod ops {
628        use crate::default::Translation2D;
629
630        #[test]
631        fn test_add() {
632            let t1 = Translation2D::new(1.0, 2.0);
633            let t2 = Translation2D::new(3.0, 4.0);
634            assert_eq!(t1 + t2, Translation2D::new(4.0, 6.0));
635
636            let t1 = Translation2D::new(1.0, 2.0);
637            let t2 = Translation2D::new(0.0, 0.0);
638            assert_eq!(t1 + t2, Translation2D::new(1.0, 2.0));
639
640            let t1 = Translation2D::new(1.0, 2.0);
641            let t2 = Translation2D::new(-3.0, -4.0);
642            assert_eq!(t1 + t2, Translation2D::new(-2.0, -2.0));
643
644            let t1 = Translation2D::new(0.0, 0.0);
645            let t2 = Translation2D::new(0.0, 0.0);
646            assert_eq!(t1 + t2, Translation2D::new(0.0, 0.0));
647        }
648
649        #[test]
650        pub fn test_add_assign() {
651            let mut t = Translation2D::new(1.0, 2.0);
652            t += Translation2D::new(3.0, 4.0);
653            assert_eq!(t, Translation2D::new(4.0, 6.0));
654
655            let mut t = Translation2D::new(1.0, 2.0);
656            t += Translation2D::new(0.0, 0.0);
657            assert_eq!(t, Translation2D::new(1.0, 2.0));
658
659            let mut t = Translation2D::new(1.0, 2.0);
660            t += Translation2D::new(-3.0, -4.0);
661            assert_eq!(t, Translation2D::new(-2.0, -2.0));
662
663            let mut t = Translation2D::new(0.0, 0.0);
664            t += Translation2D::new(0.0, 0.0);
665            assert_eq!(t, Translation2D::new(0.0, 0.0));
666        }
667
668        #[test]
669        pub fn test_sub() {
670            let t1 = Translation2D::new(1.0, 2.0);
671            let t2 = Translation2D::new(3.0, 4.0);
672            assert_eq!(t1 - t2, Translation2D::new(-2.0, -2.0));
673
674            let t1 = Translation2D::new(1.0, 2.0);
675            let t2 = Translation2D::new(0.0, 0.0);
676            assert_eq!(t1 - t2, Translation2D::new(1.0, 2.0));
677
678            let t1 = Translation2D::new(1.0, 2.0);
679            let t2 = Translation2D::new(-3.0, -4.0);
680            assert_eq!(t1 - t2, Translation2D::new(4.0, 6.0));
681
682            let t1 = Translation2D::new(0.0, 0.0);
683            let t2 = Translation2D::new(0.0, 0.0);
684            assert_eq!(t1 - t2, Translation2D::new(0.0, 0.0));
685        }
686
687        #[test]
688        pub fn test_sub_assign() {
689            let mut t = Translation2D::new(1.0, 2.0);
690            t -= Translation2D::new(3.0, 4.0);
691            assert_eq!(t, Translation2D::new(-2.0, -2.0));
692
693            let mut t = Translation2D::new(1.0, 2.0);
694            t -= Translation2D::new(0.0, 0.0);
695            assert_eq!(t, Translation2D::new(1.0, 2.0));
696
697            let mut t = Translation2D::new(1.0, 2.0);
698            t -= Translation2D::new(-3.0, -4.0);
699            assert_eq!(t, Translation2D::new(4.0, 6.0));
700
701            let mut t = Translation2D::new(0.0, 0.0);
702            t -= Translation2D::new(0.0, 0.0);
703            assert_eq!(t, Translation2D::new(0.0, 0.0));
704        }
705    }
706}
707
708#[cfg(test)]
709mod _3d {
710    #[test]
711    fn simple() {
712        use crate::{point3, Point3D, Translation3D};
713
714        struct A;
715        struct B;
716
717        type Translation = Translation3D<i32, A, B>;
718        type SrcPoint = Point3D<i32, A>;
719        type DstPoint = Point3D<i32, B>;
720
721        let tx = Translation::new(10, -10, 100);
722        let p1: SrcPoint = point3(10, 20, 30);
723        let p2: DstPoint = tx.transform_point3d(&p1);
724        assert_eq!(p2, point3(20, 10, 130));
725
726        let inv_tx = tx.inverse();
727        assert_eq!(inv_tx.transform_point3d(&p2), p1);
728
729        assert!((tx + inv_tx).is_identity());
730    }
731
732    /// Operation tests
733    mod ops {
734        use crate::default::Translation3D;
735
736        #[test]
737        pub fn test_add() {
738            let t1 = Translation3D::new(1.0, 2.0, 3.0);
739            let t2 = Translation3D::new(4.0, 5.0, 6.0);
740            assert_eq!(t1 + t2, Translation3D::new(5.0, 7.0, 9.0));
741
742            let t1 = Translation3D::new(1.0, 2.0, 3.0);
743            let t2 = Translation3D::new(0.0, 0.0, 0.0);
744            assert_eq!(t1 + t2, Translation3D::new(1.0, 2.0, 3.0));
745
746            let t1 = Translation3D::new(1.0, 2.0, 3.0);
747            let t2 = Translation3D::new(-4.0, -5.0, -6.0);
748            assert_eq!(t1 + t2, Translation3D::new(-3.0, -3.0, -3.0));
749
750            let t1 = Translation3D::new(0.0, 0.0, 0.0);
751            let t2 = Translation3D::new(0.0, 0.0, 0.0);
752            assert_eq!(t1 + t2, Translation3D::new(0.0, 0.0, 0.0));
753        }
754
755        #[test]
756        pub fn test_add_assign() {
757            let mut t = Translation3D::new(1.0, 2.0, 3.0);
758            t += Translation3D::new(4.0, 5.0, 6.0);
759            assert_eq!(t, Translation3D::new(5.0, 7.0, 9.0));
760
761            let mut t = Translation3D::new(1.0, 2.0, 3.0);
762            t += Translation3D::new(0.0, 0.0, 0.0);
763            assert_eq!(t, Translation3D::new(1.0, 2.0, 3.0));
764
765            let mut t = Translation3D::new(1.0, 2.0, 3.0);
766            t += Translation3D::new(-4.0, -5.0, -6.0);
767            assert_eq!(t, Translation3D::new(-3.0, -3.0, -3.0));
768
769            let mut t = Translation3D::new(0.0, 0.0, 0.0);
770            t += Translation3D::new(0.0, 0.0, 0.0);
771            assert_eq!(t, Translation3D::new(0.0, 0.0, 0.0));
772        }
773
774        #[test]
775        pub fn test_sub() {
776            let t1 = Translation3D::new(1.0, 2.0, 3.0);
777            let t2 = Translation3D::new(4.0, 5.0, 6.0);
778            assert_eq!(t1 - t2, Translation3D::new(-3.0, -3.0, -3.0));
779
780            let t1 = Translation3D::new(1.0, 2.0, 3.0);
781            let t2 = Translation3D::new(0.0, 0.0, 0.0);
782            assert_eq!(t1 - t2, Translation3D::new(1.0, 2.0, 3.0));
783
784            let t1 = Translation3D::new(1.0, 2.0, 3.0);
785            let t2 = Translation3D::new(-4.0, -5.0, -6.0);
786            assert_eq!(t1 - t2, Translation3D::new(5.0, 7.0, 9.0));
787
788            let t1 = Translation3D::new(0.0, 0.0, 0.0);
789            let t2 = Translation3D::new(0.0, 0.0, 0.0);
790            assert_eq!(t1 - t2, Translation3D::new(0.0, 0.0, 0.0));
791        }
792
793        #[test]
794        pub fn test_sub_assign() {
795            let mut t = Translation3D::new(1.0, 2.0, 3.0);
796            t -= Translation3D::new(4.0, 5.0, 6.0);
797            assert_eq!(t, Translation3D::new(-3.0, -3.0, -3.0));
798
799            let mut t = Translation3D::new(1.0, 2.0, 3.0);
800            t -= Translation3D::new(0.0, 0.0, 0.0);
801            assert_eq!(t, Translation3D::new(1.0, 2.0, 3.0));
802
803            let mut t = Translation3D::new(1.0, 2.0, 3.0);
804            t -= Translation3D::new(-4.0, -5.0, -6.0);
805            assert_eq!(t, Translation3D::new(5.0, 7.0, 9.0));
806
807            let mut t = Translation3D::new(0.0, 0.0, 0.0);
808            t -= Translation3D::new(0.0, 0.0, 0.0);
809            assert_eq!(t, Translation3D::new(0.0, 0.0, 0.0));
810        }
811    }
812}