use super::vec::{Vector, RFloat};
use std::default::Default;
use super::primitive::{DistanceMeasure, Intersectable, Ray, Sphere, Hit};
pub enum Pair<I, G> {
Item(I),
Group(G),
}
pub type TypedGroupPair<B, I> = Pair<I, TypedGroup<B, I>>;
#[derive(Default)]
pub struct TypedGroup<B, I> {
pub bound: B,
pub children: Vec<TypedGroupPair<B, I>>,
}
impl SphericalGroup {
fn pyramid_recursive(level: u32, p: &Vector, r: RFloat) -> TypedGroupPair<Sphere, Sphere> {
let s = Sphere {
center: *p,
radius: r,
};
if level == 1 {
return Pair::Item(s);
}
let mut g: SphericalGroup = Default::default();
g.children.reserve(5);
g.children.push(Pair::Item(s));
g.bound.center = *p;
g.bound.radius = 3.0 * r;
let rn: RFloat = 3.0 * r / 12.0f32.sqrt();
for dz in [-1i32, 1].iter().cloned() {
for dx in [-1i32, 1].iter().cloned() {
let np = *p +
Vector {
x: dx as RFloat * rn,
y: rn,
z: dz as RFloat * rn,
};
g.children.push(SphericalGroup::pyramid_recursive(level - 1, &np, r * 0.5));
}
}
Pair::Group(g)
}
pub fn pyramid(level: u32, origin: &Vector, radius: RFloat) -> SphericalGroup {
assert!(level > 1,
"Levels equal or smaller than one cause empty groups");
match SphericalGroup::pyramid_recursive(level, origin, radius) {
Pair::Group(g) => g,
_ => unreachable!(),
}
}
}
impl<B, I> Intersectable for TypedGroup<B, I>
where B: DistanceMeasure,
I: Intersectable
{
fn intersect(&self, hit: &mut Hit, ray: &Ray) {
if self.bound.distance_from_ray(&ray) >= hit.distance {
return;
}
for item in self.children.iter() {
match *item {
Pair::Item(ref v) => v.intersect(hit, &ray),
Pair::Group(ref g) => g.intersect(hit, &ray),
};
}
}
}
pub type SphericalGroup = TypedGroup<Sphere, Sphere>;
#[cfg(test)]
mod tests {
extern crate test;
impl<B, I> TypedGroup<B, I> {
fn count(&self) -> (usize, usize) {
let mut ng = 1usize;
let mut ni = 0usize;
for item in self.children.iter() {
match *item {
Pair::Item(_) => ni += 1,
Pair::Group(ref g) => {
let (gng, gni) = g.count();
ng += gng;
ni += gni;
}
}
}
(ng, ni)
}
}
use super::*;
use super::super::primitive::Intersectable;
use super::super::vec::Vector;
use super::super::primitive::{Sphere, Ray, Hit};
use std::default::Default;
fn setup_group() -> (Ray, Ray, Ray, SphericalGroup) {
let s1 = Sphere {
center: Default::default(),
radius: 1.0,
};
let mut s2: Sphere = Default::default();
s2.center.z = s2.radius * 2.0;
let mut g: SphericalGroup = Default::default();
assert!(g.children.len() == 0);
g.children.push(Pair::Item(s1));
g.children.push(Pair::Item(s2));
g.bound.radius = 3.0; let g = g;
let mut r1: Ray = Default::default();
r1.pos.x = 2.0;
r1.dir.x = -1.0;
let r1 = r1;
let mut r2 = r1;
r2.pos.z = s2.center.z;
let r2 = r2;
let mut r3 = r1;
r3.dir.x = -r3.dir.x;
let r3 = r3;
(r1, r2, r3, g)
}
#[test]
fn intersect() {
let (r1, r2, r3, g) = setup_group();
for ray in [&r1, &r2].iter() {
let mut h = Hit::missed();
g.intersect(&mut h, &ray);
assert!(!h.has_missed());
assert_eq!(h.distance, 1.0);
assert_eq!(h.pos.x, 1.0);
assert_eq!(h.pos.z, 0.0);
}
let mut h = Hit::missed();
g.intersect(&mut h, &r3);
assert!(h.has_missed());
}
#[test]
fn pyramid() {
let g = SphericalGroup::pyramid(8,
&Vector {
x: 1.0,
y: -1.0,
z: 0.0,
},
1.0);
assert_eq!(g.children.len(), 5);
assert_eq!(g.count(), (5461, 21845));
}
const ITERATIONS: usize = 10000;
#[bench]
fn bench_intersect(b: &mut test::Bencher) {
let (r1, r2, r3, g) = setup_group();
let mut h = Hit::missed();
b.iter(|| {
for _ in 0..ITERATIONS {
test::black_box(g.intersect(&mut h, &r1));
h.set_missed();
test::black_box(g.intersect(&mut h, &r2));
h.set_missed();
test::black_box(g.intersect(&mut h, &r3));
h.set_missed();
}
});
b.bytes = (ITERATIONS * 3usize) as u64;
}
}