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