mazth/
sphere.rs

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    // this shall test for intersection of bounding shapes first before procedding to test intersection using algorithms of higher complexity
41    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                        //calculate a mid point average
65                        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                    //see Ray3 for ray sphere intersection
72                    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                    //x = -plane_normal * t + sphere_center
108                    //dot( plane_normal, x ) = dot( plane_normal, plane_offset ) = k
109                    //substitution:
110                    //dot( plane_normal, -plane_normal * t + sphere_center ) = k
111                    //-t + dot( plane_normal, sphere_center ) = k
112                    //t = dot( plane_normal, sphere_center ) - k
113
114                    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}