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}