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 | |
10 | use crate::num::*; |
11 | use crate::UnknownUnit; |
12 | use crate::{point2, point3, vec2, vec3, Box2D, Box3D, Rect, Size2D}; |
13 | use crate::{Point2D, Point3D, Transform2D, Transform3D, Vector2D, Vector3D}; |
14 | use core::cmp::{Eq, PartialEq}; |
15 | use core::fmt; |
16 | use core::hash::Hash; |
17 | use core::marker::PhantomData; |
18 | use core::ops::{Add, AddAssign, Neg, Sub, SubAssign}; |
19 | #[cfg (feature = "serde" )] |
20 | use serde::{Deserialize, Serialize}; |
21 | #[cfg (feature = "bytemuck" )] |
22 | use 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 | )] |
53 | pub 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" )] |
61 | impl<'a, T, Src, Dst> arbitrary::Arbitrary<'a> for Translation2D<T, Src, Dst> |
62 | where |
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 | |
76 | impl<T: Copy, Src, Dst> Copy for Translation2D<T, Src, Dst> {} |
77 | |
78 | impl<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 | |
88 | impl<T, Src, Dst> Eq for Translation2D<T, Src, Dst> where T: Eq {} |
89 | |
90 | impl<T, Src, Dst> PartialEq for Translation2D<T, Src, Dst> |
91 | where |
92 | T: PartialEq, |
93 | { |
94 | fn eq(&self, other: &Self) -> bool { |
95 | self.x == other.x && self.y == other.y |
96 | } |
97 | } |
98 | |
99 | impl<T, Src, Dst> Hash for Translation2D<T, Src, Dst> |
100 | where |
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 | |
109 | impl<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 | |
166 | impl<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" )] |
258 | unsafe impl<T: Zeroable, Src, Dst> Zeroable for Translation2D<T, Src, Dst> {} |
259 | |
260 | #[cfg (feature = "bytemuck" )] |
261 | unsafe impl<T: Pod, Src: 'static, Dst: 'static> Pod for Translation2D<T, Src, Dst> {} |
262 | |
263 | impl<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 | |
271 | impl<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 | |
278 | impl<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 | |
286 | impl<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 | |
293 | impl<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 | |
299 | impl<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 | |
305 | impl<T, Src, Dst> Into<Transform2D<T, Src, Dst>> for Translation2D<T, Src, Dst> |
306 | where |
307 | T: Zero + One, |
308 | { |
309 | fn into(self) -> Transform2D<T, Src, Dst> { |
310 | Transform2D::translation(self.x, self.y) |
311 | } |
312 | } |
313 | |
314 | impl<T, Src, Dst> Default for Translation2D<T, Src, Dst> |
315 | where |
316 | T: Zero, |
317 | { |
318 | fn default() -> Self { |
319 | Self::identity() |
320 | } |
321 | } |
322 | |
323 | impl<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)] |
334 | pub 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 | |
342 | impl<T: Copy, Src, Dst> Copy for Translation3D<T, Src, Dst> {} |
343 | |
344 | impl<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" )] |
356 | impl<'de, T, Src, Dst> serde::Deserialize<'de> for Translation3D<T, Src, Dst> |
357 | where |
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" )] |
375 | impl<T, Src, Dst> serde::Serialize for Translation3D<T, Src, Dst> |
376 | where |
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 | |
387 | impl<T, Src, Dst> Eq for Translation3D<T, Src, Dst> where T: Eq {} |
388 | |
389 | impl<T, Src, Dst> PartialEq for Translation3D<T, Src, Dst> |
390 | where |
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 | |
398 | impl<T, Src, Dst> Hash for Translation3D<T, Src, Dst> |
399 | where |
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 | |
409 | impl<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 | |
469 | impl<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" )] |
584 | unsafe impl<T: Zeroable, Src, Dst> Zeroable for Translation3D<T, Src, Dst> {} |
585 | |
586 | #[cfg (feature = "bytemuck" )] |
587 | unsafe impl<T: Pod, Src: 'static, Dst: 'static> Pod for Translation3D<T, Src, Dst> {} |
588 | |
589 | impl<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 | |
597 | impl<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 | |
605 | impl<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 | |
613 | impl<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 | |
621 | impl<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 | |
627 | impl<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 | |
633 | impl<T, Src, Dst> Into<Transform3D<T, Src, Dst>> for Translation3D<T, Src, Dst> |
634 | where |
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 | |
642 | impl<T, Src, Dst> Default for Translation3D<T, Src, Dst> |
643 | where |
644 | T: Zero, |
645 | { |
646 | fn default() -> Self { |
647 | Self::identity() |
648 | } |
649 | } |
650 | |
651 | impl<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)] |
658 | mod _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)] |
764 | mod _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 | |