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