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};
14
15use core::cmp::{Eq, PartialEq};
16use core::fmt;
17use core::hash::Hash;
18use core::marker::PhantomData;
19use core::ops::{Add, AddAssign, Neg, Sub, SubAssign};
20
21#[cfg(feature = "bytemuck")]
22use bytemuck::{Pod, Zeroable};
23use num_traits::NumCast;
24#[cfg(feature = "serde")]
25use serde::{Deserialize, Serialize};
26
27/// A 2d transformation from a space to another that can only express translations.
28///
29/// The main benefit of this type over a [`Vector2D`] is the ability to cast
30/// between source and destination spaces.
31///
32/// Example:
33///
34/// ```
35/// use euclid::{Translation2D, Point2D, point2};
36/// struct ParentSpace;
37/// struct ChildSpace;
38/// type ScrollOffset = Translation2D<i32, ParentSpace, ChildSpace>;
39/// type ParentPoint = Point2D<i32, ParentSpace>;
40/// type ChildPoint = Point2D<i32, ChildSpace>;
41///
42/// let scrolling = ScrollOffset::new(0, 100);
43/// let p1: ParentPoint = point2(0, 0);
44/// let p2: ChildPoint = scrolling.transform_point(p1);
45/// ```
46///
47#[repr(C)]
48#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
49#[cfg_attr(
50    feature = "serde",
51    serde(bound(
52        serialize = "T: serde::Serialize",
53        deserialize = "T: serde::Deserialize<'de>"
54    ))
55)]
56pub struct Translation2D<T, Src, Dst> {
57    pub x: T,
58    pub y: T,
59    #[doc(hidden)]
60    pub _unit: PhantomData<(Src, Dst)>,
61}
62
63#[cfg(feature = "arbitrary")]
64impl<'a, T, Src, Dst> arbitrary::Arbitrary<'a> for Translation2D<T, Src, Dst>
65where
66    T: arbitrary::Arbitrary<'a>,
67{
68    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
69        let (x, y) = arbitrary::Arbitrary::arbitrary(u)?;
70        Ok(Translation2D {
71            x,
72            y,
73            _unit: PhantomData,
74        })
75    }
76}
77
78impl<T: Copy, Src, Dst> Copy for Translation2D<T, Src, Dst> {}
79
80impl<T: Clone, Src, Dst> Clone for Translation2D<T, Src, Dst> {
81    fn clone(&self) -> Self {
82        Translation2D {
83            x: self.x.clone(),
84            y: self.y.clone(),
85            _unit: PhantomData,
86        }
87    }
88}
89
90impl<T, Src, Dst> Eq for Translation2D<T, Src, Dst> where T: Eq {}
91
92impl<T, Src, Dst> PartialEq for Translation2D<T, Src, Dst>
93where
94    T: PartialEq,
95{
96    fn eq(&self, other: &Self) -> bool {
97        self.x == other.x && self.y == other.y
98    }
99}
100
101impl<T, Src, Dst> Hash for Translation2D<T, Src, Dst>
102where
103    T: Hash,
104{
105    fn hash<H: core::hash::Hasher>(&self, h: &mut H) {
106        self.x.hash(h);
107        self.y.hash(h);
108    }
109}
110
111impl<T, Src, Dst> Translation2D<T, Src, Dst> {
112    #[inline]
113    pub const fn new(x: T, y: T) -> Self {
114        Translation2D {
115            x,
116            y,
117            _unit: PhantomData,
118        }
119    }
120
121    #[inline]
122    pub fn splat(v: T) -> Self
123    where
124        T: Clone,
125    {
126        Translation2D {
127            x: v.clone(),
128            y: v,
129            _unit: PhantomData,
130        }
131    }
132
133    /// Creates no-op translation (both `x` and `y` is `zero()`).
134    #[inline]
135    pub fn identity() -> Self
136    where
137        T: Zero,
138    {
139        Self::new(T::zero(), T::zero())
140    }
141
142    /// Check if translation does nothing (both x and y is `zero()`).
143    ///
144    /// ```rust
145    /// use euclid::default::Translation2D;
146    ///
147    /// assert_eq!(Translation2D::<f32>::identity().is_identity(), true);
148    /// assert_eq!(Translation2D::new(0, 0).is_identity(), true);
149    /// assert_eq!(Translation2D::new(1, 0).is_identity(), false);
150    /// assert_eq!(Translation2D::new(0, 1).is_identity(), false);
151    /// ```
152    #[inline]
153    pub fn is_identity(&self) -> bool
154    where
155        T: Zero + PartialEq,
156    {
157        let _0 = T::zero();
158        self.x == _0 && self.y == _0
159    }
160
161    /// No-op, just cast the unit.
162    #[inline]
163    pub fn transform_size(&self, s: Size2D<T, Src>) -> Size2D<T, Dst> {
164        Size2D::new(s.width, s.height)
165    }
166}
167
168impl<T: Copy, Src, Dst> Translation2D<T, Src, Dst> {
169    /// Cast into a 2D vector.
170    #[inline]
171    pub fn to_vector(&self) -> Vector2D<T, Src> {
172        vec2(self.x, self.y)
173    }
174
175    /// Cast into an array with x and y.
176    #[inline]
177    pub fn to_array(&self) -> [T; 2] {
178        [self.x, self.y]
179    }
180
181    /// Cast into a tuple with x and y.
182    #[inline]
183    pub fn to_tuple(&self) -> (T, T) {
184        (self.x, self.y)
185    }
186
187    /// Drop the units, preserving only the numeric value.
188    #[inline]
189    pub fn to_untyped(&self) -> Translation2D<T, UnknownUnit, UnknownUnit> {
190        Translation2D {
191            x: self.x,
192            y: self.y,
193            _unit: PhantomData,
194        }
195    }
196
197    /// Tag a unitless value with units.
198    #[inline]
199    pub fn from_untyped(t: &Translation2D<T, UnknownUnit, UnknownUnit>) -> Self {
200        Translation2D {
201            x: t.x,
202            y: t.y,
203            _unit: PhantomData,
204        }
205    }
206
207    /// Returns the matrix representation of this translation.
208    #[inline]
209    pub fn to_transform(&self) -> Transform2D<T, Src, Dst>
210    where
211        T: Zero + One,
212    {
213        (*self).into()
214    }
215
216    /// Translate a point and cast its unit.
217    #[inline]
218    pub fn transform_point(&self, p: Point2D<T, Src>) -> Point2D<T::Output, Dst>
219    where
220        T: Add,
221    {
222        point2(p.x + self.x, p.y + self.y)
223    }
224
225    /// Translate a rectangle and cast its unit.
226    #[inline]
227    pub fn transform_rect(&self, r: &Rect<T, Src>) -> Rect<T::Output, Dst>
228    where
229        T: Add<Output = T>,
230    {
231        Rect {
232            origin: self.transform_point(r.origin),
233            size: self.transform_size(r.size),
234        }
235    }
236
237    /// Translate a 2D box and cast its unit.
238    #[inline]
239    pub fn transform_box(&self, r: &Box2D<T, Src>) -> Box2D<T::Output, Dst>
240    where
241        T: Add,
242    {
243        Box2D {
244            min: self.transform_point(r.min),
245            max: self.transform_point(r.max),
246        }
247    }
248
249    /// Return the inverse transformation.
250    #[inline]
251    pub fn inverse(&self) -> Translation2D<T::Output, Dst, Src>
252    where
253        T: Neg,
254    {
255        Translation2D::new(-self.x, -self.y)
256    }
257}
258
259impl<T: NumCast + Copy, Src, Dst> Translation2D<T, Src, Dst> {
260    /// Cast from one numeric representation to another, preserving the units.
261    ///
262    /// When casting from floating vector to integer coordinates, the decimals are truncated
263    /// as one would expect from a simple cast, but this behavior does not always make sense
264    /// geometrically. Consider using `round()`, `ceil()` or `floor()` before casting.
265    #[inline]
266    pub fn cast<NewT: NumCast>(self) -> Translation2D<NewT, Src, Dst> {
267        self.try_cast().unwrap()
268    }
269
270    /// Fallible cast from one numeric representation to another, preserving the units.
271    ///
272    /// When casting from floating vector to integer coordinates, the decimals are truncated
273    /// as one would expect from a simple cast, but this behavior does not always make sense
274    /// geometrically. Consider using `round()`, `ceil()` or `floor()` before casting.
275    pub fn try_cast<NewT: NumCast>(self) -> Option<Translation2D<NewT, Src, Dst>> {
276        match (NumCast::from(self.x), NumCast::from(self.y)) {
277            (Some(x), Some(y)) => Some(Translation2D::new(x, y)),
278            _ => None,
279        }
280    }
281
282    // Convenience functions for common casts.
283
284    /// Cast into an `f32` vector.
285    #[inline]
286    pub fn to_f32(self) -> Translation2D<f32, Src, Dst> {
287        self.cast()
288    }
289
290    /// Cast into an `f64` vector.
291    #[inline]
292    pub fn to_f64(self) -> Translation2D<f64, Src, Dst> {
293        self.cast()
294    }
295
296    /// Cast into an `usize` vector, truncating decimals if any.
297    ///
298    /// When casting from floating vector vectors, it is worth considering whether
299    /// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
300    /// the desired conversion behavior.
301    #[inline]
302    pub fn to_usize(self) -> Translation2D<usize, Src, Dst> {
303        self.cast()
304    }
305
306    /// Cast into an `u32` vector, truncating decimals if any.
307    ///
308    /// When casting from floating vector vectors, it is worth considering whether
309    /// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
310    /// the desired conversion behavior.
311    #[inline]
312    pub fn to_u32(self) -> Translation2D<u32, Src, Dst> {
313        self.cast()
314    }
315
316    /// Cast into an i32 vector, truncating decimals if any.
317    ///
318    /// When casting from floating vector vectors, it is worth considering whether
319    /// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
320    /// the desired conversion behavior.
321    #[inline]
322    pub fn to_i32(self) -> Translation2D<i32, Src, Dst> {
323        self.cast()
324    }
325
326    /// Cast into an i64 vector, truncating decimals if any.
327    ///
328    /// When casting from floating vector vectors, it is worth considering whether
329    /// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
330    /// the desired conversion behavior.
331    #[inline]
332    pub fn to_i64(self) -> Translation2D<i64, Src, Dst> {
333        self.cast()
334    }
335}
336
337#[cfg(feature = "bytemuck")]
338unsafe impl<T: Zeroable, Src, Dst> Zeroable for Translation2D<T, Src, Dst> {}
339
340#[cfg(feature = "bytemuck")]
341unsafe impl<T: Pod, Src: 'static, Dst: 'static> Pod for Translation2D<T, Src, Dst> {}
342
343impl<T: Add, Src, Dst1, Dst2> Add<Translation2D<T, Dst1, Dst2>> for Translation2D<T, Src, Dst1> {
344    type Output = Translation2D<T::Output, Src, Dst2>;
345
346    fn add(self, other: Translation2D<T, Dst1, Dst2>) -> Self::Output {
347        Translation2D::new(self.x + other.x, self.y + other.y)
348    }
349}
350
351impl<T: AddAssign, Src, Dst> AddAssign<Translation2D<T, Dst, Dst>> for Translation2D<T, Src, Dst> {
352    fn add_assign(&mut self, other: Translation2D<T, Dst, Dst>) {
353        self.x += other.x;
354        self.y += other.y;
355    }
356}
357
358impl<T: Sub, Src, Dst1, Dst2> Sub<Translation2D<T, Dst1, Dst2>> for Translation2D<T, Src, Dst2> {
359    type Output = Translation2D<T::Output, Src, Dst1>;
360
361    fn sub(self, other: Translation2D<T, Dst1, Dst2>) -> Self::Output {
362        Translation2D::new(self.x - other.x, self.y - other.y)
363    }
364}
365
366impl<T: SubAssign, Src, Dst> SubAssign<Translation2D<T, Dst, Dst>> for Translation2D<T, Src, Dst> {
367    fn sub_assign(&mut self, other: Translation2D<T, Dst, Dst>) {
368        self.x -= other.x;
369        self.y -= other.y;
370    }
371}
372
373impl<T, Src, Dst> From<Vector2D<T, Src>> for Translation2D<T, Src, Dst> {
374    fn from(v: Vector2D<T, Src>) -> Self {
375        Translation2D::new(v.x, v.y)
376    }
377}
378
379impl<T, Src, Dst> From<Translation2D<T, Src, Dst>> for Vector2D<T, Src> {
380    fn from(t: Translation2D<T, Src, Dst>) -> Self {
381        vec2(t.x, t.y)
382    }
383}
384
385impl<T, Src, Dst> From<Translation2D<T, Src, Dst>> for Transform2D<T, Src, Dst>
386where
387    T: Zero + One,
388{
389    fn from(t: Translation2D<T, Src, Dst>) -> Self {
390        Transform2D::translation(t.x, t.y)
391    }
392}
393
394impl<T, Src, Dst> Default for Translation2D<T, Src, Dst>
395where
396    T: Zero,
397{
398    fn default() -> Self {
399        Self::identity()
400    }
401}
402
403impl<T: fmt::Debug, Src, Dst> fmt::Debug for Translation2D<T, Src, Dst> {
404    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
405        write!(f, "Translation({:?},{:?})", self.x, self.y)
406    }
407}
408
409/// A 3d transformation from a space to another that can only express translations.
410///
411/// The main benefit of this type over a [`Vector3D`] is the ability to cast
412/// between source and destination spaces.
413#[repr(C)]
414pub struct Translation3D<T, Src, Dst> {
415    pub x: T,
416    pub y: T,
417    pub z: T,
418    #[doc(hidden)]
419    pub _unit: PhantomData<(Src, Dst)>,
420}
421
422#[cfg(feature = "arbitrary")]
423impl<'a, T, Src, Dst> arbitrary::Arbitrary<'a> for Translation3D<T, Src, Dst>
424where
425    T: arbitrary::Arbitrary<'a>,
426{
427    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
428        let (x, y, z) = arbitrary::Arbitrary::arbitrary(u)?;
429        Ok(Translation3D {
430            x,
431            y,
432            z,
433            _unit: PhantomData,
434        })
435    }
436}
437
438impl<T: Copy, Src, Dst> Copy for Translation3D<T, Src, Dst> {}
439
440impl<T: Clone, Src, Dst> Clone for Translation3D<T, Src, Dst> {
441    fn clone(&self) -> Self {
442        Translation3D {
443            x: self.x.clone(),
444            y: self.y.clone(),
445            z: self.z.clone(),
446            _unit: PhantomData,
447        }
448    }
449}
450
451#[cfg(feature = "serde")]
452impl<'de, T, Src, Dst> serde::Deserialize<'de> for Translation3D<T, Src, Dst>
453where
454    T: serde::Deserialize<'de>,
455{
456    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
457    where
458        D: serde::Deserializer<'de>,
459    {
460        let (x, y, z) = serde::Deserialize::deserialize(deserializer)?;
461        Ok(Translation3D {
462            x,
463            y,
464            z,
465            _unit: PhantomData,
466        })
467    }
468}
469
470#[cfg(feature = "serde")]
471impl<T, Src, Dst> serde::Serialize for Translation3D<T, Src, Dst>
472where
473    T: serde::Serialize,
474{
475    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
476    where
477        S: serde::Serializer,
478    {
479        (&self.x, &self.y, &self.z).serialize(serializer)
480    }
481}
482
483impl<T, Src, Dst> Eq for Translation3D<T, Src, Dst> where T: Eq {}
484
485impl<T, Src, Dst> PartialEq for Translation3D<T, Src, Dst>
486where
487    T: PartialEq,
488{
489    fn eq(&self, other: &Self) -> bool {
490        self.x == other.x && self.y == other.y && self.z == other.z
491    }
492}
493
494impl<T, Src, Dst> Hash for Translation3D<T, Src, Dst>
495where
496    T: Hash,
497{
498    fn hash<H: core::hash::Hasher>(&self, h: &mut H) {
499        self.x.hash(h);
500        self.y.hash(h);
501        self.z.hash(h);
502    }
503}
504
505impl<T, Src, Dst> Translation3D<T, Src, Dst> {
506    #[inline]
507    pub const fn new(x: T, y: T, z: T) -> Self {
508        Translation3D {
509            x,
510            y,
511            z,
512            _unit: PhantomData,
513        }
514    }
515
516    #[inline]
517    pub fn splat(v: T) -> Self
518    where
519        T: Clone,
520    {
521        Translation3D {
522            x: v.clone(),
523            y: v.clone(),
524            z: v,
525            _unit: PhantomData,
526        }
527    }
528
529    /// Creates no-op translation (`x`, `y` and `z` is `zero()`).
530    #[inline]
531    pub fn identity() -> Self
532    where
533        T: Zero,
534    {
535        Translation3D::new(T::zero(), T::zero(), T::zero())
536    }
537
538    /// Check if translation does nothing (`x`, `y` and `z` is `zero()`).
539    ///
540    /// ```rust
541    /// use euclid::default::Translation3D;
542    ///
543    /// assert_eq!(Translation3D::<f32>::identity().is_identity(), true);
544    /// assert_eq!(Translation3D::new(0, 0, 0).is_identity(), true);
545    /// assert_eq!(Translation3D::new(1, 0, 0).is_identity(), false);
546    /// assert_eq!(Translation3D::new(0, 1, 0).is_identity(), false);
547    /// assert_eq!(Translation3D::new(0, 0, 1).is_identity(), false);
548    /// ```
549    #[inline]
550    pub fn is_identity(&self) -> bool
551    where
552        T: Zero + PartialEq,
553    {
554        let _0 = T::zero();
555        self.x == _0 && self.y == _0 && self.z == _0
556    }
557
558    /// No-op, just cast the unit.
559    #[inline]
560    pub fn transform_size(self, s: Size2D<T, Src>) -> Size2D<T, Dst> {
561        Size2D::new(s.width, s.height)
562    }
563}
564
565impl<T: Copy, Src, Dst> Translation3D<T, Src, Dst> {
566    /// Cast into a 3D vector.
567    #[inline]
568    pub fn to_vector(&self) -> Vector3D<T, Src> {
569        vec3(self.x, self.y, self.z)
570    }
571
572    /// Cast into an array with x, y and z.
573    #[inline]
574    pub fn to_array(&self) -> [T; 3] {
575        [self.x, self.y, self.z]
576    }
577
578    /// Cast into a tuple with x, y and z.
579    #[inline]
580    pub fn to_tuple(&self) -> (T, T, T) {
581        (self.x, self.y, self.z)
582    }
583
584    /// Drop the units, preserving only the numeric value.
585    #[inline]
586    pub fn to_untyped(&self) -> Translation3D<T, UnknownUnit, UnknownUnit> {
587        Translation3D {
588            x: self.x,
589            y: self.y,
590            z: self.z,
591            _unit: PhantomData,
592        }
593    }
594
595    /// Tag a unitless value with units.
596    #[inline]
597    pub fn from_untyped(t: &Translation3D<T, UnknownUnit, UnknownUnit>) -> Self {
598        Translation3D {
599            x: t.x,
600            y: t.y,
601            z: t.z,
602            _unit: PhantomData,
603        }
604    }
605
606    /// Returns the matrix representation of this translation.
607    #[inline]
608    pub fn to_transform(&self) -> Transform3D<T, Src, Dst>
609    where
610        T: Zero + One,
611    {
612        (*self).into()
613    }
614
615    /// Translate a point and cast its unit.
616    #[inline]
617    pub fn transform_point3d(&self, p: &Point3D<T, Src>) -> Point3D<T::Output, Dst>
618    where
619        T: Add,
620    {
621        point3(p.x + self.x, p.y + self.y, p.z + self.z)
622    }
623
624    /// Translate a point and cast its unit.
625    #[inline]
626    pub fn transform_point2d(&self, p: &Point2D<T, Src>) -> Point2D<T::Output, Dst>
627    where
628        T: Add,
629    {
630        point2(p.x + self.x, p.y + self.y)
631    }
632
633    /// Translate a 2D box and cast its unit.
634    #[inline]
635    pub fn transform_box2d(&self, b: &Box2D<T, Src>) -> Box2D<T::Output, Dst>
636    where
637        T: Add,
638    {
639        Box2D {
640            min: self.transform_point2d(&b.min),
641            max: self.transform_point2d(&b.max),
642        }
643    }
644
645    /// Translate a 3D box and cast its unit.
646    #[inline]
647    pub fn transform_box3d(&self, b: &Box3D<T, Src>) -> Box3D<T::Output, Dst>
648    where
649        T: Add,
650    {
651        Box3D {
652            min: self.transform_point3d(&b.min),
653            max: self.transform_point3d(&b.max),
654        }
655    }
656
657    /// Translate a rectangle and cast its unit.
658    #[inline]
659    pub fn transform_rect(&self, r: &Rect<T, Src>) -> Rect<T, Dst>
660    where
661        T: Add<Output = T>,
662    {
663        Rect {
664            origin: self.transform_point2d(&r.origin),
665            size: self.transform_size(r.size),
666        }
667    }
668
669    /// Return the inverse transformation.
670    #[inline]
671    pub fn inverse(&self) -> Translation3D<T::Output, Dst, Src>
672    where
673        T: Neg,
674    {
675        Translation3D::new(-self.x, -self.y, -self.z)
676    }
677}
678
679impl<T: NumCast + Copy, Src, Dst> Translation3D<T, Src, Dst> {
680    /// Cast from one numeric representation to another, preserving the units.
681    ///
682    /// When casting from floating vector to integer coordinates, the decimals are truncated
683    /// as one would expect from a simple cast, but this behavior does not always make sense
684    /// geometrically. Consider using `round()`, `ceil()` or `floor()` before casting.
685    #[inline]
686    pub fn cast<NewT: NumCast>(self) -> Translation3D<NewT, Src, Dst> {
687        self.try_cast().unwrap()
688    }
689
690    /// Fallible cast from one numeric representation to another, preserving the units.
691    ///
692    /// When casting from floating vector to integer coordinates, the decimals are truncated
693    /// as one would expect from a simple cast, but this behavior does not always make sense
694    /// geometrically. Consider using `round()`, `ceil()` or `floor()` before casting.
695    pub fn try_cast<NewT: NumCast>(self) -> Option<Translation3D<NewT, Src, Dst>> {
696        match (
697            NumCast::from(self.x),
698            NumCast::from(self.y),
699            NumCast::from(self.z),
700        ) {
701            (Some(x), Some(y), Some(z)) => Some(Translation3D::new(x, y, z)),
702            _ => None,
703        }
704    }
705
706    // Convenience functions for common casts.
707
708    /// Cast into an `f32` vector.
709    #[inline]
710    pub fn to_f32(self) -> Translation3D<f32, Src, Dst> {
711        self.cast()
712    }
713
714    /// Cast into an `f64` vector.
715    #[inline]
716    pub fn to_f64(self) -> Translation3D<f64, Src, Dst> {
717        self.cast()
718    }
719
720    /// Cast into an `usize` vector, truncating decimals if any.
721    ///
722    /// When casting from floating vector vectors, it is worth considering whether
723    /// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
724    /// the desired conversion behavior.
725    #[inline]
726    pub fn to_usize(self) -> Translation3D<usize, Src, Dst> {
727        self.cast()
728    }
729
730    /// Cast into an `u32` vector, truncating decimals if any.
731    ///
732    /// When casting from floating vector vectors, it is worth considering whether
733    /// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
734    /// the desired conversion behavior.
735    #[inline]
736    pub fn to_u32(self) -> Translation3D<u32, Src, Dst> {
737        self.cast()
738    }
739
740    /// Cast into an i32 vector, truncating decimals if any.
741    ///
742    /// When casting from floating vector vectors, it is worth considering whether
743    /// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
744    /// the desired conversion behavior.
745    #[inline]
746    pub fn to_i32(self) -> Translation3D<i32, Src, Dst> {
747        self.cast()
748    }
749
750    /// Cast into an i64 vector, truncating decimals if any.
751    ///
752    /// When casting from floating vector vectors, it is worth considering whether
753    /// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
754    /// the desired conversion behavior.
755    #[inline]
756    pub fn to_i64(self) -> Translation3D<i64, Src, Dst> {
757        self.cast()
758    }
759}
760
761#[cfg(feature = "bytemuck")]
762unsafe impl<T: Zeroable, Src, Dst> Zeroable for Translation3D<T, Src, Dst> {}
763
764#[cfg(feature = "bytemuck")]
765unsafe impl<T: Pod, Src: 'static, Dst: 'static> Pod for Translation3D<T, Src, Dst> {}
766
767impl<T: Add, Src, Dst1, Dst2> Add<Translation3D<T, Dst1, Dst2>> for Translation3D<T, Src, Dst1> {
768    type Output = Translation3D<T::Output, Src, Dst2>;
769
770    fn add(self, other: Translation3D<T, Dst1, Dst2>) -> Self::Output {
771        Translation3D::new(self.x + other.x, self.y + other.y, self.z + other.z)
772    }
773}
774
775impl<T: AddAssign, Src, Dst> AddAssign<Translation3D<T, Dst, Dst>> for Translation3D<T, Src, Dst> {
776    fn add_assign(&mut self, other: Translation3D<T, Dst, Dst>) {
777        self.x += other.x;
778        self.y += other.y;
779        self.z += other.z;
780    }
781}
782
783impl<T: Sub, Src, Dst1, Dst2> Sub<Translation3D<T, Dst1, Dst2>> for Translation3D<T, Src, Dst2> {
784    type Output = Translation3D<T::Output, Src, Dst1>;
785
786    fn sub(self, other: Translation3D<T, Dst1, Dst2>) -> Self::Output {
787        Translation3D::new(self.x - other.x, self.y - other.y, self.z - other.z)
788    }
789}
790
791impl<T: SubAssign, Src, Dst> SubAssign<Translation3D<T, Dst, Dst>> for Translation3D<T, Src, Dst> {
792    fn sub_assign(&mut self, other: Translation3D<T, Dst, Dst>) {
793        self.x -= other.x;
794        self.y -= other.y;
795        self.z -= other.z;
796    }
797}
798
799impl<T, Src, Dst> From<Vector3D<T, Src>> for Translation3D<T, Src, Dst> {
800    fn from(v: Vector3D<T, Src>) -> Self {
801        Translation3D::new(v.x, v.y, v.z)
802    }
803}
804
805impl<T, Src, Dst> From<Translation3D<T, Src, Dst>> for Vector3D<T, Src> {
806    fn from(t: Translation3D<T, Src, Dst>) -> Self {
807        vec3(t.x, t.y, t.z)
808    }
809}
810
811impl<T, Src, Dst> From<Translation3D<T, Src, Dst>> for Transform3D<T, Src, Dst>
812where
813    T: Zero + One,
814{
815    fn from(t: Translation3D<T, Src, Dst>) -> Self {
816        Transform3D::translation(t.x, t.y, t.z)
817    }
818}
819
820impl<T, Src, Dst> Default for Translation3D<T, Src, Dst>
821where
822    T: Zero,
823{
824    fn default() -> Self {
825        Self::identity()
826    }
827}
828
829impl<T: fmt::Debug, Src, Dst> fmt::Debug for Translation3D<T, Src, Dst> {
830    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
831        write!(f, "Translation({:?},{:?},{:?})", self.x, self.y, self.z)
832    }
833}
834
835#[cfg(test)]
836mod _2d {
837    #[test]
838    fn simple() {
839        use crate::{rect, Rect, Translation2D};
840
841        struct A;
842        struct B;
843
844        type Translation = Translation2D<i32, A, B>;
845        type SrcRect = Rect<i32, A>;
846        type DstRect = Rect<i32, B>;
847
848        let tx = Translation::new(10, -10);
849        let r1: SrcRect = rect(10, 20, 30, 40);
850        let r2: DstRect = tx.transform_rect(&r1);
851        assert_eq!(r2, rect(20, 10, 30, 40));
852
853        let inv_tx = tx.inverse();
854        assert_eq!(inv_tx.transform_rect(&r2), r1);
855
856        assert!((tx + inv_tx).is_identity());
857    }
858
859    /// Operation tests
860    mod ops {
861        use crate::default::Translation2D;
862
863        #[test]
864        fn test_add() {
865            let t1 = Translation2D::new(1.0, 2.0);
866            let t2 = Translation2D::new(3.0, 4.0);
867            assert_eq!(t1 + t2, Translation2D::new(4.0, 6.0));
868
869            let t1 = Translation2D::new(1.0, 2.0);
870            let t2 = Translation2D::new(0.0, 0.0);
871            assert_eq!(t1 + t2, Translation2D::new(1.0, 2.0));
872
873            let t1 = Translation2D::new(1.0, 2.0);
874            let t2 = Translation2D::new(-3.0, -4.0);
875            assert_eq!(t1 + t2, Translation2D::new(-2.0, -2.0));
876
877            let t1 = Translation2D::new(0.0, 0.0);
878            let t2 = Translation2D::new(0.0, 0.0);
879            assert_eq!(t1 + t2, Translation2D::new(0.0, 0.0));
880        }
881
882        #[test]
883        pub fn test_add_assign() {
884            let mut t = Translation2D::new(1.0, 2.0);
885            t += Translation2D::new(3.0, 4.0);
886            assert_eq!(t, Translation2D::new(4.0, 6.0));
887
888            let mut t = Translation2D::new(1.0, 2.0);
889            t += Translation2D::new(0.0, 0.0);
890            assert_eq!(t, Translation2D::new(1.0, 2.0));
891
892            let mut t = Translation2D::new(1.0, 2.0);
893            t += Translation2D::new(-3.0, -4.0);
894            assert_eq!(t, Translation2D::new(-2.0, -2.0));
895
896            let mut t = Translation2D::new(0.0, 0.0);
897            t += Translation2D::new(0.0, 0.0);
898            assert_eq!(t, Translation2D::new(0.0, 0.0));
899        }
900
901        #[test]
902        pub fn test_sub() {
903            let t1 = Translation2D::new(1.0, 2.0);
904            let t2 = Translation2D::new(3.0, 4.0);
905            assert_eq!(t1 - t2, Translation2D::new(-2.0, -2.0));
906
907            let t1 = Translation2D::new(1.0, 2.0);
908            let t2 = Translation2D::new(0.0, 0.0);
909            assert_eq!(t1 - t2, Translation2D::new(1.0, 2.0));
910
911            let t1 = Translation2D::new(1.0, 2.0);
912            let t2 = Translation2D::new(-3.0, -4.0);
913            assert_eq!(t1 - t2, Translation2D::new(4.0, 6.0));
914
915            let t1 = Translation2D::new(0.0, 0.0);
916            let t2 = Translation2D::new(0.0, 0.0);
917            assert_eq!(t1 - t2, Translation2D::new(0.0, 0.0));
918        }
919
920        #[test]
921        pub fn test_sub_assign() {
922            let mut t = Translation2D::new(1.0, 2.0);
923            t -= Translation2D::new(3.0, 4.0);
924            assert_eq!(t, Translation2D::new(-2.0, -2.0));
925
926            let mut t = Translation2D::new(1.0, 2.0);
927            t -= Translation2D::new(0.0, 0.0);
928            assert_eq!(t, Translation2D::new(1.0, 2.0));
929
930            let mut t = Translation2D::new(1.0, 2.0);
931            t -= Translation2D::new(-3.0, -4.0);
932            assert_eq!(t, Translation2D::new(4.0, 6.0));
933
934            let mut t = Translation2D::new(0.0, 0.0);
935            t -= Translation2D::new(0.0, 0.0);
936            assert_eq!(t, Translation2D::new(0.0, 0.0));
937        }
938    }
939}
940
941#[cfg(test)]
942mod _3d {
943    #[test]
944    fn simple() {
945        use crate::{point3, Point3D, Translation3D};
946
947        struct A;
948        struct B;
949
950        type Translation = Translation3D<i32, A, B>;
951        type SrcPoint = Point3D<i32, A>;
952        type DstPoint = Point3D<i32, B>;
953
954        let tx = Translation::new(10, -10, 100);
955        let p1: SrcPoint = point3(10, 20, 30);
956        let p2: DstPoint = tx.transform_point3d(&p1);
957        assert_eq!(p2, point3(20, 10, 130));
958
959        let inv_tx = tx.inverse();
960        assert_eq!(inv_tx.transform_point3d(&p2), p1);
961
962        assert!((tx + inv_tx).is_identity());
963    }
964
965    /// Operation tests
966    mod ops {
967        use crate::default::Translation3D;
968
969        #[test]
970        pub fn test_add() {
971            let t1 = Translation3D::new(1.0, 2.0, 3.0);
972            let t2 = Translation3D::new(4.0, 5.0, 6.0);
973            assert_eq!(t1 + t2, Translation3D::new(5.0, 7.0, 9.0));
974
975            let t1 = Translation3D::new(1.0, 2.0, 3.0);
976            let t2 = Translation3D::new(0.0, 0.0, 0.0);
977            assert_eq!(t1 + t2, Translation3D::new(1.0, 2.0, 3.0));
978
979            let t1 = Translation3D::new(1.0, 2.0, 3.0);
980            let t2 = Translation3D::new(-4.0, -5.0, -6.0);
981            assert_eq!(t1 + t2, Translation3D::new(-3.0, -3.0, -3.0));
982
983            let t1 = Translation3D::new(0.0, 0.0, 0.0);
984            let t2 = Translation3D::new(0.0, 0.0, 0.0);
985            assert_eq!(t1 + t2, Translation3D::new(0.0, 0.0, 0.0));
986        }
987
988        #[test]
989        pub fn test_add_assign() {
990            let mut t = Translation3D::new(1.0, 2.0, 3.0);
991            t += Translation3D::new(4.0, 5.0, 6.0);
992            assert_eq!(t, Translation3D::new(5.0, 7.0, 9.0));
993
994            let mut t = Translation3D::new(1.0, 2.0, 3.0);
995            t += Translation3D::new(0.0, 0.0, 0.0);
996            assert_eq!(t, Translation3D::new(1.0, 2.0, 3.0));
997
998            let mut t = Translation3D::new(1.0, 2.0, 3.0);
999            t += Translation3D::new(-4.0, -5.0, -6.0);
1000            assert_eq!(t, Translation3D::new(-3.0, -3.0, -3.0));
1001
1002            let mut t = Translation3D::new(0.0, 0.0, 0.0);
1003            t += Translation3D::new(0.0, 0.0, 0.0);
1004            assert_eq!(t, Translation3D::new(0.0, 0.0, 0.0));
1005        }
1006
1007        #[test]
1008        pub fn test_sub() {
1009            let t1 = Translation3D::new(1.0, 2.0, 3.0);
1010            let t2 = Translation3D::new(4.0, 5.0, 6.0);
1011            assert_eq!(t1 - t2, Translation3D::new(-3.0, -3.0, -3.0));
1012
1013            let t1 = Translation3D::new(1.0, 2.0, 3.0);
1014            let t2 = Translation3D::new(0.0, 0.0, 0.0);
1015            assert_eq!(t1 - t2, Translation3D::new(1.0, 2.0, 3.0));
1016
1017            let t1 = Translation3D::new(1.0, 2.0, 3.0);
1018            let t2 = Translation3D::new(-4.0, -5.0, -6.0);
1019            assert_eq!(t1 - t2, Translation3D::new(5.0, 7.0, 9.0));
1020
1021            let t1 = Translation3D::new(0.0, 0.0, 0.0);
1022            let t2 = Translation3D::new(0.0, 0.0, 0.0);
1023            assert_eq!(t1 - t2, Translation3D::new(0.0, 0.0, 0.0));
1024        }
1025
1026        #[test]
1027        pub fn test_sub_assign() {
1028            let mut t = Translation3D::new(1.0, 2.0, 3.0);
1029            t -= Translation3D::new(4.0, 5.0, 6.0);
1030            assert_eq!(t, Translation3D::new(-3.0, -3.0, -3.0));
1031
1032            let mut t = Translation3D::new(1.0, 2.0, 3.0);
1033            t -= Translation3D::new(0.0, 0.0, 0.0);
1034            assert_eq!(t, Translation3D::new(1.0, 2.0, 3.0));
1035
1036            let mut t = Translation3D::new(1.0, 2.0, 3.0);
1037            t -= Translation3D::new(-4.0, -5.0, -6.0);
1038            assert_eq!(t, Translation3D::new(5.0, 7.0, 9.0));
1039
1040            let mut t = Translation3D::new(0.0, 0.0, 0.0);
1041            t -= Translation3D::new(0.0, 0.0, 0.0);
1042            assert_eq!(t, Translation3D::new(0.0, 0.0, 0.0));
1043        }
1044    }
1045}