1use super::{make_capsule_polygon, make_id};
6use crate::collision::{LocalManifold, Polygon, Segment};
7use crate::constants::{linear_slop, speculative_distance};
8use crate::distance::segment_distance;
9use crate::math_functions::{
10 add, cross_sv, dot, lerp, min_float, mul_add, neg, rotate_vector, sub, transform_point,
11 Transform, VEC2_ZERO,
12};
13
14fn clip_polygons(
17 poly_a: &Polygon,
18 poly_b: &Polygon,
19 edge_a: i32,
20 edge_b: i32,
21 flip: bool,
22) -> LocalManifold {
23 let mut manifold = LocalManifold::default();
24
25 let poly1: &Polygon;
27 let (i11, i12): (usize, usize);
28
29 let poly2: &Polygon;
31 let (i21, i22): (usize, usize);
32
33 if flip {
34 poly1 = poly_b;
35 poly2 = poly_a;
36 i11 = edge_b as usize;
37 i12 = if edge_b + 1 < poly_b.count {
38 (edge_b + 1) as usize
39 } else {
40 0
41 };
42 i21 = edge_a as usize;
43 i22 = if edge_a + 1 < poly_a.count {
44 (edge_a + 1) as usize
45 } else {
46 0
47 };
48 } else {
49 poly1 = poly_a;
50 poly2 = poly_b;
51 i11 = edge_a as usize;
52 i12 = if edge_a + 1 < poly_a.count {
53 (edge_a + 1) as usize
54 } else {
55 0
56 };
57 i21 = edge_b as usize;
58 i22 = if edge_b + 1 < poly_b.count {
59 (edge_b + 1) as usize
60 } else {
61 0
62 };
63 }
64
65 let normal = poly1.normals[i11];
66
67 let v11 = poly1.vertices[i11];
69 let v12 = poly1.vertices[i12];
70
71 let v21 = poly2.vertices[i21];
73 let v22 = poly2.vertices[i22];
74
75 let tangent = cross_sv(1.0, normal);
76
77 let lower1 = 0.0;
78 let upper1 = dot(sub(v12, v11), tangent);
79
80 let upper2 = dot(sub(v21, v11), tangent);
82 let lower2 = dot(sub(v22, v11), tangent);
83
84 if upper2 < lower1 || upper1 < lower2 {
86 return manifold;
87 }
88
89 let mut v_lower = if lower2 < lower1 && upper2 - lower2 > f32::EPSILON {
90 lerp(v22, v21, (lower1 - lower2) / (upper2 - lower2))
91 } else {
92 v22
93 };
94
95 let mut v_upper = if upper2 > upper1 && upper2 - lower2 > f32::EPSILON {
96 lerp(v22, v21, (upper1 - lower2) / (upper2 - lower2))
97 } else {
98 v21
99 };
100
101 let separation_lower = dot(sub(v_lower, v11), normal);
102 let separation_upper = dot(sub(v_upper, v11), normal);
103
104 let r1 = poly1.radius;
105 let r2 = poly2.radius;
106
107 v_lower = mul_add(v_lower, 0.5 * (r1 - r2 - separation_lower), normal);
109 v_upper = mul_add(v_upper, 0.5 * (r1 - r2 - separation_upper), normal);
110
111 let radius = r1 + r2;
112
113 if !flip {
114 manifold.normal = normal;
115
116 {
117 let cp = &mut manifold.points[0];
118 cp.point = v_lower;
119 cp.separation = separation_lower - radius;
120 cp.id = make_id(i11 as i32, i22 as i32);
121 manifold.point_count += 1;
122 }
123 {
124 let cp = &mut manifold.points[1];
125 cp.point = v_upper;
126 cp.separation = separation_upper - radius;
127 cp.id = make_id(i12 as i32, i21 as i32);
128 manifold.point_count += 1;
129 }
130 } else {
131 manifold.normal = neg(normal);
132
133 {
134 let cp = &mut manifold.points[0];
135 cp.point = v_upper;
136 cp.separation = separation_upper - radius;
137 cp.id = make_id(i21 as i32, i12 as i32);
138 manifold.point_count += 1;
139 }
140 {
141 let cp = &mut manifold.points[1];
142 cp.point = v_lower;
143 cp.separation = separation_lower - radius;
144 cp.id = make_id(i22 as i32, i11 as i32);
145 manifold.point_count += 1;
146 }
147 }
148
149 manifold
150}
151
152fn find_max_separation(poly1: &Polygon, poly2: &Polygon) -> (f32, i32) {
155 let count1 = poly1.count as usize;
156 let count2 = poly2.count as usize;
157 let n1s = &poly1.normals;
158 let v1s = &poly1.vertices;
159 let v2s = &poly2.vertices;
160
161 let mut best_index = 0i32;
162 let mut max_separation = -f32::MAX;
163 for i in 0..count1 {
164 let n = n1s[i];
166 let v1 = v1s[i];
167
168 let mut si = f32::MAX;
170 for v2 in v2s.iter().take(count2) {
171 let sij = dot(n, sub(*v2, v1));
172 if sij < si {
173 si = sij;
174 }
175 }
176
177 if si > max_separation {
178 max_separation = si;
179 best_index = i as i32;
180 }
181 }
182
183 (max_separation, best_index)
184}
185
186pub fn collide_polygons(polygon_a: &Polygon, polygon_b: &Polygon, xf: Transform) -> LocalManifold {
206 let origin = polygon_a.vertices[0];
207 let slop = linear_slop();
208 let speculative = speculative_distance();
209
210 let xfs = Transform {
212 p: sub(xf.p, origin),
213 q: xf.q,
214 };
215
216 let mut local_poly_a = Polygon {
217 count: polygon_a.count,
218 radius: polygon_a.radius,
219 ..Default::default()
220 };
221 local_poly_a.vertices[0] = VEC2_ZERO;
222 local_poly_a.normals[0] = polygon_a.normals[0];
223 for i in 1..local_poly_a.count as usize {
224 local_poly_a.vertices[i] = sub(polygon_a.vertices[i], origin);
225 local_poly_a.normals[i] = polygon_a.normals[i];
226 }
227
228 let mut local_poly_b = Polygon {
230 count: polygon_b.count,
231 radius: polygon_b.radius,
232 ..Default::default()
233 };
234 for i in 0..local_poly_b.count as usize {
235 local_poly_b.vertices[i] = transform_point(xfs, polygon_b.vertices[i]);
236 local_poly_b.normals[i] = rotate_vector(xfs.q, polygon_b.normals[i]);
237 }
238
239 let (separation_a, mut edge_a) = find_max_separation(&local_poly_a, &local_poly_b);
240 let (separation_b, mut edge_b) = find_max_separation(&local_poly_b, &local_poly_a);
241
242 let radius = local_poly_a.radius + local_poly_b.radius;
243
244 if separation_a > speculative + radius || separation_b > speculative + radius {
245 return LocalManifold::default();
246 }
247
248 let flip;
250 if separation_a >= separation_b {
251 flip = false;
252
253 let search_direction = local_poly_a.normals[edge_a as usize];
254
255 let count = local_poly_b.count as usize;
257 let normals = &local_poly_b.normals;
258 edge_b = 0;
259 let mut min_dot = f32::MAX;
260 for (i, normal) in normals.iter().enumerate().take(count) {
261 let dot_ = dot(search_direction, *normal);
262 if dot_ < min_dot {
263 min_dot = dot_;
264 edge_b = i as i32;
265 }
266 }
267 } else {
268 flip = true;
269
270 let search_direction = local_poly_b.normals[edge_b as usize];
271
272 let count = local_poly_a.count as usize;
274 let normals = &local_poly_a.normals;
275 edge_a = 0;
276 let mut min_dot = f32::MAX;
277 for (i, normal) in normals.iter().enumerate().take(count) {
278 let dot_ = dot(search_direction, *normal);
279 if dot_ < min_dot {
280 min_dot = dot_;
281 edge_a = i as i32;
282 }
283 }
284 }
285
286 let mut manifold = LocalManifold::default();
287
288 if separation_a > 0.1 * slop || separation_b > 0.1 * slop {
290 let i11 = edge_a as usize;
293 let i12 = if edge_a + 1 < local_poly_a.count {
294 (edge_a + 1) as usize
295 } else {
296 0
297 };
298 let i21 = edge_b as usize;
299 let i22 = if edge_b + 1 < local_poly_b.count {
300 (edge_b + 1) as usize
301 } else {
302 0
303 };
304
305 let v11 = local_poly_a.vertices[i11];
306 let v12 = local_poly_a.vertices[i12];
307 let v21 = local_poly_b.vertices[i21];
308 let v22 = local_poly_b.vertices[i22];
309
310 let result = segment_distance(v11, v12, v21, v22);
311 debug_assert!(result.distance_squared > 0.0);
312 let distance = result.distance_squared.sqrt();
313 let separation = distance - radius;
314
315 if distance - radius > speculative {
316 return manifold;
318 }
319
320 manifold = clip_polygons(&local_poly_a, &local_poly_b, edge_a, edge_b, flip);
322
323 let mut min_separation = f32::MAX;
324 for i in 0..manifold.point_count as usize {
325 min_separation = min_float(min_separation, manifold.points[i].separation);
326 }
327
328 if separation + 0.1 * slop < min_separation {
330 let feature: Option<(
332 crate::math_functions::Vec2,
333 crate::math_functions::Vec2,
334 usize,
335 usize,
336 )> = if result.fraction1 == 0.0 && result.fraction2 == 0.0 {
337 Some((v11, v21, i11, i21))
338 } else if result.fraction1 == 0.0 && result.fraction2 == 1.0 {
339 Some((v11, v22, i11, i22))
340 } else if result.fraction1 == 1.0 && result.fraction2 == 0.0 {
341 Some((v12, v21, i12, i21))
342 } else if result.fraction1 == 1.0 && result.fraction2 == 1.0 {
343 Some((v12, v22, i12, i22))
344 } else {
345 None
346 };
347
348 if let Some((va, vb, ia, ib)) = feature {
349 let mut normal = sub(vb, va);
350 let inv_distance = 1.0 / distance;
351 normal.x *= inv_distance;
352 normal.y *= inv_distance;
353
354 let c1 = mul_add(va, local_poly_a.radius, normal);
355 let c2 = mul_add(vb, -local_poly_b.radius, normal);
356
357 manifold.normal = normal;
358 manifold.points[0].point = lerp(c1, c2, 0.5);
359 manifold.points[0].separation = distance - radius;
360 manifold.points[0].id = make_id(ia as i32, ib as i32);
361 manifold.point_count = 1;
362 }
363 }
364 } else {
365 manifold = clip_polygons(&local_poly_a, &local_poly_b, edge_a, edge_b, flip);
367 }
368
369 for i in 0..manifold.point_count as usize {
371 manifold.points[i].point = add(manifold.points[i].point, origin);
372 }
373
374 manifold
375}
376
377pub fn collide_segment_and_polygon(
380 segment_a: &Segment,
381 polygon_b: &Polygon,
382 xf: Transform,
383) -> LocalManifold {
384 let polygon_a = make_capsule_polygon(segment_a.point1, segment_a.point2, 0.0);
385 collide_polygons(&polygon_a, polygon_b, xf)
386}