rs_math3d/
primitives.rs

1// Copyright 2020-Present (c) Raja Lehtihet & Wael El Oraiby
2//
3// Redistribution and use in source and binary forms, with or without
4// modification, are permitted provided that the following conditions are met:
5//
6// 1. Redistributions of source code must retain the above copyright notice,
7// this list of conditions and the following disclaimer.
8//
9// 2. Redistributions in binary form must reproduce the above copyright notice,
10// this list of conditions and the following disclaimer in the documentation
11// and/or other materials provided with the distribution.
12//
13// 3. Neither the name of the copyright holder nor the names of its contributors
14// may be used to endorse or promote products derived from this software without
15// specific prior written permission.
16//
17// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27// POSSIBILITY OF SUCH DAMAGE.
28
29//! Geometric primitives for 3D graphics and collision detection.
30//!
31//! This module provides common geometric shapes and primitives used in
32//! 3D graphics, physics simulations, and collision detection systems.
33//!
34//! # Examples
35//!
36//! ```
37//! use rs_math3d::primitives::{Ray, Plane, Tri3};
38//! use rs_math3d::vector::Vector3;
39//! use rs_math3d::EPS_F32;
40//! 
41//! // Create a ray from origin pointing along +X axis
42//! let ray = Ray::new(
43//!     &Vector3::new(0.0f32, 0.0, 0.0),
44//!     &Vector3::new(1.0, 0.0, 0.0),
45//!     EPS_F32,
46//! ).unwrap();
47//! 
48//! // Create a plane at z=5 facing down
49//! let plane = Plane::new(
50//!     &Vector3::new(0.0f32, 0.0, -1.0),
51//!     &Vector3::new(0.0, 0.0, 5.0)
52//! );
53//! ```
54
55use crate::matrix::*;
56use crate::scalar::*;
57use crate::vector::*;
58use num_traits::{Zero, One};
59
60/// A 2D dimension with width and height.
61#[repr(C)]
62#[derive(Debug, Clone, Copy, Default)]
63pub struct Dimension<T: Scalar> {
64    /// Width component.
65    pub width: T,
66    /// Height component.
67    pub height: T,
68}
69
70impl<T: Scalar> Dimension<T> {
71    /// Creates a new dimension with the specified width and height.
72    pub fn new(width: T, height: T) -> Self {
73        Self { width, height }
74    }
75}
76
77/// A 2D axis-aligned rectangle.
78///
79/// Defined by its top-left corner position (x, y) and dimensions (width, height).
80#[repr(C)]
81#[derive(Debug, Clone, Copy, Default)]
82pub struct Rect<T: Scalar> {
83    /// X coordinate of the top-left corner.
84    pub x: T,
85    /// Y coordinate of the top-left corner.
86    pub y: T,
87    /// Rectangle width.
88    pub width: T,
89    /// Rectangle height.
90    pub height: T,
91}
92
93impl<T: Scalar> Rect<T> {
94    /// Creates a new rectangle from position and dimensions.
95    pub fn new(x: T, y: T, width: T, height: T) -> Self {
96        Self {
97            x,
98            y,
99            width,
100            height,
101        }
102    }
103    /// Creates a rectangle from two corner points.
104    ///
105    /// The points don't need to be min/max - they will be sorted.
106    pub fn from(min_vec: &Vector2<T>, max_vec: &Vector2<T>) -> Self {
107        let min_x = T::min(min_vec.x, max_vec.x);
108        let min_y = T::min(min_vec.y, max_vec.y);
109        let max_x = T::max(min_vec.x, max_vec.x);
110        let max_y = T::max(min_vec.y, max_vec.y);
111        Self {
112            x: min_x,
113            y: min_y,
114            width: max_x - min_x,
115            height: max_y - min_y,
116        }
117    }
118
119    /// Returns the minimum corner (top-left) of the rectangle.
120    pub fn min(&self) -> Vector2<T> {
121        Vector2::new(self.x, self.y)
122    }
123    /// Returns the maximum corner (bottom-right) of the rectangle.
124    pub fn max(&self) -> Vector2<T> {
125        Vector2::new(self.x + self.width, self.y + self.height)
126    }
127    /// Computes the intersection of two rectangles.
128    ///
129    /// Returns `None` if the rectangles don't overlap.
130    pub fn intersect(&self, other: &Self) -> Option<Self> {
131        let smx = self.max();
132        let smn = self.min();
133        let omx = other.max();
134        let omn = other.min();
135
136        if smx.x < omn.x || smx.y < omn.y || smn.x > omx.x || smn.y > omx.y {
137            return None;
138        }
139
140        let min_vec = Vector2::max(&self.min(), &other.min());
141        let max_vec = Vector2::min(&self.max(), &other.max());
142        Some(Self::from(&min_vec, &max_vec))
143    }
144
145    /// Checks if a point is inside the rectangle.
146    pub fn contains(&self, p: &Vector2<T>) -> bool {
147        p.x >= self.x && p.y >= self.y && p.x <= self.x + self.width && p.y <= self.y + self.height
148    }
149}
150
151/// A 3D axis-aligned bounding box (AABB).
152///
153/// Defined by its minimum and maximum corners.
154#[repr(C)]
155#[derive(Debug, Clone, Copy, Default)]
156pub struct Box3<T: Scalar> {
157    /// Minimum corner.
158    pub min: Vector3<T>,
159    /// Maximum corner.
160    pub max: Vector3<T>,
161}
162
163impl<T: Scalar> Box3<T> {
164    /// Creates a new box from two corner points.
165    ///
166    /// The points are automatically sorted to find min/max.
167    pub fn new(v0: &Vector3<T>, v1: &Vector3<T>) -> Self {
168        Self {
169            min: Vector3::min(v0, v1),
170            max: Vector3::max(v0, v1),
171        }
172    }
173    /// Checks if two boxes overlap.
174    pub fn overlap(&self, other: &Self) -> bool {
175        if self.max.x < other.min.x {
176            return false;
177        }
178        if self.max.y < other.min.y {
179            return false;
180        }
181        if self.max.z < other.min.z {
182            return false;
183        }
184
185        if self.min.x > other.max.x {
186            return false;
187        }
188        if self.min.y > other.max.y {
189            return false;
190        }
191        if self.min.z > other.max.z {
192            return false;
193        }
194        return true;
195    }
196
197    /// Expands the box to include a point.
198    pub fn add(&self, p: &Vector3<T>) -> Self {
199        Self {
200            min: Vector3::min(&p, &self.min),
201            max: Vector3::max(&p, &self.max),
202        }
203    }
204}
205
206impl<T: FloatScalar> Box3<T> {
207    /// Returns the center point of the box.
208    pub fn center(&self) -> Vector3<T> {
209        (self.max + self.min) * T::half()
210    }
211    /// Returns the half-extents of the box from center to max.
212    pub fn extent(&self) -> Vector3<T> {
213        self.max - self.center()
214    }
215
216    /// Subdivides the box into 8 octants.
217    ///
218    /// Returns an array of 8 boxes representing the octree subdivision.
219    pub fn subdivide(&self) -> [Self; 8] {
220        let cube_table: [Vector3<i32>; 8] = [
221            Vector3::new(0, 1, 0),
222            Vector3::new(1, 1, 0),
223            Vector3::new(1, 1, 1),
224            Vector3::new(0, 1, 1),
225            Vector3::new(0, 0, 0),
226            Vector3::new(1, 0, 0),
227            Vector3::new(1, 0, 1),
228            Vector3::new(0, 0, 1),
229        ];
230
231        let ps: [Vector3<T>; 2] = [self.min, self.max];
232        let mut vs = [Vector3::zero(); 8];
233        for i in 0..8 {
234            vs[i] = Vector3::new(
235                ps[cube_table[i].x as usize].x,
236                ps[cube_table[i].y as usize].y,
237                ps[cube_table[i].z as usize].z,
238            );
239        }
240
241        let c = self.center();
242        let mut out = [Box3 {
243            min: Vector3::zero(),
244            max: Vector3::zero(),
245        }; 8];
246        for i in 0..8 {
247            out[i] = Self::new(&Vector3::min(&c, &vs[i]), &Vector3::max(&c, &vs[i]));
248        }
249        out
250    }
251}
252
253/// An infinite line in 2D or 3D space.
254///
255/// Defined by a point on the line and a direction vector.
256#[repr(C)]
257#[derive(Debug, Clone, Copy, Default)]
258pub struct Line<T: Scalar, V: Vector<T>> {
259    /// Point on the line.
260    pub p: V,
261    /// Direction of the line.
262    pub d: V,
263    t: core::marker::PhantomData<T>,
264}
265
266impl<T: Scalar, V: Vector<T>> Line<T, V> {
267    /// Creates a new line from a point and direction.
268    ///
269    /// Returns `None` if the direction is too small.
270    pub fn new(p: &V, d: &V, epsilon: T) -> Option<Self> {
271        let d_ss = V::dot(d, d);
272        if d_ss <= epsilon * epsilon {
273            return None;
274        }
275        Some(Self {
276            p: *p,
277            d: *d,
278            t: core::marker::PhantomData,
279        })
280    }
281    /// Creates a line from two points.
282    ///
283    /// The line passes through both points, with direction from s to e.
284    ///
285    /// Returns `None` if the points are too close.
286    pub fn from_start_end(s: &V, e: &V, epsilon: T) -> Option<Self> {
287        let dir = *e - *s;
288        Self::new(s, &dir, epsilon)
289    }
290    /// Finds the closest point on the line to a given point.
291    ///
292    /// Returns:
293    /// - `t`: Parameter value where the closest point occurs
294    /// - Point: The closest point on the line
295    ///
296    /// Returns `None` if the line direction is too small.
297    pub fn closest_point_on_line(&self, p: &V, epsilon: T) -> Option<(T, V)> {
298        let p_dir = *p - self.p;
299
300        let d_sp = V::dot(&self.d, &p_dir);
301        let d_ss = V::dot(&self.d, &self.d);
302
303        if d_ss <= epsilon * epsilon {
304            return None;
305        }
306
307        let t = d_sp / d_ss;
308
309        Some((t, self.p + self.d * t))
310    }
311}
312
313impl<T: FloatScalar, V: FloatVector<T>> Line<T, V> {
314    /// Returns a line with normalized direction vector.
315    ///
316    /// Returns `None` if the direction is too small.
317    pub fn normalize(&self, epsilon: T) -> Option<Self> {
318        let d_ss = V::dot(&self.d, &self.d);
319        if d_ss <= epsilon * epsilon {
320            return None;
321        }
322        let inv_len = <T as One>::one() / d_ss.tsqrt();
323        Some(Self {
324            p: self.p,
325            d: self.d * inv_len,
326            t: core::marker::PhantomData,
327        })
328    }
329}
330
331/// Finds the shortest segment connecting two 3D lines.
332///
333/// Returns `None` if the lines are parallel (within epsilon tolerance).
334pub fn shortest_segment3d_between_lines3d<T: FloatScalar>(
335    line0: &Line<T, Vector3<T>>,
336    line1: &Line<T, Vector3<T>>,
337    epsilon: T,
338) -> Option<Segment<T, Vector3<T>>> {
339    let s0 = line0.p;
340    let s1 = line1.p;
341
342    let d1 = line1.d;
343    let d0 = line0.d;
344
345    let eps_sq = epsilon * epsilon;
346    let d0_len_sq = Vector3::dot(&d0, &d0);
347    let d1_len_sq = Vector3::dot(&d1, &d1);
348    if d0_len_sq <= eps_sq || d1_len_sq <= eps_sq {
349        return None;
350    }
351
352    let cross = Vector3::cross(&d1, &d0);
353    let cross_len_sq = Vector3::dot(&cross, &cross);
354    if cross_len_sq <= eps_sq {
355        return None;
356    }
357
358    let normal = Vector3::normalize(&cross);
359    let n0 = Vector3::normalize(&Vector3::cross(&normal, &d0));
360    let n1 = Vector3::normalize(&Vector3::cross(&normal, &d1));
361
362    let plane0 = Plane::new(&n0, &s0);
363    let plane1 = Plane::new(&n1, &s1);
364
365    let p1 = plane0.intersect_line(line1, epsilon);
366    let p0 = plane1.intersect_line(line0, epsilon);
367
368    match (p0, p1) {
369        (Some((_, s)), Some((_, e))) => Some(Segment::new(&s, &e)),
370        _ => None,
371    }
372}
373
374/// A line segment with defined start and end points.
375#[repr(C)]
376#[derive(Clone, Copy, Default)]
377pub struct Segment<T: Scalar, V: Vector<T>> {
378    /// Start point.
379    pub s: V,
380    /// End point.
381    pub e: V,
382    t: core::marker::PhantomData<T>,
383}
384
385impl<T: Scalar, V: Vector<T>> Segment<T, V> {
386    /// Creates a new segment from start to end point.
387    pub fn new(s: &V, e: &V) -> Self {
388        Self {
389            s: *s,
390            e: *e,
391            t: core::marker::PhantomData,
392        }
393    }
394    /// Finds the closest point on the segment to a given point.
395    ///
396    /// Returns:
397    /// - `t`: Parameter value \[0,1\] where 0=start, 1=end
398    /// - Point: The closest point on the segment
399    ///
400    /// Returns `None` if the segment is too small.
401    pub fn closest_point_on_segment(&self, p: &V, epsilon: T) -> Option<(T, V)> {
402        let dir = self.e - self.s;
403        let p_dir = *p - self.s;
404
405        let d_sp = V::dot(&dir, &p_dir);
406        let d_ss = V::dot(&dir, &dir);
407
408        if d_ss <= epsilon * epsilon {
409            return None;
410        }
411
412        if d_sp < <T as Zero>::zero() {
413            return Some((<T as Zero>::zero(), self.s));
414        } else if d_sp > d_ss {
415            return Some((<T as One>::one(), self.e));
416        }
417
418        let t = d_sp / d_ss;
419
420        Some((t, self.s + dir * t))
421    }
422}
423
424impl<T: FloatScalar, V: FloatVector<T>> Segment<T, V> {
425    /// Computes the distance from a point to the segment.
426    ///
427    /// Returns `None` if the segment is too small.
428    pub fn distance(&self, p: &V, epsilon: T) -> Option<T> {
429        self.closest_point_on_segment(p, epsilon)
430            .map(|(_, p_on_seg)| V::length(&(p_on_seg - *p)))
431    }
432}
433
434/// A ray with an origin and direction.
435///
436/// Commonly used for ray casting and intersection tests.
437#[repr(C)]
438#[derive(Clone, Copy, Default)]
439pub struct Ray<T: Scalar, V: Vector<T>> {
440    /// Ray origin.
441    pub start: V,
442    /// Ray direction (typically normalized).
443    pub direction: V,
444    t: core::marker::PhantomData<T>,
445}
446
447impl<T: FloatScalar, V: FloatVector<T>> Ray<T, V> {
448    /// Creates a new ray with normalized direction.
449    ///
450    /// Returns `None` if the direction is too small.
451    pub fn new(start: &V, direction: &V, epsilon: T) -> Option<Self> {
452        let d_ss = V::dot(direction, direction);
453        if d_ss <= epsilon * epsilon {
454            return None;
455        }
456        let inv_len = <T as One>::one() / d_ss.tsqrt();
457        Some(Self {
458            start: *start,
459            direction: *direction * inv_len,
460            t: core::marker::PhantomData,
461        })
462    }
463}
464
465impl<T: Scalar> Ray<T, Vector3<T>> {
466    /// Computes ray-plane intersection.
467    ///
468    /// Returns the intersection point, or `None` if the ray doesn't hit the plane
469    /// (parallel or pointing away).
470    ///
471    /// Returns `None` if the ray is parallel to the plane within `epsilon`.
472    pub fn intersect_plane(&self, p: &Plane<T>, epsilon: T) -> Option<Vector3<T>> {
473        let n = p.normal();
474        let denom = Vector3::dot(&n, &self.direction);
475        if denom.tabs() <= epsilon {
476            return None;
477        }
478        let t: T = -(p.d + Vector3::dot(&n, &self.start)) / denom;
479        if t < <T as Zero>::zero() {
480            None
481        } else {
482            Some(self.direction * t + self.start)
483        }
484    }
485}
486
487////////////////////////////////////////////////////////////////////////////////
488/// A sphere defined by center and radius.
489////////////////////////////////////////////////////////////////////////////////
490#[repr(C)]
491#[derive(Clone, Copy, Default)]
492pub struct Sphere3<T: FloatScalar> {
493    /// Sphere center.
494    pub center: Vector3<T>,
495    /// Sphere radius.
496    pub radius: T,
497}
498
499impl<T: FloatScalar> Sphere3<T> {
500    /// Creates a sphere from center and radius.
501    pub fn new(center: Vector3<T>, radius: T) -> Self {
502        Self {
503            center: center,
504            radius: radius,
505        }
506    }
507}
508
509////////////////////////////////////////////////////////////////////////////////
510/// A triangle defined by three vertices.
511////////////////////////////////////////////////////////////////////////////////
512#[repr(C)]
513#[derive(Clone, Copy, Default)]
514pub struct Tri3<T: FloatScalar> {
515    vertices: [Vector3<T>; 3],
516}
517
518impl<T: FloatScalar> Tri3<T> {
519    /// Creates a triangle from three vertices.
520    pub fn new(vertices: [Vector3<T>; 3]) -> Self {
521        Self { vertices: vertices }
522    }
523    /// Returns the triangle vertices.
524    pub fn vertices(&self) -> &[Vector3<T>; 3] {
525        &self.vertices
526    }
527
528    /// Computes barycentric coordinates of a point in the triangle plane.
529    pub fn barycentric_coordinates(&self, pt: &Vector3<T>) -> Vector3<T> {
530        let v0 = self.vertices[0];
531        let v1 = self.vertices[1];
532        let v2 = self.vertices[2];
533        let e0 = v1 - v0;
534        let e1 = v2 - v0;
535        let vp = *pt - v0;
536
537        let d00 = Vector3::dot(&e0, &e0);
538        let d01 = Vector3::dot(&e0, &e1);
539        let d11 = Vector3::dot(&e1, &e1);
540        let d20 = Vector3::dot(&vp, &e0);
541        let d21 = Vector3::dot(&vp, &e1);
542        let denom = d00 * d11 - d01 * d01;
543        let v = (d11 * d20 - d01 * d21) / denom;
544        let w = (d00 * d21 - d01 * d20) / denom;
545        let u = <T as One>::one() - v - w;
546        Vector3::new(u, v, w)
547    }
548}
549
550////////////////////////////////////////////////////////////////////////////////
551/// A plane defined by ax + by + cz + d = 0.
552////////////////////////////////////////////////////////////////////////////////
553#[repr(C)]
554#[derive(Clone, Copy, Default)]
555pub struct Plane<T: Scalar> {
556    a: T,
557    b: T,
558    c: T,
559    d: T,
560}
561
562impl<T: Scalar> Plane<T> {
563    /// Returns the plane normal vector.
564    pub fn normal(&self) -> Vector3<T> {
565        Vector3::new(self.a, self.b, self.c)
566    }
567    /// Returns the plane constant term (d).
568    pub fn constant(&self) -> T {
569        self.d
570    }
571
572    /// Intersects the plane with a ray.
573    pub fn intersect_ray(&self, r: &Ray<T, Vector3<T>>, epsilon: T) -> Option<Vector3<T>> {
574        r.intersect_plane(self, epsilon)
575    }
576
577    /// Computes line-plane intersection.
578    ///
579    /// Returns the parameter t and intersection point, or `None` if parallel.
580    pub fn intersect_line(
581        &self,
582        line: &Line<T, Vector3<T>>,
583        epsilon: T,
584    ) -> Option<(T, Vector3<T>)> {
585        let s = line.p;
586        let dir = line.d;
587        let n = self.normal();
588
589        let denom = Vector3::dot(&n, &dir);
590        if denom.tabs() < epsilon {
591            None
592        } else {
593            let t = -(self.constant() + Vector3::dot(&n, &s)) / denom;
594            Some((t, dir * t + s))
595        }
596    }
597}
598
599impl<T: FloatScalar> Plane<T> {
600    /// Creates a plane from a normal and a point on the plane.
601    pub fn new(n: &Vector3<T>, p: &Vector3<T>) -> Self {
602        let norm = Vector3::normalize(n);
603        let d = Vector3::dot(&norm, p);
604        Self {
605            a: norm.x,
606            b: norm.y,
607            c: norm.z,
608            d: -d,
609        }
610    }
611
612    /// Creates a plane from triangle vertices.
613    pub fn from_tri(v0: &Vector3<T>, v1: &Vector3<T>, v2: &Vector3<T>) -> Self {
614        let n = tri_normal(v0, v1, v2);
615        Self::new(&n, v0)
616    }
617    /// Creates a plane from quad vertices.
618    pub fn from_quad(v0: &Vector3<T>, v1: &Vector3<T>, v2: &Vector3<T>, v3: &Vector3<T>) -> Self {
619        let n = quad_normal(v0, v1, v2, v3);
620        let c = (*v0 + *v1 + *v2 + *v3) * T::quarter();
621        Self::new(&n, &c)
622    }
623}
624
625////////////////////////////////////////////////////////////////////////////////
626/// A parametric plane defined by center and axis vectors.
627////////////////////////////////////////////////////////////////////////////////
628#[repr(C)]
629#[derive(Clone, Copy, Default)]
630pub struct ParametricPlane<T: Scalar> {
631    /// Plane center point.
632    pub center: Vector3<T>,
633    /// Plane X axis direction.
634    pub x_axis: Vector3<T>,
635    /// Plane Y axis direction.
636    pub y_axis: Vector3<T>,
637}
638
639impl<T: FloatScalar> ParametricPlane<T> {
640    /// Creates a parametric plane from center and axes.
641    pub fn new(center: &Vector3<T>, x_axis: &Vector3<T>, y_axis: &Vector3<T>) -> Self {
642        Self {
643            center: *center,
644            x_axis: *x_axis,
645            y_axis: *y_axis,
646        }
647    }
648
649    /// Converts the parametric plane to an infinite plane.
650    pub fn plane(&self) -> Plane<T> {
651        Plane::new(&self.normal(), &self.center)
652    }
653
654    /// Computes the normal vector (cross product of axes).
655    pub fn normal(&self) -> Vector3<T> {
656        Vector3::cross(&self.x_axis, &self.y_axis).normalize()
657    }
658
659    /// Intersects the plane with a ray.
660    pub fn intersect_ray(&self, r: &Ray<T, Vector3<T>>, epsilon: T) -> Option<Vector3<T>> {
661        r.intersect_plane(&self.plane(), epsilon)
662    }
663
664    /// Intersects the plane with a line.
665    pub fn intersect_line(
666        &self,
667        line: &Line<T, Vector3<T>>,
668        epsilon: T,
669    ) -> Option<(T, Vector3<T>)> {
670        self.plane().intersect_line(line, epsilon)
671    }
672
673    /// Projects a 3D point onto the plane's 2D coordinates.
674    pub fn project(&self, v: &Vector3<T>) -> Vector2<T> {
675        let p = *v - self.center;
676        let x_coord = Vector3::dot(&p, &self.x_axis) / Vector3::dot(&self.x_axis, &self.x_axis);
677        let y_coord = Vector3::dot(&p, &self.y_axis) / Vector3::dot(&self.y_axis, &self.y_axis);
678        Vector2::new(x_coord, y_coord)
679    }
680}
681
682////////////////////////////////////////////////////////////////////////////////
683/// Computes the normal vector of a triangle.
684////////////////////////////////////////////////////////////////////////////////
685pub fn tri_normal<T: FloatScalar>(v0: &Vector3<T>, v1: &Vector3<T>, v2: &Vector3<T>) -> Vector3<T> {
686    let v10 = *v1 - *v0;
687    let v20 = *v2 - *v0;
688    Vector3::normalize(&Vector3::cross(&v10, &v20))
689}
690
691/// Computes the normal vector of a quadrilateral.
692///
693/// Uses the cross product of the two diagonals for a more stable result.
694pub fn quad_normal<T: FloatScalar>(
695    v0: &Vector3<T>,
696    v1: &Vector3<T>,
697    v2: &Vector3<T>,
698    v3: &Vector3<T>,
699) -> Vector3<T> {
700    let v20 = *v2 - *v0;
701    let v31 = *v3 - *v1;
702    Vector3::normalize(&Vector3::cross(&v20, &v31))
703}
704
705#[cfg(test)]
706mod tests {
707    use super::*;
708    #[test]
709    pub fn test_barycentric() {
710        let v0 = Vector3::new(0.0, 0.0, 0.0);
711        let v1 = Vector3::new(0.0, 1.0, 0.0);
712        let v2 = Vector3::new(0.0, 0.0, 1.0);
713
714        let tri = Tri3::new([v0, v1, v2]);
715        let pp0 = tri.barycentric_coordinates(&v0);
716        assert!(f32::abs(pp0.x - 1.0) < 0.01);
717        assert!(f32::abs(pp0.y) < 0.01);
718        assert!(f32::abs(pp0.z) < 0.01);
719
720        let pp1 = tri.barycentric_coordinates(&v1);
721        assert!(f32::abs(pp1.x) < 0.01);
722        assert!(f32::abs(pp1.y - 1.0) < 0.01);
723        assert!(f32::abs(pp1.z) < 0.01);
724
725        let pp2 = tri.barycentric_coordinates(&v2);
726        assert!(f32::abs(pp2.x) < 0.01);
727        assert!(f32::abs(pp2.y) < 0.01);
728        assert!(f32::abs(pp2.z - 1.0) < 0.01);
729    }
730
731    #[test]
732    pub fn test_barycentric_translated() {
733        // Test with a triangle not at the origin
734        let v0 = Vector3::new(1.0, 2.0, 3.0);
735        let v1 = Vector3::new(4.0, 2.0, 3.0);
736        let v2 = Vector3::new(1.0, 5.0, 3.0);
737
738        let tri = Tri3::new([v0, v1, v2]);
739        
740        // Test vertices
741        let pp0 = tri.barycentric_coordinates(&v0);
742        assert!(f32::abs(pp0.x - 1.0) < 0.001);
743        assert!(f32::abs(pp0.y) < 0.001);
744        assert!(f32::abs(pp0.z) < 0.001);
745
746        let pp1 = tri.barycentric_coordinates(&v1);
747        assert!(f32::abs(pp1.x) < 0.001);
748        assert!(f32::abs(pp1.y - 1.0) < 0.001);
749        assert!(f32::abs(pp1.z) < 0.001);
750
751        let pp2 = tri.barycentric_coordinates(&v2);
752        assert!(f32::abs(pp2.x) < 0.001);
753        assert!(f32::abs(pp2.y) < 0.001);
754        assert!(f32::abs(pp2.z - 1.0) < 0.001);
755
756        // Test center point
757        let center = (v0 + v1 + v2) / 3.0;
758        let pp_center = tri.barycentric_coordinates(&center);
759        assert!(f32::abs(pp_center.x - 1.0/3.0) < 0.001);
760        assert!(f32::abs(pp_center.y - 1.0/3.0) < 0.001);
761        assert!(f32::abs(pp_center.z - 1.0/3.0) < 0.001);
762        
763        // Verify barycentric coordinates sum to 1
764        assert!(f32::abs((pp_center.x + pp_center.y + pp_center.z) - 1.0) < 0.001);
765    }
766
767    #[test]
768    pub fn test_barycentric_edge_midpoints() {
769        let v0 = Vector3::new(0.0, 0.0, 0.0);
770        let v1 = Vector3::new(2.0, 0.0, 0.0);
771        let v2 = Vector3::new(0.0, 2.0, 0.0);
772
773        let tri = Tri3::new([v0, v1, v2]);
774        
775        // Midpoint of edge v0-v1
776        let mid01 = (v0 + v1) / 2.0;
777        let pp_mid01 = tri.barycentric_coordinates(&mid01);
778        assert!(f32::abs(pp_mid01.x - 0.5) < 0.001);
779        assert!(f32::abs(pp_mid01.y - 0.5) < 0.001);
780        assert!(f32::abs(pp_mid01.z) < 0.001);
781
782        // Midpoint of edge v0-v2
783        let mid02 = (v0 + v2) / 2.0;
784        let pp_mid02 = tri.barycentric_coordinates(&mid02);
785        assert!(f32::abs(pp_mid02.x - 0.5) < 0.001);
786        assert!(f32::abs(pp_mid02.y) < 0.001);
787        assert!(f32::abs(pp_mid02.z - 0.5) < 0.001);
788
789        // Midpoint of edge v1-v2
790        let mid12 = (v1 + v2) / 2.0;
791        let pp_mid12 = tri.barycentric_coordinates(&mid12);
792        assert!(f32::abs(pp_mid12.x) < 0.001);
793        assert!(f32::abs(pp_mid12.y - 0.5) < 0.001);
794        assert!(f32::abs(pp_mid12.z - 0.5) < 0.001);
795    }
796
797    #[test]
798    pub fn test_barycentric_degenerate_triangle() {
799        let v0 = Vector3::new(0.0f32, 0.0, 0.0);
800        let v1 = Vector3::new(1.0f32, 0.0, 0.0);
801        let v2 = Vector3::new(2.0f32, 0.0, 0.0);
802
803        let tri = Tri3::new([v0, v1, v2]);
804        let test_point = Vector3::new(0.5f32, 0.0, 0.0);
805        let bary = tri.barycentric_coordinates(&test_point);
806        assert!(!bary.x.is_finite());
807        assert!(!bary.y.is_finite());
808        assert!(!bary.z.is_finite());
809    }
810
811    #[test]
812    pub fn test_barycentric_interpolation() {
813        // Test that we can reconstruct points using barycentric coordinates
814        let v0 = Vector3::new(1.0, 0.0, 0.0);
815        let v1 = Vector3::new(0.0, 1.0, 0.0);
816        let v2 = Vector3::new(0.0, 0.0, 1.0);
817
818        let tri = Tri3::new([v0, v1, v2]);
819        
820        // Test arbitrary point inside triangle
821        let test_point = Vector3::new(0.2, 0.3, 0.5);
822        let bary = tri.barycentric_coordinates(&test_point);
823        
824        // Reconstruct the point using barycentric coordinates
825        let reconstructed = v0 * bary.x + v1 * bary.y + v2 * bary.z;
826        
827        assert!(f32::abs(reconstructed.x - test_point.x) < 0.001);
828        assert!(f32::abs(reconstructed.y - test_point.y) < 0.001);
829        assert!(f32::abs(reconstructed.z - test_point.z) < 0.001);
830    }
831
832    #[test]
833    pub fn test_box3_overlap() {
834        let a = Box3::new(
835            &Vector3::new(0.0, 0.0, 0.0),
836            &Vector3::new(1.0, 1.0, 1.0),
837        );
838        let b = Box3::new(
839            &Vector3::new(0.5, 0.5, 0.5),
840            &Vector3::new(1.5, 1.5, 1.5),
841        );
842        assert!(a.overlap(&b));
843
844        let c = Box3::new(
845            &Vector3::new(2.0, 0.0, 0.0),
846            &Vector3::new(3.0, 1.0, 1.0),
847        );
848        assert!(!a.overlap(&c));
849
850        let d = Box3::new(
851            &Vector3::new(0.0, 2.0, 0.0),
852            &Vector3::new(1.0, 3.0, 1.0),
853        );
854        assert!(!a.overlap(&d));
855
856        let e = Box3::new(
857            &Vector3::new(0.0, 0.0, 2.0),
858            &Vector3::new(1.0, 1.0, 3.0),
859        );
860        assert!(!a.overlap(&e));
861
862        let f = Box3::new(
863            &Vector3::new(1.0, 0.0, 0.0),
864            &Vector3::new(2.0, 1.0, 1.0),
865        );
866        assert!(a.overlap(&f));
867    }
868
869    #[test]
870    fn test_line_new_zero_direction() {
871        let p = Vector3::new(0.0f32, 0.0, 0.0);
872        let d = Vector3::new(0.0f32, 0.0, 0.0);
873        assert!(Line::new(&p, &d, EPS_F32).is_none());
874    }
875
876    #[test]
877    fn test_line_from_start_end_zero_direction() {
878        let p = Vector3::new(1.0f32, 2.0, 3.0);
879        assert!(Line::from_start_end(&p, &p, EPS_F32).is_none());
880    }
881
882    #[test]
883    fn test_line_closest_point_valid() {
884        let p = Vector3::new(0.0f32, 0.0, 0.0);
885        let d = Vector3::new(1.0f32, 0.0, 0.0);
886        let line = Line::new(&p, &d, EPS_F32).expect("line should be valid");
887        let target = Vector3::new(2.0f32, 1.0, 0.0);
888        let (t, closest) = line
889            .closest_point_on_line(&target, EPS_F32)
890            .expect("closest point should exist");
891        assert!((t - 2.0).abs() < 0.001);
892        assert!((closest.x - 2.0).abs() < 0.001);
893        assert!(closest.y.abs() < 0.001);
894        assert!(closest.z.abs() < 0.001);
895    }
896
897    #[test]
898    fn test_line_normalize_zero_direction() {
899        let line = Line {
900            p: Vector3::new(0.0f32, 0.0, 0.0),
901            d: Vector3::new(0.0f32, 0.0, 0.0),
902            t: core::marker::PhantomData,
903        };
904        assert!(line.normalize(EPS_F32).is_none());
905    }
906
907    #[test]
908    fn test_line_normalize_valid_direction() {
909        let p = Vector3::new(0.0f32, 0.0, 0.0);
910        let d = Vector3::new(2.0f32, 0.0, 0.0);
911        let line = Line::new(&p, &d, EPS_F32).expect("line should be valid");
912        let norm = line.normalize(EPS_F32).expect("normalize should succeed");
913        assert!((norm.d.length() - 1.0).abs() < 0.001);
914    }
915
916    #[test]
917    fn test_segment_distance_zero_length() {
918        let p = Vector3::new(0.0f32, 0.0, 0.0);
919        let seg = Segment::new(&p, &p);
920        let target = Vector3::new(1.0f32, 0.0, 0.0);
921        assert!(seg.distance(&target, EPS_F32).is_none());
922        assert!(seg.closest_point_on_segment(&target, EPS_F32).is_none());
923    }
924
925    #[test]
926    fn test_ray_new_zero_direction() {
927        let p = Vector3::new(0.0f32, 0.0, 0.0);
928        let d = Vector3::new(0.0f32, 0.0, 0.0);
929        assert!(Ray::new(&p, &d, EPS_F32).is_none());
930    }
931
932    #[test]
933    fn test_shortest_segment_parallel_lines() {
934        let p0 = Vector3::new(0.0f32, 0.0, 0.0);
935        let p1 = Vector3::new(0.0f32, 1.0, 0.0);
936        let d = Vector3::new(1.0f32, 0.0, 0.0);
937        let l0 = Line::new(&p0, &d, EPS_F32).expect("line should be valid");
938        let l1 = Line::new(&p1, &d, EPS_F32).expect("line should be valid");
939        assert!(shortest_segment3d_between_lines3d(&l0, &l1, EPS_F32).is_none());
940    }
941
942    #[test]
943    fn test_ray_intersect_plane_parallel() {
944        let ray = Ray::new(
945            &Vector3::new(0.0f32, 0.0, 0.0),
946            &Vector3::new(1.0, 0.0, 0.0),
947            EPS_F32,
948        )
949        .expect("ray should be valid");
950        let plane = Plane::new(
951            &Vector3::new(0.0f32, 1.0, 0.0),
952            &Vector3::new(0.0, 0.0, 0.0),
953        );
954        assert!(ray.intersect_plane(&plane, EPS_F32).is_none());
955        assert!(plane.intersect_ray(&ray, EPS_F32).is_none());
956    }
957
958    #[test]
959    fn test_ray_intersect_plane_hit() {
960        let ray = Ray::new(
961            &Vector3::new(0.0f32, -1.0, 0.0),
962            &Vector3::new(0.0, 1.0, 0.0),
963            EPS_F32,
964        )
965        .expect("ray should be valid");
966        let plane = Plane::new(
967            &Vector3::new(0.0f32, 1.0, 0.0),
968            &Vector3::new(0.0, 0.0, 0.0),
969        );
970        let hit = ray.intersect_plane(&plane, EPS_F32).expect("should hit");
971        assert!(hit.y.abs() < 0.001);
972    }
973
974    #[test]
975    fn test_box3_center_extent_subdivide() {
976        let b = Box3::new(
977            &Vector3::new(0.0f32, 0.0, 0.0),
978            &Vector3::new(2.0, 2.0, 2.0),
979        );
980        let center = b.center();
981        let extent = b.extent();
982        assert!((center.x - 1.0).abs() < 0.001);
983        assert!((center.y - 1.0).abs() < 0.001);
984        assert!((center.z - 1.0).abs() < 0.001);
985        assert!((extent.x - 1.0).abs() < 0.001);
986        assert!((extent.y - 1.0).abs() < 0.001);
987        assert!((extent.z - 1.0).abs() < 0.001);
988
989        let subs = b.subdivide();
990        assert_eq!(subs.len(), 8);
991        for sub in subs.iter() {
992            assert!(sub.min.x >= 0.0 && sub.min.y >= 0.0 && sub.min.z >= 0.0);
993            assert!(sub.max.x <= 2.0 && sub.max.y <= 2.0 && sub.max.z <= 2.0);
994        }
995    }
996
997    #[test]
998    fn test_parametric_plane_project() {
999        let plane = ParametricPlane::new(
1000            &Vector3::new(1.0f32, 2.0, 3.0),
1001            &Vector3::new(1.0, 0.0, 0.0),
1002            &Vector3::new(0.0, 1.0, 0.0),
1003        );
1004        let point = Vector3::new(3.0f32, 6.0, 3.0);
1005        let uv = plane.project(&point);
1006        assert!((uv.x - 2.0).abs() < 0.001);
1007        assert!((uv.y - 4.0).abs() < 0.001);
1008    }
1009}