1use crate::math::{Real, Vector};
2use crate::query::{PointProjection, PointQuery, PointQueryWithLocation};
3use crate::shape::{FeatureId, Tetrahedron, TetrahedronPointLocation};
4
5impl PointQuery for Tetrahedron {
6 #[inline]
7 fn project_local_point(&self, pt: Vector, solid: bool) -> PointProjection {
8 self.project_local_point_and_get_location(pt, solid).0
9 }
10
11 #[inline]
12 fn project_local_point_and_get_feature(&self, pt: Vector) -> (PointProjection, FeatureId) {
13 let (proj, loc) = self.project_local_point_and_get_location(pt, false);
14 let feature = match loc {
15 TetrahedronPointLocation::OnVertex(i) => FeatureId::Vertex(i),
16 TetrahedronPointLocation::OnEdge(i, _) => FeatureId::Edge(i),
17 TetrahedronPointLocation::OnFace(i, _) => FeatureId::Face(i),
18 TetrahedronPointLocation::OnSolid => unreachable!(),
19 };
20
21 (proj, feature)
22 }
23}
24
25impl PointQueryWithLocation for Tetrahedron {
26 type Location = TetrahedronPointLocation;
27
28 #[inline]
29 fn project_local_point_and_get_location(
30 &self,
31 pt: Vector,
32 solid: bool,
33 ) -> (PointProjection, Self::Location) {
34 let ab = self.b - self.a;
35 let ac = self.c - self.a;
36 let ad = self.d - self.a;
37 let ap = pt - self.a;
38
39 let ap_ab = ap.dot(ab);
43 let ap_ac = ap.dot(ac);
44 let ap_ad = ap.dot(ad);
45
46 if ap_ab <= 0.0 && ap_ac <= 0.0 && ap_ad <= 0.0 {
47 let proj = PointProjection::new(false, self.a);
49 return (proj, TetrahedronPointLocation::OnVertex(0));
50 }
51
52 let bc = self.c - self.b;
53 let bd = self.d - self.b;
54 let bp = pt - self.b;
55
56 let bp_bc = bp.dot(bc);
57 let bp_bd = bp.dot(bd);
58 let bp_ab = bp.dot(ab);
59
60 if bp_bc <= 0.0 && bp_bd <= 0.0 && bp_ab >= 0.0 {
61 let proj = PointProjection::new(false, self.b);
63 return (proj, TetrahedronPointLocation::OnVertex(1));
64 }
65
66 let cd = self.d - self.c;
67 let cp = pt - self.c;
68
69 let cp_ac = cp.dot(ac);
70 let cp_bc = cp.dot(bc);
71 let cp_cd = cp.dot(cd);
72
73 if cp_cd <= 0.0 && cp_bc >= 0.0 && cp_ac >= 0.0 {
74 let proj = PointProjection::new(false, self.c);
76 return (proj, TetrahedronPointLocation::OnVertex(2));
77 }
78
79 let dp = pt - self.d;
80
81 let dp_cd = dp.dot(cd);
82 let dp_bd = dp.dot(bd);
83 let dp_ad = dp.dot(ad);
84
85 if dp_ad >= 0.0 && dp_bd >= 0.0 && dp_cd >= 0.0 {
86 let proj = PointProjection::new(false, self.d);
88 return (proj, TetrahedronPointLocation::OnVertex(3));
89 }
90
91 #[inline(always)]
95 fn check_edge(
96 i: usize,
97 a: Vector,
98 _: Vector,
99 nabc: Vector,
100 nabd: Vector,
101 ap: Vector,
102 ab: Vector,
103 ap_ab: Real, bp_ab: Real, ) -> (
106 Real,
107 Real,
108 Option<(PointProjection, TetrahedronPointLocation)>,
109 ) {
110 let ab_ab = ap_ab - bp_ab;
111
112 let ap_x_ab = ap.cross(ab);
119 let dabc = ap_x_ab.dot(nabc);
120 let dabd = ap_x_ab.dot(nabd);
121
122 if ab_ab != 0.0 && dabc >= 0.0 && dabd >= 0.0 && ap_ab >= 0.0 && ap_ab <= ab_ab {
124 let u = ap_ab / ab_ab;
126 let bcoords = [1.0 - u, u];
127 let res = a + ab * u;
128 let proj = PointProjection::new(false, res);
129 (
130 dabc,
131 dabd,
132 Some((proj, TetrahedronPointLocation::OnEdge(i as u32, bcoords))),
133 )
134 } else {
135 (dabc, dabd, None)
136 }
137 }
138
139 let nabc = ab.cross(ac);
143 let nabd = ab.cross(ad);
144 let (dabc, dabd, res) = check_edge(
145 0, self.a, self.b, nabc, nabd, ap, ab, ap_ab,
146 bp_ab, );
148 if let Some(res) = res {
149 return res;
150 }
151
152 let nacd = ac.cross(ad);
160 let (dacd, dacb, res) = check_edge(
161 1, self.a, self.c, nacd, -nabc, ap, ac, ap_ac,
162 cp_ac, );
164 if let Some(res) = res {
165 return res;
166 }
167
168 let (dadb, dadc, res) = check_edge(
176 2, self.a, self.d, -nabd, -nacd, ap, ad, ap_ad,
177 dp_ad, );
179 if let Some(res) = res {
180 return res;
181 }
182
183 let nbcd = bc.cross(bd);
190 let (dbca, dbcd, res) = check_edge(
192 3, self.b, self.c, nabc, nbcd, bp, bc, bp_bc,
193 cp_bc, );
195 if let Some(res) = res {
196 return res;
197 }
198
199 let (dbdc, dbda, res) = check_edge(
209 4, self.b, self.d, -nbcd, nabd, bp, bd, bp_bd,
210 dp_bd, );
212 if let Some(res) = res {
213 return res;
214 }
215
216 let (dcda, dcdb, res) = check_edge(
225 5, self.c, self.d, nacd, nbcd, cp, cd, cp_cd,
226 dp_cd, );
228 if let Some(res) = res {
229 return res;
230 }
231
232 #[inline(always)]
236 fn check_face(
237 i: usize,
238 a: Vector,
239 b: Vector,
240 c: Vector,
241 ap: Vector,
242 bp: Vector,
243 cp: Vector,
244 ab: Vector,
245 ac: Vector,
246 ad: Vector,
247 dabc: Real,
248 dbca: Real,
249 dacb: Real,
250 ) -> Option<(PointProjection, TetrahedronPointLocation)> {
253 if dabc < 0.0 && dbca < 0.0 && dacb < 0.0 {
254 let n = ab.cross(ac); if n.dot(ad) * n.dot(ap) < 0.0 {
256 let normal = n.try_normalize()?;
271 let vc = normal.dot(ap.cross(bp));
272 let va = normal.dot(bp.cross(cp));
273 let vb = normal.dot(cp.cross(ap));
274
275 let denom = va + vb + vc;
276 assert!(denom != 0.0);
277 let inv_denom = 1.0 / denom;
278
279 let bcoords = [va * inv_denom, vb * inv_denom, vc * inv_denom];
280 let res = a * bcoords[0] + b * bcoords[1] + c * bcoords[2];
281 let proj = PointProjection::new(false, res);
282
283 return Some((proj, TetrahedronPointLocation::OnFace(i as u32, bcoords)));
284 }
285 }
286 None
287 }
288
289 if let Some(res) = check_face(
291 0, self.a, self.b, self.c, ap, bp, cp, ab, ac, ad, dabc, dbca,
292 dacb,
293 ) {
296 return res;
297 }
298
299 if let Some(res) = check_face(
301 1, self.a, self.b, self.d, ap, bp, dp, ab, ad, ac, dadb, dabd,
302 dbda,
303 ) {
306 return res;
307 }
308 if let Some(res) = check_face(
310 2, self.a, self.c, self.d, ap, cp, dp, ac, ad, ab, dacd, dcda,
311 dadc,
312 ) {
315 return res;
316 }
317 if let Some(res) = check_face(
319 3, self.b, self.c, self.d, bp, cp, dp, bc, bd, -ab, dbcd, dcdb,
320 dbdc,
321 ) {
324 return res;
325 }
326
327 if !solid {
328 unimplemented!(
330 "Non-solid ray-cast/point projection on a tetrahedron is not yet implemented."
331 )
332 }
333
334 let proj = PointProjection::new(true, pt);
335 (proj, TetrahedronPointLocation::OnSolid)
336 }
337}