1use i_bound::IBound;
2use i_shape::{IShape, ShapeType};
3use i_vicinity::IVicinity;
4
5use bound::AxisAlignedBBox;
6use mat::Mat3x1;
7
8#[derive(Debug, Clone)]
9pub struct Sphere {
10 pub _ori: Mat3x1<f64>,
11 pub _radius: f64,
12 pub _bound: AxisAlignedBBox,
13 pub _vicinity: f64,
14}
15
16impl Sphere {
17 pub fn init(origin: &[f64], r: f64) -> Sphere {
18 assert!(origin.len() == 3);
19 Sphere {
20 _ori: Mat3x1 {
21 _val: [origin[0], origin[1], origin[2]],
22 },
23 _radius: r,
24 _bound: AxisAlignedBBox::init(ShapeType::Sphere, &[&origin[0..3], &[r]].concat()),
25 _vicinity: 0.000001f64,
26 }
27 }
28}
29
30impl IShape for Sphere {
31 fn get_shape_data(&self) -> Vec<f64> {
32 vec![self._ori[0], self._ori[1], self._ori[2], self._radius]
33 }
34 fn get_type(&self) -> ShapeType {
35 ShapeType::Sphere
36 }
37 fn get_bound(&self) -> &dyn IBound {
38 &self._bound
39 }
40 fn get_intersect(&self, other: &dyn IShape) -> (bool, Option<Mat3x1<f64>>) {
42 if !self.get_bound().intersect(other.get_bound()) {
43 return (false, None);
44 } else {
45 match other.get_type() {
46 ShapeType::Sphere => {
47 let other_shape_data = other.get_shape_data();
48 let b_off = Mat3x1 {
49 _val: [
50 other_shape_data[0],
51 other_shape_data[1],
52 other_shape_data[2],
53 ],
54 };
55 let a_r = self._radius;
56 let b_r = other_shape_data[3];
57
58 let a_off = self._ori;
59 let c = b_off.minus(&a_off).unwrap();
60 let d = c.magnitude().unwrap();
61 if d > b_r + a_r {
62 return (false, None);
63 } else {
64 let f = a_r / (a_r + b_r);
66 let g = c.scale(f).unwrap();
67 return (true, Some(a_off.plus(&g).unwrap()));
68 }
69 }
70 ShapeType::Ray => {
71 return other.get_intersect(self);
73 }
74 ShapeType::Point => {
75 let other_shape_data = other.get_shape_data();
76 let b_off = Mat3x1 {
77 _val: [
78 other_shape_data[0],
79 other_shape_data[1],
80 other_shape_data[2],
81 ],
82 };
83 let d = b_off.minus(&self._ori).unwrap();
84 for i in 0..3 {
85 if d[i] > self._radius {
86 return (false, None);
87 }
88 }
89 return (true, Some(b_off));
90 }
91 ShapeType::Plane => {
92 let other_shape_data = other.get_shape_data();
93 let b_off = Mat3x1 {
94 _val: [
95 other_shape_data[0],
96 other_shape_data[1],
97 other_shape_data[2],
98 ],
99 };
100 let b_nor = Mat3x1 {
101 _val: [
102 other_shape_data[3],
103 other_shape_data[4],
104 other_shape_data[5],
105 ],
106 };
107 let k = b_nor.dot(&b_off).unwrap();
115 let t = b_nor.dot(&self._ori).unwrap() - k;
116 if t > self._radius {
117 return (false, None);
118 } else {
119 return (
120 true,
121 Some(b_nor.scale(-t).unwrap().plus(&self._ori).unwrap()),
122 );
123 }
124 }
125 _ => {
126 unimplemented!();
127 }
128 }
129 }
130 }
131 fn get_support(&self, v: &Mat3x1<f64>) -> Option<Mat3x1<f64>> {
132 if v.magnitude() != Some(0f64) {
133 let v_adjusted = v
134 .normalize()
135 .expect("normalization unsuccessful")
136 .scale(self._radius)
137 .expect("scale unsuccessful");
138 let o = self
139 ._ori
140 .plus(&v_adjusted)
141 .expect("support operation unsuccessful.");
142 Some(o)
143 } else {
144 None
145 }
146 }
147}
148
149impl IVicinity<f64> for Sphere {
150 fn set_vicinity(&mut self, epsilon: f64) {
151 self._vicinity = epsilon.abs();
152 }
153 fn within_vicinity(&self, a: f64, b: f64) -> bool {
154 if a + self._vicinity >= b && a - self._vicinity <= b {
155 true
156 } else {
157 false
158 }
159 }
160}