1#![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#[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 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 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 result.merge(t);
365 }
366 }
367 }
368 result.merge_slice(&cylinder_roots);
369 Some(result)
370 }
371 CylinderKind::Finite => {
372 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 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 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 #[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 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 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 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, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 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}