use {Intersection, Plane, Polygon};
use euclid::{Trig, TypedRect, TypedScale, TypedTransform3D, TypedVector3D};
use euclid::approxeq::ApproxEq;
use num_traits::{Float, One, Zero};
use std::{fmt, mem, ops};
#[derive(Debug)]
pub struct Clipper<T, U> {
clips: Vec<Plane<T, U>>,
results: Vec<Polygon<T, U>>,
temp: Vec<Polygon<T, U>>,
}
impl<
T: Copy + fmt::Debug + ApproxEq<T> +
ops::Sub<T, Output=T> + ops::Add<T, Output=T> +
ops::Mul<T, Output=T> + ops::Div<T, Output=T> +
Zero + One + Float,
U: fmt::Debug,
> Clipper<T, U> {
pub fn new() -> Self {
Clipper {
clips: Vec::new(),
results: Vec::new(),
temp: Vec::new(),
}
}
pub fn reset(&mut self) {
self.clips.clear();
}
pub fn add_frustum<V>(
&mut self,
t: &TypedTransform3D<T, U, V>,
bounds: Option<TypedRect<T, V>>,
) {
let mw = TypedVector3D::new(t.m14, t.m24, t.m34);
self.clips.extend(Plane::from_unnormalized(mw, t.m44));
if let Some(bounds) = bounds {
let mx = TypedVector3D::new(t.m11, t.m21, t.m31);
let left = bounds.origin.x;
self.clips.extend(Plane::from_unnormalized(
mx - mw * TypedScale::new(left),
t.m41 - t.m44 * left,
));
let right = bounds.origin.x + bounds.size.width;
self.clips.extend(Plane::from_unnormalized(
mw * TypedScale::new(right) - mx,
t.m44 * right - t.m41,
));
let my = TypedVector3D::new(t.m12, t.m22, t.m32);
let top = bounds.origin.y;
self.clips.extend(Plane::from_unnormalized(
my - mw * TypedScale::new(top),
t.m42 - t.m44 * top,
));
let bottom = bounds.origin.y + bounds.size.height;
self.clips.extend(Plane::from_unnormalized(
mw * TypedScale::new(bottom) - my,
t.m44 * bottom - t.m42,
));
}
}
pub fn add(&mut self, plane: Plane<T, U>) {
self.clips.push(plane);
}
pub fn clip(&mut self, polygon: Polygon<T, U>) -> &[Polygon<T, U>] {
self.results.clear();
self.results.push(polygon);
for clip in &self.clips {
self.temp.clear();
mem::swap(&mut self.results, &mut self.temp);
for mut poly in self.temp.drain(..) {
if let Intersection::Inside(line) = poly.intersect_plane(clip) {
let (res1, res2) = poly.split(&line);
self.results.extend(
res1
.into_iter()
.chain(res2)
.filter(|p| clip.signed_distance_sum_to(p) > T::zero())
);
}
if clip.signed_distance_sum_to(&poly) > T::zero() {
self.results.push(poly);
}
}
}
&self.results
}
pub fn clip_transformed<'a, V>(
&'a mut self,
polygon: Polygon<T, U>,
transform: &'a TypedTransform3D<T, U, V>,
bounds: Option<TypedRect<T, V>>,
) -> impl 'a + Iterator<Item = Polygon<T, V>>
where
T: Trig,
V: 'a + fmt::Debug,
{
let num_planes = if bounds.is_some() {5} else {1};
self.add_frustum(transform, bounds);
self.clip(polygon);
for _ in 0 .. num_planes {
self.clips.pop();
}
self.results
.drain(..)
.flat_map(move |poly| poly.transform(transform))
}
}