Skip to main content

u_geometry/
primitives.rs

1//! Core geometric primitives.
2//!
3//! Provides fundamental 2D and 3D types used throughout the library.
4//!
5//! # Design
6//!
7//! Points and vectors are mathematically distinct: points live in affine space,
8//! vectors in linear space. Operator overloading enforces this:
9//! - `Point - Point = Vector`
10//! - `Point + Vector = Point`
11//! - `Vector + Vector = Vector`
12
13use std::ops::{Add, Mul, Sub};
14
15/// A 2D point.
16#[derive(Debug, Clone, Copy, PartialEq)]
17#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
18pub struct Point2 {
19    pub x: f64,
20    pub y: f64,
21}
22
23impl Point2 {
24    /// Creates a new point.
25    #[inline]
26    pub fn new(x: f64, y: f64) -> Self {
27        Self { x, y }
28    }
29
30    /// Origin point (0, 0).
31    pub const ORIGIN: Self = Self { x: 0.0, y: 0.0 };
32
33    /// Euclidean distance to another point.
34    ///
35    /// # Complexity
36    /// O(1)
37    #[inline]
38    pub fn distance_to(&self, other: &Self) -> f64 {
39        let dx = self.x - other.x;
40        let dy = self.y - other.y;
41        (dx * dx + dy * dy).sqrt()
42    }
43
44    /// Squared Euclidean distance (avoids sqrt).
45    #[inline]
46    pub fn distance_sq(&self, other: &Self) -> f64 {
47        let dx = self.x - other.x;
48        let dy = self.y - other.y;
49        dx * dx + dy * dy
50    }
51
52    /// Converts to a tuple.
53    #[inline]
54    pub fn to_tuple(self) -> (f64, f64) {
55        (self.x, self.y)
56    }
57
58    /// Creates from a tuple.
59    #[inline]
60    pub fn from_tuple(t: (f64, f64)) -> Self {
61        Self { x: t.0, y: t.1 }
62    }
63}
64
65impl From<(f64, f64)> for Point2 {
66    fn from(t: (f64, f64)) -> Self {
67        Self::from_tuple(t)
68    }
69}
70
71impl From<Point2> for (f64, f64) {
72    fn from(p: Point2) -> Self {
73        p.to_tuple()
74    }
75}
76
77impl Sub for Point2 {
78    type Output = Vector2;
79
80    fn sub(self, rhs: Self) -> Vector2 {
81        Vector2::new(self.x - rhs.x, self.y - rhs.y)
82    }
83}
84
85impl Add<Vector2> for Point2 {
86    type Output = Point2;
87
88    fn add(self, rhs: Vector2) -> Point2 {
89        Point2::new(self.x + rhs.x, self.y + rhs.y)
90    }
91}
92
93/// A 2D vector.
94#[derive(Debug, Clone, Copy, PartialEq)]
95#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
96pub struct Vector2 {
97    pub x: f64,
98    pub y: f64,
99}
100
101impl Vector2 {
102    /// Creates a new vector.
103    #[inline]
104    pub fn new(x: f64, y: f64) -> Self {
105        Self { x, y }
106    }
107
108    /// Zero vector.
109    pub const ZERO: Self = Self { x: 0.0, y: 0.0 };
110
111    /// Euclidean length.
112    #[inline]
113    pub fn length(&self) -> f64 {
114        (self.x * self.x + self.y * self.y).sqrt()
115    }
116
117    /// Squared length (avoids sqrt).
118    #[inline]
119    pub fn length_sq(&self) -> f64 {
120        self.x * self.x + self.y * self.y
121    }
122
123    /// Returns a normalized (unit-length) vector, or zero vector if length is ~0.
124    #[inline]
125    pub fn normalized(&self) -> Self {
126        let len = self.length();
127        if len < 1e-15 {
128            Self::ZERO
129        } else {
130            Self::new(self.x / len, self.y / len)
131        }
132    }
133
134    /// 2D cross product (z-component of the 3D cross product).
135    ///
136    /// Returns a positive value if `other` is counter-clockwise from `self`,
137    /// negative if clockwise, zero if parallel.
138    #[inline]
139    pub fn cross(&self, other: &Self) -> f64 {
140        self.x * other.y - self.y * other.x
141    }
142
143    /// Dot product.
144    #[inline]
145    pub fn dot(&self, other: &Self) -> f64 {
146        self.x * other.x + self.y * other.y
147    }
148
149    /// Returns the perpendicular vector (rotated 90 degrees CCW).
150    #[inline]
151    pub fn perp(&self) -> Self {
152        Self::new(-self.y, self.x)
153    }
154}
155
156impl Add for Vector2 {
157    type Output = Self;
158
159    fn add(self, rhs: Self) -> Self {
160        Self::new(self.x + rhs.x, self.y + rhs.y)
161    }
162}
163
164impl Sub for Vector2 {
165    type Output = Self;
166
167    fn sub(self, rhs: Self) -> Self {
168        Self::new(self.x - rhs.x, self.y - rhs.y)
169    }
170}
171
172impl Mul<f64> for Vector2 {
173    type Output = Self;
174
175    fn mul(self, rhs: f64) -> Self {
176        Self::new(self.x * rhs, self.y * rhs)
177    }
178}
179
180/// A 2D line segment.
181#[derive(Debug, Clone, Copy, PartialEq)]
182#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
183pub struct Segment2 {
184    pub start: Point2,
185    pub end: Point2,
186}
187
188impl Segment2 {
189    /// Creates a new segment.
190    pub fn new(start: Point2, end: Point2) -> Self {
191        Self { start, end }
192    }
193
194    /// Segment length.
195    #[inline]
196    pub fn length(&self) -> f64 {
197        self.start.distance_to(&self.end)
198    }
199
200    /// Squared length.
201    #[inline]
202    pub fn length_sq(&self) -> f64 {
203        self.start.distance_sq(&self.end)
204    }
205
206    /// Direction vector (not normalized).
207    #[inline]
208    pub fn direction(&self) -> Vector2 {
209        self.end - self.start
210    }
211
212    /// Midpoint.
213    #[inline]
214    pub fn midpoint(&self) -> Point2 {
215        Point2::new(
216            (self.start.x + self.end.x) * 0.5,
217            (self.start.y + self.end.y) * 0.5,
218        )
219    }
220}
221
222/// A 2D axis-aligned bounding box.
223///
224/// # Invariant
225/// `min.x <= max.x` and `min.y <= max.y`.
226#[derive(Debug, Clone, Copy, PartialEq)]
227#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
228pub struct AABB2 {
229    /// Minimum corner.
230    pub min: Point2,
231    /// Maximum corner.
232    pub max: Point2,
233}
234
235impl AABB2 {
236    /// Creates an AABB from min/max corners.
237    pub fn new(min_x: f64, min_y: f64, max_x: f64, max_y: f64) -> Self {
238        Self {
239            min: Point2::new(min_x, min_y),
240            max: Point2::new(max_x, max_y),
241        }
242    }
243
244    /// Creates an AABB enclosing a set of points.
245    ///
246    /// Returns `None` if the slice is empty.
247    ///
248    /// # Complexity
249    /// O(n)
250    pub fn from_points(points: &[Point2]) -> Option<Self> {
251        let first = points.first()?;
252        let mut min_x = first.x;
253        let mut min_y = first.y;
254        let mut max_x = first.x;
255        let mut max_y = first.y;
256
257        for p in points.iter().skip(1) {
258            min_x = min_x.min(p.x);
259            min_y = min_y.min(p.y);
260            max_x = max_x.max(p.x);
261            max_y = max_y.max(p.y);
262        }
263
264        Some(Self::new(min_x, min_y, max_x, max_y))
265    }
266
267    /// Width (x extent).
268    #[inline]
269    pub fn width(&self) -> f64 {
270        self.max.x - self.min.x
271    }
272
273    /// Height (y extent).
274    #[inline]
275    pub fn height(&self) -> f64 {
276        self.max.y - self.min.y
277    }
278
279    /// Area.
280    #[inline]
281    pub fn area(&self) -> f64 {
282        self.width() * self.height()
283    }
284
285    /// Center point.
286    #[inline]
287    pub fn center(&self) -> Point2 {
288        Point2::new(
289            (self.min.x + self.max.x) * 0.5,
290            (self.min.y + self.max.y) * 0.5,
291        )
292    }
293
294    /// Whether this AABB contains a point.
295    #[inline]
296    pub fn contains_point(&self, p: &Point2) -> bool {
297        p.x >= self.min.x && p.x <= self.max.x && p.y >= self.min.y && p.y <= self.max.y
298    }
299
300    /// Whether two AABBs intersect.
301    #[inline]
302    pub fn intersects(&self, other: &Self) -> bool {
303        self.min.x <= other.max.x
304            && self.max.x >= other.min.x
305            && self.min.y <= other.max.y
306            && self.max.y >= other.min.y
307    }
308
309    /// Returns the intersection of two AABBs, or `None` if they don't overlap.
310    pub fn intersection(&self, other: &Self) -> Option<Self> {
311        if !self.intersects(other) {
312            return None;
313        }
314        Some(Self::new(
315            self.min.x.max(other.min.x),
316            self.min.y.max(other.min.y),
317            self.max.x.min(other.max.x),
318            self.max.y.min(other.max.y),
319        ))
320    }
321
322    /// Returns the union (bounding box) of two AABBs.
323    pub fn union(&self, other: &Self) -> Self {
324        Self::new(
325            self.min.x.min(other.min.x),
326            self.min.y.min(other.min.y),
327            self.max.x.max(other.max.x),
328            self.max.y.max(other.max.y),
329        )
330    }
331
332    /// Returns a new AABB expanded by `margin` on all sides.
333    pub fn expand(&self, margin: f64) -> Self {
334        Self::new(
335            self.min.x - margin,
336            self.min.y - margin,
337            self.max.x + margin,
338            self.max.y + margin,
339        )
340    }
341}
342
343// ======================== 3D Primitives ========================
344
345/// A 3D point.
346#[derive(Debug, Clone, Copy, PartialEq)]
347#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
348pub struct Point3 {
349    pub x: f64,
350    pub y: f64,
351    pub z: f64,
352}
353
354impl Point3 {
355    /// Creates a new 3D point.
356    #[inline]
357    pub fn new(x: f64, y: f64, z: f64) -> Self {
358        Self { x, y, z }
359    }
360
361    /// Origin point (0, 0, 0).
362    pub const ORIGIN: Self = Self {
363        x: 0.0,
364        y: 0.0,
365        z: 0.0,
366    };
367
368    /// Euclidean distance to another point.
369    ///
370    /// # Complexity
371    /// O(1)
372    #[inline]
373    pub fn distance_to(&self, other: &Self) -> f64 {
374        self.distance_sq(other).sqrt()
375    }
376
377    /// Squared Euclidean distance (avoids sqrt).
378    #[inline]
379    pub fn distance_sq(&self, other: &Self) -> f64 {
380        let dx = self.x - other.x;
381        let dy = self.y - other.y;
382        let dz = self.z - other.z;
383        dx * dx + dy * dy + dz * dz
384    }
385
386    /// Converts to a tuple.
387    #[inline]
388    pub fn to_tuple(self) -> (f64, f64, f64) {
389        (self.x, self.y, self.z)
390    }
391
392    /// Creates from a tuple.
393    #[inline]
394    pub fn from_tuple(t: (f64, f64, f64)) -> Self {
395        Self {
396            x: t.0,
397            y: t.1,
398            z: t.2,
399        }
400    }
401
402    /// Converts to an array `[x, y, z]`.
403    #[inline]
404    pub fn to_array(self) -> [f64; 3] {
405        [self.x, self.y, self.z]
406    }
407
408    /// Creates from an array `[x, y, z]`.
409    #[inline]
410    pub fn from_array(a: [f64; 3]) -> Self {
411        Self {
412            x: a[0],
413            y: a[1],
414            z: a[2],
415        }
416    }
417}
418
419impl From<(f64, f64, f64)> for Point3 {
420    fn from(t: (f64, f64, f64)) -> Self {
421        Self::from_tuple(t)
422    }
423}
424
425impl From<Point3> for (f64, f64, f64) {
426    fn from(p: Point3) -> Self {
427        p.to_tuple()
428    }
429}
430
431impl From<[f64; 3]> for Point3 {
432    fn from(a: [f64; 3]) -> Self {
433        Self::from_array(a)
434    }
435}
436
437impl Sub for Point3 {
438    type Output = Vector3;
439
440    fn sub(self, rhs: Self) -> Vector3 {
441        Vector3::new(self.x - rhs.x, self.y - rhs.y, self.z - rhs.z)
442    }
443}
444
445impl Add<Vector3> for Point3 {
446    type Output = Point3;
447
448    fn add(self, rhs: Vector3) -> Point3 {
449        Point3::new(self.x + rhs.x, self.y + rhs.y, self.z + rhs.z)
450    }
451}
452
453impl Sub<Vector3> for Point3 {
454    type Output = Point3;
455
456    fn sub(self, rhs: Vector3) -> Point3 {
457        Point3::new(self.x - rhs.x, self.y - rhs.y, self.z - rhs.z)
458    }
459}
460
461/// A 3D vector.
462#[derive(Debug, Clone, Copy, PartialEq)]
463#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
464pub struct Vector3 {
465    pub x: f64,
466    pub y: f64,
467    pub z: f64,
468}
469
470impl Vector3 {
471    /// Creates a new 3D vector.
472    #[inline]
473    pub fn new(x: f64, y: f64, z: f64) -> Self {
474        Self { x, y, z }
475    }
476
477    /// Zero vector.
478    pub const ZERO: Self = Self {
479        x: 0.0,
480        y: 0.0,
481        z: 0.0,
482    };
483
484    /// Unit vector along the X axis.
485    pub const UNIT_X: Self = Self {
486        x: 1.0,
487        y: 0.0,
488        z: 0.0,
489    };
490
491    /// Unit vector along the Y axis.
492    pub const UNIT_Y: Self = Self {
493        x: 0.0,
494        y: 1.0,
495        z: 0.0,
496    };
497
498    /// Unit vector along the Z axis.
499    pub const UNIT_Z: Self = Self {
500        x: 0.0,
501        y: 0.0,
502        z: 1.0,
503    };
504
505    /// Euclidean length.
506    #[inline]
507    pub fn length(&self) -> f64 {
508        self.length_sq().sqrt()
509    }
510
511    /// Squared length (avoids sqrt).
512    #[inline]
513    pub fn length_sq(&self) -> f64 {
514        self.x * self.x + self.y * self.y + self.z * self.z
515    }
516
517    /// Returns a normalized (unit-length) vector, or zero vector if length is ~0.
518    #[inline]
519    pub fn normalized(&self) -> Self {
520        let len = self.length();
521        if len < 1e-15 {
522            Self::ZERO
523        } else {
524            Self::new(self.x / len, self.y / len, self.z / len)
525        }
526    }
527
528    /// Cross product.
529    ///
530    /// Returns a vector perpendicular to both `self` and `other`,
531    /// following the right-hand rule.
532    ///
533    /// # Reference
534    /// Standard 3D cross product: `a × b = (a.y*b.z - a.z*b.y, a.z*b.x - a.x*b.z, a.x*b.y - a.y*b.x)`
535    #[inline]
536    pub fn cross(&self, other: &Self) -> Self {
537        Self::new(
538            self.y * other.z - self.z * other.y,
539            self.z * other.x - self.x * other.z,
540            self.x * other.y - self.y * other.x,
541        )
542    }
543
544    /// Dot product.
545    #[inline]
546    pub fn dot(&self, other: &Self) -> f64 {
547        self.x * other.x + self.y * other.y + self.z * other.z
548    }
549
550    /// Converts to an array `[x, y, z]`.
551    #[inline]
552    pub fn to_array(self) -> [f64; 3] {
553        [self.x, self.y, self.z]
554    }
555}
556
557impl Add for Vector3 {
558    type Output = Self;
559
560    fn add(self, rhs: Self) -> Self {
561        Self::new(self.x + rhs.x, self.y + rhs.y, self.z + rhs.z)
562    }
563}
564
565impl Sub for Vector3 {
566    type Output = Self;
567
568    fn sub(self, rhs: Self) -> Self {
569        Self::new(self.x - rhs.x, self.y - rhs.y, self.z - rhs.z)
570    }
571}
572
573impl Mul<f64> for Vector3 {
574    type Output = Self;
575
576    fn mul(self, rhs: f64) -> Self {
577        Self::new(self.x * rhs, self.y * rhs, self.z * rhs)
578    }
579}
580
581/// A 3D axis-aligned bounding box.
582///
583/// # Invariant
584/// `min.x <= max.x`, `min.y <= max.y`, `min.z <= max.z`.
585///
586/// # Reference
587/// Ericson (2005), "Real-Time Collision Detection", Ch. 4.2
588#[derive(Debug, Clone, Copy, PartialEq)]
589#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
590pub struct AABB3 {
591    /// Minimum corner.
592    pub min: Point3,
593    /// Maximum corner.
594    pub max: Point3,
595}
596
597impl AABB3 {
598    /// Creates an AABB from min/max coordinates.
599    pub fn new(min_x: f64, min_y: f64, min_z: f64, max_x: f64, max_y: f64, max_z: f64) -> Self {
600        Self {
601            min: Point3::new(min_x, min_y, min_z),
602            max: Point3::new(max_x, max_y, max_z),
603        }
604    }
605
606    /// Creates an AABB from min corner and dimensions.
607    pub fn from_min_size(min: Point3, width: f64, depth: f64, height: f64) -> Self {
608        Self {
609            min,
610            max: Point3::new(min.x + width, min.y + depth, min.z + height),
611        }
612    }
613
614    /// Creates an AABB enclosing a set of 3D points.
615    ///
616    /// Returns `None` if the slice is empty.
617    ///
618    /// # Complexity
619    /// O(n)
620    pub fn from_points(points: &[Point3]) -> Option<Self> {
621        let first = points.first()?;
622        let mut min_x = first.x;
623        let mut min_y = first.y;
624        let mut min_z = first.z;
625        let mut max_x = first.x;
626        let mut max_y = first.y;
627        let mut max_z = first.z;
628
629        for p in points.iter().skip(1) {
630            min_x = min_x.min(p.x);
631            min_y = min_y.min(p.y);
632            min_z = min_z.min(p.z);
633            max_x = max_x.max(p.x);
634            max_y = max_y.max(p.y);
635            max_z = max_z.max(p.z);
636        }
637
638        Some(Self::new(min_x, min_y, min_z, max_x, max_y, max_z))
639    }
640
641    /// Width (x extent).
642    #[inline]
643    pub fn width(&self) -> f64 {
644        self.max.x - self.min.x
645    }
646
647    /// Depth (y extent).
648    #[inline]
649    pub fn depth(&self) -> f64 {
650        self.max.y - self.min.y
651    }
652
653    /// Height (z extent).
654    #[inline]
655    pub fn height(&self) -> f64 {
656        self.max.z - self.min.z
657    }
658
659    /// Volume.
660    #[inline]
661    pub fn volume(&self) -> f64 {
662        self.width() * self.depth() * self.height()
663    }
664
665    /// Surface area.
666    ///
667    /// Useful as a BVH splitting heuristic (SAH).
668    #[inline]
669    pub fn surface_area(&self) -> f64 {
670        let w = self.width();
671        let d = self.depth();
672        let h = self.height();
673        2.0 * (w * d + w * h + d * h)
674    }
675
676    /// Center point.
677    #[inline]
678    pub fn center(&self) -> Point3 {
679        Point3::new(
680            (self.min.x + self.max.x) * 0.5,
681            (self.min.y + self.max.y) * 0.5,
682            (self.min.z + self.max.z) * 0.5,
683        )
684    }
685
686    /// Whether this AABB contains a point.
687    #[inline]
688    pub fn contains_point(&self, p: &Point3) -> bool {
689        p.x >= self.min.x
690            && p.x <= self.max.x
691            && p.y >= self.min.y
692            && p.y <= self.max.y
693            && p.z >= self.min.z
694            && p.z <= self.max.z
695    }
696
697    /// Whether this AABB fully contains another.
698    #[inline]
699    pub fn contains(&self, other: &Self) -> bool {
700        self.min.x <= other.min.x
701            && self.min.y <= other.min.y
702            && self.min.z <= other.min.z
703            && self.max.x >= other.max.x
704            && self.max.y >= other.max.y
705            && self.max.z >= other.max.z
706    }
707
708    /// Whether two AABBs intersect.
709    ///
710    /// # Complexity
711    /// O(1)
712    #[inline]
713    pub fn intersects(&self, other: &Self) -> bool {
714        self.min.x <= other.max.x
715            && self.max.x >= other.min.x
716            && self.min.y <= other.max.y
717            && self.max.y >= other.min.y
718            && self.min.z <= other.max.z
719            && self.max.z >= other.min.z
720    }
721
722    /// Returns the intersection of two AABBs, or `None` if they don't overlap.
723    pub fn intersection(&self, other: &Self) -> Option<Self> {
724        if !self.intersects(other) {
725            return None;
726        }
727        Some(Self::new(
728            self.min.x.max(other.min.x),
729            self.min.y.max(other.min.y),
730            self.min.z.max(other.min.z),
731            self.max.x.min(other.max.x),
732            self.max.y.min(other.max.y),
733            self.max.z.min(other.max.z),
734        ))
735    }
736
737    /// Returns the union (bounding box) of two AABBs.
738    pub fn union(&self, other: &Self) -> Self {
739        Self::new(
740            self.min.x.min(other.min.x),
741            self.min.y.min(other.min.y),
742            self.min.z.min(other.min.z),
743            self.max.x.max(other.max.x),
744            self.max.y.max(other.max.y),
745            self.max.z.max(other.max.z),
746        )
747    }
748
749    /// Returns a new AABB expanded by `margin` on all sides.
750    pub fn expand(&self, margin: f64) -> Self {
751        Self::new(
752            self.min.x - margin,
753            self.min.y - margin,
754            self.min.z - margin,
755            self.max.x + margin,
756            self.max.y + margin,
757            self.max.z + margin,
758        )
759    }
760}
761
762#[cfg(test)]
763mod tests {
764    use super::*;
765
766    #[test]
767    fn test_point2_distance() {
768        let a = Point2::new(0.0, 0.0);
769        let b = Point2::new(3.0, 4.0);
770        assert!((a.distance_to(&b) - 5.0).abs() < 1e-10);
771    }
772
773    #[test]
774    fn test_point2_sub_gives_vector() {
775        let a = Point2::new(3.0, 5.0);
776        let b = Point2::new(1.0, 2.0);
777        let v = a - b;
778        assert!((v.x - 2.0).abs() < 1e-10);
779        assert!((v.y - 3.0).abs() < 1e-10);
780    }
781
782    #[test]
783    fn test_point2_add_vector() {
784        let p = Point2::new(1.0, 2.0);
785        let v = Vector2::new(3.0, 4.0);
786        let q = p + v;
787        assert!((q.x - 4.0).abs() < 1e-10);
788        assert!((q.y - 6.0).abs() < 1e-10);
789    }
790
791    #[test]
792    fn test_vector2_operations() {
793        let v = Vector2::new(3.0, 4.0);
794        assert!((v.length() - 5.0).abs() < 1e-10);
795        assert!((v.length_sq() - 25.0).abs() < 1e-10);
796
797        let n = v.normalized();
798        assert!((n.length() - 1.0).abs() < 1e-10);
799    }
800
801    #[test]
802    fn test_vector2_cross() {
803        let a = Vector2::new(1.0, 0.0);
804        let b = Vector2::new(0.0, 1.0);
805        assert!((a.cross(&b) - 1.0).abs() < 1e-10);
806        assert!((b.cross(&a) - (-1.0)).abs() < 1e-10);
807    }
808
809    #[test]
810    fn test_vector2_dot() {
811        let a = Vector2::new(1.0, 2.0);
812        let b = Vector2::new(3.0, 4.0);
813        assert!((a.dot(&b) - 11.0).abs() < 1e-10);
814    }
815
816    #[test]
817    fn test_vector2_perp() {
818        let v = Vector2::new(1.0, 0.0);
819        let p = v.perp();
820        assert!((p.x - 0.0).abs() < 1e-10);
821        assert!((p.y - 1.0).abs() < 1e-10);
822    }
823
824    #[test]
825    fn test_vector2_normalized_zero() {
826        let v = Vector2::ZERO;
827        let n = v.normalized();
828        assert!((n.length()).abs() < 1e-10);
829    }
830
831    #[test]
832    fn test_segment2() {
833        let s = Segment2::new(Point2::new(0.0, 0.0), Point2::new(3.0, 4.0));
834        assert!((s.length() - 5.0).abs() < 1e-10);
835        let mid = s.midpoint();
836        assert!((mid.x - 1.5).abs() < 1e-10);
837        assert!((mid.y - 2.0).abs() < 1e-10);
838    }
839
840    #[test]
841    fn test_aabb2_from_points() {
842        let points = vec![
843            Point2::new(0.0, 0.0),
844            Point2::new(10.0, 5.0),
845            Point2::new(3.0, 8.0),
846        ];
847        let aabb = AABB2::from_points(&points).unwrap();
848        assert!((aabb.min.x - 0.0).abs() < 1e-10);
849        assert!((aabb.min.y - 0.0).abs() < 1e-10);
850        assert!((aabb.max.x - 10.0).abs() < 1e-10);
851        assert!((aabb.max.y - 8.0).abs() < 1e-10);
852    }
853
854    #[test]
855    fn test_aabb2_from_points_empty() {
856        assert!(AABB2::from_points(&[]).is_none());
857    }
858
859    #[test]
860    fn test_aabb2_area() {
861        let aabb = AABB2::new(0.0, 0.0, 10.0, 20.0);
862        assert!((aabb.area() - 200.0).abs() < 1e-10);
863    }
864
865    #[test]
866    fn test_aabb2_contains() {
867        let aabb = AABB2::new(0.0, 0.0, 10.0, 10.0);
868        assert!(aabb.contains_point(&Point2::new(5.0, 5.0)));
869        assert!(aabb.contains_point(&Point2::new(0.0, 0.0))); // on boundary
870        assert!(!aabb.contains_point(&Point2::new(11.0, 5.0)));
871    }
872
873    #[test]
874    fn test_aabb2_intersection() {
875        let a = AABB2::new(0.0, 0.0, 10.0, 10.0);
876        let b = AABB2::new(5.0, 5.0, 15.0, 15.0);
877        let int = a.intersection(&b).unwrap();
878        assert!((int.min.x - 5.0).abs() < 1e-10);
879        assert!((int.min.y - 5.0).abs() < 1e-10);
880        assert!((int.max.x - 10.0).abs() < 1e-10);
881        assert!((int.max.y - 10.0).abs() < 1e-10);
882
883        let c = AABB2::new(20.0, 20.0, 30.0, 30.0);
884        assert!(a.intersection(&c).is_none());
885    }
886
887    #[test]
888    fn test_aabb2_union() {
889        let a = AABB2::new(0.0, 0.0, 10.0, 10.0);
890        let b = AABB2::new(5.0, 5.0, 15.0, 15.0);
891        let u = a.union(&b);
892        assert!((u.min.x - 0.0).abs() < 1e-10);
893        assert!((u.min.y - 0.0).abs() < 1e-10);
894        assert!((u.max.x - 15.0).abs() < 1e-10);
895        assert!((u.max.y - 15.0).abs() < 1e-10);
896    }
897
898    #[test]
899    fn test_aabb2_expand() {
900        let aabb = AABB2::new(5.0, 5.0, 10.0, 10.0);
901        let expanded = aabb.expand(1.0);
902        assert!((expanded.min.x - 4.0).abs() < 1e-10);
903        assert!((expanded.min.y - 4.0).abs() < 1e-10);
904        assert!((expanded.max.x - 11.0).abs() < 1e-10);
905        assert!((expanded.max.y - 11.0).abs() < 1e-10);
906    }
907
908    #[test]
909    fn test_point2_from_tuple() {
910        let p: Point2 = (3.0, 4.0).into();
911        assert!((p.x - 3.0).abs() < 1e-10);
912        assert!((p.y - 4.0).abs() < 1e-10);
913
914        let t: (f64, f64) = p.into();
915        assert!((t.0 - 3.0).abs() < 1e-10);
916    }
917
918    // ======================== 3D Tests ========================
919
920    #[test]
921    fn test_point3_distance() {
922        let a = Point3::new(0.0, 0.0, 0.0);
923        let b = Point3::new(1.0, 2.0, 2.0);
924        assert!((a.distance_to(&b) - 3.0).abs() < 1e-10);
925    }
926
927    #[test]
928    fn test_point3_sub_gives_vector() {
929        let a = Point3::new(3.0, 5.0, 7.0);
930        let b = Point3::new(1.0, 2.0, 3.0);
931        let v = a - b;
932        assert!((v.x - 2.0).abs() < 1e-10);
933        assert!((v.y - 3.0).abs() < 1e-10);
934        assert!((v.z - 4.0).abs() < 1e-10);
935    }
936
937    #[test]
938    fn test_point3_add_vector() {
939        let p = Point3::new(1.0, 2.0, 3.0);
940        let v = Vector3::new(4.0, 5.0, 6.0);
941        let q = p + v;
942        assert!((q.x - 5.0).abs() < 1e-10);
943        assert!((q.y - 7.0).abs() < 1e-10);
944        assert!((q.z - 9.0).abs() < 1e-10);
945    }
946
947    #[test]
948    fn test_point3_sub_vector() {
949        let p = Point3::new(5.0, 7.0, 9.0);
950        let v = Vector3::new(1.0, 2.0, 3.0);
951        let q = p - v;
952        assert!((q.x - 4.0).abs() < 1e-10);
953        assert!((q.y - 5.0).abs() < 1e-10);
954        assert!((q.z - 6.0).abs() < 1e-10);
955    }
956
957    #[test]
958    fn test_point3_conversions() {
959        let p = Point3::new(1.0, 2.0, 3.0);
960        let t: (f64, f64, f64) = p.into();
961        assert!((t.0 - 1.0).abs() < 1e-10);
962        assert!((t.1 - 2.0).abs() < 1e-10);
963        assert!((t.2 - 3.0).abs() < 1e-10);
964
965        let p2: Point3 = (4.0, 5.0, 6.0).into();
966        assert!((p2.x - 4.0).abs() < 1e-10);
967
968        let a = p.to_array();
969        assert!((a[0] - 1.0).abs() < 1e-10);
970        let p3 = Point3::from_array([7.0, 8.0, 9.0]);
971        assert!((p3.z - 9.0).abs() < 1e-10);
972    }
973
974    #[test]
975    fn test_vector3_operations() {
976        let v = Vector3::new(1.0, 2.0, 2.0);
977        assert!((v.length() - 3.0).abs() < 1e-10);
978        assert!((v.length_sq() - 9.0).abs() < 1e-10);
979
980        let n = v.normalized();
981        assert!((n.length() - 1.0).abs() < 1e-10);
982    }
983
984    #[test]
985    fn test_vector3_cross() {
986        let x = Vector3::UNIT_X;
987        let y = Vector3::UNIT_Y;
988        let z = x.cross(&y);
989        assert!((z.x - 0.0).abs() < 1e-10);
990        assert!((z.y - 0.0).abs() < 1e-10);
991        assert!((z.z - 1.0).abs() < 1e-10);
992
993        // Anti-commutativity
994        let neg_z = y.cross(&x);
995        assert!((neg_z.z - (-1.0)).abs() < 1e-10);
996    }
997
998    #[test]
999    fn test_vector3_dot() {
1000        let a = Vector3::new(1.0, 2.0, 3.0);
1001        let b = Vector3::new(4.0, 5.0, 6.0);
1002        assert!((a.dot(&b) - 32.0).abs() < 1e-10); // 4+10+18
1003    }
1004
1005    #[test]
1006    fn test_vector3_normalized_zero() {
1007        let v = Vector3::ZERO;
1008        let n = v.normalized();
1009        assert!(n.length() < 1e-10);
1010    }
1011
1012    #[test]
1013    fn test_vector3_arithmetic() {
1014        let a = Vector3::new(1.0, 2.0, 3.0);
1015        let b = Vector3::new(4.0, 5.0, 6.0);
1016        let sum = a + b;
1017        assert!((sum.x - 5.0).abs() < 1e-10);
1018        let diff = b - a;
1019        assert!((diff.x - 3.0).abs() < 1e-10);
1020        let scaled = a * 2.0;
1021        assert!((scaled.z - 6.0).abs() < 1e-10);
1022    }
1023
1024    #[test]
1025    fn test_aabb3_basics() {
1026        let aabb = AABB3::new(0.0, 0.0, 0.0, 10.0, 20.0, 30.0);
1027        assert!((aabb.width() - 10.0).abs() < 1e-10);
1028        assert!((aabb.depth() - 20.0).abs() < 1e-10);
1029        assert!((aabb.height() - 30.0).abs() < 1e-10);
1030        assert!((aabb.volume() - 6000.0).abs() < 1e-10);
1031        assert!((aabb.surface_area() - 2200.0).abs() < 1e-10); // 2*(200+300+600)
1032    }
1033
1034    #[test]
1035    fn test_aabb3_from_min_size() {
1036        let aabb = AABB3::from_min_size(Point3::new(1.0, 2.0, 3.0), 4.0, 5.0, 6.0);
1037        assert!((aabb.max.x - 5.0).abs() < 1e-10);
1038        assert!((aabb.max.y - 7.0).abs() < 1e-10);
1039        assert!((aabb.max.z - 9.0).abs() < 1e-10);
1040    }
1041
1042    #[test]
1043    fn test_aabb3_from_points() {
1044        let points = vec![
1045            Point3::new(0.0, 0.0, 0.0),
1046            Point3::new(10.0, 5.0, 3.0),
1047            Point3::new(3.0, 8.0, 7.0),
1048        ];
1049        let aabb = AABB3::from_points(&points).unwrap();
1050        assert!((aabb.min.x - 0.0).abs() < 1e-10);
1051        assert!((aabb.max.y - 8.0).abs() < 1e-10);
1052        assert!((aabb.max.z - 7.0).abs() < 1e-10);
1053    }
1054
1055    #[test]
1056    fn test_aabb3_from_points_empty() {
1057        assert!(AABB3::from_points(&[]).is_none());
1058    }
1059
1060    #[test]
1061    fn test_aabb3_contains_point() {
1062        let aabb = AABB3::new(0.0, 0.0, 0.0, 10.0, 10.0, 10.0);
1063        assert!(aabb.contains_point(&Point3::new(5.0, 5.0, 5.0)));
1064        assert!(aabb.contains_point(&Point3::new(0.0, 0.0, 0.0))); // on boundary
1065        assert!(!aabb.contains_point(&Point3::new(11.0, 5.0, 5.0)));
1066    }
1067
1068    #[test]
1069    fn test_aabb3_contains_aabb() {
1070        let outer = AABB3::new(0.0, 0.0, 0.0, 10.0, 10.0, 10.0);
1071        let inner = AABB3::new(2.0, 2.0, 2.0, 8.0, 8.0, 8.0);
1072        assert!(outer.contains(&inner));
1073        assert!(!inner.contains(&outer));
1074    }
1075
1076    #[test]
1077    fn test_aabb3_intersects() {
1078        let a = AABB3::new(0.0, 0.0, 0.0, 10.0, 10.0, 10.0);
1079        let b = AABB3::new(5.0, 5.0, 5.0, 15.0, 15.0, 15.0);
1080        assert!(a.intersects(&b));
1081
1082        let c = AABB3::new(20.0, 20.0, 20.0, 30.0, 30.0, 30.0);
1083        assert!(!a.intersects(&c));
1084    }
1085
1086    #[test]
1087    fn test_aabb3_intersection() {
1088        let a = AABB3::new(0.0, 0.0, 0.0, 10.0, 10.0, 10.0);
1089        let b = AABB3::new(5.0, 5.0, 5.0, 15.0, 15.0, 15.0);
1090        let int = a.intersection(&b).unwrap();
1091        assert!((int.min.x - 5.0).abs() < 1e-10);
1092        assert!((int.max.z - 10.0).abs() < 1e-10);
1093        assert!((int.volume() - 125.0).abs() < 1e-10); // 5*5*5
1094
1095        let c = AABB3::new(20.0, 20.0, 20.0, 30.0, 30.0, 30.0);
1096        assert!(a.intersection(&c).is_none());
1097    }
1098
1099    #[test]
1100    fn test_aabb3_union() {
1101        let a = AABB3::new(0.0, 0.0, 0.0, 10.0, 10.0, 10.0);
1102        let b = AABB3::new(5.0, 5.0, 5.0, 15.0, 15.0, 15.0);
1103        let u = a.union(&b);
1104        assert!((u.min.x - 0.0).abs() < 1e-10);
1105        assert!((u.max.x - 15.0).abs() < 1e-10);
1106        assert!((u.volume() - 3375.0).abs() < 1e-10); // 15*15*15
1107    }
1108
1109    #[test]
1110    fn test_aabb3_expand() {
1111        let aabb = AABB3::new(5.0, 5.0, 5.0, 10.0, 10.0, 10.0);
1112        let expanded = aabb.expand(1.0);
1113        assert!((expanded.min.x - 4.0).abs() < 1e-10);
1114        assert!((expanded.max.z - 11.0).abs() < 1e-10);
1115    }
1116
1117    #[test]
1118    fn test_aabb3_center() {
1119        let aabb = AABB3::new(0.0, 0.0, 0.0, 10.0, 20.0, 30.0);
1120        let c = aabb.center();
1121        assert!((c.x - 5.0).abs() < 1e-10);
1122        assert!((c.y - 10.0).abs() < 1e-10);
1123        assert!((c.z - 15.0).abs() < 1e-10);
1124    }
1125
1126    // ======================== Serde Tests ========================
1127
1128    #[cfg(feature = "serde")]
1129    mod serde_tests {
1130        use super::*;
1131
1132        #[test]
1133        fn test_point2_roundtrip() {
1134            let p = Point2::new(3.14, 2.72);
1135            let json = serde_json::to_string(&p).unwrap();
1136            let p2: Point2 = serde_json::from_str(&json).unwrap();
1137            assert_eq!(p, p2);
1138        }
1139
1140        #[test]
1141        fn test_vector2_roundtrip() {
1142            let v = Vector2::new(-1.0, 5.5);
1143            let json = serde_json::to_string(&v).unwrap();
1144            let v2: Vector2 = serde_json::from_str(&json).unwrap();
1145            assert_eq!(v, v2);
1146        }
1147
1148        #[test]
1149        fn test_segment2_roundtrip() {
1150            let s = Segment2::new(Point2::new(0.0, 0.0), Point2::new(10.0, 20.0));
1151            let json = serde_json::to_string(&s).unwrap();
1152            let s2: Segment2 = serde_json::from_str(&json).unwrap();
1153            assert_eq!(s, s2);
1154        }
1155
1156        #[test]
1157        fn test_aabb2_roundtrip() {
1158            let aabb = AABB2::new(1.0, 2.0, 10.0, 20.0);
1159            let json = serde_json::to_string(&aabb).unwrap();
1160            let aabb2: AABB2 = serde_json::from_str(&json).unwrap();
1161            assert_eq!(aabb, aabb2);
1162        }
1163
1164        #[test]
1165        fn test_point3_roundtrip() {
1166            let p = Point3::new(1.0, 2.0, 3.0);
1167            let json = serde_json::to_string(&p).unwrap();
1168            let p2: Point3 = serde_json::from_str(&json).unwrap();
1169            assert_eq!(p, p2);
1170        }
1171
1172        #[test]
1173        fn test_vector3_roundtrip() {
1174            let v = Vector3::new(4.0, 5.0, 6.0);
1175            let json = serde_json::to_string(&v).unwrap();
1176            let v2: Vector3 = serde_json::from_str(&json).unwrap();
1177            assert_eq!(v, v2);
1178        }
1179
1180        #[test]
1181        fn test_aabb3_roundtrip() {
1182            let aabb = AABB3::new(0.0, 0.0, 0.0, 10.0, 20.0, 30.0);
1183            let json = serde_json::to_string(&aabb).unwrap();
1184            let aabb2: AABB3 = serde_json::from_str(&json).unwrap();
1185            assert_eq!(aabb, aabb2);
1186        }
1187    }
1188}