File indexing completed on 2025-08-03 03:49:55

0001 /*
0002 * Copyright (c) 2007-2009 Erin Catto http://www.gphysics.com
0003 *
0004 * This software is provided 'as-is', without any express or implied
0005 * warranty.  In no event will the authors be held liable for any damages
0006 * arising from the use of this software.
0007 * Permission is granted to anyone to use this software for any purpose,
0008 * including commercial applications, and to alter it and redistribute it
0009 * freely, subject to the following restrictions:
0010 * 1. The origin of this software must not be misrepresented; you must not
0011 * claim that you wrote the original software. If you use this software
0012 * in a product, an acknowledgment in the product documentation would be
0013 * appreciated but is not required.
0014 * 2. Altered source versions must be plainly marked as such, and must not be
0015 * misrepresented as being the original software.
0016 * 3. This notice may not be removed or altered from any source distribution.
0017 */
0018 
0019 #include <Box2D/Collision/b2Collision.h>
0020 #include <Box2D/Collision/Shapes/b2CircleShape.h>
0021 #include <Box2D/Collision/Shapes/b2EdgeShape.h>
0022 #include <Box2D/Collision/Shapes/b2PolygonShape.h>
0023 
0024 enum b2EdgeType
0025 {
0026     b2_isolated,
0027     b2_concave,
0028     b2_flat,
0029     b2_convex
0030 };
0031 
0032 // Compute contact points for edge versus circle.
0033 // This accounts for edge connectivity.
0034 void b2CollideEdgeAndCircle(b2Manifold* manifold,
0035                             const b2EdgeShape* edgeA, const b2Transform& xfA,
0036                             const b2CircleShape* circleB, const b2Transform& xfB)
0037 {
0038     manifold->pointCount = 0;
0039 
0040     // Compute circle in frame of edge
0041     b2Vec2 Q = b2MulT(xfA, b2Mul(xfB, circleB->m_p));
0042 
0043     b2Vec2 A = edgeA->m_vertex1, B = edgeA->m_vertex2;
0044     b2Vec2 e = B - A;
0045 
0046     // Barycentric coordinates
0047     qreal u = b2Dot(e, B - Q);
0048     qreal v = b2Dot(e, Q - A);
0049 
0050     qreal radius = edgeA->m_radius + circleB->m_radius;
0051 
0052     b2ContactFeature cf;
0053     cf.indexB = 0;
0054     cf.typeB = b2ContactFeature::e_vertex;
0055 
0056     // Region A
0057     if (v <= 0.0f)
0058     {
0059         b2Vec2 P = A;
0060         b2Vec2 d = Q - P;
0061         qreal dd = b2Dot(d, d);
0062         if (dd > radius * radius)
0063         {
0064             return;
0065         }
0066 
0067         // Is there an edge connected to A?
0068         if (edgeA->m_hasVertex0)
0069         {
0070             b2Vec2 A1 = edgeA->m_vertex0;
0071             b2Vec2 B1 = A;
0072             b2Vec2 e1 = B1 - A1;
0073             qreal u1 = b2Dot(e1, B1 - Q);
0074 
0075             // Is the circle in Region AB of the previous edge?
0076             if (u1 > 0.0f)
0077             {
0078                 return;
0079             }
0080         }
0081 
0082         cf.indexA = 0;
0083         cf.typeA = b2ContactFeature::e_vertex;
0084         manifold->pointCount = 1;
0085         manifold->type = b2Manifold::e_circles;
0086         manifold->localNormal.SetZero();
0087         manifold->localPoint = P;
0088         manifold->points[0].id.key = 0;
0089         manifold->points[0].id.cf = cf;
0090         manifold->points[0].localPoint = circleB->m_p;
0091         return;
0092     }
0093     
0094     // Region B
0095     if (u <= 0.0f)
0096     {
0097         b2Vec2 P = B;
0098         b2Vec2 d = Q - P;
0099         qreal dd = b2Dot(d, d);
0100         if (dd > radius * radius)
0101         {
0102             return;
0103         }
0104 
0105         // Is there an edge connected to B?
0106         if (edgeA->m_hasVertex3)
0107         {
0108             b2Vec2 B2 = edgeA->m_vertex3;
0109             b2Vec2 A2 = B;
0110             b2Vec2 e2 = B2 - A2;
0111             qreal v2 = b2Dot(e2, Q - A2);
0112 
0113             // Is the circle in Region AB of the next edge?
0114             if (v2 > 0.0f)
0115             {
0116                 return;
0117             }
0118         }
0119 
0120         cf.indexA = 1;
0121         cf.typeA = b2ContactFeature::e_vertex;
0122         manifold->pointCount = 1;
0123         manifold->type = b2Manifold::e_circles;
0124         manifold->localNormal.SetZero();
0125         manifold->localPoint = P;
0126         manifold->points[0].id.key = 0;
0127         manifold->points[0].id.cf = cf;
0128         manifold->points[0].localPoint = circleB->m_p;
0129         return;
0130     }
0131 
0132     // Region AB
0133     qreal den = b2Dot(e, e);
0134     b2Assert(den > 0.0f);
0135     b2Vec2 P = (1.0f / den) * (u * A + v * B);
0136     b2Vec2 d = Q - P;
0137     qreal dd = b2Dot(d, d);
0138     if (dd > radius * radius)
0139     {
0140         return;
0141     }
0142 
0143     b2Vec2 n(-e.y, e.x);
0144     if (b2Dot(n, Q - A) < 0.0f)
0145     {
0146         n.Set(-n.x, -n.y);
0147     }
0148     n.Normalize();
0149 
0150     cf.indexA = 0;
0151     cf.typeA = b2ContactFeature::e_face;
0152     manifold->pointCount = 1;
0153     manifold->type = b2Manifold::e_faceA;
0154     manifold->localNormal = n;
0155     manifold->localPoint = A;
0156     manifold->points[0].id.key = 0;
0157     manifold->points[0].id.cf = cf;
0158     manifold->points[0].localPoint = circleB->m_p;
0159 }
0160 
0161 struct b2EPAxis
0162 {
0163     enum Type
0164     {
0165         e_unknown,
0166         e_edgeA,
0167         e_edgeB
0168     };
0169 
0170     Type type;
0171     int32 index;
0172     qreal separation;
0173 };
0174 
0175 // Edge shape plus more stuff.
0176 struct b2FatEdge
0177 {
0178     b2Vec2 v0, v1, v2, v3;
0179     b2Vec2 normal;
0180     bool hasVertex0, hasVertex3;
0181 };
0182 
0183 // This lets us treat an edge shape and a polygon in the same
0184 // way in the SAT collider.
0185 struct b2EPProxy
0186 {
0187     b2Vec2 vertices[b2_maxPolygonVertices];
0188     b2Vec2 normals[b2_maxPolygonVertices];
0189     b2Vec2 centroid;
0190     int32 count;
0191 };
0192 
0193 // This class collides and edge and a polygon, taking into account edge adjacency.
0194 struct b2EPCollider
0195 {
0196     b2EPCollider(const b2EdgeShape* edgeA, const b2Transform& xfA,
0197                 const b2PolygonShape* polygonB_in, const b2Transform& xfB);
0198 
0199     void Collide(b2Manifold* manifold);
0200 
0201     void ComputeAdjacency();
0202     b2EPAxis ComputeEdgeSeparation();
0203     b2EPAxis ComputePolygonSeparation();
0204     void FindIncidentEdge(b2ClipVertex c[2], const b2EPProxy* proxy1, int32 edge1, const b2EPProxy* proxy2);
0205 
0206     b2FatEdge m_edgeA;
0207 
0208     b2EPProxy m_proxyA, m_proxyB;
0209 
0210     b2Transform m_xf;
0211     b2Vec2 m_normal0, m_normal2;
0212     b2Vec2 m_limit11, m_limit12;
0213     b2Vec2 m_limit21, m_limit22;
0214     qreal m_radius;
0215 };
0216 
0217 b2EPCollider::b2EPCollider(const b2EdgeShape* edgeA, const b2Transform& xfA,
0218                 const b2PolygonShape* polygonB, const b2Transform& xfB)
0219 {
0220     m_xf = b2MulT(xfA, xfB);
0221 
0222     // Edge geometry
0223     m_edgeA.v0 = edgeA->m_vertex0;
0224     m_edgeA.v1 = edgeA->m_vertex1;
0225     m_edgeA.v2 = edgeA->m_vertex2;
0226     m_edgeA.v3 = edgeA->m_vertex3;
0227     b2Vec2 e = m_edgeA.v2 - m_edgeA.v1;
0228 
0229     // Normal points outwards in CCW order.
0230     m_edgeA.normal.Set(e.y, -e.x);
0231     m_edgeA.normal.Normalize();
0232     m_edgeA.hasVertex0 = edgeA->m_hasVertex0;
0233     m_edgeA.hasVertex3 = edgeA->m_hasVertex3;
0234 
0235     // Proxy for edge
0236     m_proxyA.vertices[0] = m_edgeA.v1;
0237     m_proxyA.vertices[1] = m_edgeA.v2;
0238     m_proxyA.normals[0] = m_edgeA.normal;
0239     m_proxyA.normals[1] = -m_edgeA.normal;
0240     m_proxyA.centroid = 0.5f * (m_edgeA.v1 + m_edgeA.v2);
0241     m_proxyA.count = 2;
0242 
0243     // Proxy for polygon
0244     m_proxyB.count = polygonB->m_vertexCount;
0245     m_proxyB.centroid = b2Mul(m_xf, polygonB->m_centroid);
0246     for (int32 i = 0; i < polygonB->m_vertexCount; ++i)
0247     {
0248         m_proxyB.vertices[i] = b2Mul(m_xf, polygonB->m_vertices[i]);
0249         m_proxyB.normals[i] = b2Mul(m_xf.R, polygonB->m_normals[i]);
0250     }
0251 
0252     m_radius = 2.0f * b2_polygonRadius;
0253 
0254     m_limit11.SetZero();
0255     m_limit12.SetZero();
0256     m_limit21.SetZero();
0257     m_limit22.SetZero();
0258 }
0259 
0260 // Collide an edge and polygon. This uses the SAT and clipping to produce up to 2 contact points.
0261 // Edge adjacency is handle to produce locally valid contact points and normals. This is intended
0262 // to allow the polygon to slide smoothly over an edge chain.
0263 //
0264 // Algorithm
0265 // 1. Classify front-side or back-side collision with edge.
0266 // 2. Compute separation
0267 // 3. Process adjacent edges
0268 // 4. Classify adjacent edge as convex, flat, null, or concave
0269 // 5. Skip null or concave edges. Concave edges get a separate manifold.
0270 // 6. If the edge is flat, compute contact points as normal. Discard boundary points.
0271 // 7. If the edge is convex, compute it's separation.
0272 // 8. Use the minimum separation of up to three edges. If the minimum separation
0273 //    is not the primary edge, return.
0274 // 9. If the minimum separation is the primary edge, compute the contact points and return.
0275 void b2EPCollider::Collide(b2Manifold* manifold)
0276 {
0277     manifold->pointCount = 0;
0278 
0279     ComputeAdjacency();
0280 
0281     b2EPAxis edgeAxis = ComputeEdgeSeparation();
0282 
0283     // If no valid normal can be found than this edge should not collide.
0284     // This can happen on the middle edge of a 3-edge zig-zag chain.
0285     if (edgeAxis.type == b2EPAxis::e_unknown)
0286     {
0287         return;
0288     }
0289 
0290     if (edgeAxis.separation > m_radius)
0291     {
0292         return;
0293     }
0294 
0295     b2EPAxis polygonAxis = ComputePolygonSeparation();
0296     if (polygonAxis.type != b2EPAxis::e_unknown && polygonAxis.separation > m_radius)
0297     {
0298         return;
0299     }
0300 
0301     // Use hysteresis for jitter reduction.
0302     const qreal k_relativeTol = 0.98f;
0303     const qreal k_absoluteTol = 0.001f;
0304 
0305     b2EPAxis primaryAxis;
0306     if (polygonAxis.type == b2EPAxis::e_unknown)
0307     {
0308         primaryAxis = edgeAxis;
0309     }
0310     else if (polygonAxis.separation > k_relativeTol * edgeAxis.separation + k_absoluteTol)
0311     {
0312         primaryAxis = polygonAxis;
0313     }
0314     else
0315     {
0316         primaryAxis = edgeAxis;
0317     }
0318 
0319     b2EPProxy* proxy1;
0320     b2EPProxy* proxy2;
0321     b2ClipVertex incidentEdge[2];
0322     if (primaryAxis.type == b2EPAxis::e_edgeA)
0323     {
0324         proxy1 = &m_proxyA;
0325         proxy2 = &m_proxyB;
0326         manifold->type = b2Manifold::e_faceA;
0327     }
0328     else
0329     {
0330         proxy1 = &m_proxyB;
0331         proxy2 = &m_proxyA;
0332         manifold->type = b2Manifold::e_faceB;
0333     }
0334 
0335     int32 edge1 = primaryAxis.index;
0336 
0337     FindIncidentEdge(incidentEdge, proxy1, primaryAxis.index, proxy2);
0338     int32 count1 = proxy1->count;
0339     const b2Vec2* vertices1 = proxy1->vertices;
0340 
0341     int32 iv1 = edge1;
0342     int32 iv2 = edge1 + 1 < count1 ? edge1 + 1 : 0;
0343 
0344     b2Vec2 v11 = vertices1[iv1];
0345     b2Vec2 v12 = vertices1[iv2];
0346 
0347     b2Vec2 tangent = v12 - v11;
0348     tangent.Normalize();
0349     
0350     b2Vec2 normal = b2Cross(tangent, 1.0f);
0351     b2Vec2 planePoint = 0.5f * (v11 + v12);
0352 
0353     // Face offset.
0354     qreal frontOffset = b2Dot(normal, v11);
0355 
0356     // Side offsets, extended by polytope skin thickness.
0357     qreal sideOffset1 = -b2Dot(tangent, v11) + m_radius;
0358     qreal sideOffset2 = b2Dot(tangent, v12) + m_radius;
0359 
0360     // Clip incident edge against extruded edge1 side edges.
0361     b2ClipVertex clipPoints1[2];
0362     b2ClipVertex clipPoints2[2];
0363     int np;
0364 
0365     // Clip to box side 1
0366     np = b2ClipSegmentToLine(clipPoints1, incidentEdge, -tangent, sideOffset1, iv1);
0367 
0368     if (np < b2_maxManifoldPoints)
0369     {
0370         return;
0371     }
0372 
0373     // Clip to negative box side 1
0374     np = b2ClipSegmentToLine(clipPoints2, clipPoints1,  tangent, sideOffset2, iv2);
0375 
0376     if (np < b2_maxManifoldPoints)
0377     {
0378         return;
0379     }
0380 
0381     // Now clipPoints2 contains the clipped points.
0382     if (primaryAxis.type == b2EPAxis::e_edgeA)
0383     {
0384         manifold->localNormal = normal;
0385         manifold->localPoint = planePoint;
0386     }
0387     else
0388     {
0389         manifold->localNormal = b2MulT(m_xf.R, normal);
0390         manifold->localPoint = b2MulT(m_xf, planePoint);
0391     }
0392 
0393     int32 pointCount = 0;
0394     for (int32 i = 0; i < b2_maxManifoldPoints; ++i)
0395     {
0396         qreal separation;
0397         
0398         separation = b2Dot(normal, clipPoints2[i].v) - frontOffset;
0399 
0400         if (separation <= m_radius)
0401         {
0402             b2ManifoldPoint* cp = manifold->points + pointCount;
0403 
0404             if (primaryAxis.type == b2EPAxis::e_edgeA)
0405             {
0406                 cp->localPoint = b2MulT(m_xf, clipPoints2[i].v);
0407                 cp->id = clipPoints2[i].id;
0408             }
0409             else
0410             {
0411                 cp->localPoint = clipPoints2[i].v;
0412                 cp->id.cf.typeA = clipPoints2[i].id.cf.typeB;
0413                 cp->id.cf.typeB = clipPoints2[i].id.cf.typeA;
0414                 cp->id.cf.indexA = clipPoints2[i].id.cf.indexB;
0415                 cp->id.cf.indexB = clipPoints2[i].id.cf.indexA;
0416             }
0417 
0418             ++pointCount;
0419         }
0420     }
0421 
0422     manifold->pointCount = pointCount;
0423 }
0424 
0425 // Compute allowable normal ranges based on adjacency.
0426 // A normal n is allowable iff:
0427 // cross(n, n1) >= 0.0f && cross(n2, n) >= 0.0f
0428 // n points from A to B (edge to polygon)
0429 void b2EPCollider::ComputeAdjacency()
0430 {
0431     b2Vec2 v0 = m_edgeA.v0;
0432     b2Vec2 v1 = m_edgeA.v1;
0433     b2Vec2 v2 = m_edgeA.v2;
0434     b2Vec2 v3 = m_edgeA.v3;
0435 
0436     // Determine allowable the normal regions based on adjacency.
0437     // Note: it may be possible that no normal is admissable.
0438     b2Vec2 centerB = m_proxyB.centroid;
0439     if (m_edgeA.hasVertex0)
0440     {
0441         b2Vec2 e0 = v1 - v0;
0442         b2Vec2 e1 = v2 - v1;
0443         b2Vec2 n0(e0.y, -e0.x);
0444         b2Vec2 n1(e1.y, -e1.x);
0445         n0.Normalize();
0446         n1.Normalize();
0447 
0448         bool convex = b2Cross(n0, n1) >= 0.0f;
0449         bool front0 = b2Dot(n0, centerB - v0) >= 0.0f;
0450         bool front1 = b2Dot(n1, centerB - v1) >= 0.0f;
0451 
0452         if (convex)
0453         {
0454             if (front0 || front1)
0455             {
0456                 m_limit11 = n1;
0457                 m_limit12 = n0;
0458             }
0459             else
0460             {
0461                 m_limit11 = -n1;
0462                 m_limit12 = -n0;
0463             }
0464         }
0465         else
0466         {
0467             if (front0 && front1)
0468             {
0469                 m_limit11 = n0;
0470                 m_limit12 = n1;
0471             }
0472             else
0473             {
0474                 m_limit11 = -n0;
0475                 m_limit12 = -n1;
0476             }
0477         }
0478     }
0479     else
0480     {
0481         m_limit11.SetZero();
0482         m_limit12.SetZero();
0483     }
0484 
0485     if (m_edgeA.hasVertex3)
0486     {
0487         b2Vec2 e1 = v2 - v1;
0488         b2Vec2 e2 = v3 - v2;
0489         b2Vec2 n1(e1.y, -e1.x);
0490         b2Vec2 n2(e2.y, -e2.x);
0491         n1.Normalize();
0492         n2.Normalize();
0493 
0494         bool convex = b2Cross(n1, n2) >= 0.0f;
0495         bool front1 = b2Dot(n1, centerB - v1) >= 0.0f;
0496         bool front2 = b2Dot(n2, centerB - v2) >= 0.0f;
0497 
0498         if (convex)
0499         {
0500             if (front1 || front2)
0501             {
0502                 m_limit21 = n2;
0503                 m_limit22 = n1;
0504             }
0505             else
0506             {
0507                 m_limit21 = -n2;
0508                 m_limit22 = -n1;
0509             }
0510         }
0511         else
0512         {
0513             if (front1 && front2)
0514             {
0515                 m_limit21 = n1;
0516                 m_limit22 = n2;
0517             }
0518             else
0519             {
0520                 m_limit21 = -n1;
0521                 m_limit22 = -n2;
0522             }
0523         }
0524     }
0525     else
0526     {
0527         m_limit21.SetZero();
0528         m_limit22.SetZero();
0529     }
0530 }
0531 
0532 b2EPAxis b2EPCollider::ComputeEdgeSeparation()
0533 {
0534     // EdgeA separation
0535     b2EPAxis bestAxis;
0536     bestAxis.type = b2EPAxis::e_unknown;
0537     bestAxis.index = -1;
0538     bestAxis.separation = -FLT_MAX;
0539     b2Vec2 normals[2] = {m_edgeA.normal, -m_edgeA.normal};
0540     
0541     for (int32 i = 0; i < 2; ++i)
0542     {
0543         b2Vec2 n = normals[i];
0544 
0545         // Adjacency
0546         bool valid1 = b2Cross(n, m_limit11) >= -b2_angularSlop && b2Cross(m_limit12, n) >= -b2_angularSlop;
0547         bool valid2 = b2Cross(n, m_limit21) >= -b2_angularSlop && b2Cross(m_limit22, n) >= -b2_angularSlop;
0548 
0549         if (valid1 == false || valid2 == false)
0550         {
0551             continue;
0552         }
0553         
0554         b2EPAxis axis;
0555         axis.type = b2EPAxis::e_edgeA;
0556         axis.index = i;
0557         axis.separation = FLT_MAX;
0558 
0559         for (int32 j = 0; j < m_proxyB.count; ++j)
0560         {
0561             qreal s = b2Dot(n, m_proxyB.vertices[j] - m_edgeA.v1);
0562             if (s < axis.separation)
0563             {
0564                 axis.separation = s;
0565             }
0566         }
0567 
0568         if (axis.separation > m_radius)
0569         {
0570             return axis;
0571         }
0572 
0573         if (axis.separation > bestAxis.separation)
0574         {
0575             bestAxis = axis;
0576         }
0577     }
0578 
0579     return bestAxis;
0580 }
0581 
0582 b2EPAxis b2EPCollider::ComputePolygonSeparation()
0583 {
0584     b2EPAxis axis;
0585     axis.type = b2EPAxis::e_unknown;
0586     axis.index = -1;
0587     axis.separation = -FLT_MAX;
0588     for (int32 i = 0; i < m_proxyB.count; ++i)
0589     {
0590         b2Vec2 n = -m_proxyB.normals[i];
0591 
0592         // Adjacency
0593         bool valid1 = b2Cross(n, m_limit11) >= -b2_angularSlop && b2Cross(m_limit12, n) >= -b2_angularSlop;
0594         bool valid2 = b2Cross(n, m_limit21) >= -b2_angularSlop && b2Cross(m_limit22, n) >= -b2_angularSlop;
0595 
0596         if (valid1 == false && valid2 == false)
0597         {
0598             continue;
0599         }
0600 
0601         qreal s1 = b2Dot(n, m_proxyB.vertices[i] - m_edgeA.v1);
0602         qreal s2 = b2Dot(n, m_proxyB.vertices[i] - m_edgeA.v2);
0603         qreal s = b2Min(s1, s2);
0604 
0605         if (s > m_radius)
0606         {
0607             axis.type = b2EPAxis::e_edgeB;
0608             axis.index = i;
0609             axis.separation = s;
0610         }
0611 
0612         if (s > axis.separation)
0613         {
0614             axis.type = b2EPAxis::e_edgeB;
0615             axis.index = i;
0616             axis.separation = s;
0617         }
0618     }
0619 
0620     return axis;
0621 }
0622 
0623 void b2EPCollider::FindIncidentEdge(b2ClipVertex c[2], const b2EPProxy* proxy1, int32 edge1, const b2EPProxy* proxy2)
0624 {
0625     int32 count1 = proxy1->count;
0626     const b2Vec2* normals1 = proxy1->normals;
0627 
0628     int32 count2 = proxy2->count;
0629     const b2Vec2* vertices2 = proxy2->vertices;
0630     const b2Vec2* normals2 = proxy2->normals;
0631 
0632     b2Assert(0 <= edge1 && edge1 < count1);
0633 
0634     // Get the normal of the reference edge in proxy2's frame.
0635     b2Vec2 normal1 = normals1[edge1];
0636 
0637     // Find the incident edge on proxy2.
0638     int32 index = 0;
0639     qreal minDot = b2_maxFloat;
0640     for (int32 i = 0; i < count2; ++i)
0641     {
0642         qreal dot = b2Dot(normal1, normals2[i]);
0643         if (dot < minDot)
0644         {
0645             minDot = dot;
0646             index = i;
0647         }
0648     }
0649 
0650     // Build the clip vertices for the incident edge.
0651     int32 i1 = index;
0652     int32 i2 = i1 + 1 < count2 ? i1 + 1 : 0;
0653 
0654     c[0].v = vertices2[i1];
0655     c[0].id.cf.indexA = (uint8)edge1;
0656     c[0].id.cf.indexB = (uint8)i1;
0657     c[0].id.cf.typeA = b2ContactFeature::e_face;
0658     c[0].id.cf.typeB = b2ContactFeature::e_vertex;
0659 
0660     c[1].v = vertices2[i2];
0661     c[1].id.cf.indexA = (uint8)edge1;
0662     c[1].id.cf.indexB = (uint8)i2;
0663     c[1].id.cf.typeA = b2ContactFeature::e_face;
0664     c[1].id.cf.typeB = b2ContactFeature::e_vertex;
0665 }
0666 
0667 void b2CollideEdgeAndPolygon(   b2Manifold* manifold,
0668                                 const b2EdgeShape* edgeA, const b2Transform& xfA,
0669                                 const b2PolygonShape* polygonB, const b2Transform& xfB)
0670 {
0671     b2EPCollider collider(edgeA, xfA, polygonB, xfB);
0672     collider.Collide(manifold);
0673 }