1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
use crate::base::{Shape, Intersection};
use crate::line3::Line3;
use list::List;
use vector3::Vector3;
use std::fmt;
#[derive(Clone, Copy)]
pub struct Sphere {
pub c: Vector3, // Center of the sphere
pub r: f64, // Radius of the sphere
}
impl Sphere {
/// Creates a new `Sphere`.
/// The sphere is defined by the center of the sphere, `c`, and the radius of the sphere, `r`.
pub fn new(c: &Vector3, r: f64) -> Sphere {
Self { c: *c, r }
}
}
impl Shape for Sphere {
/// Returns the normal (normalized) of the sphere at a given point (that should be in the surface of the sphere).
fn normal(&self, point: &Vector3) -> Vector3 {
(*point - self.c).normalize()
}
/// Returns the intersections of the sphere with a line.
/// If the line doesn´t collide the sphere, it returns an empty list.
/// If the line is tangent to the sphere, it returns a list with the single lambda value.
/// If the line instersects the sphere, it returns a list with the two intersection lambdas.
/// The lambda value is used to calculate the point of intersection with the line.calc_point(lambda).
fn intersects(&self, line: &Line3) -> List<f64> {
let mut intersections: List<f64> = List::<f64>::new();
// line.qa is the module2 of the director vector of the line, and has already be verified to be not 0
// So, no need to check for division by 0
let o: Vector3 = line.a - self.c;
let b: f64 = line.v.dot(&o) * 2.0;
let c: f64 = o.x.powi(2) + o.y.powi(2) + o.z.powi(2) - self.r.powi(2);
let discrim: f64 = b.powi(2) - 4. * line.qa * c;
if discrim > 0. {
let discrim2: f64 = discrim.sqrt();
intersections.push((-b + discrim2) / (2. * line.qa));
intersections.push((-b - discrim2) / (2. * line.qa));
} else if discrim == 0. {
intersections.push((-b) / (2. * line.qa));
}
intersections
}
/// Returns the closest positive intersection of the sphere with a line.
/// If the line doesn´t collide the sphere, it returns Option None.
/// If the line instersects the sphere, it returns the closest positive intersection lambda
/// The lambda value is used to calculate the point of intersection with the line.calc_point(lambda).
fn closest_intersection(&self, line: &Line3) -> Option<Intersection> {
// line.qa is the module2 of the director vector of the line, and has alrady be verified to be not 0
// So, no need to check for division by 0
let o: Vector3 = line.a - self.c;
let b: f64 = line.v.dot(&o) * 2.0;
let c: f64 = o.x.powi(2) + o.y.powi(2) + o.z.powi(2) - self.r.powi(2);
let discrim: f64 = b.powi(2) - 4. * line.qa * c;
if discrim > 0. {
let discrim2: f64 = discrim.sqrt();
let intersection1: f64 = (-b + discrim2) / (2. * line.qa);
let intersection2: f64 = (-b - discrim2) / (2. * line.qa);
if intersection1 > 0. && intersection2 > 0. {
return Some(Intersection::new(intersection1.min(intersection2), None));
} else if intersection1 > 0. {
return Some(Intersection::new(intersection1 , None));
} else if intersection2 > 0. {
return Some(Intersection::new(intersection2, None));
}
} else if discrim == 0. {
Some((-b) / (2. * line.qa));
}
return None;
}
}
impl fmt::Display for Sphere {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"({}, {}, {}) Radius = {}",
self.c.x, self.c.y, self.c.z, self.r
)
}
}