b2_collide_edge.cpp
Go to the documentation of this file.
1 // MIT License
2 
3 // Copyright (c) 2019 Erin Catto
4 
5 // Permission is hereby granted, free of charge, to any person obtaining a copy
6 // of this software and associated documentation files (the "Software"), to deal
7 // in the Software without restriction, including without limitation the rights
8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 // copies of the Software, and to permit persons to whom the Software is
10 // furnished to do so, subject to the following conditions:
11 
12 // The above copyright notice and this permission notice shall be included in all
13 // copies or substantial portions of the Software.
14 
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 // SOFTWARE.
22 
23 #include "box2d/b2_collision.h"
24 #include "box2d/b2_circle_shape.h"
25 #include "box2d/b2_edge_shape.h"
26 #include "box2d/b2_polygon_shape.h"
27 
28 
29 // Compute contact points for edge versus circle.
30 // This accounts for edge connectivity.
32  const b2EdgeShape* edgeA, const b2Transform& xfA,
33  const b2CircleShape* circleB, const b2Transform& xfB)
34 {
35  manifold->pointCount = 0;
36 
37  // Compute circle in frame of edge
38  b2Vec2 Q = b2MulT(xfA, b2Mul(xfB, circleB->m_p));
39 
40  b2Vec2 A = edgeA->m_vertex1, B = edgeA->m_vertex2;
41  b2Vec2 e = B - A;
42 
43  // Normal points to the right for a CCW winding
44  b2Vec2 n(e.y, -e.x);
45  float offset = b2Dot(n, Q - A);
46 
47  bool oneSided = edgeA->m_oneSided;
48  if (oneSided && offset < 0.0f)
49  {
50  return;
51  }
52 
53  // Barycentric coordinates
54  float u = b2Dot(e, B - Q);
55  float v = b2Dot(e, Q - A);
56 
57  float radius = edgeA->m_radius + circleB->m_radius;
58 
60  cf.indexB = 0;
62 
63  // Region A
64  if (v <= 0.0f)
65  {
66  b2Vec2 P = A;
67  b2Vec2 d = Q - P;
68  float dd = b2Dot(d, d);
69  if (dd > radius * radius)
70  {
71  return;
72  }
73 
74  // Is there an edge connected to A?
75  if (edgeA->m_oneSided)
76  {
77  b2Vec2 A1 = edgeA->m_vertex0;
78  b2Vec2 B1 = A;
79  b2Vec2 e1 = B1 - A1;
80  float u1 = b2Dot(e1, B1 - Q);
81 
82  // Is the circle in Region AB of the previous edge?
83  if (u1 > 0.0f)
84  {
85  return;
86  }
87  }
88 
89  cf.indexA = 0;
91  manifold->pointCount = 1;
92  manifold->type = b2Manifold::e_circles;
93  manifold->localNormal.SetZero();
94  manifold->localPoint = P;
95  manifold->points[0].id.key = 0;
96  manifold->points[0].id.cf = cf;
97  manifold->points[0].localPoint = circleB->m_p;
98  return;
99  }
100 
101  // Region B
102  if (u <= 0.0f)
103  {
104  b2Vec2 P = B;
105  b2Vec2 d = Q - P;
106  float dd = b2Dot(d, d);
107  if (dd > radius * radius)
108  {
109  return;
110  }
111 
112  // Is there an edge connected to B?
113  if (edgeA->m_oneSided)
114  {
115  b2Vec2 B2 = edgeA->m_vertex3;
116  b2Vec2 A2 = B;
117  b2Vec2 e2 = B2 - A2;
118  float v2 = b2Dot(e2, Q - A2);
119 
120  // Is the circle in Region AB of the next edge?
121  if (v2 > 0.0f)
122  {
123  return;
124  }
125  }
126 
127  cf.indexA = 1;
129  manifold->pointCount = 1;
130  manifold->type = b2Manifold::e_circles;
131  manifold->localNormal.SetZero();
132  manifold->localPoint = P;
133  manifold->points[0].id.key = 0;
134  manifold->points[0].id.cf = cf;
135  manifold->points[0].localPoint = circleB->m_p;
136  return;
137  }
138 
139  // Region AB
140  float den = b2Dot(e, e);
141  b2Assert(den > 0.0f);
142  b2Vec2 P = (1.0f / den) * (u * A + v * B);
143  b2Vec2 d = Q - P;
144  float dd = b2Dot(d, d);
145  if (dd > radius * radius)
146  {
147  return;
148  }
149 
150  if (offset < 0.0f)
151  {
152  n.Set(-n.x, -n.y);
153  }
154  n.Normalize();
155 
156  cf.indexA = 0;
158  manifold->pointCount = 1;
159  manifold->type = b2Manifold::e_faceA;
160  manifold->localNormal = n;
161  manifold->localPoint = A;
162  manifold->points[0].id.key = 0;
163  manifold->points[0].id.cf = cf;
164  manifold->points[0].localPoint = circleB->m_p;
165 }
166 
167 // This structure is used to keep track of the best separating axis.
168 struct b2EPAxis
169 {
170  enum Type
171  {
175  };
176 
180  float separation;
181 };
182 
183 // This holds polygon B expressed in frame A.
185 {
189 };
190 
191 // Reference face used for clipping
193 {
194  int32 i1, i2;
195  b2Vec2 v1, v2;
197 
199  float sideOffset1;
200 
202  float sideOffset2;
203 };
204 
205 static b2EPAxis b2ComputeEdgeSeparation(const b2TempPolygon& polygonB, const b2Vec2& v1, const b2Vec2& normal1)
206 {
207  b2EPAxis axis;
208  axis.type = b2EPAxis::e_edgeA;
209  axis.index = -1;
210  axis.separation = -FLT_MAX;
211  axis.normal.SetZero();
212 
213  b2Vec2 axes[2] = { normal1, -normal1 };
214 
215  // Find axis with least overlap (min-max problem)
216  for (int32 j = 0; j < 2; ++j)
217  {
218  float sj = FLT_MAX;
219 
220  // Find deepest polygon vertex along axis j
221  for (int32 i = 0; i < polygonB.count; ++i)
222  {
223  float si = b2Dot(axes[j], polygonB.vertices[i] - v1);
224  if (si < sj)
225  {
226  sj = si;
227  }
228  }
229 
230  if (sj > axis.separation)
231  {
232  axis.index = j;
233  axis.separation = sj;
234  axis.normal = axes[j];
235  }
236  }
237 
238  return axis;
239 }
240 
241 static b2EPAxis b2ComputePolygonSeparation(const b2TempPolygon& polygonB, const b2Vec2& v1, const b2Vec2& v2)
242 {
243  b2EPAxis axis;
244  axis.type = b2EPAxis::e_unknown;
245  axis.index = -1;
246  axis.separation = -FLT_MAX;
247  axis.normal.SetZero();
248 
249  for (int32 i = 0; i < polygonB.count; ++i)
250  {
251  b2Vec2 n = -polygonB.normals[i];
252 
253  float s1 = b2Dot(n, polygonB.vertices[i] - v1);
254  float s2 = b2Dot(n, polygonB.vertices[i] - v2);
255  float s = b2Min(s1, s2);
256 
257  if (s > axis.separation)
258  {
259  axis.type = b2EPAxis::e_edgeB;
260  axis.index = i;
261  axis.separation = s;
262  axis.normal = n;
263  }
264  }
265 
266  return axis;
267 }
268 
270  const b2EdgeShape* edgeA, const b2Transform& xfA,
271  const b2PolygonShape* polygonB, const b2Transform& xfB)
272 {
273  manifold->pointCount = 0;
274 
275  b2Transform xf = b2MulT(xfA, xfB);
276 
277  b2Vec2 centroidB = b2Mul(xf, polygonB->m_centroid);
278 
279  b2Vec2 v1 = edgeA->m_vertex1;
280  b2Vec2 v2 = edgeA->m_vertex2;
281 
282  b2Vec2 edge1 = v2 - v1;
283  edge1.Normalize();
284 
285  // Normal points to the right for a CCW winding
286  b2Vec2 normal1(edge1.y, -edge1.x);
287  float offset1 = b2Dot(normal1, centroidB - v1);
288 
289  bool oneSided = edgeA->m_oneSided;
290  if (oneSided && offset1 < 0.0f)
291  {
292  return;
293  }
294 
295  // Get polygonB in frameA
296  b2TempPolygon tempPolygonB;
297  tempPolygonB.count = polygonB->m_count;
298  for (int32 i = 0; i < polygonB->m_count; ++i)
299  {
300  tempPolygonB.vertices[i] = b2Mul(xf, polygonB->m_vertices[i]);
301  tempPolygonB.normals[i] = b2Mul(xf.q, polygonB->m_normals[i]);
302  }
303 
304  float radius = polygonB->m_radius + edgeA->m_radius;
305 
306  b2EPAxis edgeAxis = b2ComputeEdgeSeparation(tempPolygonB, v1, normal1);
307  if (edgeAxis.separation > radius)
308  {
309  return;
310  }
311 
312  b2EPAxis polygonAxis = b2ComputePolygonSeparation(tempPolygonB, v1, v2);
313  if (polygonAxis.separation > radius)
314  {
315  return;
316  }
317 
318  // Use hysteresis for jitter reduction.
319  const float k_relativeTol = 0.98f;
320  const float k_absoluteTol = 0.001f;
321 
322  b2EPAxis primaryAxis;
323  if (polygonAxis.separation - radius > k_relativeTol * (edgeAxis.separation - radius) + k_absoluteTol)
324  {
325  primaryAxis = polygonAxis;
326  }
327  else
328  {
329  primaryAxis = edgeAxis;
330  }
331 
332  if (oneSided)
333  {
334  // Smooth collision
335  // See https://box2d.org/posts/2020/06/ghost-collisions/
336 
337  b2Vec2 edge0 = v1 - edgeA->m_vertex0;
338  edge0.Normalize();
339  b2Vec2 normal0(edge0.y, -edge0.x);
340  bool convex1 = b2Cross(edge0, edge1) >= 0.0f;
341 
342  b2Vec2 edge2 = edgeA->m_vertex3 - v2;
343  edge2.Normalize();
344  b2Vec2 normal2(edge2.y, -edge2.x);
345  bool convex2 = b2Cross(edge1, edge2) >= 0.0f;
346 
347  const float sinTol = 0.1f;
348  bool side1 = b2Dot(primaryAxis.normal, edge1) <= 0.0f;
349 
350  // Check Gauss Map
351  if (side1)
352  {
353  if (convex1)
354  {
355  if (b2Cross(primaryAxis.normal, normal0) > sinTol)
356  {
357  // Skip region
358  return;
359  }
360 
361  // Admit region
362  }
363  else
364  {
365  // Snap region
366  primaryAxis = edgeAxis;
367  }
368  }
369  else
370  {
371  if (convex2)
372  {
373  if (b2Cross(normal2, primaryAxis.normal) > sinTol)
374  {
375  // Skip region
376  return;
377  }
378 
379  // Admit region
380  }
381  else
382  {
383  // Snap region
384  primaryAxis = edgeAxis;
385  }
386  }
387  }
388 
389  b2ClipVertex clipPoints[2];
390  b2ReferenceFace ref;
391  if (primaryAxis.type == b2EPAxis::e_edgeA)
392  {
393  manifold->type = b2Manifold::e_faceA;
394 
395  // Search for the polygon normal that is most anti-parallel to the edge normal.
396  int32 bestIndex = 0;
397  float bestValue = b2Dot(primaryAxis.normal, tempPolygonB.normals[0]);
398  for (int32 i = 1; i < tempPolygonB.count; ++i)
399  {
400  float value = b2Dot(primaryAxis.normal, tempPolygonB.normals[i]);
401  if (value < bestValue)
402  {
403  bestValue = value;
404  bestIndex = i;
405  }
406  }
407 
408  int32 i1 = bestIndex;
409  int32 i2 = i1 + 1 < tempPolygonB.count ? i1 + 1 : 0;
410 
411  clipPoints[0].v = tempPolygonB.vertices[i1];
412  clipPoints[0].id.cf.indexA = 0;
413  clipPoints[0].id.cf.indexB = static_cast<uint8>(i1);
414  clipPoints[0].id.cf.typeA = b2ContactFeature::e_face;
415  clipPoints[0].id.cf.typeB = b2ContactFeature::e_vertex;
416 
417  clipPoints[1].v = tempPolygonB.vertices[i2];
418  clipPoints[1].id.cf.indexA = 0;
419  clipPoints[1].id.cf.indexB = static_cast<uint8>(i2);
420  clipPoints[1].id.cf.typeA = b2ContactFeature::e_face;
421  clipPoints[1].id.cf.typeB = b2ContactFeature::e_vertex;
422 
423  ref.i1 = 0;
424  ref.i2 = 1;
425  ref.v1 = v1;
426  ref.v2 = v2;
427  ref.normal = primaryAxis.normal;
428  ref.sideNormal1 = -edge1;
429  ref.sideNormal2 = edge1;
430  }
431  else
432  {
433  manifold->type = b2Manifold::e_faceB;
434 
435  clipPoints[0].v = v2;
436  clipPoints[0].id.cf.indexA = 1;
437  clipPoints[0].id.cf.indexB = static_cast<uint8>(primaryAxis.index);
438  clipPoints[0].id.cf.typeA = b2ContactFeature::e_vertex;
439  clipPoints[0].id.cf.typeB = b2ContactFeature::e_face;
440 
441  clipPoints[1].v = v1;
442  clipPoints[1].id.cf.indexA = 0;
443  clipPoints[1].id.cf.indexB = static_cast<uint8>(primaryAxis.index);
444  clipPoints[1].id.cf.typeA = b2ContactFeature::e_vertex;
445  clipPoints[1].id.cf.typeB = b2ContactFeature::e_face;
446 
447  ref.i1 = primaryAxis.index;
448  ref.i2 = ref.i1 + 1 < tempPolygonB.count ? ref.i1 + 1 : 0;
449  ref.v1 = tempPolygonB.vertices[ref.i1];
450  ref.v2 = tempPolygonB.vertices[ref.i2];
451  ref.normal = tempPolygonB.normals[ref.i1];
452 
453  // CCW winding
454  ref.sideNormal1.Set(ref.normal.y, -ref.normal.x);
455  ref.sideNormal2 = -ref.sideNormal1;
456  }
457 
458  ref.sideOffset1 = b2Dot(ref.sideNormal1, ref.v1);
459  ref.sideOffset2 = b2Dot(ref.sideNormal2, ref.v2);
460 
461  // Clip incident edge against reference face side planes
462  b2ClipVertex clipPoints1[2];
463  b2ClipVertex clipPoints2[2];
464  int32 np;
465 
466  // Clip to side 1
467  np = b2ClipSegmentToLine(clipPoints1, clipPoints, ref.sideNormal1, ref.sideOffset1, ref.i1);
468 
469  if (np < b2_maxManifoldPoints)
470  {
471  return;
472  }
473 
474  // Clip to side 2
475  np = b2ClipSegmentToLine(clipPoints2, clipPoints1, ref.sideNormal2, ref.sideOffset2, ref.i2);
476 
477  if (np < b2_maxManifoldPoints)
478  {
479  return;
480  }
481 
482  // Now clipPoints2 contains the clipped points.
483  if (primaryAxis.type == b2EPAxis::e_edgeA)
484  {
485  manifold->localNormal = ref.normal;
486  manifold->localPoint = ref.v1;
487  }
488  else
489  {
490  manifold->localNormal = polygonB->m_normals[ref.i1];
491  manifold->localPoint = polygonB->m_vertices[ref.i1];
492  }
493 
494  int32 pointCount = 0;
495  for (int32 i = 0; i < b2_maxManifoldPoints; ++i)
496  {
497  float separation;
498 
499  separation = b2Dot(ref.normal, clipPoints2[i].v - ref.v1);
500 
501  if (separation <= radius)
502  {
503  b2ManifoldPoint* cp = manifold->points + pointCount;
504 
505  if (primaryAxis.type == b2EPAxis::e_edgeA)
506  {
507  cp->localPoint = b2MulT(xf, clipPoints2[i].v);
508  cp->id = clipPoints2[i].id;
509  }
510  else
511  {
512  cp->localPoint = clipPoints2[i].v;
513  cp->id.cf.typeA = clipPoints2[i].id.cf.typeB;
514  cp->id.cf.typeB = clipPoints2[i].id.cf.typeA;
515  cp->id.cf.indexA = clipPoints2[i].id.cf.indexB;
516  cp->id.cf.indexB = clipPoints2[i].id.cf.indexA;
517  }
518 
519  ++pointCount;
520  }
521  }
522 
523  manifold->pointCount = pointCount;
524 }
d
uint8 indexB
Feature index on shapeB.
Definition: b2_collision.h:53
uint8 typeA
The feature type on shapeA.
Definition: b2_collision.h:54
T b2Min(T a, T b)
Definition: b2_math.h:626
void b2CollideEdgeAndPolygon(b2Manifold *manifold, const b2EdgeShape *edgeA, const b2Transform &xfA, const b2PolygonShape *polygonB, const b2Transform &xfB)
Compute the collision manifold between an edge and a polygon.
b2Vec2 b2Mul(const b2Mat22 &A, const b2Vec2 &v)
Definition: b2_math.h:422
Used for computing contact manifolds.
Definition: b2_collision.h:146
float b2Dot(const b2Vec2 &a, const b2Vec2 &b)
Perform the dot product on two vectors.
Definition: b2_math.h:395
b2Vec2 localNormal
not use for Type::e_points
Definition: b2_collision.h:109
b2Rot q
Definition: b2_math.h:361
f
float x
Definition: b2_math.h:128
static b2EPAxis b2ComputeEdgeSeparation(const b2TempPolygon &polygonB, const b2Vec2 &v1, const b2Vec2 &normal1)
float y
Definition: b2_math.h:128
XmlRpcServer s
b2ContactID id
uniquely identifies a contact point between two shapes
Definition: b2_collision.h:80
uint8 typeB
The feature type on shapeB.
Definition: b2_collision.h:55
b2ContactFeature cf
Definition: b2_collision.h:61
#define b2_maxManifoldPoints
Definition: b2_common.h:51
uint32 key
Used to quickly compare contact ids.
Definition: b2_collision.h:62
b2Vec2 m_vertex0
Optional adjacent vertices. These are used for smooth collision.
Definition: b2_edge_shape.h:69
b2Vec2 normals[b2_maxPolygonVertices]
void SetZero()
Set this vector to all zeros.
Definition: b2_math.h:50
A solid circle shape.
A 2D column vector.
Definition: b2_math.h:41
b2ContactID id
Definition: b2_collision.h:149
signed int int32
Definition: b2_types.h:28
b2Vec2 m_p
Position.
B2_API int32 b2ClipSegmentToLine(b2ClipVertex vOut[2], const b2ClipVertex vIn[2], const b2Vec2 &normal, float offset, int32 vertexIndexA)
Clipping for contact manifolds.
float m_radius
Definition: b2_shape.h:102
void Set(float x_, float y_)
Set this vector to some specified coordinates.
Definition: b2_math.h:53
float b2Cross(const b2Vec2 &a, const b2Vec2 &b)
Perform the cross product on two vectors. In 2D this produces a scalar.
Definition: b2_math.h:401
b2Vec2 m_vertices[b2_maxPolygonVertices]
int32 pointCount
the number of manifold points
Definition: b2_collision.h:112
b2Vec2 vertices[b2_maxPolygonVertices]
b2Vec2 m_vertex3
Definition: b2_edge_shape.h:69
b2Vec2 localPoint
usage depends on manifold type
Definition: b2_collision.h:77
uint8 indexA
Feature index on shapeA.
Definition: b2_collision.h:52
b2Vec2 localPoint
usage depends on manifold type
Definition: b2_collision.h:110
b2ManifoldPoint points[b2_maxManifoldPoints]
the points of contact
Definition: b2_collision.h:108
unsigned char uint8
Definition: b2_types.h:29
b2Vec2 b2MulT(const b2Mat22 &A, const b2Vec2 &v)
Definition: b2_math.h:429
b2Vec2 m_vertex1
These are the edge vertices.
Definition: b2_edge_shape.h:66
float Normalize()
Convert this vector into a unit vector. Returns the length.
Definition: b2_math.h:102
b2Vec2 m_vertex2
Definition: b2_edge_shape.h:66
#define b2_maxPolygonVertices
Definition: b2_settings.h:53
bool m_oneSided
Uses m_vertex0 and m_vertex3 to create smooth collision.
Definition: b2_edge_shape.h:72
#define b2Assert(A)
Definition: b2_common.h:37
b2Vec2 m_normals[b2_maxPolygonVertices]
static b2EPAxis b2ComputePolygonSeparation(const b2TempPolygon &polygonB, const b2Vec2 &v1, const b2Vec2 &v2)
void b2CollideEdgeAndCircle(b2Manifold *manifold, const b2EdgeShape *edgeA, const b2Transform &xfA, const b2CircleShape *circleB, const b2Transform &xfB)
Compute the collision manifold between an edge and a circle.


mvsim
Author(s):
autogenerated on Tue Jul 4 2023 03:08:19