diff options
Diffstat (limited to 'third_party/rust/euclid/src/translation.rs')
-rw-r--r-- | third_party/rust/euclid/src/translation.rs | 867 |
1 files changed, 867 insertions, 0 deletions
diff --git a/third_party/rust/euclid/src/translation.rs b/third_party/rust/euclid/src/translation.rs new file mode 100644 index 0000000000..45126e8d44 --- /dev/null +++ b/third_party/rust/euclid/src/translation.rs @@ -0,0 +1,867 @@ +// Copyright 2018 The Servo Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use crate::num::*; +use crate::UnknownUnit; +use crate::{point2, point3, vec2, vec3, Box2D, Box3D, Rect, Size2D}; +use crate::{Point2D, Point3D, Transform2D, Transform3D, Vector2D, Vector3D}; +use core::cmp::{Eq, PartialEq}; +use core::fmt; +use core::hash::Hash; +use core::marker::PhantomData; +use core::ops::{Add, AddAssign, Neg, Sub, SubAssign}; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; +#[cfg(feature = "bytemuck")] +use bytemuck::{Zeroable, Pod}; + +/// A 2d transformation from a space to another that can only express translations. +/// +/// The main benefit of this type over a Vector2D is the ability to cast +/// between a source and a destination spaces. +/// +/// Example: +/// +/// ``` +/// use euclid::{Translation2D, Point2D, point2}; +/// struct ParentSpace; +/// struct ChildSpace; +/// type ScrollOffset = Translation2D<i32, ParentSpace, ChildSpace>; +/// type ParentPoint = Point2D<i32, ParentSpace>; +/// type ChildPoint = Point2D<i32, ChildSpace>; +/// +/// let scrolling = ScrollOffset::new(0, 100); +/// let p1: ParentPoint = point2(0, 0); +/// let p2: ChildPoint = scrolling.transform_point(p1); +/// ``` +/// +#[repr(C)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr( + feature = "serde", + serde(bound( + serialize = "T: serde::Serialize", + deserialize = "T: serde::Deserialize<'de>" + )) +)] +pub struct Translation2D<T, Src, Dst> { + pub x: T, + pub y: T, + #[doc(hidden)] + pub _unit: PhantomData<(Src, Dst)>, +} + +#[cfg(feature = "arbitrary")] +impl<'a, T, Src, Dst> arbitrary::Arbitrary<'a> for Translation2D<T, Src, Dst> +where + T: arbitrary::Arbitrary<'a>, +{ + fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> + { + let (x, y) = arbitrary::Arbitrary::arbitrary(u)?; + Ok(Translation2D { + x, + y, + _unit: PhantomData, + }) + } +} + +impl<T: Copy, Src, Dst> Copy for Translation2D<T, Src, Dst> {} + +impl<T: Clone, Src, Dst> Clone for Translation2D<T, Src, Dst> { + fn clone(&self) -> Self { + Translation2D { + x: self.x.clone(), + y: self.y.clone(), + _unit: PhantomData, + } + } +} + +impl<T, Src, Dst> Eq for Translation2D<T, Src, Dst> where T: Eq {} + +impl<T, Src, Dst> PartialEq for Translation2D<T, Src, Dst> +where + T: PartialEq, +{ + fn eq(&self, other: &Self) -> bool { + self.x == other.x && self.y == other.y + } +} + +impl<T, Src, Dst> Hash for Translation2D<T, Src, Dst> +where + T: Hash, +{ + fn hash<H: core::hash::Hasher>(&self, h: &mut H) { + self.x.hash(h); + self.y.hash(h); + } +} + +impl<T, Src, Dst> Translation2D<T, Src, Dst> { + #[inline] + pub const fn new(x: T, y: T) -> Self { + Translation2D { + x, + y, + _unit: PhantomData, + } + } + + #[inline] + pub fn splat(v: T) -> Self + where + T: Clone, + { + Translation2D { + x: v.clone(), + y: v, + _unit: PhantomData, + } + } + + /// Creates no-op translation (both `x` and `y` is `zero()`). + #[inline] + pub fn identity() -> Self + where + T: Zero, + { + Self::new(T::zero(), T::zero()) + } + + /// Check if translation does nothing (both x and y is `zero()`). + /// + /// ```rust + /// use euclid::default::Translation2D; + /// + /// assert_eq!(Translation2D::<f32>::identity().is_identity(), true); + /// assert_eq!(Translation2D::new(0, 0).is_identity(), true); + /// assert_eq!(Translation2D::new(1, 0).is_identity(), false); + /// assert_eq!(Translation2D::new(0, 1).is_identity(), false); + /// ``` + #[inline] + pub fn is_identity(&self) -> bool + where + T: Zero + PartialEq, + { + let _0 = T::zero(); + self.x == _0 && self.y == _0 + } + + /// No-op, just cast the unit. + #[inline] + pub fn transform_size(&self, s: Size2D<T, Src>) -> Size2D<T, Dst> { + Size2D::new(s.width, s.height) + } +} + +impl<T: Copy, Src, Dst> Translation2D<T, Src, Dst> { + /// Cast into a 2D vector. + #[inline] + pub fn to_vector(&self) -> Vector2D<T, Src> { + vec2(self.x, self.y) + } + + /// Cast into an array with x and y. + #[inline] + pub fn to_array(&self) -> [T; 2] { + [self.x, self.y] + } + + /// Cast into a tuple with x and y. + #[inline] + pub fn to_tuple(&self) -> (T, T) { + (self.x, self.y) + } + + /// Drop the units, preserving only the numeric value. + #[inline] + pub fn to_untyped(&self) -> Translation2D<T, UnknownUnit, UnknownUnit> { + Translation2D { + x: self.x, + y: self.y, + _unit: PhantomData, + } + } + + /// Tag a unitless value with units. + #[inline] + pub fn from_untyped(t: &Translation2D<T, UnknownUnit, UnknownUnit>) -> Self { + Translation2D { + x: t.x, + y: t.y, + _unit: PhantomData, + } + } + + /// Returns the matrix representation of this translation. + #[inline] + pub fn to_transform(&self) -> Transform2D<T, Src, Dst> + where + T: Zero + One, + { + (*self).into() + } + + /// Translate a point and cast its unit. + #[inline] + pub fn transform_point(&self, p: Point2D<T, Src>) -> Point2D<T::Output, Dst> + where + T: Add, + { + point2(p.x + self.x, p.y + self.y) + } + + /// Translate a rectangle and cast its unit. + #[inline] + pub fn transform_rect(&self, r: &Rect<T, Src>) -> Rect<T::Output, Dst> + where + T: Add<Output = T>, + { + Rect { + origin: self.transform_point(r.origin), + size: self.transform_size(r.size), + } + } + + /// Translate a 2D box and cast its unit. + #[inline] + pub fn transform_box(&self, r: &Box2D<T, Src>) -> Box2D<T::Output, Dst> + where + T: Add, + { + Box2D { + min: self.transform_point(r.min), + max: self.transform_point(r.max), + } + } + + /// Return the inverse transformation. + #[inline] + pub fn inverse(&self) -> Translation2D<T::Output, Dst, Src> + where + T: Neg, + { + Translation2D::new(-self.x, -self.y) + } +} + +#[cfg(feature = "bytemuck")] +unsafe impl<T: Zeroable, Src, Dst> Zeroable for Translation2D<T, Src, Dst> {} + +#[cfg(feature = "bytemuck")] +unsafe impl<T: Pod, Src: 'static, Dst: 'static> Pod for Translation2D<T, Src, Dst> {} + +impl<T: Add, Src, Dst1, Dst2> Add<Translation2D<T, Dst1, Dst2>> for Translation2D<T, Src, Dst1> { + type Output = Translation2D<T::Output, Src, Dst2>; + + fn add(self, other: Translation2D<T, Dst1, Dst2>) -> Self::Output { + Translation2D::new(self.x + other.x, self.y + other.y) + } +} + +impl<T: AddAssign, Src, Dst> AddAssign<Translation2D<T, Dst, Dst>> for Translation2D<T, Src, Dst> { + fn add_assign(&mut self, other: Translation2D<T, Dst, Dst>) { + self.x += other.x; + self.y += other.y; + } +} + +impl<T: Sub, Src, Dst1, Dst2> Sub<Translation2D<T, Dst1, Dst2>> for Translation2D<T, Src, Dst2> { + type Output = Translation2D<T::Output, Src, Dst1>; + + fn sub(self, other: Translation2D<T, Dst1, Dst2>) -> Self::Output { + Translation2D::new(self.x - other.x, self.y - other.y) + } +} + +impl<T: SubAssign, Src, Dst> SubAssign<Translation2D<T, Dst, Dst>> for Translation2D<T, Src, Dst> { + fn sub_assign(&mut self, other: Translation2D<T, Dst, Dst>) { + self.x -= other.x; + self.y -= other.y; + } +} + +impl<T, Src, Dst> From<Vector2D<T, Src>> for Translation2D<T, Src, Dst> { + fn from(v: Vector2D<T, Src>) -> Self { + Translation2D::new(v.x, v.y) + } +} + +impl<T, Src, Dst> Into<Vector2D<T, Src>> for Translation2D<T, Src, Dst> { + fn into(self) -> Vector2D<T, Src> { + vec2(self.x, self.y) + } +} + +impl<T, Src, Dst> Into<Transform2D<T, Src, Dst>> for Translation2D<T, Src, Dst> +where + T: Zero + One, +{ + fn into(self) -> Transform2D<T, Src, Dst> { + Transform2D::translation(self.x, self.y) + } +} + +impl<T, Src, Dst> Default for Translation2D<T, Src, Dst> +where + T: Zero, +{ + fn default() -> Self { + Self::identity() + } +} + +impl<T: fmt::Debug, Src, Dst> fmt::Debug for Translation2D<T, Src, Dst> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Translation({:?},{:?})", self.x, self.y) + } +} + +/// A 3d transformation from a space to another that can only express translations. +/// +/// The main benefit of this type over a Vector3D is the ability to cast +/// between a source and a destination spaces. +#[repr(C)] +pub struct Translation3D<T, Src, Dst> { + pub x: T, + pub y: T, + pub z: T, + #[doc(hidden)] + pub _unit: PhantomData<(Src, Dst)>, +} + +impl<T: Copy, Src, Dst> Copy for Translation3D<T, Src, Dst> {} + +impl<T: Clone, Src, Dst> Clone for Translation3D<T, Src, Dst> { + fn clone(&self) -> Self { + Translation3D { + x: self.x.clone(), + y: self.y.clone(), + z: self.z.clone(), + _unit: PhantomData, + } + } +} + +#[cfg(feature = "serde")] +impl<'de, T, Src, Dst> serde::Deserialize<'de> for Translation3D<T, Src, Dst> +where + T: serde::Deserialize<'de>, +{ + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where + D: serde::Deserializer<'de>, + { + let (x, y, z) = serde::Deserialize::deserialize(deserializer)?; + Ok(Translation3D { + x, + y, + z, + _unit: PhantomData, + }) + } +} + +#[cfg(feature = "serde")] +impl<T, Src, Dst> serde::Serialize for Translation3D<T, Src, Dst> +where + T: serde::Serialize, +{ + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: serde::Serializer, + { + (&self.x, &self.y, &self.z).serialize(serializer) + } +} + +impl<T, Src, Dst> Eq for Translation3D<T, Src, Dst> where T: Eq {} + +impl<T, Src, Dst> PartialEq for Translation3D<T, Src, Dst> +where + T: PartialEq, +{ + fn eq(&self, other: &Self) -> bool { + self.x == other.x && self.y == other.y && self.z == other.z + } +} + +impl<T, Src, Dst> Hash for Translation3D<T, Src, Dst> +where + T: Hash, +{ + fn hash<H: core::hash::Hasher>(&self, h: &mut H) { + self.x.hash(h); + self.y.hash(h); + self.z.hash(h); + } +} + +impl<T, Src, Dst> Translation3D<T, Src, Dst> { + #[inline] + pub const fn new(x: T, y: T, z: T) -> Self { + Translation3D { + x, + y, + z, + _unit: PhantomData, + } + } + + #[inline] + pub fn splat(v: T) -> Self + where + T: Clone, + { + Translation3D { + x: v.clone(), + y: v.clone(), + z: v, + _unit: PhantomData, + } + } + + /// Creates no-op translation (`x`, `y` and `z` is `zero()`). + #[inline] + pub fn identity() -> Self + where + T: Zero, + { + Translation3D::new(T::zero(), T::zero(), T::zero()) + } + + /// Check if translation does nothing (`x`, `y` and `z` is `zero()`). + /// + /// ```rust + /// use euclid::default::Translation3D; + /// + /// assert_eq!(Translation3D::<f32>::identity().is_identity(), true); + /// assert_eq!(Translation3D::new(0, 0, 0).is_identity(), true); + /// assert_eq!(Translation3D::new(1, 0, 0).is_identity(), false); + /// assert_eq!(Translation3D::new(0, 1, 0).is_identity(), false); + /// assert_eq!(Translation3D::new(0, 0, 1).is_identity(), false); + /// ``` + #[inline] + pub fn is_identity(&self) -> bool + where + T: Zero + PartialEq, + { + let _0 = T::zero(); + self.x == _0 && self.y == _0 && self.z == _0 + } + + /// No-op, just cast the unit. + #[inline] + pub fn transform_size(self, s: Size2D<T, Src>) -> Size2D<T, Dst> { + Size2D::new(s.width, s.height) + } +} + +impl<T: Copy, Src, Dst> Translation3D<T, Src, Dst> { + /// Cast into a 3D vector. + #[inline] + pub fn to_vector(&self) -> Vector3D<T, Src> { + vec3(self.x, self.y, self.z) + } + + /// Cast into an array with x, y and z. + #[inline] + pub fn to_array(&self) -> [T; 3] { + [self.x, self.y, self.z] + } + + /// Cast into a tuple with x, y and z. + #[inline] + pub fn to_tuple(&self) -> (T, T, T) { + (self.x, self.y, self.z) + } + + /// Drop the units, preserving only the numeric value. + #[inline] + pub fn to_untyped(&self) -> Translation3D<T, UnknownUnit, UnknownUnit> { + Translation3D { + x: self.x, + y: self.y, + z: self.z, + _unit: PhantomData, + } + } + + /// Tag a unitless value with units. + #[inline] + pub fn from_untyped(t: &Translation3D<T, UnknownUnit, UnknownUnit>) -> Self { + Translation3D { + x: t.x, + y: t.y, + z: t.z, + _unit: PhantomData, + } + } + + /// Returns the matrix representation of this translation. + #[inline] + pub fn to_transform(&self) -> Transform3D<T, Src, Dst> + where + T: Zero + One, + { + (*self).into() + } + + /// Translate a point and cast its unit. + #[inline] + pub fn transform_point3d(&self, p: &Point3D<T, Src>) -> Point3D<T::Output, Dst> + where + T: Add, + { + point3(p.x + self.x, p.y + self.y, p.z + self.z) + } + + /// Translate a point and cast its unit. + #[inline] + pub fn transform_point2d(&self, p: &Point2D<T, Src>) -> Point2D<T::Output, Dst> + where + T: Add, + { + point2(p.x + self.x, p.y + self.y) + } + + /// Translate a 2D box and cast its unit. + #[inline] + pub fn transform_box2d(&self, b: &Box2D<T, Src>) -> Box2D<T::Output, Dst> + where + T: Add, + { + Box2D { + min: self.transform_point2d(&b.min), + max: self.transform_point2d(&b.max), + } + } + + /// Translate a 3D box and cast its unit. + #[inline] + pub fn transform_box3d(&self, b: &Box3D<T, Src>) -> Box3D<T::Output, Dst> + where + T: Add, + { + Box3D { + min: self.transform_point3d(&b.min), + max: self.transform_point3d(&b.max), + } + } + + /// Translate a rectangle and cast its unit. + #[inline] + pub fn transform_rect(&self, r: &Rect<T, Src>) -> Rect<T, Dst> + where + T: Add<Output = T>, + { + Rect { + origin: self.transform_point2d(&r.origin), + size: self.transform_size(r.size), + } + } + + /// Return the inverse transformation. + #[inline] + pub fn inverse(&self) -> Translation3D<T::Output, Dst, Src> + where + T: Neg, + { + Translation3D::new(-self.x, -self.y, -self.z) + } +} + +#[cfg(feature = "bytemuck")] +unsafe impl<T: Zeroable, Src, Dst> Zeroable for Translation3D<T, Src, Dst> {} + +#[cfg(feature = "bytemuck")] +unsafe impl<T: Pod, Src: 'static, Dst: 'static> Pod for Translation3D<T, Src, Dst> {} + +impl<T: Add, Src, Dst1, Dst2> Add<Translation3D<T, Dst1, Dst2>> for Translation3D<T, Src, Dst1> { + type Output = Translation3D<T::Output, Src, Dst2>; + + fn add(self, other: Translation3D<T, Dst1, Dst2>) -> Self::Output { + Translation3D::new(self.x + other.x, self.y + other.y, self.z + other.z) + } +} + +impl<T: AddAssign, Src, Dst> AddAssign<Translation3D<T, Dst, Dst>> for Translation3D<T, Src, Dst> { + fn add_assign(&mut self, other: Translation3D<T, Dst, Dst>) { + self.x += other.x; + self.y += other.y; + self.z += other.z; + } +} + +impl<T: Sub, Src, Dst1, Dst2> Sub<Translation3D<T, Dst1, Dst2>> for Translation3D<T, Src, Dst2> { + type Output = Translation3D<T::Output, Src, Dst1>; + + fn sub(self, other: Translation3D<T, Dst1, Dst2>) -> Self::Output { + Translation3D::new(self.x - other.x, self.y - other.y, self.z - other.z) + } +} + +impl<T: SubAssign, Src, Dst> SubAssign<Translation3D<T, Dst, Dst>> for Translation3D<T, Src, Dst> { + fn sub_assign(&mut self, other: Translation3D<T, Dst, Dst>) { + self.x -= other.x; + self.y -= other.y; + self.z -= other.z; + } +} + +impl<T, Src, Dst> From<Vector3D<T, Src>> for Translation3D<T, Src, Dst> { + fn from(v: Vector3D<T, Src>) -> Self { + Translation3D::new(v.x, v.y, v.z) + } +} + +impl<T, Src, Dst> Into<Vector3D<T, Src>> for Translation3D<T, Src, Dst> { + fn into(self) -> Vector3D<T, Src> { + vec3(self.x, self.y, self.z) + } +} + +impl<T, Src, Dst> Into<Transform3D<T, Src, Dst>> for Translation3D<T, Src, Dst> +where + T: Zero + One, +{ + fn into(self) -> Transform3D<T, Src, Dst> { + Transform3D::translation(self.x, self.y, self.z) + } +} + +impl<T, Src, Dst> Default for Translation3D<T, Src, Dst> +where + T: Zero, +{ + fn default() -> Self { + Self::identity() + } +} + +impl<T: fmt::Debug, Src, Dst> fmt::Debug for Translation3D<T, Src, Dst> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Translation({:?},{:?},{:?})", self.x, self.y, self.z) + } +} + +#[cfg(test)] +mod _2d { + #[test] + fn simple() { + use crate::{rect, Rect, Translation2D}; + + struct A; + struct B; + + type Translation = Translation2D<i32, A, B>; + type SrcRect = Rect<i32, A>; + type DstRect = Rect<i32, B>; + + let tx = Translation::new(10, -10); + let r1: SrcRect = rect(10, 20, 30, 40); + let r2: DstRect = tx.transform_rect(&r1); + assert_eq!(r2, rect(20, 10, 30, 40)); + + let inv_tx = tx.inverse(); + assert_eq!(inv_tx.transform_rect(&r2), r1); + + assert!((tx + inv_tx).is_identity()); + } + + /// Operation tests + mod ops { + use crate::default::Translation2D; + + #[test] + fn test_add() { + let t1 = Translation2D::new(1.0, 2.0); + let t2 = Translation2D::new(3.0, 4.0); + assert_eq!(t1 + t2, Translation2D::new(4.0, 6.0)); + + let t1 = Translation2D::new(1.0, 2.0); + let t2 = Translation2D::new(0.0, 0.0); + assert_eq!(t1 + t2, Translation2D::new(1.0, 2.0)); + + let t1 = Translation2D::new(1.0, 2.0); + let t2 = Translation2D::new(-3.0, -4.0); + assert_eq!(t1 + t2, Translation2D::new(-2.0, -2.0)); + + let t1 = Translation2D::new(0.0, 0.0); + let t2 = Translation2D::new(0.0, 0.0); + assert_eq!(t1 + t2, Translation2D::new(0.0, 0.0)); + } + + #[test] + pub fn test_add_assign() { + let mut t = Translation2D::new(1.0, 2.0); + t += Translation2D::new(3.0, 4.0); + assert_eq!(t, Translation2D::new(4.0, 6.0)); + + let mut t = Translation2D::new(1.0, 2.0); + t += Translation2D::new(0.0, 0.0); + assert_eq!(t, Translation2D::new(1.0, 2.0)); + + let mut t = Translation2D::new(1.0, 2.0); + t += Translation2D::new(-3.0, -4.0); + assert_eq!(t, Translation2D::new(-2.0, -2.0)); + + let mut t = Translation2D::new(0.0, 0.0); + t += Translation2D::new(0.0, 0.0); + assert_eq!(t, Translation2D::new(0.0, 0.0)); + } + + #[test] + pub fn test_sub() { + let t1 = Translation2D::new(1.0, 2.0); + let t2 = Translation2D::new(3.0, 4.0); + assert_eq!(t1 - t2, Translation2D::new(-2.0, -2.0)); + + let t1 = Translation2D::new(1.0, 2.0); + let t2 = Translation2D::new(0.0, 0.0); + assert_eq!(t1 - t2, Translation2D::new(1.0, 2.0)); + + let t1 = Translation2D::new(1.0, 2.0); + let t2 = Translation2D::new(-3.0, -4.0); + assert_eq!(t1 - t2, Translation2D::new(4.0, 6.0)); + + let t1 = Translation2D::new(0.0, 0.0); + let t2 = Translation2D::new(0.0, 0.0); + assert_eq!(t1 - t2, Translation2D::new(0.0, 0.0)); + } + + #[test] + pub fn test_sub_assign() { + let mut t = Translation2D::new(1.0, 2.0); + t -= Translation2D::new(3.0, 4.0); + assert_eq!(t, Translation2D::new(-2.0, -2.0)); + + let mut t = Translation2D::new(1.0, 2.0); + t -= Translation2D::new(0.0, 0.0); + assert_eq!(t, Translation2D::new(1.0, 2.0)); + + let mut t = Translation2D::new(1.0, 2.0); + t -= Translation2D::new(-3.0, -4.0); + assert_eq!(t, Translation2D::new(4.0, 6.0)); + + let mut t = Translation2D::new(0.0, 0.0); + t -= Translation2D::new(0.0, 0.0); + assert_eq!(t, Translation2D::new(0.0, 0.0)); + } + } +} + +#[cfg(test)] +mod _3d { + #[test] + fn simple() { + use crate::{point3, Point3D, Translation3D}; + + struct A; + struct B; + + type Translation = Translation3D<i32, A, B>; + type SrcPoint = Point3D<i32, A>; + type DstPoint = Point3D<i32, B>; + + let tx = Translation::new(10, -10, 100); + let p1: SrcPoint = point3(10, 20, 30); + let p2: DstPoint = tx.transform_point3d(&p1); + assert_eq!(p2, point3(20, 10, 130)); + + let inv_tx = tx.inverse(); + assert_eq!(inv_tx.transform_point3d(&p2), p1); + + assert!((tx + inv_tx).is_identity()); + } + + /// Operation tests + mod ops { + use crate::default::Translation3D; + + #[test] + pub fn test_add() { + let t1 = Translation3D::new(1.0, 2.0, 3.0); + let t2 = Translation3D::new(4.0, 5.0, 6.0); + assert_eq!(t1 + t2, Translation3D::new(5.0, 7.0, 9.0)); + + let t1 = Translation3D::new(1.0, 2.0, 3.0); + let t2 = Translation3D::new(0.0, 0.0, 0.0); + assert_eq!(t1 + t2, Translation3D::new(1.0, 2.0, 3.0)); + + let t1 = Translation3D::new(1.0, 2.0, 3.0); + let t2 = Translation3D::new(-4.0, -5.0, -6.0); + assert_eq!(t1 + t2, Translation3D::new(-3.0, -3.0, -3.0)); + + let t1 = Translation3D::new(0.0, 0.0, 0.0); + let t2 = Translation3D::new(0.0, 0.0, 0.0); + assert_eq!(t1 + t2, Translation3D::new(0.0, 0.0, 0.0)); + } + + #[test] + pub fn test_add_assign() { + let mut t = Translation3D::new(1.0, 2.0, 3.0); + t += Translation3D::new(4.0, 5.0, 6.0); + assert_eq!(t, Translation3D::new(5.0, 7.0, 9.0)); + + let mut t = Translation3D::new(1.0, 2.0, 3.0); + t += Translation3D::new(0.0, 0.0, 0.0); + assert_eq!(t, Translation3D::new(1.0, 2.0, 3.0)); + + let mut t = Translation3D::new(1.0, 2.0, 3.0); + t += Translation3D::new(-4.0, -5.0, -6.0); + assert_eq!(t, Translation3D::new(-3.0, -3.0, -3.0)); + + let mut t = Translation3D::new(0.0, 0.0, 0.0); + t += Translation3D::new(0.0, 0.0, 0.0); + assert_eq!(t, Translation3D::new(0.0, 0.0, 0.0)); + } + + #[test] + pub fn test_sub() { + let t1 = Translation3D::new(1.0, 2.0, 3.0); + let t2 = Translation3D::new(4.0, 5.0, 6.0); + assert_eq!(t1 - t2, Translation3D::new(-3.0, -3.0, -3.0)); + + let t1 = Translation3D::new(1.0, 2.0, 3.0); + let t2 = Translation3D::new(0.0, 0.0, 0.0); + assert_eq!(t1 - t2, Translation3D::new(1.0, 2.0, 3.0)); + + let t1 = Translation3D::new(1.0, 2.0, 3.0); + let t2 = Translation3D::new(-4.0, -5.0, -6.0); + assert_eq!(t1 - t2, Translation3D::new(5.0, 7.0, 9.0)); + + let t1 = Translation3D::new(0.0, 0.0, 0.0); + let t2 = Translation3D::new(0.0, 0.0, 0.0); + assert_eq!(t1 - t2, Translation3D::new(0.0, 0.0, 0.0)); + } + + #[test] + pub fn test_sub_assign() { + let mut t = Translation3D::new(1.0, 2.0, 3.0); + t -= Translation3D::new(4.0, 5.0, 6.0); + assert_eq!(t, Translation3D::new(-3.0, -3.0, -3.0)); + + let mut t = Translation3D::new(1.0, 2.0, 3.0); + t -= Translation3D::new(0.0, 0.0, 0.0); + assert_eq!(t, Translation3D::new(1.0, 2.0, 3.0)); + + let mut t = Translation3D::new(1.0, 2.0, 3.0); + t -= Translation3D::new(-4.0, -5.0, -6.0); + assert_eq!(t, Translation3D::new(5.0, 7.0, 9.0)); + + let mut t = Translation3D::new(0.0, 0.0, 0.0); + t -= Translation3D::new(0.0, 0.0, 0.0); + assert_eq!(t, Translation3D::new(0.0, 0.0, 0.0)); + } + } +} |