1use crate::math::{Point, Real, Vector, DIM};
2use crate::query::{PointProjection, PointQuery, PointQueryWithLocation};
3use crate::shape::{FeatureId, Triangle, TrianglePointLocation};
4
5#[inline]
6fn compute_result(pt: &Point<Real>, proj: Point<Real>) -> 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: &Point<Real>, 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(
28 &self,
29 pt: &Point<Real>,
30 ) -> (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: &Point<Real>,
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<Real>,
113 ac: &Vector<Real>,
114 bc: &Vector<Real>,
115 ap: &Vector<Real>,
116 bp: &Vector<Real>,
117 cp: &Vector<Real>,
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(ac);
128 let vc = n * ab.perp(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(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(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.norm_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.norm_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.norm_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 if DIM != 2 {
221 if va + vb + vc != 0.0 {
225 let denom = 1.0 / (va + vb + vc);
226 let v = vb * denom;
227 let w = vc * denom;
228 let bcoords = [1.0 - v - w, v, w];
229 let res = a + ab * v + ac * w;
230
231 return (
232 compute_result(pt, res),
233 TrianglePointLocation::OnFace(face_side as u32, bcoords),
234 );
235 }
236 }
237 }
238 }
239
240 if solid {
243 (
244 PointProjection::new(true, *pt),
245 TrianglePointLocation::OnSolid,
246 )
247 } else {
248 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;
257 let d_ab = ap.norm_squared() - (ab.norm_squared() * v * v);
258 let d_ac = ap.norm_squared() - (ac.norm_squared() * w * w);
259 let d_bc = bp.norm_squared() - (bc.norm_squared() * u * u);
260
261 let proj;
262 let loc;
263
264 if d_ab < d_ac {
265 if d_ab < d_bc {
266 let bcoords = [1.0 - v, v];
268 proj = a + ab * v;
269 loc = TrianglePointLocation::OnEdge(0, bcoords);
270 } else {
271 let bcoords = [1.0 - u, u];
273 proj = b + bc * u;
274 loc = TrianglePointLocation::OnEdge(1, bcoords);
275 }
276 } else if d_ac < d_bc {
277 let bcoords = [1.0 - w, w];
279 proj = a + ac * w;
280 loc = TrianglePointLocation::OnEdge(2, bcoords);
281 } else {
282 let bcoords = [1.0 - u, u];
284 proj = b + bc * u;
285 loc = TrianglePointLocation::OnEdge(1, bcoords);
286 }
287
288 (PointProjection::new(true, proj), loc)
289 }
290 }
291}