1use crate::{Intersection, NegativeHemisphereError, Plane, Polygon};
2
3use euclid::default::{Rect, Scale, Transform3D, Vector3D};
4
5use std::{fmt, iter, mem};
6
7#[derive(Debug)]
9pub struct Clipper<A> {
10 clips: Vec<Plane>,
11 results: Vec<Polygon<A>>,
12 temp: Vec<Polygon<A>>,
13}
14
15impl<A: Copy + fmt::Debug> Clipper<A> {
16 pub fn new() -> Self {
18 Clipper {
19 clips: Vec::new(),
20 results: Vec::new(),
21 temp: Vec::new(),
22 }
23 }
24
25 pub fn reset(&mut self) {
27 self.clips.clear();
28 }
29
30 pub fn frustum_planes(
32 t: &Transform3D<f64>,
33 bounds: Option<Rect<f64>>,
34 ) -> Result<impl Iterator<Item = Plane>, NegativeHemisphereError> {
35 let mw = Vector3D::new(t.m14, t.m24, t.m34);
36 let plane_positive = Plane::from_unnormalized(mw, t.m44)?;
37
38 let bounds_iter_maybe = match bounds {
39 Some(bounds) => {
40 let mx = Vector3D::new(t.m11, t.m21, t.m31);
41 let left = bounds.origin.x;
42 let plane_left =
43 Plane::from_unnormalized(mx - mw * Scale::new(left), t.m41 - t.m44 * left)?;
44 let right = bounds.origin.x + bounds.size.width;
45 let plane_right =
46 Plane::from_unnormalized(mw * Scale::new(right) - mx, t.m44 * right - t.m41)?;
47
48 let my = Vector3D::new(t.m12, t.m22, t.m32);
49 let top = bounds.origin.y;
50 let plane_top =
51 Plane::from_unnormalized(my - mw * Scale::new(top), t.m42 - t.m44 * top)?;
52 let bottom = bounds.origin.y + bounds.size.height;
53 let plane_bottom =
54 Plane::from_unnormalized(mw * Scale::new(bottom) - my, t.m44 * bottom - t.m42)?;
55
56 Some(
57 plane_left
58 .into_iter()
59 .chain(plane_right)
60 .chain(plane_top)
61 .chain(plane_bottom),
62 )
63 }
64 None => None,
65 };
66
67 Ok(bounds_iter_maybe
68 .into_iter()
69 .flat_map(|pi| pi)
70 .chain(plane_positive))
71 }
72
73 pub fn add(&mut self, plane: Plane) {
76 self.clips.push(plane);
77 }
78
79 pub fn clip(&mut self, polygon: Polygon<A>) -> &[Polygon<A>] {
81 log::debug!("\tClipping {:?}", polygon);
82 self.results.clear();
83 self.results.push(polygon);
84
85 for clip in &self.clips {
86 self.temp.clear();
87 mem::swap(&mut self.results, &mut self.temp);
88
89 for mut poly in self.temp.drain(..) {
90 let dist = match poly.intersect_plane(clip) {
91 Intersection::Inside(line) => {
92 let (res1, res2) = poly.split_with_normal(&line, &clip.normal);
93 self.results.extend(
94 iter::once(poly)
95 .chain(res1)
96 .chain(res2)
97 .filter(|p| clip.signed_distance_sum_to(p) > 0.0),
98 );
99 continue;
100 }
101 Intersection::Coplanar => {
102 let ndot = poly.plane.normal.dot(clip.normal);
103 clip.offset - ndot * poly.plane.offset
104 }
105 Intersection::Outside => clip.signed_distance_sum_to(&poly),
106 };
107
108 if dist > 0.0 {
109 self.results.push(poly);
110 }
111 }
112 }
113
114 &self.results
115 }
116
117 pub fn clip_transformed<'a>(
121 &'a mut self,
122 polygon: Polygon<A>,
123 transform: &'a Transform3D<f64>,
124 bounds: Option<Rect<f64>>,
125 ) -> Result<impl 'a + Iterator<Item = Polygon<A>>, NegativeHemisphereError> {
126 let planes = Self::frustum_planes(transform, bounds)?;
127
128 let old_count = self.clips.len();
129 self.clips.extend(planes);
130 self.clip(polygon);
131 while self.clips.len() > old_count {
133 self.clips.pop();
134 }
135
136 let polys = self
137 .results
138 .drain(..)
139 .flat_map(move |poly| poly.transform(transform));
140 Ok(polys)
141 }
142}