1use crate::math::{Pose, Vector};
2use crate::shape::{PackedFeatureId, Segment, Triangle};
3#[cfg(feature = "alloc")]
4use crate::{
5 math::{Real, Vector2},
6 query::details::closest_points_segment_segment_2d,
7 query::{ContactManifold, TrackedContact},
8 utils::WBasis,
9};
10
11#[derive(Debug, Clone)]
14pub struct PolygonalFeature {
15 pub vertices: [Vector; 4],
17 pub vids: [PackedFeatureId; 4],
19 pub eids: [PackedFeatureId; 4],
21 pub fid: PackedFeatureId,
23 pub num_vertices: usize,
25}
26
27impl Default for PolygonalFeature {
28 fn default() -> Self {
29 Self {
30 vertices: [Vector::ZERO; 4],
31 vids: [PackedFeatureId::UNKNOWN; 4],
32 eids: [PackedFeatureId::UNKNOWN; 4],
33 fid: PackedFeatureId::UNKNOWN,
34 num_vertices: 0,
35 }
36 }
37}
38
39impl From<Triangle> for PolygonalFeature {
40 fn from(tri: Triangle) -> Self {
41 Self {
42 vertices: [tri.a, tri.b, tri.c, tri.c],
43 vids: PackedFeatureId::vertices([0, 1, 2, 2]),
44 eids: PackedFeatureId::edges([0, 1, 2, 2]),
45 fid: PackedFeatureId::face(0),
46 num_vertices: 3,
47 }
48 }
49}
50
51impl From<Segment> for PolygonalFeature {
52 fn from(seg: Segment) -> Self {
53 Self {
54 vertices: [seg.a, seg.b, seg.b, seg.b],
55 vids: PackedFeatureId::vertices([0, 1, 1, 1]),
56 eids: PackedFeatureId::edges([0, 0, 0, 0]),
57 fid: PackedFeatureId::face(0),
58 num_vertices: 2,
59 }
60 }
61}
62
63impl PolygonalFeature {
64 pub fn new() -> Self {
66 Self::default()
67 }
68
69 pub fn transform_by(&mut self, pos: &Pose) {
71 for p in &mut self.vertices[0..self.num_vertices] {
72 *p = pos * *p;
73 }
74 }
75
76 #[cfg(feature = "alloc")]
78 pub fn contacts<ManifoldData, ContactData: Default + Copy>(
79 pos12: &Pose,
80 _pos21: &Pose,
81 sep_axis1: Vector,
82 _sep_axis2: Vector,
83 feature1: &Self,
84 feature2: &Self,
85 manifold: &mut ContactManifold<ManifoldData, ContactData>,
86 flipped: bool,
87 ) {
88 match (feature1.num_vertices, feature2.num_vertices) {
89 (2, 2) => {
90 Self::contacts_edge_edge(pos12, feature1, sep_axis1, feature2, manifold, flipped)
91 }
92 _ => Self::contacts_face_face(pos12, feature1, sep_axis1, feature2, manifold, flipped),
93 }
94 }
95
96 #[cfg(feature = "alloc")]
97 fn contacts_edge_edge<ManifoldData, ContactData: Default + Copy>(
98 pos12: &Pose,
99 face1: &PolygonalFeature,
100 sep_axis1: Vector,
101 face2: &PolygonalFeature,
102 manifold: &mut ContactManifold<ManifoldData, ContactData>,
103 flipped: bool,
104 ) {
105 let basis = sep_axis1.orthonormal_basis();
110 let projected_edge1 = [
111 Vector2::new(
112 face1.vertices[0].dot(basis[0]),
113 face1.vertices[0].dot(basis[1]),
114 ),
115 Vector2::new(
116 face1.vertices[1].dot(basis[0]),
117 face1.vertices[1].dot(basis[1]),
118 ),
119 ];
120
121 let vertices2_1 = [pos12 * face2.vertices[0], pos12 * face2.vertices[1]];
122 let projected_edge2 = [
123 Vector2::new(vertices2_1[0].dot(basis[0]), vertices2_1[0].dot(basis[1])),
124 Vector2::new(vertices2_1[1].dot(basis[0]), vertices2_1[1].dot(basis[1])),
125 ];
126
127 let tangent1 = (projected_edge1[1] - projected_edge1[0]).try_normalize();
128 let tangent2 = (projected_edge2[1] - projected_edge2[0]).try_normalize();
129
130 if let (Some(tangent1), Some(tangent2)) = (tangent1, tangent2) {
133 let parallel = tangent1.dot(tangent2) >= crate::utils::COS_FRAC_PI_8;
134
135 if !parallel {
136 let seg1 = (&projected_edge1[0], &projected_edge1[1]);
137 let seg2 = (&projected_edge2[0], &projected_edge2[1]);
138 let (loc1, loc2) = closest_points_segment_segment_2d(seg1, seg2);
139
140 let bcoords1 = loc1.barycentric_coordinates();
142 let bcoords2 = loc2.barycentric_coordinates();
143
144 let edge1 = (face1.vertices[0], face1.vertices[1]);
145 let edge2 = (vertices2_1[0], vertices2_1[1]);
146 let local_p1 = edge1.0 * bcoords1[0] + edge1.1 * bcoords1[1];
147 let local_p2_1 = edge2.0 * bcoords2[0] + edge2.1 * bcoords2[1];
148 let dist = (local_p2_1 - local_p1).dot(sep_axis1);
149
150 manifold.points.push(TrackedContact::flipped(
151 local_p1,
152 pos12.inverse_transform_point(local_p2_1),
153 face1.eids[0],
154 face2.eids[0],
155 dist,
156 flipped,
157 ));
158
159 return;
160 }
161 }
162
163 if let Some(clips) = crate::query::details::clip_segment_segment(
168 (face1.vertices[0], face1.vertices[1]),
169 (vertices2_1[0], vertices2_1[1]),
170 ) {
171 let feature_at = |face: &PolygonalFeature, id| match id {
172 0 => face.vids[0],
173 1 => face.eids[0],
174 2 => face.vids[1],
175 _ => unreachable!(),
176 };
177
178 manifold.points.push(TrackedContact::flipped(
179 (clips.0).0,
180 pos12.inverse_transform_point((clips.0).1),
181 feature_at(face1, (clips.0).2),
182 feature_at(face2, (clips.0).3),
183 ((clips.0).1 - (clips.0).0).dot(sep_axis1),
184 flipped,
185 ));
186
187 manifold.points.push(TrackedContact::flipped(
188 (clips.1).0,
189 pos12.inverse_transform_point((clips.1).1),
190 feature_at(face1, (clips.1).2),
191 feature_at(face2, (clips.1).3),
192 ((clips.1).1 - (clips.1).0).dot(sep_axis1),
193 flipped,
194 ));
195 }
196 }
197
198 #[cfg(feature = "alloc")]
199 fn contacts_face_face<ManifoldData, ContactData: Default + Copy>(
200 pos12: &Pose,
201 face1: &PolygonalFeature,
202 sep_axis1: Vector,
203 face2: &PolygonalFeature,
204 manifold: &mut ContactManifold<ManifoldData, ContactData>,
205 flipped: bool,
206 ) {
207 let basis = sep_axis1.orthonormal_basis();
212 let projected_face1 = [
213 Vector2::new(
214 face1.vertices[0].dot(basis[0]),
215 face1.vertices[0].dot(basis[1]),
216 ),
217 Vector2::new(
218 face1.vertices[1].dot(basis[0]),
219 face1.vertices[1].dot(basis[1]),
220 ),
221 Vector2::new(
222 face1.vertices[2].dot(basis[0]),
223 face1.vertices[2].dot(basis[1]),
224 ),
225 Vector2::new(
226 face1.vertices[3].dot(basis[0]),
227 face1.vertices[3].dot(basis[1]),
228 ),
229 ];
230
231 let vertices2_1 = [
232 pos12 * face2.vertices[0],
233 pos12 * face2.vertices[1],
234 pos12 * face2.vertices[2],
235 pos12 * face2.vertices[3],
236 ];
237 let projected_face2 = [
238 Vector2::new(vertices2_1[0].dot(basis[0]), vertices2_1[0].dot(basis[1])),
239 Vector2::new(vertices2_1[1].dot(basis[0]), vertices2_1[1].dot(basis[1])),
240 Vector2::new(vertices2_1[2].dot(basis[0]), vertices2_1[2].dot(basis[1])),
241 Vector2::new(vertices2_1[3].dot(basis[0]), vertices2_1[3].dot(basis[1])),
242 ];
243
244 if face2.num_vertices > 2 {
246 let normal2_1 =
247 (vertices2_1[2] - vertices2_1[1]).cross(vertices2_1[0] - vertices2_1[1]);
248 let denom = normal2_1.dot(sep_axis1);
249
250 if !relative_eq!(denom, 0.0) {
251 let last_index2 = face2.num_vertices - 1;
252
253 #[allow(clippy::needless_range_loop)] 'point_loop1: for i in 0..face1.num_vertices {
255 let p1 = projected_face1[i];
256
257 let mut sign = (projected_face2[0] - projected_face2[last_index2])
258 .perp_dot(p1 - projected_face2[last_index2]);
259 for j in 0..last_index2 {
260 let new_sign = (projected_face2[j + 1] - projected_face2[j])
261 .perp_dot(p1 - projected_face2[j]);
262
263 if sign == 0.0 {
264 sign = new_sign;
265 } else if sign * new_sign < 0.0 {
266 continue 'point_loop1;
268 }
269 }
270
271 let dist = (vertices2_1[0] - face1.vertices[i]).dot(normal2_1) / denom;
274 let local_p1 = face1.vertices[i];
275 let local_p2_1 = face1.vertices[i] + dist * sep_axis1;
276
277 manifold.points.push(TrackedContact::flipped(
278 local_p1,
279 pos12.inverse_transform_point(local_p2_1),
280 face1.vids[i],
281 face2.fid,
282 dist,
283 flipped,
284 ));
285 }
286 }
287 }
288
289 if face1.num_vertices > 2 {
290 let normal1 = (face1.vertices[2] - face1.vertices[1])
291 .cross(face1.vertices[0] - face1.vertices[1]);
292
293 let denom = -normal1.dot(sep_axis1);
294 if !relative_eq!(denom, 0.0) {
295 let last_index1 = face1.num_vertices - 1;
296 'point_loop2: for i in 0..face2.num_vertices {
297 let p2 = projected_face2[i];
298
299 let mut sign = (projected_face1[0] - projected_face1[last_index1])
300 .perp_dot(p2 - projected_face1[last_index1]);
301 for j in 0..last_index1 {
302 let new_sign = (projected_face1[j + 1] - projected_face1[j])
303 .perp_dot(p2 - projected_face1[j]);
304
305 if sign == 0.0 {
306 sign = new_sign;
307 } else if sign * new_sign < 0.0 {
308 continue 'point_loop2;
310 }
311 }
312
313 let dist = (face1.vertices[0] - vertices2_1[i]).dot(normal1) / denom;
316 let local_p2_1 = vertices2_1[i];
317 let local_p1 = vertices2_1[i] - dist * sep_axis1;
318
319 manifold.points.push(TrackedContact::flipped(
320 local_p1,
321 pos12.inverse_transform_point(local_p2_1),
322 face1.fid,
323 face2.vids[i],
324 dist,
325 flipped,
326 ));
327 }
328 }
329 }
330
331 for j in 0..face2.num_vertices {
334 let projected_edge2 = [
335 projected_face2[j],
336 projected_face2[(j + 1) % face2.num_vertices],
337 ];
338
339 for i in 0..face1.num_vertices {
340 let projected_edge1 = [
341 projected_face1[i],
342 projected_face1[(i + 1) % face1.num_vertices],
343 ];
344 if let Some(bcoords) = closest_points_line2d(projected_edge1, projected_edge2) {
345 if bcoords.0 > 0.0 && bcoords.0 < 1.0 && bcoords.1 > 0.0 && bcoords.1 < 1.0 {
346 let edge1 = (
348 face1.vertices[i],
349 face1.vertices[(i + 1) % face1.num_vertices],
350 );
351 let edge2 = (vertices2_1[j], vertices2_1[(j + 1) % face2.num_vertices]);
352 let local_p1 = edge1.0 * (1.0 - bcoords.0) + edge1.1 * bcoords.0;
353 let local_p2_1 = edge2.0 * (1.0 - bcoords.1) + edge2.1 * bcoords.1;
354 let dist = (local_p2_1 - local_p1).dot(sep_axis1);
355
356 manifold.points.push(TrackedContact::flipped(
357 local_p1,
358 pos12.inverse_transform_point(local_p2_1),
359 face1.eids[i],
360 face2.eids[j],
361 dist,
362 flipped,
363 ));
364 }
365 }
366 }
367 }
368 }
369}
370
371#[cfg(feature = "alloc")]
374fn closest_points_line2d(edge1: [Vector2; 2], edge2: [Vector2; 2]) -> Option<(Real, Real)> {
375 use crate::approx::AbsDiffEq;
376
377 let dir1 = edge1[1] - edge1[0];
379 let dir2 = edge2[1] - edge2[0];
380 let r = edge1[0] - edge2[0];
381
382 let a = dir1.length_squared();
383 let e = dir2.length_squared();
384 let f = dir2.dot(r);
385
386 let eps = Real::default_epsilon();
387
388 if a <= eps && e <= eps {
389 Some((0.0, 0.0))
390 } else if a <= eps {
391 Some((0.0, f / e))
392 } else {
393 let c = dir1.dot(r);
394 if e <= eps {
395 Some((-c / a, 0.0))
396 } else {
397 let b = dir1.dot(dir2);
398 let ae = a * e;
399 let bb = b * b;
400 let denom = ae - bb;
401
402 let parallel = denom <= eps || approx::ulps_eq!(ae, bb);
404
405 let s = if !parallel {
406 (b * f - c * e) / denom
407 } else {
408 0.0
409 };
410
411 if parallel {
412 None
413 } else {
414 Some((s, (b * s + f) / e))
415 }
416 }
417 }
418}