fyrox_math/
ray.rs

1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21// Clippy complains about normal mathematical symbols like A, B, C for quadratic equation.
22#![allow(clippy::many_single_char_names)]
23
24use crate::aabb::AxisAlignedBoundingBox;
25use crate::{is_point_inside_triangle, plane::Plane, solve_quadratic};
26use nalgebra::{Matrix4, Point3, Vector3};
27
28#[derive(Copy, Clone, Debug, PartialEq)]
29pub struct Ray {
30    pub origin: Vector3<f32>,
31    pub dir: Vector3<f32>,
32}
33
34impl Default for Ray {
35    #[inline]
36    fn default() -> Self {
37        Ray {
38            origin: Vector3::new(0.0, 0.0, 0.0),
39            dir: Vector3::new(0.0, 0.0, 1.0),
40        }
41    }
42}
43
44/// Pair of ray equation parameters.
45#[derive(Clone, Debug, Copy)]
46pub struct IntersectionResult {
47    pub min: f32,
48    pub max: f32,
49}
50
51impl IntersectionResult {
52    #[inline]
53    pub fn from_slice(roots: &[f32]) -> Self {
54        let mut min = f32::MAX;
55        let mut max = -f32::MAX;
56        for n in roots {
57            min = min.min(*n);
58            max = max.max(*n);
59        }
60        Self { min, max }
61    }
62
63    #[inline]
64    pub fn from_set(results: &[Option<IntersectionResult>]) -> Option<Self> {
65        let mut result = None;
66        for v in results {
67            match result {
68                None => result = *v,
69                Some(ref mut result) => {
70                    if let Some(v) = v {
71                        result.merge(v.min);
72                        result.merge(v.max);
73                    }
74                }
75            }
76        }
77        result
78    }
79
80    /// Updates min and max ray equation parameters according to a new parameter -
81    /// expands range if `param` was outside of that range.
82    #[inline]
83    pub fn merge(&mut self, param: f32) {
84        if param < self.min {
85            self.min = param;
86        }
87        if param > self.max {
88            self.max = param;
89        }
90    }
91
92    #[inline]
93    pub fn merge_slice(&mut self, params: &[f32]) {
94        for param in params {
95            self.merge(*param)
96        }
97    }
98}
99
100pub enum CylinderKind {
101    Infinite,
102    Finite,
103    Capped,
104}
105
106impl Ray {
107    /// Creates ray from two points. May fail if begin == end.
108    #[inline]
109    pub fn from_two_points(begin: Vector3<f32>, end: Vector3<f32>) -> Self {
110        Ray {
111            origin: begin,
112            dir: end - begin,
113        }
114    }
115
116    #[inline]
117    pub fn new(origin: Vector3<f32>, dir: Vector3<f32>) -> Self {
118        Self { origin, dir }
119    }
120
121    /// Checks intersection with sphere. Returns two intersection points or none
122    /// if there was no intersection.
123    #[inline]
124    pub fn sphere_intersection_points(
125        &self,
126        position: &Vector3<f32>,
127        radius: f32,
128    ) -> Option<[Vector3<f32>; 2]> {
129        self.try_eval_points(self.sphere_intersection(position, radius))
130    }
131
132    #[inline]
133    pub fn sphere_intersection(
134        &self,
135        position: &Vector3<f32>,
136        radius: f32,
137    ) -> Option<IntersectionResult> {
138        let d = self.origin - *position;
139        let a = self.dir.dot(&self.dir);
140        let b = 2.0 * self.dir.dot(&d);
141        let c = d.dot(&d) - radius * radius;
142        solve_quadratic(a, b, c).map(|roots| IntersectionResult::from_slice(&roots))
143    }
144
145    /// Checks intersection with sphere.
146    #[inline]
147    pub fn is_intersect_sphere(&self, position: &Vector3<f32>, radius: f32) -> bool {
148        let d = self.origin - position;
149        let a = self.dir.dot(&self.dir);
150        let b = 2.0 * self.dir.dot(&d);
151        let c = d.dot(&d) - radius * radius;
152        let discriminant = b * b - 4.0 * a * c;
153        discriminant >= 0.0
154    }
155
156    /// Returns t factor (at pt=o+d*t equation) for projection of given point at ray
157    #[inline]
158    pub fn project_point(&self, point: &Vector3<f32>) -> f32 {
159        (point - self.origin).dot(&self.dir) / self.dir.norm_squared()
160    }
161
162    /// Returns point on ray which defined by pt=o+d*t equation.
163    #[inline]
164    pub fn get_point(&self, t: f32) -> Vector3<f32> {
165        self.origin + self.dir.scale(t)
166    }
167
168    #[inline]
169    pub fn box_intersection(
170        &self,
171        min: &Vector3<f32>,
172        max: &Vector3<f32>,
173    ) -> Option<IntersectionResult> {
174        let (mut tmin, mut tmax) = if self.dir.x >= 0.0 {
175            (
176                (min.x - self.origin.x) / self.dir.x,
177                (max.x - self.origin.x) / self.dir.x,
178            )
179        } else {
180            (
181                (max.x - self.origin.x) / self.dir.x,
182                (min.x - self.origin.x) / self.dir.x,
183            )
184        };
185
186        let (tymin, tymax) = if self.dir.y >= 0.0 {
187            (
188                (min.y - self.origin.y) / self.dir.y,
189                (max.y - self.origin.y) / self.dir.y,
190            )
191        } else {
192            (
193                (max.y - self.origin.y) / self.dir.y,
194                (min.y - self.origin.y) / self.dir.y,
195            )
196        };
197
198        if tmin > tymax || (tymin > tmax) {
199            return None;
200        }
201        if tymin > tmin {
202            tmin = tymin;
203        }
204        if tymax < tmax {
205            tmax = tymax;
206        }
207        let (tzmin, tzmax) = if self.dir.z >= 0.0 {
208            (
209                (min.z - self.origin.z) / self.dir.z,
210                (max.z - self.origin.z) / self.dir.z,
211            )
212        } else {
213            (
214                (max.z - self.origin.z) / self.dir.z,
215                (min.z - self.origin.z) / self.dir.z,
216            )
217        };
218
219        if (tmin > tzmax) || (tzmin > tmax) {
220            return None;
221        }
222        if tzmin > tmin {
223            tmin = tzmin;
224        }
225        if tzmax < tmax {
226            tmax = tzmax;
227        }
228        if tmin <= 1.0 && tmax >= 0.0 {
229            Some(IntersectionResult {
230                min: tmin,
231                max: tmax,
232            })
233        } else {
234            None
235        }
236    }
237
238    #[inline]
239    pub fn box_intersection_points(
240        &self,
241        min: &Vector3<f32>,
242        max: &Vector3<f32>,
243    ) -> Option<[Vector3<f32>; 2]> {
244        self.try_eval_points(self.box_intersection(min, max))
245    }
246
247    #[inline]
248    pub fn aabb_intersection(&self, aabb: &AxisAlignedBoundingBox) -> Option<IntersectionResult> {
249        self.box_intersection(&aabb.min, &aabb.max)
250    }
251
252    #[inline]
253    pub fn aabb_intersection_points(
254        &self,
255        aabb: &AxisAlignedBoundingBox,
256    ) -> Option<[Vector3<f32>; 2]> {
257        self.box_intersection_points(&aabb.min, &aabb.max)
258    }
259
260    /// Solves plane equation in order to find ray equation parameter.
261    /// There is no intersection if result < 0.
262    #[inline]
263    pub fn plane_intersection(&self, plane: &Plane) -> f32 {
264        let u = -(self.origin.dot(&plane.normal) + plane.d);
265        let v = self.dir.dot(&plane.normal);
266        u / v
267    }
268
269    #[inline]
270    pub fn plane_intersection_point(&self, plane: &Plane) -> Option<Vector3<f32>> {
271        let t = self.plane_intersection(plane);
272        if !(0.0..=1.0).contains(&t) {
273            None
274        } else {
275            Some(self.get_point(t))
276        }
277    }
278
279    #[inline]
280    pub fn triangle_intersection(
281        &self,
282        vertices: &[Vector3<f32>; 3],
283    ) -> Option<(f32, Vector3<f32>)> {
284        let ba = vertices[1] - vertices[0];
285        let ca = vertices[2] - vertices[0];
286        let plane = Plane::from_normal_and_point(&ba.cross(&ca), &vertices[0])?;
287
288        let t = self.plane_intersection(&plane);
289        if (0.0..=1.0).contains(&t) {
290            let point = self.get_point(t);
291            if is_point_inside_triangle(&point, vertices) {
292                return Some((t, point));
293            }
294        }
295        None
296    }
297
298    #[inline]
299    pub fn triangle_intersection_point(
300        &self,
301        vertices: &[Vector3<f32>; 3],
302    ) -> Option<Vector3<f32>> {
303        let ba = vertices[1] - vertices[0];
304        let ca = vertices[2] - vertices[0];
305        let plane = Plane::from_normal_and_point(&ba.cross(&ca), &vertices[0])?;
306
307        if let Some(point) = self.plane_intersection_point(&plane) {
308            if is_point_inside_triangle(&point, vertices) {
309                return Some(point);
310            }
311        }
312        None
313    }
314
315    /// Generic ray-cylinder intersection test.
316    ///
317    /// <https://mrl.nyu.edu/~dzorin/rend05/lecture2.pdf>
318    ///
319    ///  Infinite cylinder oriented along line pa + va * t:
320    ///      sqr_len(q - pa - dot(va, q - pa) * va) - r ^ 2 = 0
321    ///  where q - point on cylinder, substitute q with ray p + v * t:
322    ///     sqr_len(p - pa + vt - dot(va, p - pa + vt) * va) - r ^ 2 = 0
323    ///  reduce to A * t * t + B * t + C = 0 (quadratic equation), where:
324    ///     A = sqr_len(v - dot(v, va) * va)
325    ///     B = 2 * dot(v - dot(v, va) * va, dp - dot(dp, va) * va)
326    ///     C = sqr_len(dp - dot(dp, va) * va) - r ^ 2
327    ///     where dp = p - pa
328    ///  to find intersection points we have to solve quadratic equation
329    ///  to get root which will be t parameter of ray equation.
330    #[inline]
331    pub fn cylinder_intersection(
332        &self,
333        pa: &Vector3<f32>,
334        pb: &Vector3<f32>,
335        r: f32,
336        kind: CylinderKind,
337    ) -> Option<IntersectionResult> {
338        let va = (*pb - *pa)
339            .try_normalize(f32::EPSILON)
340            .unwrap_or_else(|| Vector3::new(0.0, 1.0, 0.0));
341        let vl = self.dir - va.scale(self.dir.dot(&va));
342        let dp = self.origin - *pa;
343        let dpva = dp - va.scale(dp.dot(&va));
344
345        let a = vl.norm_squared();
346        let b = 2.0 * vl.dot(&dpva);
347        let c = dpva.norm_squared() - r * r;
348
349        // Get roots for cylinder surfaces
350        if let Some(cylinder_roots) = solve_quadratic(a, b, c) {
351            match kind {
352                CylinderKind::Infinite => Some(IntersectionResult::from_slice(&cylinder_roots)),
353                CylinderKind::Capped => {
354                    let mut result = IntersectionResult::from_slice(&cylinder_roots);
355                    // In case of cylinder with caps we have to check intersection with caps
356                    for (cap_center, cap_normal) in [(pa, -va), (pb, va)].iter() {
357                        let cap_plane =
358                            Plane::from_normal_and_point(cap_normal, cap_center).unwrap();
359                        let t = self.plane_intersection(&cap_plane);
360                        if t > 0.0 {
361                            let intersection = self.get_point(t);
362                            if (*cap_center - intersection).norm_squared() <= r * r {
363                                // Point inside cap bounds
364                                result.merge(t);
365                            }
366                        }
367                    }
368                    result.merge_slice(&cylinder_roots);
369                    Some(result)
370                }
371                CylinderKind::Finite => {
372                    // In case of finite cylinder without caps we have to check that intersection
373                    // points on cylinder surface are between two planes of caps.
374                    let mut result = None;
375                    for root in cylinder_roots.iter() {
376                        let int_point = self.get_point(*root);
377                        if (int_point - *pa).dot(&va) >= 0.0 && (*pb - int_point).dot(&va) >= 0.0 {
378                            match &mut result {
379                                None => {
380                                    result = Some(IntersectionResult {
381                                        min: *root,
382                                        max: *root,
383                                    })
384                                }
385                                Some(result) => result.merge(*root),
386                            }
387                        }
388                    }
389                    result
390                }
391            }
392        } else {
393            // We have no roots, so no intersection.
394            None
395        }
396    }
397
398    #[inline]
399    pub fn try_eval_points(&self, result: Option<IntersectionResult>) -> Option<[Vector3<f32>; 2]> {
400        match result {
401            None => None,
402            Some(result) => {
403                let a = if result.min >= 0.0 && result.min <= 1.0 {
404                    Some(self.get_point(result.min))
405                } else {
406                    None
407                };
408
409                let b = if result.max >= 0.0 && result.max <= 1.0 {
410                    Some(self.get_point(result.max))
411                } else {
412                    None
413                };
414
415                match a {
416                    None => b.map(|b| [b, b]),
417                    Some(a) => match b {
418                        None => Some([a, a]),
419                        Some(b) => Some([a, b]),
420                    },
421                }
422            }
423        }
424    }
425
426    #[inline]
427    pub fn capsule_intersection(
428        &self,
429        pa: &Vector3<f32>,
430        pb: &Vector3<f32>,
431        radius: f32,
432    ) -> Option<[Vector3<f32>; 2]> {
433        // Dumb approach - check intersection with finite cylinder without caps,
434        // then check two sphere caps.
435        let cylinder = self.cylinder_intersection(pa, pb, radius, CylinderKind::Finite);
436        let cap_a = self.sphere_intersection(pa, radius);
437        let cap_b = self.sphere_intersection(pb, radius);
438        self.try_eval_points(IntersectionResult::from_set(&[cylinder, cap_a, cap_b]))
439    }
440
441    /// Transforms ray using given matrix. This method is useful when you need to
442    /// transform ray into some object space to simplify calculations. For example
443    /// you may have mesh with lots of triangles, and in one way you would take all
444    /// vertices, transform them into world space by some matrix, then do intersection
445    /// test in world space. This works, but too inefficient, much more faster would
446    /// be to put ray into object space and do intersection test in object space. This
447    /// removes vertex*matrix multiplication and significantly improves performance.
448    #[must_use = "Method does not modify ray, instead it returns transformed copy"]
449    #[inline]
450    pub fn transform(&self, mat: Matrix4<f32>) -> Self {
451        Self {
452            origin: mat.transform_point(&Point3::from(self.origin)).coords,
453            dir: mat.transform_vector(&self.dir),
454        }
455    }
456}
457
458#[cfg(test)]
459mod test {
460    use nalgebra::Matrix4;
461
462    use crate::{
463        aabb::AxisAlignedBoundingBox,
464        plane::Plane,
465        ray::{CylinderKind, Ray},
466        Vector3,
467    };
468
469    use super::IntersectionResult;
470
471    #[test]
472    fn intersection() {
473        let triangle = [
474            Vector3::new(0.0, 0.5, 0.0),
475            Vector3::new(-0.5, -0.5, 0.0),
476            Vector3::new(0.5, -0.5, 0.0),
477        ];
478        let ray = Ray::from_two_points(Vector3::new(0.0, 0.0, -2.0), Vector3::new(0.0, 0.0, -1.0));
479        assert!(ray.triangle_intersection_point(&triangle).is_none());
480    }
481
482    #[test]
483    fn default_for_ray() {
484        let ray = Ray::default();
485        assert_eq!(ray.origin, Vector3::new(0.0, 0.0, 0.0));
486        assert_eq!(ray.dir, Vector3::new(0.0, 0.0, 1.0));
487    }
488
489    #[test]
490    fn intersection_result_from_slice() {
491        let ir = IntersectionResult::from_slice(&[0.0, -1.0, 1.0]);
492        assert_eq!(ir.min, -1.0);
493        assert_eq!(ir.max, 1.0);
494    }
495
496    #[test]
497    fn intersection_result_from_set() {
498        assert!(IntersectionResult::from_set(&[None, None]).is_none());
499
500        let ir = IntersectionResult::from_set(&[
501            Some(IntersectionResult {
502                min: -1.0,
503                max: 0.0,
504            }),
505            Some(IntersectionResult { min: 0.0, max: 1.0 }),
506        ]);
507        assert!(ir.is_some());
508        assert_eq!(ir.unwrap().min, -1.0);
509        assert_eq!(ir.unwrap().max, 1.0);
510    }
511
512    #[test]
513    fn intersection_result_merge() {
514        let mut ir = IntersectionResult {
515            min: -1.0,
516            max: 1.0,
517        };
518        ir.merge(-10.0);
519        ir.merge(10.0);
520
521        assert_eq!(ir.min, -10.0);
522        assert_eq!(ir.max, 10.0);
523    }
524
525    #[test]
526    fn intersection_result_merge_slice() {
527        let mut ir = IntersectionResult {
528            min: -1.0,
529            max: 1.0,
530        };
531        ir.merge_slice(&[-10.0, 0.0, 10.0]);
532
533        assert_eq!(ir.min, -10.0);
534        assert_eq!(ir.max, 10.0);
535    }
536
537    #[test]
538    fn ray_new() {
539        let ray = Ray::new(Vector3::new(0.0, 0.0, 0.0), Vector3::new(1.0, 1.0, 1.0));
540
541        assert_eq!(ray.origin, Vector3::new(0.0, 0.0, 0.0));
542        assert_eq!(ray.dir, Vector3::new(1.0, 1.0, 1.0));
543    }
544
545    #[test]
546    fn ray_try_eval_points() {
547        let ray = Ray::new(Vector3::new(0.0, 0.0, 0.0), Vector3::new(1.0, 1.0, 1.0));
548
549        assert!(ray.try_eval_points(None).is_none());
550
551        let ir = IntersectionResult { min: 0.0, max: 1.0 };
552        assert_eq!(
553            ray.try_eval_points(Some(ir)),
554            Some([Vector3::new(0.0, 0.0, 0.0), Vector3::new(1.0, 1.0, 1.0)])
555        );
556
557        let ir = IntersectionResult {
558            min: -1.0,
559            max: 1.0,
560        };
561        assert_eq!(
562            ray.try_eval_points(Some(ir)),
563            Some([Vector3::new(1.0, 1.0, 1.0), Vector3::new(1.0, 1.0, 1.0)])
564        );
565
566        let ir = IntersectionResult {
567            min: 0.0,
568            max: 10.0,
569        };
570        assert_eq!(
571            ray.try_eval_points(Some(ir)),
572            Some([Vector3::new(0.0, 0.0, 0.0), Vector3::new(0.0, 0.0, 0.0)])
573        );
574
575        let ir = IntersectionResult {
576            min: -10.0,
577            max: 10.0,
578        };
579        assert_eq!(ray.try_eval_points(Some(ir)), None);
580    }
581
582    #[test]
583    fn ray_sphere_intersection() {
584        let ray = Ray::new(Vector3::new(0.0, 0.0, 0.0), Vector3::new(1.0, 0.0, 0.0));
585
586        assert!(ray
587            .sphere_intersection(&Vector3::new(-10.0, -10.0, -10.0), 1.0)
588            .is_none());
589
590        let result = ray.sphere_intersection(&Vector3::new(0.0, 0.0, 0.0), 1.0);
591        assert_eq!(result.unwrap().min, -1.0);
592        assert_eq!(result.unwrap().max, 1.0);
593    }
594
595    #[test]
596    fn ray_sphere_intersection_points() {
597        let ray = Ray::new(Vector3::new(0.0, 0.0, 0.0), Vector3::new(1.0, 0.0, 0.0));
598
599        assert!(ray
600            .sphere_intersection_points(&Vector3::new(-10.0, -10.0, -10.0), 1.0)
601            .is_none());
602
603        assert_eq!(
604            ray.sphere_intersection_points(&Vector3::new(0.0, 0.0, 0.0), 1.0),
605            Some([Vector3::new(1.0, 0.0, 0.0), Vector3::new(1.0, 0.0, 0.0)])
606        );
607    }
608
609    #[test]
610    fn ray_is_intersect_sphere() {
611        let ray = Ray::new(Vector3::new(0.0, 0.0, 0.0), Vector3::new(1.0, 0.0, 0.0));
612
613        assert!(!ray.is_intersect_sphere(&Vector3::new(-10.0, -10.0, -10.0), 1.0));
614        assert!(ray.is_intersect_sphere(&Vector3::new(0.0, 0.0, 0.0), 1.0));
615    }
616
617    #[test]
618    fn ray_project_point() {
619        let ray = Ray::new(Vector3::new(0.0, 0.0, 0.0), Vector3::new(1.0, 1.0, 1.0));
620
621        assert_eq!(ray.project_point(&Vector3::new(0.0, 0.0, 0.0)), 0.0);
622        assert_eq!(ray.project_point(&Vector3::new(1.0, 0.0, 0.0)), 0.33333334);
623        assert_eq!(ray.project_point(&Vector3::new(0.0, 1.0, 0.0)), 0.33333334);
624        assert_eq!(ray.project_point(&Vector3::new(0.0, 0.0, 1.0)), 0.33333334);
625        assert_eq!(ray.project_point(&Vector3::new(1.0, 1.0, 1.0)), 1.0);
626    }
627
628    #[test]
629    fn ray_get_point() {
630        let ray = Ray::new(Vector3::new(0.0, 0.0, 0.0), Vector3::new(1.0, 1.0, 1.0));
631
632        assert_eq!(ray.get_point(0.0), Vector3::new(0.0, 0.0, 0.0));
633        assert_eq!(ray.get_point(10.0), Vector3::new(10.0, 10.0, 10.0));
634    }
635
636    #[test]
637    fn ray_box_intersection() {
638        let ray = Ray::new(Vector3::new(0.0, 0.0, 0.0), Vector3::new(1.0, 1.0, 1.0));
639        let ir = ray.box_intersection(
640            &Vector3::new(1.0, 1.0, 1.0),
641            &Vector3::new(10.0, 10.0, 10.0),
642        );
643        assert_eq!(ir.unwrap().min, 1.0);
644        assert_eq!(ir.unwrap().max, 10.0);
645
646        assert!(ray
647            .box_intersection(&Vector3::new(1.0, 1.0, 0.0), &Vector3::new(10.0, 10.0, 0.0))
648            .is_none());
649        assert!(ray
650            .box_intersection(&Vector3::new(1.0, 0.0, 1.0), &Vector3::new(10.0, 0.0, 10.0))
651            .is_none());
652
653        let ray = Ray::new(Vector3::new(0.0, 0.0, 0.0), Vector3::new(-1.0, -1.0, -1.0));
654        let ir = ray.box_intersection(
655            &Vector3::new(-10.0, -10.0, -10.0),
656            &Vector3::new(-1.0, -1.0, -1.0),
657        );
658        assert_eq!(ir.unwrap().min, 1.0);
659        assert_eq!(ir.unwrap().max, 10.0);
660    }
661
662    #[test]
663    fn ray_box_intersection_points() {
664        let ray = Ray::new(Vector3::new(0.0, 0.0, 0.0), Vector3::new(1.0, 1.0, 1.0));
665
666        assert!(ray
667            .box_intersection_points(&Vector3::new(1.0, 1.0, 0.0), &Vector3::new(10.0, 10.0, 0.0))
668            .is_none());
669        assert_eq!(
670            ray.box_intersection_points(
671                &Vector3::new(1.0, 1.0, 1.0),
672                &Vector3::new(10.0, 10.0, 10.0)
673            ),
674            Some([Vector3::new(1.0, 1.0, 1.0), Vector3::new(1.0, 1.0, 1.0)])
675        );
676    }
677
678    #[test]
679    fn ray_aabb_intersection() {
680        let ray = Ray::new(Vector3::new(0.0, 0.0, 0.0), Vector3::new(1.0, 1.0, 1.0));
681
682        assert!(ray
683            .aabb_intersection(&AxisAlignedBoundingBox {
684                min: Vector3::new(1.0, 1.0, 0.0),
685                max: Vector3::new(10.0, 10.0, 0.0)
686            })
687            .is_none());
688
689        let ir = ray.aabb_intersection(&AxisAlignedBoundingBox {
690            min: Vector3::new(1.0, 1.0, 1.0),
691            max: Vector3::new(10.0, 10.0, 10.0),
692        });
693        assert_eq!(ir.unwrap().min, 1.0);
694        assert_eq!(ir.unwrap().max, 10.0);
695    }
696
697    #[test]
698    fn ray_aabb_intersection_points() {
699        let ray = Ray::new(Vector3::new(0.0, 0.0, 0.0), Vector3::new(1.0, 1.0, 1.0));
700
701        assert!(ray
702            .aabb_intersection_points(&AxisAlignedBoundingBox {
703                min: Vector3::new(1.0, 1.0, 0.0),
704                max: Vector3::new(10.0, 10.0, 0.0)
705            })
706            .is_none());
707
708        assert_eq!(
709            ray.aabb_intersection_points(&AxisAlignedBoundingBox {
710                min: Vector3::new(1.0, 1.0, 1.0),
711                max: Vector3::new(10.0, 10.0, 10.0),
712            }),
713            Some([Vector3::new(1.0, 1.0, 1.0), Vector3::new(1.0, 1.0, 1.0)])
714        );
715    }
716
717    #[test]
718    fn ray_plane_intersection() {
719        let ray = Ray::new(Vector3::new(0.0, 0.0, 0.0), Vector3::new(1.0, 1.0, 1.0));
720
721        assert_eq!(
722            ray.plane_intersection(
723                &Plane::from_normal_and_point(
724                    &Vector3::new(1.0, 1.0, 1.0),
725                    &Vector3::new(0.0, 0.0, 0.0)
726                )
727                .unwrap()
728            ),
729            0.0
730        );
731    }
732
733    #[test]
734    fn ray_plane_intersection_point() {
735        let ray = Ray::new(Vector3::new(0.0, 0.0, 0.0), Vector3::new(1.0, 1.0, 1.0));
736
737        assert_eq!(
738            ray.plane_intersection_point(
739                &Plane::from_normal_and_point(
740                    &Vector3::new(1.0, 1.0, 1.0),
741                    &Vector3::new(0.0, 0.0, 0.0)
742                )
743                .unwrap()
744            ),
745            Some(Vector3::new(0.0, 0.0, 0.0))
746        );
747
748        let ray = Ray::new(Vector3::new(0.0, 0.0, 0.0), Vector3::new(1.0, 1.0, 0.0));
749
750        assert_eq!(
751            ray.plane_intersection_point(
752                &Plane::from_normal_and_point(
753                    &Vector3::new(0.0, 0.0, 1.0),
754                    &Vector3::new(1.0, 1.0, 1.0),
755                )
756                .unwrap()
757            ),
758            None
759        );
760    }
761
762    #[test]
763    fn ray_triangle_intersection() {
764        let ray = Ray::new(Vector3::new(0.0, 0.0, 0.0), Vector3::new(1.0, 1.0, 1.0));
765
766        assert_eq!(
767            ray.triangle_intersection(&[
768                Vector3::new(0.0, 0.0, 0.0),
769                Vector3::new(1.0, 0.0, 0.0),
770                Vector3::new(1.0, 1.0, 0.0),
771            ]),
772            Some((0.0, Vector3::new(0.0, 0.0, 0.0)))
773        );
774
775        assert_eq!(
776            ray.triangle_intersection(&[
777                Vector3::new(1.0, 0.0, 0.0),
778                Vector3::new(1.0, -1.0, 0.0),
779                Vector3::new(-1.0, -1.0, 0.0),
780            ]),
781            None
782        );
783    }
784
785    #[test]
786    fn ray_triangle_intersection_point() {
787        let ray = Ray::new(Vector3::new(0.0, 0.0, 0.0), Vector3::new(1.0, 1.0, 1.0));
788
789        assert_eq!(
790            ray.triangle_intersection_point(&[
791                Vector3::new(0.0, 0.0, 0.0),
792                Vector3::new(1.0, 0.0, 0.0),
793                Vector3::new(1.0, 1.0, 0.0),
794            ]),
795            Some(Vector3::new(0.0, 0.0, 0.0))
796        );
797
798        assert_eq!(
799            ray.triangle_intersection_point(&[
800                Vector3::new(1.0, 0.0, 0.0),
801                Vector3::new(1.0, -1.0, 0.0),
802                Vector3::new(-1.0, -1.0, 0.0),
803            ]),
804            None
805        );
806    }
807
808    #[test]
809    fn ray_cylinder_intersection() {
810        let ray = Ray::new(Vector3::new(0.0, 0.0, 0.0), Vector3::new(1.0, 1.0, 1.0));
811
812        // Infinite
813        let ir = ray.cylinder_intersection(
814            &Vector3::new(0.0, 0.0, 0.0),
815            &Vector3::new(1.0, 0.0, 0.0),
816            1.0,
817            CylinderKind::Infinite,
818        );
819        assert_eq!(ir.unwrap().min, -0.70710677);
820        assert_eq!(ir.unwrap().max, 0.70710677);
821
822        // Finite
823        let ir = ray.cylinder_intersection(
824            &Vector3::new(0.0, 0.0, 0.0),
825            &Vector3::new(1.0, 0.0, 0.0),
826            1.0,
827            CylinderKind::Finite,
828        );
829        assert_eq!(ir.unwrap().min, 0.70710677);
830        assert_eq!(ir.unwrap().max, 0.70710677);
831
832        // Capped
833        let ir = ray.cylinder_intersection(
834            &Vector3::new(0.0, 0.0, 0.0),
835            &Vector3::new(1.0, 0.0, 0.0),
836            1.0,
837            CylinderKind::Capped,
838        );
839        assert_eq!(ir.unwrap().min, -0.70710677);
840        assert_eq!(ir.unwrap().max, 0.70710677);
841    }
842
843    #[test]
844    fn ray_capsule_intersection() {
845        let ray = Ray::new(Vector3::new(0.0, 0.0, 0.0), Vector3::new(1.0, 1.0, 1.0));
846
847        assert_eq!(
848            ray.capsule_intersection(
849                &Vector3::new(0.0, 0.0, 0.0),
850                &Vector3::new(1.0, 0.0, 0.0),
851                1.0,
852            ),
853            Some([
854                Vector3::new(0.70710677, 0.70710677, 0.70710677),
855                Vector3::new(0.70710677, 0.70710677, 0.70710677)
856            ])
857        );
858        assert_eq!(
859            ray.capsule_intersection(
860                &Vector3::new(10.0, 0.0, 0.0),
861                &Vector3::new(11.0, 0.0, 0.0),
862                1.0,
863            ),
864            None
865        );
866    }
867
868    #[test]
869    fn ray_transform() {
870        let ray = Ray::new(Vector3::new(0.0, 0.0, 0.0), Vector3::new(1.0, 1.0, 1.0));
871
872        let new_ray = ray.transform(Matrix4::new(
873            1.0, 0.0, 0.0, 0.0, //
874            0.0, 1.0, 0.0, 0.0, //
875            0.0, 0.0, 1.0, 0.0, //
876            0.0, 0.0, 0.0, 1.0,
877        ));
878
879        assert_eq!(ray.origin, new_ray.origin);
880        assert_eq!(ray.dir, new_ray.dir);
881    }
882}