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