1use crate::math::{Isometry, Point, Vector, DIM};
2use crate::query::{PointProjection, PointQuery, PointQueryWithLocation};
3use crate::shape::{FeatureId, Triangle, TrianglePointLocation};
4use na::{self, RealField};
5
6#[inline]
7fn compute_result<N: RealField + Copy>(pt: &Point<N>, proj: Point<N>) -> PointProjection<N> {
8 #[cfg(feature = "dim2")]
9 {
10 PointProjection::new(*pt == proj, proj)
11 }
12
13 #[cfg(feature = "dim3")]
14 {
15 PointProjection::new(relative_eq!(proj, *pt), proj)
18 }
19}
20
21impl<N: RealField + Copy> PointQuery<N> for Triangle<N> {
22 #[inline]
23 fn project_point(&self, m: &Isometry<N>, pt: &Point<N>, solid: bool) -> PointProjection<N> {
24 let (projection, _) = self.project_point_with_location(m, pt, solid);
25 projection
26 }
27
28 #[inline]
29 fn project_point_with_feature(
30 &self,
31 m: &Isometry<N>,
32 pt: &Point<N>,
33 ) -> (PointProjection<N>, FeatureId) {
34 let (proj, loc) = if DIM == 2 {
35 self.project_point_with_location(m, pt, false)
36 } else {
37 self.project_point_with_location(m, pt, true)
38 };
39
40 let feature = match loc {
41 TrianglePointLocation::OnVertex(i) => FeatureId::Vertex(i),
42 #[cfg(feature = "dim3")]
43 TrianglePointLocation::OnEdge(i, _) => FeatureId::Edge(i),
44 #[cfg(feature = "dim2")]
45 TrianglePointLocation::OnEdge(i, _) => FeatureId::Face(i),
46 TrianglePointLocation::OnFace(i, _) => FeatureId::Face(i),
47 TrianglePointLocation::OnSolid => FeatureId::Face(0),
48 };
49
50 (proj, feature)
51 }
52
53 }
56
57impl<N: RealField + Copy> PointQueryWithLocation<N> for Triangle<N> {
58 type Location = TrianglePointLocation<N>;
59
60 #[inline]
61 fn project_point_with_location(
62 &self,
63 m: &Isometry<N>,
64 pt: &Point<N>,
65 solid: bool,
66 ) -> (PointProjection<N>, Self::Location) {
67 let a = self.a;
68 let b = self.b;
69 let c = self.c;
70 let p = m.inverse_transform_point(pt);
71
72 let _1 = na::one::<N>();
73
74 let ab = b - a;
75 let ac = c - a;
76 let ap = p - a;
77
78 let ab_ap = ab.dot(&ap);
79 let ac_ap = ac.dot(&ap);
80
81 if ab_ap <= na::zero() && ac_ap <= na::zero() {
82 return (
84 compute_result(pt, m * a),
85 TrianglePointLocation::OnVertex(0),
86 );
87 }
88
89 let bp = p - b;
90 let ab_bp = ab.dot(&bp);
91 let ac_bp = ac.dot(&bp);
92
93 if ab_bp >= na::zero() && ac_bp <= ab_bp {
94 return (
96 compute_result(pt, m * b),
97 TrianglePointLocation::OnVertex(1),
98 );
99 }
100
101 let cp = p - c;
102 let ab_cp = ab.dot(&cp);
103 let ac_cp = ac.dot(&cp);
104
105 if ac_cp >= na::zero() && ab_cp <= ac_cp {
106 return (
108 compute_result(pt, m * c),
109 TrianglePointLocation::OnVertex(2),
110 );
111 }
112
113 enum ProjectionInfo<N> {
114 OnAB,
115 OnAC,
116 OnBC,
117 OnFace(usize, N, N, N),
119 }
120
121 fn stable_check_edges_voronoi<N: RealField + Copy>(
125 ab: &Vector<N>,
126 ac: &Vector<N>,
127 bc: &Vector<N>,
128 ap: &Vector<N>,
129 bp: &Vector<N>,
130 cp: &Vector<N>,
131 ab_ap: N,
132 ab_bp: N,
133 ac_ap: N,
134 ac_cp: N,
135 ac_bp: N,
136 ab_cp: N,
137 ) -> ProjectionInfo<N> {
138 #[cfg(feature = "dim2")]
139 {
140 let n = ab.perp(&ac);
141 let vc = n * ab.perp(&ap);
142 if vc < na::zero() && ab_ap >= na::zero() && ab_bp <= na::zero() {
143 return ProjectionInfo::OnAB;
144 }
145
146 let vb = -n * ac.perp(&cp);
147 if vb < na::zero() && ac_ap >= na::zero() && ac_cp <= na::zero() {
148 return ProjectionInfo::OnAC;
149 }
150
151 let va = n * bc.perp(&bp);
152 if va < na::zero() && ac_bp - ab_bp >= na::zero() && ab_cp - ac_cp >= na::zero() {
153 return ProjectionInfo::OnBC;
154 }
155
156 return ProjectionInfo::OnFace(0, va, vb, vc);
157 }
158 #[cfg(feature = "dim3")]
159 {
160 let n;
161
162 #[cfg(feature = "improved_fixed_point_support")]
163 {
164 let scaled_n = ab.cross(&ac);
165 n = scaled_n.try_normalize(N::zero()).unwrap_or(scaled_n);
166 }
167
168 #[cfg(not(feature = "improved_fixed_point_support"))]
169 {
170 n = ab.cross(&ac);
171 }
172
173 let vc = n.dot(&ab.cross(&ap));
174 if vc < na::zero() && ab_ap >= na::zero() && ab_bp <= na::zero() {
175 return ProjectionInfo::OnAB;
176 }
177
178 let vb = -n.dot(&ac.cross(&cp));
179 if vb < na::zero() && ac_ap >= na::zero() && ac_cp <= na::zero() {
180 return ProjectionInfo::OnAC;
181 }
182
183 let va = n.dot(&bc.cross(&bp));
184 if va < na::zero() && ac_bp - ab_bp >= na::zero() && ab_cp - ac_cp >= na::zero() {
185 return ProjectionInfo::OnBC;
186 }
187
188 let clockwise = if n.dot(&ap) >= N::zero() { 0 } else { 1 };
189
190 return ProjectionInfo::OnFace(clockwise, va, vb, vc);
191 }
192 }
193
194 let bc = c - b;
195 match stable_check_edges_voronoi(
196 &ab, &ac, &bc, &ap, &bp, &cp, ab_ap, ab_bp, ac_ap, ac_cp, ac_bp, ab_cp,
197 ) {
198 ProjectionInfo::OnAB => {
199 let v = ab_ap / ab.norm_squared();
201 let bcoords = [_1 - v, v];
202
203 let res = a + ab * v;
204 return (
205 compute_result(pt, m * res),
206 TrianglePointLocation::OnEdge(0, bcoords),
207 );
208 }
209 ProjectionInfo::OnAC => {
210 let w = ac_ap / ac.norm_squared();
212 let bcoords = [_1 - w, w];
213
214 let res = a + ac * w;
215 return (
216 compute_result(pt, m * res),
217 TrianglePointLocation::OnEdge(2, bcoords),
218 );
219 }
220 ProjectionInfo::OnBC => {
221 let w = bc.dot(&bp) / bc.norm_squared();
223 let bcoords = [_1 - w, w];
224
225 let res = b + bc * w;
226 return (
227 compute_result(pt, m * res),
228 TrianglePointLocation::OnEdge(1, bcoords),
229 );
230 }
231 ProjectionInfo::OnFace(face_side, va, vb, vc) => {
232 if DIM != 2 {
234 if va + vb + vc != N::zero() {
238 let denom = _1 / (va + vb + vc);
239 let v = vb * denom;
240 let w = vc * denom;
241 let bcoords = [_1 - v - w, v, w];
242 let res = a + ab * v + ac * w;
243
244 return (
245 compute_result(pt, m * res),
246 TrianglePointLocation::OnFace(face_side, bcoords),
247 );
248 }
249 }
250 }
251 }
252
253 if solid {
256 (
257 PointProjection::new(true, *pt),
258 TrianglePointLocation::OnSolid,
259 )
260 } else {
261 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;
270 let d_ab = ap.norm_squared() - (ab.norm_squared() * v * v);
271 let d_ac = ap.norm_squared() - (ac.norm_squared() * u * u);
272 let d_bc = bp.norm_squared() - (bc.norm_squared() * w * w);
273
274 let mut proj;
275 let loc;
276
277 if d_ab < d_ac {
278 if d_ab < d_bc {
279 let bcoords = [_1 - v, v];
281 proj = a + ab * v;
282 proj = m * proj;
283 loc = TrianglePointLocation::OnEdge(0, bcoords);
284 } else {
285 let bcoords = [_1 - u, u];
287 proj = b + bc * u;
288 proj = m * proj;
289 loc = TrianglePointLocation::OnEdge(1, bcoords);
290 }
291 } else {
292 if d_ac < d_bc {
293 let bcoords = [_1 - w, w];
295 proj = a + ac * w;
296 proj = m * proj;
297 loc = TrianglePointLocation::OnEdge(2, bcoords);
298 } else {
299 let bcoords = [_1 - u, u];
301 proj = b + bc * u;
302 proj = m * proj;
303 loc = TrianglePointLocation::OnEdge(1, bcoords);
304 }
305 }
306
307 (PointProjection::new(true, proj), loc)
308 }
309 }
310}