1#![warn(missing_docs)]
12
13mod bsp;
14mod clip;
15mod polygon;
16
17pub use polygon::PlaneCut;
18
19use euclid::{
20    approxeq::ApproxEq,
21    default::{Point3D, Scale, Vector3D},
22};
23
24use std::ops;
25
26pub use self::bsp::BspSplitter;
27pub use self::clip::Clipper;
28pub use self::polygon::{Intersection, LineProjection, Polygon};
29
30fn is_zero(value: f64) -> bool {
31    (value * value).approx_eq(&0.0)
33}
34
35fn is_zero_vec(vec: Vector3D<f64>) -> bool {
36    vec.dot(vec).approx_eq(&0.0)
37}
38
39#[derive(Debug)]
41pub struct Line {
42    pub origin: Point3D<f64>,
44    pub dir: Vector3D<f64>,
46}
47
48impl Line {
49    pub fn is_valid(&self) -> bool {
51        is_zero(self.dir.dot(self.dir) - 1.0)
52    }
53    pub fn matches(&self, other: &Self) -> bool {
55        let diff = self.origin - other.origin;
56        is_zero_vec(self.dir.cross(other.dir)) && is_zero_vec(self.dir.cross(diff))
57    }
58
59    fn intersect_edge(&self, edge: ops::Range<Point3D<f64>>) -> Option<f64> {
62        let edge_vec = edge.end - edge.start;
63        let origin_vec = self.origin - edge.start;
64        let pr = origin_vec - self.dir * self.dir.dot(origin_vec);
69        let pb = edge_vec - self.dir * self.dir.dot(edge_vec);
70        let denom = pb.dot(pb);
71        if denom.approx_eq(&0.0) {
72            None
73        } else {
74            Some(pr.dot(pb) / denom)
75        }
76    }
77}
78
79#[derive(Debug, PartialEq)]
84pub struct Plane {
85    pub normal: Vector3D<f64>,
87    pub offset: f64,
90}
91
92impl Clone for Plane {
93    fn clone(&self) -> Self {
94        Plane {
95            normal: self.normal.clone(),
96            offset: self.offset.clone(),
97        }
98    }
99}
100
101#[derive(Clone, Debug, Hash, PartialEq, PartialOrd)]
104pub struct NegativeHemisphereError;
105
106impl Plane {
107    pub fn from_unnormalized(
109        normal: Vector3D<f64>,
110        offset: f64,
111    ) -> Result<Option<Self>, NegativeHemisphereError> {
112        let square_len = normal.square_length();
113        if square_len < f64::approx_epsilon() * f64::approx_epsilon() {
114            if offset > 0.0 {
115                Ok(None)
116            } else {
117                Err(NegativeHemisphereError)
118            }
119        } else {
120            let kf = 1.0 / square_len.sqrt();
121            Ok(Some(Plane {
122                normal: normal * Scale::new(kf),
123                offset: offset * kf,
124            }))
125        }
126    }
127
128    pub fn contains(&self, other: &Self) -> bool {
130        self.normal == other.normal && self.offset == other.offset
132    }
133
134    pub fn signed_distance_to(&self, point: &Point3D<f64>) -> f64 {
138        point.to_vector().dot(self.normal) + self.offset
139    }
140
141    pub fn distance_to_line(&self, line: &Line) -> f64 {
144        self.signed_distance_to(&line.origin) / -self.normal.dot(line.dir)
145    }
146
147    pub fn signed_distance_sum_to<A>(&self, poly: &Polygon<A>) -> f64 {
151        poly.points
152            .iter()
153            .fold(0.0, |u, p| u + self.signed_distance_to(p))
154    }
155
156    pub fn are_outside(&self, points: &[Point3D<f64>]) -> bool {
160        let d0 = self.signed_distance_to(&points[0]);
161        points[1..]
162            .iter()
163            .all(|p| self.signed_distance_to(p) * d0 > 0.0)
164    }
165
166    pub fn intersect(&self, other: &Self) -> Option<Line> {
169        let w = self.normal.dot(other.normal);
175        let divisor = 1.0 - w * w;
176        if divisor < f64::approx_epsilon() * f64::approx_epsilon() {
177            return None;
178        }
179        let origin = Point3D::origin() + self.normal * ((other.offset * w - self.offset) / divisor)
180            - other.normal * ((other.offset - self.offset * w) / divisor);
181
182        let cross_dir = self.normal.cross(other.normal);
183        Some(Line {
187            origin,
188            dir: cross_dir.normalize(),
189        })
190    }
191}
192
193#[doc(hidden)]
196pub fn make_grid(count: usize) -> Vec<Polygon<usize>> {
197    let mut polys: Vec<Polygon<usize>> = Vec::with_capacity(count * 3);
198    let len = count as f64;
199    polys.extend((0..count).map(|i| Polygon {
200        points: [
201            Point3D::new(0.0, i as f64, 0.0),
202            Point3D::new(len, i as f64, 0.0),
203            Point3D::new(len, i as f64, len),
204            Point3D::new(0.0, i as f64, len),
205        ],
206        plane: Plane {
207            normal: Vector3D::new(0.0, 1.0, 0.0),
208            offset: -(i as f64),
209        },
210        anchor: 0,
211    }));
212    polys.extend((0..count).map(|i| Polygon {
213        points: [
214            Point3D::new(i as f64, 0.0, 0.0),
215            Point3D::new(i as f64, len, 0.0),
216            Point3D::new(i as f64, len, len),
217            Point3D::new(i as f64, 0.0, len),
218        ],
219        plane: Plane {
220            normal: Vector3D::new(1.0, 0.0, 0.0),
221            offset: -(i as f64),
222        },
223        anchor: 0,
224    }));
225    polys.extend((0..count).map(|i| Polygon {
226        points: [
227            Point3D::new(0.0, 0.0, i as f64),
228            Point3D::new(len, 0.0, i as f64),
229            Point3D::new(len, len, i as f64),
230            Point3D::new(0.0, len, i as f64),
231        ],
232        plane: Plane {
233            normal: Vector3D::new(0.0, 0.0, 1.0),
234            offset: -(i as f64),
235        },
236        anchor: 0,
237    }));
238    polys
239}