1use u_geometry::nalgebra_types::{
4 Isometry2, Isometry3, NaPoint2 as Point2, NaPoint3 as Point3, NaVector2 as Vector2,
5 NaVector3 as Vector3, RealField, Rotation3,
6};
7
8#[cfg(feature = "serde")]
9use serde::{Deserialize, Serialize};
10
11#[derive(Debug, Clone, Copy, PartialEq)]
13#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
14pub struct Transform2D<S: RealField + Copy> {
15 pub tx: S,
17 pub ty: S,
19 pub angle: S,
21}
22
23impl<S: RealField + Copy> Transform2D<S> {
24 pub fn identity() -> Self {
26 Self {
27 tx: S::zero(),
28 ty: S::zero(),
29 angle: S::zero(),
30 }
31 }
32
33 pub fn translation(tx: S, ty: S) -> Self {
35 Self {
36 tx,
37 ty,
38 angle: S::zero(),
39 }
40 }
41
42 pub fn rotation(angle: S) -> Self {
44 Self {
45 tx: S::zero(),
46 ty: S::zero(),
47 angle,
48 }
49 }
50
51 pub fn new(tx: S, ty: S, angle: S) -> Self {
53 Self { tx, ty, angle }
54 }
55
56 pub fn to_isometry(&self) -> Isometry2<S> {
58 Isometry2::new(Vector2::new(self.tx, self.ty), self.angle)
59 }
60
61 pub fn from_isometry(iso: &Isometry2<S>) -> Self {
63 Self {
64 tx: iso.translation.x,
65 ty: iso.translation.y,
66 angle: iso.rotation.angle(),
67 }
68 }
69
70 pub fn transform_point(&self, x: S, y: S) -> (S, S) {
72 let iso = self.to_isometry();
73 let p = iso.transform_point(&Point2::new(x, y));
74 (p.x, p.y)
75 }
76
77 pub fn transform_points(&self, points: &[(S, S)]) -> Vec<(S, S)> {
79 let iso = self.to_isometry();
80 points
81 .iter()
82 .map(|(x, y)| {
83 let p = iso.transform_point(&Point2::new(*x, *y));
84 (p.x, p.y)
85 })
86 .collect()
87 }
88
89 pub fn then(&self, other: &Self) -> Self {
91 let iso1 = self.to_isometry();
92 let iso2 = other.to_isometry();
93 Self::from_isometry(&(iso1 * iso2))
94 }
95
96 pub fn inverse(&self) -> Self {
98 Self::from_isometry(&self.to_isometry().inverse())
99 }
100
101 pub fn is_identity(&self, epsilon: S) -> bool {
103 self.tx.abs() < epsilon && self.ty.abs() < epsilon && self.angle.abs() < epsilon
104 }
105}
106
107impl<S: RealField + Copy> Default for Transform2D<S> {
108 fn default() -> Self {
109 Self::identity()
110 }
111}
112
113#[derive(Debug, Clone, Copy, PartialEq)]
115#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
116pub struct Transform3D<S: RealField + Copy> {
117 pub tx: S,
119 pub ty: S,
121 pub tz: S,
123 pub rx: S,
125 pub ry: S,
127 pub rz: S,
129}
130
131impl<S: RealField + Copy> Transform3D<S> {
132 pub fn identity() -> Self {
134 Self {
135 tx: S::zero(),
136 ty: S::zero(),
137 tz: S::zero(),
138 rx: S::zero(),
139 ry: S::zero(),
140 rz: S::zero(),
141 }
142 }
143
144 pub fn translation(tx: S, ty: S, tz: S) -> Self {
146 Self {
147 tx,
148 ty,
149 tz,
150 rx: S::zero(),
151 ry: S::zero(),
152 rz: S::zero(),
153 }
154 }
155
156 pub fn rotation(rx: S, ry: S, rz: S) -> Self {
158 Self {
159 tx: S::zero(),
160 ty: S::zero(),
161 tz: S::zero(),
162 rx,
163 ry,
164 rz,
165 }
166 }
167
168 pub fn new(tx: S, ty: S, tz: S, rx: S, ry: S, rz: S) -> Self {
170 Self {
171 tx,
172 ty,
173 tz,
174 rx,
175 ry,
176 rz,
177 }
178 }
179
180 pub fn to_isometry(&self) -> Isometry3<S> {
182 let rotation = Rotation3::from_euler_angles(self.rx, self.ry, self.rz);
183 Isometry3::from_parts(
184 Vector3::new(self.tx, self.ty, self.tz).into(),
185 rotation.into(),
186 )
187 }
188
189 pub fn from_isometry(iso: &Isometry3<S>) -> Self {
191 let (rx, ry, rz) = iso.rotation.euler_angles();
192 Self {
193 tx: iso.translation.x,
194 ty: iso.translation.y,
195 tz: iso.translation.z,
196 rx,
197 ry,
198 rz,
199 }
200 }
201
202 pub fn transform_point(&self, x: S, y: S, z: S) -> (S, S, S) {
204 let iso = self.to_isometry();
205 let p = iso.transform_point(&Point3::new(x, y, z));
206 (p.x, p.y, p.z)
207 }
208
209 pub fn transform_points(&self, points: &[(S, S, S)]) -> Vec<(S, S, S)> {
211 let iso = self.to_isometry();
212 points
213 .iter()
214 .map(|(x, y, z)| {
215 let p = iso.transform_point(&Point3::new(*x, *y, *z));
216 (p.x, p.y, p.z)
217 })
218 .collect()
219 }
220
221 pub fn then(&self, other: &Self) -> Self {
223 let iso1 = self.to_isometry();
224 let iso2 = other.to_isometry();
225 Self::from_isometry(&(iso1 * iso2))
226 }
227
228 pub fn inverse(&self) -> Self {
230 Self::from_isometry(&self.to_isometry().inverse())
231 }
232
233 pub fn is_identity(&self, epsilon: S) -> bool {
235 self.tx.abs() < epsilon
236 && self.ty.abs() < epsilon
237 && self.tz.abs() < epsilon
238 && self.rx.abs() < epsilon
239 && self.ry.abs() < epsilon
240 && self.rz.abs() < epsilon
241 }
242}
243
244impl<S: RealField + Copy> Default for Transform3D<S> {
245 fn default() -> Self {
246 Self::identity()
247 }
248}
249
250#[derive(Debug, Clone, Copy, PartialEq)]
252#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
253pub struct AABB2D<S> {
254 pub min_x: S,
256 pub min_y: S,
258 pub max_x: S,
260 pub max_y: S,
262}
263
264impl<S: RealField + Copy> AABB2D<S> {
265 pub fn new(min_x: S, min_y: S, max_x: S, max_y: S) -> Self {
267 Self {
268 min_x,
269 min_y,
270 max_x,
271 max_y,
272 }
273 }
274
275 pub fn from_points(points: &[(S, S)]) -> Option<Self> {
277 if points.is_empty() {
278 return None;
279 }
280
281 let mut min_x = points[0].0;
282 let mut min_y = points[0].1;
283 let mut max_x = points[0].0;
284 let mut max_y = points[0].1;
285
286 for (x, y) in points.iter().skip(1) {
287 min_x = min_x.min(*x);
288 min_y = min_y.min(*y);
289 max_x = max_x.max(*x);
290 max_y = max_y.max(*y);
291 }
292
293 Some(Self {
294 min_x,
295 min_y,
296 max_x,
297 max_y,
298 })
299 }
300
301 pub fn width(&self) -> S {
303 self.max_x - self.min_x
304 }
305
306 pub fn height(&self) -> S {
308 self.max_y - self.min_y
309 }
310
311 pub fn area(&self) -> S {
313 self.width() * self.height()
314 }
315
316 pub fn center(&self) -> (S, S) {
318 let two = S::one() + S::one();
319 (
320 (self.min_x + self.max_x) / two,
321 (self.min_y + self.max_y) / two,
322 )
323 }
324
325 pub fn contains_point(&self, x: S, y: S) -> bool {
327 x >= self.min_x && x <= self.max_x && y >= self.min_y && y <= self.max_y
328 }
329
330 pub fn intersects(&self, other: &Self) -> bool {
332 self.min_x <= other.max_x
333 && self.max_x >= other.min_x
334 && self.min_y <= other.max_y
335 && self.max_y >= other.min_y
336 }
337
338 pub fn intersection(&self, other: &Self) -> Option<Self> {
340 if !self.intersects(other) {
341 return None;
342 }
343
344 Some(Self {
345 min_x: self.min_x.max(other.min_x),
346 min_y: self.min_y.max(other.min_y),
347 max_x: self.max_x.min(other.max_x),
348 max_y: self.max_y.min(other.max_y),
349 })
350 }
351
352 pub fn union(&self, other: &Self) -> Self {
354 Self {
355 min_x: self.min_x.min(other.min_x),
356 min_y: self.min_y.min(other.min_y),
357 max_x: self.max_x.max(other.max_x),
358 max_y: self.max_y.max(other.max_y),
359 }
360 }
361
362 pub fn expand(&self, margin: S) -> Self {
364 Self {
365 min_x: self.min_x - margin,
366 min_y: self.min_y - margin,
367 max_x: self.max_x + margin,
368 max_y: self.max_y + margin,
369 }
370 }
371}
372
373#[derive(Debug, Clone, Copy, PartialEq)]
375#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
376pub struct AABB3D<S> {
377 pub min_x: S,
379 pub min_y: S,
381 pub min_z: S,
383 pub max_x: S,
385 pub max_y: S,
387 pub max_z: S,
389}
390
391impl<S: RealField + Copy> AABB3D<S> {
392 pub fn new(min_x: S, min_y: S, min_z: S, max_x: S, max_y: S, max_z: S) -> Self {
394 Self {
395 min_x,
396 min_y,
397 min_z,
398 max_x,
399 max_y,
400 max_z,
401 }
402 }
403
404 pub fn from_points(points: &[(S, S, S)]) -> Option<Self> {
406 if points.is_empty() {
407 return None;
408 }
409
410 let mut min_x = points[0].0;
411 let mut min_y = points[0].1;
412 let mut min_z = points[0].2;
413 let mut max_x = points[0].0;
414 let mut max_y = points[0].1;
415 let mut max_z = points[0].2;
416
417 for (x, y, z) in points.iter().skip(1) {
418 min_x = min_x.min(*x);
419 min_y = min_y.min(*y);
420 min_z = min_z.min(*z);
421 max_x = max_x.max(*x);
422 max_y = max_y.max(*y);
423 max_z = max_z.max(*z);
424 }
425
426 Some(Self {
427 min_x,
428 min_y,
429 min_z,
430 max_x,
431 max_y,
432 max_z,
433 })
434 }
435
436 pub fn width(&self) -> S {
438 self.max_x - self.min_x
439 }
440
441 pub fn depth(&self) -> S {
443 self.max_y - self.min_y
444 }
445
446 pub fn height(&self) -> S {
448 self.max_z - self.min_z
449 }
450
451 pub fn volume(&self) -> S {
453 self.width() * self.depth() * self.height()
454 }
455
456 pub fn center(&self) -> (S, S, S) {
458 let two = S::one() + S::one();
459 (
460 (self.min_x + self.max_x) / two,
461 (self.min_y + self.max_y) / two,
462 (self.min_z + self.max_z) / two,
463 )
464 }
465
466 pub fn contains_point(&self, x: S, y: S, z: S) -> bool {
468 x >= self.min_x
469 && x <= self.max_x
470 && y >= self.min_y
471 && y <= self.max_y
472 && z >= self.min_z
473 && z <= self.max_z
474 }
475
476 pub fn intersects(&self, other: &Self) -> bool {
478 self.min_x <= other.max_x
479 && self.max_x >= other.min_x
480 && self.min_y <= other.max_y
481 && self.max_y >= other.min_y
482 && self.min_z <= other.max_z
483 && self.max_z >= other.min_z
484 }
485
486 pub fn intersection(&self, other: &Self) -> Option<Self> {
488 if !self.intersects(other) {
489 return None;
490 }
491
492 Some(Self {
493 min_x: self.min_x.max(other.min_x),
494 min_y: self.min_y.max(other.min_y),
495 min_z: self.min_z.max(other.min_z),
496 max_x: self.max_x.min(other.max_x),
497 max_y: self.max_y.min(other.max_y),
498 max_z: self.max_z.min(other.max_z),
499 })
500 }
501
502 pub fn union(&self, other: &Self) -> Self {
504 Self {
505 min_x: self.min_x.min(other.min_x),
506 min_y: self.min_y.min(other.min_y),
507 min_z: self.min_z.min(other.min_z),
508 max_x: self.max_x.max(other.max_x),
509 max_y: self.max_y.max(other.max_y),
510 max_z: self.max_z.max(other.max_z),
511 }
512 }
513
514 pub fn expand(&self, margin: S) -> Self {
516 Self {
517 min_x: self.min_x - margin,
518 min_y: self.min_y - margin,
519 min_z: self.min_z - margin,
520 max_x: self.max_x + margin,
521 max_y: self.max_y + margin,
522 max_z: self.max_z + margin,
523 }
524 }
525}
526
527#[cfg(test)]
528mod tests {
529 use super::*;
530 use approx::assert_relative_eq;
531 use std::f64::consts::PI;
532
533 #[test]
534 fn test_transform2d_identity() {
535 let t = Transform2D::<f64>::identity();
536 let (x, y) = t.transform_point(1.0, 2.0);
537 assert_relative_eq!(x, 1.0, epsilon = 1e-10);
538 assert_relative_eq!(y, 2.0, epsilon = 1e-10);
539 }
540
541 #[test]
542 fn test_transform2d_translation() {
543 let t = Transform2D::translation(10.0, 20.0);
544 let (x, y) = t.transform_point(1.0, 2.0);
545 assert_relative_eq!(x, 11.0, epsilon = 1e-10);
546 assert_relative_eq!(y, 22.0, epsilon = 1e-10);
547 }
548
549 #[test]
550 fn test_transform2d_rotation() {
551 let t = Transform2D::rotation(PI / 2.0);
552 let (x, y) = t.transform_point(1.0, 0.0);
553 assert_relative_eq!(x, 0.0, epsilon = 1e-10);
554 assert_relative_eq!(y, 1.0, epsilon = 1e-10);
555 }
556
557 #[test]
558 fn test_transform2d_inverse() {
559 let t = Transform2D::new(10.0, 20.0, PI / 4.0);
560 let inv = t.inverse();
561 let composed = t.then(&inv);
562 assert!(composed.is_identity(1e-10));
563 }
564
565 #[test]
566 fn test_transform3d_translation() {
567 let t = Transform3D::translation(10.0, 20.0, 30.0);
568 let (x, y, z) = t.transform_point(1.0, 2.0, 3.0);
569 assert_relative_eq!(x, 11.0, epsilon = 1e-10);
570 assert_relative_eq!(y, 22.0, epsilon = 1e-10);
571 assert_relative_eq!(z, 33.0, epsilon = 1e-10);
572 }
573
574 #[test]
575 fn test_aabb2d_from_points() {
576 let points = vec![(0.0, 0.0), (10.0, 5.0), (3.0, 8.0)];
577 let aabb = AABB2D::from_points(&points).unwrap();
578 assert_relative_eq!(aabb.min_x, 0.0);
579 assert_relative_eq!(aabb.min_y, 0.0);
580 assert_relative_eq!(aabb.max_x, 10.0);
581 assert_relative_eq!(aabb.max_y, 8.0);
582 }
583
584 #[test]
585 fn test_aabb2d_intersection() {
586 let a = AABB2D::new(0.0, 0.0, 10.0, 10.0);
587 let b = AABB2D::new(5.0, 5.0, 15.0, 15.0);
588 let intersection = a.intersection(&b).unwrap();
589 assert_relative_eq!(intersection.min_x, 5.0);
590 assert_relative_eq!(intersection.min_y, 5.0);
591 assert_relative_eq!(intersection.max_x, 10.0);
592 assert_relative_eq!(intersection.max_y, 10.0);
593 }
594
595 #[test]
596 fn test_aabb3d_volume() {
597 let aabb = AABB3D::new(0.0, 0.0, 0.0, 10.0, 20.0, 30.0);
598 assert_relative_eq!(aabb.volume(), 6000.0);
599 }
600}