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