1#![deny(missing_docs)]
2
3use collide::CollisionInfo;
9use inner_space::{InnerSpace, VectorSpace};
10use scalars::{Sqrt, Zero};
11
12#[derive(Copy, Clone, Debug)]
14pub struct Ray<V: VectorSpace> {
15 pub origin: V,
17 pub direction: V,
19}
20
21impl<V: Copy + InnerSpace> Ray<V> {
22 pub fn new(origin: V, direction: V) -> Self {
24 Self { origin, direction }
25 }
26
27 pub fn intersect_sphere(
30 &self,
31 center: V,
32 radius: V::Scalar,
33 ) -> Option<(V::Scalar, CollisionInfo<V>)> {
34 let offset = self.origin - center;
35 let direction_squared = self.direction.magnitude2();
36 let half_b = self.direction.dot(&offset);
37 let c = offset.magnitude2() - radius * radius;
38 let discriminant = half_b * half_b - direction_squared * c;
39
40 if discriminant < V::Scalar::zero() {
41 return None;
42 }
43
44 let sqrt_discriminant = discriminant.sqrt();
45 let mut parameter = (-half_b - sqrt_discriminant) / direction_squared;
46 if parameter < V::Scalar::zero() {
47 parameter = (-half_b + sqrt_discriminant) / direction_squared;
48 if parameter < V::Scalar::zero() {
49 return None;
50 }
51 }
52
53 let hit_point = self.origin + self.direction * parameter;
54 let normal = (hit_point - center) / radius;
55 let surface_point = center + normal * radius;
56
57 Some((
58 parameter,
59 CollisionInfo {
60 self_contact: hit_point,
61 other_contact: surface_point,
62 vector: self.direction * parameter,
63 },
64 ))
65 }
66}