use alga::general::SupersetOf;
use nalgebra::{Point3, Scalar, Unit};
use num::{Bounded, One, Zero}; use std::ops::Range;
use clamp::{Clamped, ClampedRange};
use math::{self, FPoint3, FRay3, FScalar, FVector3, FromSpace, LowerBound, Mask, UPoint3, UScalar,
UVector3, UpperBound};
#[derive(Clone, Copy)]
pub struct LogWidthRange;
impl ClampedRange<u8> for LogWidthRange {
fn max_value() -> u8 {
31
}
fn min_value() -> u8 {
4
}
}
pub type LogWidth = Clamped<u8, LogWidthRange>;
impl LogWidth {
pub fn unit() -> Self {
LogWidth::new(8)
}
pub fn exp(&self) -> UScalar {
UScalar::one() << self.to_inner()
}
}
#[derive(Clone, Copy)]
pub enum Axis {
X = 0,
Y = 1,
Z = 2,
}
impl Axis {
pub fn range() -> Range<usize> {
(Axis::X as usize)..(Axis::Z as usize + 1)
}
pub fn to_vector(&self) -> FVector3 {
match *self {
Axis::X => FVector3::x(),
Axis::Y => FVector3::y(),
Axis::Z => FVector3::z(),
}
}
}
impl From<usize> for Axis {
fn from(index: usize) -> Self {
match index {
0 => Axis::X,
1 => Axis::Y,
2 => Axis::Z,
_ => panic!(), }
}
}
pub enum Direction {
Positive,
Negative,
}
pub enum Orientation {
Left,
Right,
Top,
Bottom,
Front,
Back,
}
impl Orientation {
pub fn axis(&self) -> Axis {
match *self {
Orientation::Left | Orientation::Right => Axis::X,
Orientation::Top | Orientation::Bottom => Axis::Y,
Orientation::Front | Orientation::Back => Axis::Z,
}
}
pub fn direction(&self) -> Direction {
match *self {
Orientation::Left | Orientation::Bottom | Orientation::Back => Direction::Positive,
Orientation::Right | Orientation::Top | Orientation::Front => Direction::Negative,
}
}
}
pub trait PointNormal {
fn normal(&self, point: &FPoint3) -> FVector3;
}
pub trait Intersects<T> {
fn intersects(&self, other: &T) -> bool;
}
pub struct RayIntersection {
pub distance: FScalar,
pub point: FPoint3,
pub normal: Unit<FVector3>,
}
impl RayIntersection {
fn new(distance: FScalar, point: FPoint3, normal: FVector3) -> Self {
RayIntersection {
distance: distance,
point: point,
normal: Unit::new_normalize(normal),
}
}
}
pub trait PartialRayCast {
fn partial_ray_intersection(&self, ray: &FRay3) -> Option<(FScalar, FScalar)>;
}
pub trait RayCast: PartialRayCast {
fn ray_intersection(&self, ray: &FRay3) -> Option<RayIntersection>;
}
impl<T> Intersects<FRay3> for T
where
T: PartialRayCast,
{
fn intersects(&self, ray: &FRay3) -> bool {
self.partial_ray_intersection(ray).is_some()
}
}
impl<T> RayCast for T
where
T: PartialRayCast + PointNormal,
{
fn ray_intersection(&self, ray: &FRay3) -> Option<RayIntersection> {
self.partial_ray_intersection(ray).map(|(distance, _)| {
let point = ray.origin + (*ray.direction * distance);
RayIntersection::new(distance, point, self.normal(&point))
})
}
}
pub struct AABB {
pub origin: UPoint3,
pub extent: UVector3,
}
impl AABB {
pub fn new(origin: UPoint3, extent: UVector3) -> Self {
AABB {
origin: origin,
extent: extent,
}
}
pub fn union(&self, other: &Self) -> Self {
let start = self.origin.lower_bound(&other.origin);
let end = self.endpoint().upper_bound(&other.endpoint());
AABB::new(start, end - start)
}
pub fn midpoint(&self) -> UPoint3 {
self.origin + (self.extent / 2)
}
pub fn endpoint(&self) -> UPoint3 {
self.origin + self.extent
}
}
impl Intersects<AABB> for AABB {
fn intersects(&self, aabb: &AABB) -> bool {
for axis in Axis::range() {
if (self.origin[axis] + self.extent[axis]) < aabb.origin[axis] {
return false;
}
if self.origin[axis] > (aabb.origin[axis] + aabb.extent[axis]) {
return false;
}
}
true
}
}
impl<T> Intersects<Point3<T>> for AABB
where
T: PartialOrd + Scalar + SupersetOf<UScalar>,
{
fn intersects(&self, point: &Point3<T>) -> bool {
use nalgebra::convert;
for axis in Axis::range() {
if convert::<UScalar, T>(self.origin[axis] + self.extent[axis]) < point[axis] {
return false;
}
if convert::<UScalar, T>(self.origin[axis]) > point[axis] {
return false;
}
}
true
}
}
impl PartialRayCast for AABB {
fn partial_ray_intersection(&self, ray: &FRay3) -> Option<(FScalar, FScalar)> {
let mut min = FVector3::zero();
let mut max = FVector3::zero();
for axis in Axis::range() {
let lower = self.origin[axis] as FScalar;
let upper = lower + self.extent[axis] as FScalar;
let origin = ray.origin[axis];
let direction = ray.direction[axis];
let (lower, upper) =
math::ordered_pair((lower - origin) / direction, (upper - origin) / direction);
min[axis] = lower;
max[axis] = upper;
}
let min = math::partial_max(math::partial_max(min.x, min.y), min.z);
let max = math::partial_min(math::partial_min(max.x, max.y), max.z);
if max < 0.0 || min > max {
None
}
else {
Some((min, max))
}
}
}
impl PointNormal for AABB {
fn normal(&self, point: &FPoint3) -> FVector3 {
let point = point - FPoint3::from_space(self.midpoint());
let mut min_distance = FScalar::max_value();
let mut normal = FVector3::zero();
for axis in Axis::range() {
let distance = (self.extent[axis] as FScalar - point[axis].abs()).abs();
if distance < min_distance {
min_distance = distance;
normal = Axis::from(axis).to_vector() * point[axis].signum();
}
}
normal
}
}
#[derive(Clone, Copy)]
pub struct Partition {
origin: UPoint3,
width: LogWidth,
}
impl Partition {
pub fn at_point(point: &UPoint3, width: LogWidth) -> Self {
Partition {
origin: point.mask(!UScalar::zero() << width.to_inner()),
width: width,
}
}
pub fn at_index(&self, index: usize) -> Option<Self> {
if self.is_min_width() {
None
}
else {
let width = self.width - 1;
Some(Partition {
origin: self.origin + vector_at_index(index, width),
width: width,
})
}
}
pub fn origin(&self) -> &UPoint3 {
&self.origin
}
pub fn width(&self) -> LogWidth {
self.width
}
pub fn midpoint(&self) -> UPoint3 {
let m = (self.width - 1).exp();
self.origin + UVector3::new(m, m, m)
}
pub fn extent(&self) -> UVector3 {
(UVector3::new(1, 1, 1) * self.width.exp()) - UVector3::new(1, 1, 1)
}
pub fn aabb(&self) -> AABB {
AABB::new(self.origin, self.extent())
}
pub fn is_min_width(&self) -> bool {
self.width == LogWidth::min_value()
}
}
pub trait Spatial {
fn partition(&self) -> &Partition;
fn depth(&self) -> u8;
fn aabb(&self) -> AABB {
self.partition().aabb()
}
}
#[cfg_attr(rustfmt, rustfmt_skip)]
pub fn index_at_point(point: &UPoint3, width: LogWidth) -> usize {
let width = width.to_inner();
(( (point.x >> width) & UScalar::one() ) |
(((point.y >> width) & UScalar::one()) << 1) |
(((point.z >> width) & UScalar::one()) << 2)) as usize
}
#[cfg_attr(rustfmt, rustfmt_skip)]
pub fn vector_at_index(index: usize, width: LogWidth) -> UVector3 {
assert!(index < 8);
let index = index as UScalar;
let width = width.exp();
UVector3::new(
( index & UScalar::one()) * width,
((index >> 1) & UScalar::one()) * width,
((index >> 2) & UScalar::one()) * width,
)
}