#![warn(missing_docs)]
extern crate binary_space_partition;
extern crate euclid;
#[macro_use]
extern crate log;
extern crate num_traits;
mod bsp;
mod clip;
mod polygon;
use euclid::{Point3D, Scale, Vector3D};
use euclid::approxeq::ApproxEq;
use num_traits::{Float, One, Zero};
use std::ops;
pub use self::bsp::BspSplitter;
pub use self::clip::Clipper;
pub use self::polygon::{Intersection, LineProjection, Polygon};
fn is_zero<T>(value: T) -> bool where
T: Copy + Zero + ApproxEq<T> + ops::Mul<T, Output=T> {
(value * value).approx_eq(&T::zero())
}
fn is_zero_vec<T, U>(vec: Vector3D<T, U>) -> bool where
T: Copy + Zero + ApproxEq<T> +
ops::Add<T, Output=T> + ops::Sub<T, Output=T> + ops::Mul<T, Output=T> {
vec.dot(vec).approx_eq(&T::zero())
}
#[derive(Debug)]
pub struct Line<T, U> {
pub origin: Point3D<T, U>,
pub dir: Vector3D<T, U>,
}
impl<T, U> Line<T, U> where
T: Copy + One + Zero + ApproxEq<T> +
ops::Add<T, Output=T> + ops::Sub<T, Output=T> + ops::Mul<T, Output=T>
{
pub fn is_valid(&self) -> bool {
is_zero(self.dir.dot(self.dir) - T::one())
}
pub fn matches(&self, other: &Self) -> bool {
let diff = self.origin - other.origin;
is_zero_vec(self.dir.cross(other.dir)) &&
is_zero_vec(self.dir.cross(diff))
}
fn intersect_edge(
&self,
edge: ops::Range<Point3D<T, U>>,
) -> Option<T>
where T: ops::Div<T, Output=T>
{
let edge_vec = edge.end - edge.start;
let origin_vec = self.origin - edge.start;
let pr = origin_vec - self.dir * self.dir.dot(origin_vec);
let pb = edge_vec - self.dir * self.dir.dot(edge_vec);
let denom = pb.dot(pb);
if denom.approx_eq(&T::zero()) {
None
} else {
Some(pr.dot(pb) / denom)
}
}
}
#[derive(Debug, PartialEq)]
pub struct Plane<T, U> {
pub normal: Vector3D<T, U>,
pub offset: T,
}
impl<T: Clone, U> Clone for Plane<T, U> {
fn clone(&self) -> Self {
Plane {
normal: self.normal.clone(),
offset: self.offset.clone(),
}
}
}
#[derive(Clone, Debug, Hash, PartialEq, PartialOrd)]
pub struct NegativeHemisphereError;
impl<
T: Copy + Zero + One + Float + ApproxEq<T> +
ops::Sub<T, Output=T> + ops::Add<T, Output=T> +
ops::Mul<T, Output=T> + ops::Div<T, Output=T>,
U,
> Plane<T, U> {
pub fn from_unnormalized(
normal: Vector3D<T, U>, offset: T
) -> Result<Option<Self>, NegativeHemisphereError> {
let square_len = normal.square_length();
if square_len < T::approx_epsilon() * T::approx_epsilon() {
if offset > T::zero() {
Ok(None)
} else {
Err(NegativeHemisphereError)
}
} else {
let kf = T::one() / square_len.sqrt();
Ok(Some(Plane {
normal: normal * Scale::new(kf),
offset: offset * kf,
}))
}
}
pub fn contains(&self, other: &Self) -> bool {
self.normal == other.normal && self.offset == other.offset
}
pub fn signed_distance_to(&self, point: &Point3D<T, U>) -> T {
point.to_vector().dot(self.normal) + self.offset
}
pub fn distance_to_line(&self, line: &Line<T, U>) -> T
where T: ops::Neg<Output=T> {
self.signed_distance_to(&line.origin) / -self.normal.dot(line.dir)
}
pub fn signed_distance_sum_to<A>(&self, poly: &Polygon<T, U, A>) -> T {
poly.points
.iter()
.fold(T::zero(), |u, p| u + self.signed_distance_to(p))
}
pub fn are_outside(&self, points: &[Point3D<T, U>]) -> bool {
let d0 = self.signed_distance_to(&points[0]);
points[1..]
.iter()
.all(|p| self.signed_distance_to(p) * d0 > T::zero())
}
pub fn intersect(&self, other: &Self) -> Option<Line<T, U>> {
let w = self.normal.dot(other.normal);
let divisor = T::one() - w * w;
if divisor < T::approx_epsilon() * T::approx_epsilon() {
return None
}
let origin = Point3D::origin() +
self.normal * ((other.offset * w - self.offset) / divisor) -
other.normal* ((other.offset - self.offset * w) / divisor);
let cross_dir = self.normal.cross(other.normal);
Some(Line {
origin,
dir: cross_dir.normalize(),
})
}
}
pub trait Splitter<T, U, A> {
fn reset(&mut self);
fn add(&mut self, polygon: Polygon<T, U, A>);
fn sort(&mut self, view: Vector3D<T, U>) -> &[Polygon<T, U, A>];
fn solve(
&mut self,
input: &[Polygon<T, U, A>],
view: Vector3D<T, U>,
) -> &[Polygon<T, U, A>]
where
T: Clone,
U: Clone,
A: Copy,
{
self.reset();
for p in input {
self.add(p.clone());
}
self.sort(view)
}
}
#[doc(hidden)]
pub fn make_grid(count: usize) -> Vec<Polygon<f32, (), usize>> {
let mut polys: Vec<Polygon<f32, (), usize>> = Vec::with_capacity(count*3);
let len = count as f32;
polys.extend((0 .. count).map(|i| Polygon {
points: [
Point3D::new(0.0, i as f32, 0.0),
Point3D::new(len, i as f32, 0.0),
Point3D::new(len, i as f32, len),
Point3D::new(0.0, i as f32, len),
],
plane: Plane {
normal: Vector3D::new(0.0, 1.0, 0.0),
offset: -(i as f32),
},
anchor: 0,
}));
polys.extend((0 .. count).map(|i| Polygon {
points: [
Point3D::new(i as f32, 0.0, 0.0),
Point3D::new(i as f32, len, 0.0),
Point3D::new(i as f32, len, len),
Point3D::new(i as f32, 0.0, len),
],
plane: Plane {
normal: Vector3D::new(1.0, 0.0, 0.0),
offset: -(i as f32),
},
anchor: 0,
}));
polys.extend((0 .. count).map(|i| Polygon {
points: [
Point3D::new(0.0, 0.0, i as f32),
Point3D::new(len, 0.0, i as f32),
Point3D::new(len, len, i as f32),
Point3D::new(0.0, len, i as f32),
],
plane: Plane {
normal: Vector3D::new(0.0, 0.0, 1.0),
offset: -(i as f32),
},
anchor: 0,
}));
polys
}