use i_bound::IBound;
use i_shape::{IShape, ShapeType};
use i_vicinity::IVicinity;
use bound::AxisAlignedBBox;
use mat::Mat3x1;
#[derive(Debug, Clone)]
pub struct Sphere {
pub _ori: Mat3x1<f64>,
pub _radius: f64,
pub _bound: AxisAlignedBBox,
pub _vicinity: f64,
}
impl Sphere {
pub fn init(origin: &[f64], r: f64) -> Sphere {
assert!(origin.len() == 3);
Sphere {
_ori: Mat3x1 {
_val: [origin[0], origin[1], origin[2]],
},
_radius: r,
_bound: AxisAlignedBBox::init(ShapeType::Sphere, &[&origin[0..3], &[r]].concat()),
_vicinity: 0.000001f64,
}
}
}
impl IShape for Sphere {
fn get_shape_data(&self) -> Vec<f64> {
vec![self._ori[0], self._ori[1], self._ori[2], self._radius]
}
fn get_type(&self) -> ShapeType {
ShapeType::Sphere
}
fn get_bound(&self) -> &dyn IBound {
&self._bound
}
fn get_intersect(&self, other: &dyn IShape) -> (bool, Option<Mat3x1<f64>>) {
if !self.get_bound().intersect(other.get_bound()) {
return (false, None);
} else {
match other.get_type() {
ShapeType::Sphere => {
let other_shape_data = other.get_shape_data();
let b_off = Mat3x1 {
_val: [
other_shape_data[0],
other_shape_data[1],
other_shape_data[2],
],
};
let a_r = self._radius;
let b_r = other_shape_data[3];
let a_off = self._ori;
let c = b_off.minus(&a_off).unwrap();
let d = c.magnitude().unwrap();
if d > b_r + a_r {
return (false, None);
} else {
let f = a_r / (a_r + b_r);
let g = c.scale(f).unwrap();
return (true, Some(a_off.plus(&g).unwrap()));
}
}
ShapeType::Ray => {
return other.get_intersect(self);
}
ShapeType::Point => {
let other_shape_data = other.get_shape_data();
let b_off = Mat3x1 {
_val: [
other_shape_data[0],
other_shape_data[1],
other_shape_data[2],
],
};
let d = b_off.minus(&self._ori).unwrap();
for i in 0..3 {
if d[i] > self._radius {
return (false, None);
}
}
return (true, Some(b_off));
}
ShapeType::Plane => {
let other_shape_data = other.get_shape_data();
let b_off = Mat3x1 {
_val: [
other_shape_data[0],
other_shape_data[1],
other_shape_data[2],
],
};
let b_nor = Mat3x1 {
_val: [
other_shape_data[3],
other_shape_data[4],
other_shape_data[5],
],
};
let k = b_nor.dot(&b_off).unwrap();
let t = b_nor.dot(&self._ori).unwrap() - k;
if t > self._radius {
return (false, None);
} else {
return (
true,
Some(b_nor.scale(-t).unwrap().plus(&self._ori).unwrap()),
);
}
}
_ => {
unimplemented!();
}
}
}
}
fn get_support(&self, v: &Mat3x1<f64>) -> Option<Mat3x1<f64>> {
if v.magnitude() != Some(0f64) {
let v_adjusted = v
.normalize()
.expect("normalization unsuccessful")
.scale(self._radius)
.expect("scale unsuccessful");
let o = self
._ori
.plus(&v_adjusted)
.expect("support operation unsuccessful.");
Some(o)
} else {
None
}
}
}
impl IVicinity<f64> for Sphere {
fn set_vicinity(&mut self, epsilon: f64) {
self._vicinity = epsilon.abs();
}
fn within_vicinity(&self, a: f64, b: f64) -> bool {
if a + self._vicinity >= b && a - self._vicinity <= b {
true
} else {
false
}
}
}