1use crate::math::{Real, Vector, DIM};
2use crate::query::{PointProjection, PointQuery, PointQueryWithLocation};
3use crate::shape::{FeatureId, Triangle, TrianglePointLocation};
4
5#[inline]
6fn compute_result(pt: Vector, proj: Vector) -> PointProjection {
7 #[cfg(feature = "dim2")]
8 {
9 PointProjection::new(pt == proj, proj)
10 }
11
12 #[cfg(feature = "dim3")]
13 {
14 PointProjection::new(relative_eq!(proj, pt), proj)
17 }
18}
19
20impl PointQuery for Triangle {
21 #[inline]
22 fn project_local_point(&self, pt: Vector, solid: bool) -> PointProjection {
23 self.project_local_point_and_get_location(pt, solid).0
24 }
25
26 #[inline]
27 fn project_local_point_and_get_feature(&self, pt: Vector) -> (PointProjection, FeatureId) {
28 let (proj, loc) = if DIM == 2 {
29 self.project_local_point_and_get_location(pt, false)
30 } else {
31 self.project_local_point_and_get_location(pt, true)
32 };
33
34 let feature = match loc {
35 TrianglePointLocation::OnVertex(i) => FeatureId::Vertex(i),
36 #[cfg(feature = "dim3")]
37 TrianglePointLocation::OnEdge(i, _) => FeatureId::Edge(i),
38 #[cfg(feature = "dim2")]
39 TrianglePointLocation::OnEdge(i, _) => FeatureId::Face(i),
40 TrianglePointLocation::OnFace(i, _) => FeatureId::Face(i),
41 TrianglePointLocation::OnSolid => FeatureId::Face(0),
42 };
43
44 (proj, feature)
45 }
46
47 }
50
51impl PointQueryWithLocation for Triangle {
52 type Location = TrianglePointLocation;
53
54 #[inline]
55 fn project_local_point_and_get_location(
56 &self,
57 pt: Vector,
58 solid: bool,
59 ) -> (PointProjection, Self::Location) {
60 let a = self.a;
64 let b = self.b;
65 let c = self.c;
66
67 let ab = b - a;
68 let ac = c - a;
69 let ap = pt - a;
70
71 let ab_ap = ab.dot(ap);
72 let ac_ap = ac.dot(ap);
73
74 if ab_ap <= 0.0 && ac_ap <= 0.0 {
75 return (compute_result(pt, a), TrianglePointLocation::OnVertex(0));
77 }
78
79 let bp = pt - b;
80 let ab_bp = ab.dot(bp);
81 let ac_bp = ac.dot(bp);
82
83 if ab_bp >= 0.0 && ac_bp <= ab_bp {
84 return (compute_result(pt, b), TrianglePointLocation::OnVertex(1));
86 }
87
88 let cp = pt - c;
89 let ab_cp = ab.dot(cp);
90 let ac_cp = ac.dot(cp);
91
92 if ac_cp >= 0.0 && ab_cp <= ac_cp {
93 return (compute_result(pt, c), TrianglePointLocation::OnVertex(2));
95 }
96
97 enum ProjectionInfo {
98 OnAB,
99 OnAC,
100 OnBC,
101 OnFace(usize, Real, Real, Real),
103 }
104
105 fn stable_check_edges_voronoi(
109 ab: Vector,
110 ac: Vector,
111 bc: Vector,
112 ap: Vector,
113 bp: Vector,
114 cp: Vector,
115 ab_ap: Real,
116 ab_bp: Real,
117 ac_ap: Real,
118 ac_cp: Real,
119 ac_bp: Real,
120 ab_cp: Real,
121 ) -> ProjectionInfo {
122 #[cfg(feature = "dim2")]
123 {
124 let n = ab.perp_dot(ac);
125 let vc = n * ab.perp_dot(ap);
126 if vc < 0.0 && ab_ap >= 0.0 && ab_bp <= 0.0 {
127 return ProjectionInfo::OnAB;
128 }
129
130 let vb = -n * ac.perp_dot(cp);
131 if vb < 0.0 && ac_ap >= 0.0 && ac_cp <= 0.0 {
132 return ProjectionInfo::OnAC;
133 }
134
135 let va = n * bc.perp_dot(bp);
136 if va < 0.0 && ac_bp - ab_bp >= 0.0 && ab_cp - ac_cp >= 0.0 {
137 return ProjectionInfo::OnBC;
138 }
139
140 ProjectionInfo::OnFace(0, va, vb, vc)
141 }
142 #[cfg(feature = "dim3")]
143 {
144 let n;
145
146 #[cfg(feature = "improved_fixed_point_support")]
147 {
148 let scaled_n = ab.cross(ac);
149 n = scaled_n.try_normalize(0.0).unwrap_or(scaled_n);
150 }
151
152 #[cfg(not(feature = "improved_fixed_point_support"))]
153 {
154 n = ab.cross(ac);
155 }
156
157 let vc = n.dot(ab.cross(ap));
158 if vc < 0.0 && ab_ap >= 0.0 && ab_bp <= 0.0 {
159 return ProjectionInfo::OnAB;
160 }
161
162 let vb = -n.dot(ac.cross(cp));
163 if vb < 0.0 && ac_ap >= 0.0 && ac_cp <= 0.0 {
164 return ProjectionInfo::OnAC;
165 }
166
167 let va = n.dot(bc.cross(bp));
168 if va < 0.0 && ac_bp - ab_bp >= 0.0 && ab_cp - ac_cp >= 0.0 {
169 return ProjectionInfo::OnBC;
170 }
171
172 let clockwise = if n.dot(ap) >= 0.0 { 0 } else { 1 };
173
174 ProjectionInfo::OnFace(clockwise, va, vb, vc)
175 }
176 }
177
178 let bc = c - b;
179 match stable_check_edges_voronoi(
180 ab, ac, bc, ap, bp, cp, ab_ap, ab_bp, ac_ap, ac_cp, ac_bp, ab_cp,
181 ) {
182 ProjectionInfo::OnAB => {
183 let v = ab_ap / ab.length_squared();
185 let bcoords = [1.0 - v, v];
186
187 let res = a + ab * v;
188 return (
189 compute_result(pt, res),
190 TrianglePointLocation::OnEdge(0, bcoords),
191 );
192 }
193 ProjectionInfo::OnAC => {
194 let w = ac_ap / ac.length_squared();
196 let bcoords = [1.0 - w, w];
197
198 let res = a + ac * w;
199 return (
200 compute_result(pt, res),
201 TrianglePointLocation::OnEdge(2, bcoords),
202 );
203 }
204 ProjectionInfo::OnBC => {
205 let w = bc.dot(bp) / bc.length_squared();
207 let bcoords = [1.0 - w, w];
208
209 let res = b + bc * w;
210 return (
211 compute_result(pt, res),
212 TrianglePointLocation::OnEdge(1, bcoords),
213 );
214 }
215 ProjectionInfo::OnFace(face_side, va, vb, vc) => {
216 if DIM != 2 {
218 if va + vb + vc != 0.0 {
222 let denom = 1.0 / (va + vb + vc);
223 let v = vb * denom;
224 let w = vc * denom;
225 let bcoords = [1.0 - v - w, v, w];
226 let res = a + ab * v + ac * w;
227
228 return (
229 compute_result(pt, res),
230 TrianglePointLocation::OnFace(face_side as u32, bcoords),
231 );
232 }
233 }
234 }
235 }
236
237 if solid {
240 (
241 PointProjection::new(true, pt),
242 TrianglePointLocation::OnSolid,
243 )
244 } else {
245 let v = ab_ap / (ab_ap - ab_bp); let w = ac_ap / (ac_ap - ac_cp); let u = (ac_bp - ab_bp) / (ac_bp - ab_bp + ab_cp - ac_cp); let bc = c - b;
254 let d_ab = ap.length_squared() - (ab.length_squared() * v * v);
255 let d_ac = ap.length_squared() - (ac.length_squared() * w * w);
256 let d_bc = bp.length_squared() - (bc.length_squared() * u * u);
257
258 let proj;
259 let loc;
260
261 if d_ab < d_ac {
262 if d_ab < d_bc {
263 let bcoords = [1.0 - v, v];
265 proj = a + ab * v;
266 loc = TrianglePointLocation::OnEdge(0, bcoords);
267 } else {
268 let bcoords = [1.0 - u, u];
270 proj = b + bc * u;
271 loc = TrianglePointLocation::OnEdge(1, bcoords);
272 }
273 } else if d_ac < d_bc {
274 let bcoords = [1.0 - w, w];
276 proj = a + ac * w;
277 loc = TrianglePointLocation::OnEdge(2, bcoords);
278 } else {
279 let bcoords = [1.0 - u, u];
281 proj = b + bc * u;
282 loc = TrianglePointLocation::OnEdge(1, bcoords);
283 }
284
285 (PointProjection::new(true, proj), loc)
286 }
287 }
288}