1use crate::math::{Real, Vector, DIM};
2use crate::query::{PointProjection, PointQuery, PointQueryWithLocation};
3use crate::shape::{FeatureId, Segment, SegmentPointLocation, Triangle, TrianglePointLocation};
4#[cfg(feature = "dim3")]
5use crate::utils::relative_eq_vector;
6
7#[inline]
8fn compute_result(pt: Vector, proj: Vector) -> PointProjection {
9 #[cfg(feature = "dim2")]
10 {
11 PointProjection::new(pt == proj, proj)
12 }
13
14 #[cfg(feature = "dim3")]
15 {
16 let inside = relative_eq_vector(proj, pt);
19 PointProjection::new(inside, proj)
20 }
21}
22
23impl PointQuery for Triangle {
24 #[inline]
25 fn project_local_point(&self, pt: Vector, solid: bool) -> PointProjection {
26 self.project_local_point_and_get_location(pt, solid).0
27 }
28
29 #[inline]
30 fn project_local_point_and_get_feature(&self, pt: Vector) -> (PointProjection, FeatureId) {
31 let (proj, loc) = if DIM == 2 {
32 self.project_local_point_and_get_location(pt, false)
33 } else {
34 self.project_local_point_and_get_location(pt, true)
35 };
36
37 let feature = match loc {
38 TrianglePointLocation::OnVertex(i) => FeatureId::Vertex(i),
39 #[cfg(feature = "dim3")]
40 TrianglePointLocation::OnEdge(i, _) => FeatureId::Edge(i),
41 #[cfg(feature = "dim2")]
42 TrianglePointLocation::OnEdge(i, _) => FeatureId::Face(i),
43 TrianglePointLocation::OnFace(i, _) => FeatureId::Face(i),
44 TrianglePointLocation::OnSolid => FeatureId::Face(0),
45 };
46
47 (proj, feature)
48 }
49
50 }
53
54impl PointQueryWithLocation for Triangle {
55 type Location = TrianglePointLocation;
56
57 #[inline]
58 fn project_local_point_and_get_location(
59 &self,
60 pt: Vector,
61 solid: bool,
62 ) -> (PointProjection, Self::Location) {
63 let a = self.a;
67 let b = self.b;
68 let c = self.c;
69
70 let ab = b - a;
71 let ac = c - a;
72 let ap = pt - a;
73
74 let ab_ap = ab.dot(ap);
75 let ac_ap = ac.dot(ap);
76
77 if ab_ap <= 0.0 && ac_ap <= 0.0 {
78 return (compute_result(pt, a), TrianglePointLocation::OnVertex(0));
80 }
81
82 let bp = pt - b;
83 let ab_bp = ab.dot(bp);
84 let ac_bp = ac.dot(bp);
85
86 if ab_bp >= 0.0 && ac_bp <= ab_bp {
87 return (compute_result(pt, b), TrianglePointLocation::OnVertex(1));
89 }
90
91 let cp = pt - c;
92 let ab_cp = ab.dot(cp);
93 let ac_cp = ac.dot(cp);
94
95 if ac_cp >= 0.0 && ab_cp <= ac_cp {
96 return (compute_result(pt, c), TrianglePointLocation::OnVertex(2));
98 }
99
100 enum ProjectionInfo {
101 OnAB,
102 OnAC,
103 OnBC,
104 OnFace(usize, Real, Real, Real),
106 }
107
108 fn stable_check_edges_voronoi(
112 ab: Vector,
113 ac: Vector,
114 bc: Vector,
115 ap: Vector,
116 bp: Vector,
117 cp: Vector,
118 ab_ap: Real,
119 ab_bp: Real,
120 ac_ap: Real,
121 ac_cp: Real,
122 ac_bp: Real,
123 ab_cp: Real,
124 ) -> ProjectionInfo {
125 #[cfg(feature = "dim2")]
126 {
127 let n = ab.perp_dot(ac);
128 let vc = n * ab.perp_dot(ap);
129 if vc < 0.0 && ab_ap >= 0.0 && ab_bp <= 0.0 {
130 return ProjectionInfo::OnAB;
131 }
132
133 let vb = -n * ac.perp_dot(cp);
134 if vb < 0.0 && ac_ap >= 0.0 && ac_cp <= 0.0 {
135 return ProjectionInfo::OnAC;
136 }
137
138 let va = n * bc.perp_dot(bp);
139 if va < 0.0 && ac_bp - ab_bp >= 0.0 && ab_cp - ac_cp >= 0.0 {
140 return ProjectionInfo::OnBC;
141 }
142
143 ProjectionInfo::OnFace(0, va, vb, vc)
144 }
145 #[cfg(feature = "dim3")]
146 {
147 let n;
148
149 #[cfg(feature = "improved_fixed_point_support")]
150 {
151 let scaled_n = ab.cross(ac);
152 n = scaled_n.try_normalize(0.0).unwrap_or(scaled_n);
153 }
154
155 #[cfg(not(feature = "improved_fixed_point_support"))]
156 {
157 n = ab.cross(ac);
158 }
159
160 let vc = n.dot(ab.cross(ap));
161 if vc < 0.0 && ab_ap >= 0.0 && ab_bp <= 0.0 {
162 return ProjectionInfo::OnAB;
163 }
164
165 let vb = -n.dot(ac.cross(cp));
166 if vb < 0.0 && ac_ap >= 0.0 && ac_cp <= 0.0 {
167 return ProjectionInfo::OnAC;
168 }
169
170 let va = n.dot(bc.cross(bp));
171 if va < 0.0 && ac_bp - ab_bp >= 0.0 && ab_cp - ac_cp >= 0.0 {
172 return ProjectionInfo::OnBC;
173 }
174
175 let clockwise = if n.dot(ap) >= 0.0 { 0 } else { 1 };
176
177 ProjectionInfo::OnFace(clockwise, va, vb, vc)
178 }
179 }
180
181 let bc = c - b;
182 match stable_check_edges_voronoi(
183 ab, ac, bc, ap, bp, cp, ab_ap, ab_bp, ac_ap, ac_cp, ac_bp, ab_cp,
184 ) {
185 ProjectionInfo::OnAB => {
186 let v = ab_ap / ab.length_squared();
188 let bcoords = [1.0 - v, v];
189
190 let res = a + ab * v;
191 return (
192 compute_result(pt, res),
193 TrianglePointLocation::OnEdge(0, bcoords),
194 );
195 }
196 ProjectionInfo::OnAC => {
197 let w = ac_ap / ac.length_squared();
199 let bcoords = [1.0 - w, w];
200
201 let res = a + ac * w;
202 return (
203 compute_result(pt, res),
204 TrianglePointLocation::OnEdge(2, bcoords),
205 );
206 }
207 ProjectionInfo::OnBC => {
208 let w = bc.dot(bp) / bc.length_squared();
210 let bcoords = [1.0 - w, w];
211
212 let res = b + bc * w;
213 return (
214 compute_result(pt, res),
215 TrianglePointLocation::OnEdge(1, bcoords),
216 );
217 }
218 ProjectionInfo::OnFace(face_side, va, vb, vc) => {
219 let denom = va + vb + vc;
221
222 if denom == 0.0 {
223 let sq_ab = ab.length_squared();
228 let sq_ac = ac.length_squared();
229 let sq_bc = bc.length_squared();
230
231 let (seg, eid, vids) = if sq_ab >= sq_ac && sq_ab >= sq_bc {
233 (Segment::new(a, b), 0, [0, 1])
234 } else if sq_ac >= sq_bc {
235 (Segment::new(a, c), 2, [0, 2])
236 } else {
237 (Segment::new(b, c), 1, [1, 2])
238 };
239
240 let (proj, loc) = seg.project_local_point_and_get_location(pt, solid);
241 let loc = match loc {
242 SegmentPointLocation::OnVertex(i) => {
243 TrianglePointLocation::OnVertex(vids[i as usize])
244 }
245 SegmentPointLocation::OnEdge(bcoords) => {
246 TrianglePointLocation::OnEdge(eid, bcoords)
247 }
248 };
249
250 return (proj, loc);
251 } else if DIM != 2 {
252 let v = vb / denom;
255 let w = vc / denom;
256 let bcoords = [1.0 - v - w, v, w];
257 let res = a + ab * v + ac * w;
258
259 return (
260 compute_result(pt, res),
261 TrianglePointLocation::OnFace(face_side as u32, bcoords),
262 );
263 }
264 }
265 }
266
267 if solid {
270 (
271 PointProjection::new(true, pt),
272 TrianglePointLocation::OnSolid,
273 )
274 } else {
275 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;
284 let d_ab = ap.length_squared() - (ab.length_squared() * v * v);
285 let d_ac = ap.length_squared() - (ac.length_squared() * w * w);
286 let d_bc = bp.length_squared() - (bc.length_squared() * u * u);
287
288 let proj;
289 let loc;
290
291 if d_ab < d_ac {
292 if d_ab < d_bc {
293 let bcoords = [1.0 - v, v];
295 proj = a + ab * v;
296 loc = TrianglePointLocation::OnEdge(0, bcoords);
297 } else {
298 let bcoords = [1.0 - u, u];
300 proj = b + bc * u;
301 loc = TrianglePointLocation::OnEdge(1, bcoords);
302 }
303 } else if d_ac < d_bc {
304 let bcoords = [1.0 - w, w];
306 proj = a + ac * w;
307 loc = TrianglePointLocation::OnEdge(2, bcoords);
308 } else {
309 let bcoords = [1.0 - u, u];
311 proj = b + bc * u;
312 loc = TrianglePointLocation::OnEdge(1, bcoords);
313 }
314
315 (PointProjection::new(true, proj), loc)
316 }
317 }
318}