123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698 |
- /*
- * Copyright (c) 2007-2009 Erin Catto http://www.box2d.org
- *
- * This software is provided 'as-is', without any express or implied
- * warranty. In no event will the authors be held liable for any damages
- * arising from the use of this software.
- * Permission is granted to anyone to use this software for any purpose,
- * including commercial applications, and to alter it and redistribute it
- * freely, subject to the following restrictions:
- * 1. The origin of this software must not be misrepresented; you must not
- * claim that you wrote the original software. If you use this software
- * in a product, an acknowledgment in the product documentation would be
- * appreciated but is not required.
- * 2. Altered source versions must be plainly marked as such, and must not be
- * misrepresented as being the original software.
- * 3. This notice may not be removed or altered from any source distribution.
- */
- #include <Box2D/Collision/b2Collision.h>
- #include <Box2D/Collision/Shapes/b2CircleShape.h>
- #include <Box2D/Collision/Shapes/b2EdgeShape.h>
- #include <Box2D/Collision/Shapes/b2PolygonShape.h>
- // Compute contact points for edge versus circle.
- // This accounts for edge connectivity.
- void b2CollideEdgeAndCircle(b2Manifold* manifold,
- const b2EdgeShape* edgeA, const b2Transform& xfA,
- const b2CircleShape* circleB, const b2Transform& xfB)
- {
- manifold->pointCount = 0;
-
- // Compute circle in frame of edge
- b2Vec2 Q = b2MulT(xfA, b2Mul(xfB, circleB->m_p));
-
- b2Vec2 A = edgeA->m_vertex1, B = edgeA->m_vertex2;
- b2Vec2 e = B - A;
-
- // Barycentric coordinates
- float32 u = b2Dot(e, B - Q);
- float32 v = b2Dot(e, Q - A);
-
- float32 radius = edgeA->m_radius + circleB->m_radius;
-
- b2ContactFeature cf;
- cf.indexB = 0;
- cf.typeB = b2ContactFeature::e_vertex;
-
- // Region A
- if (v <= 0.0f)
- {
- b2Vec2 P = A;
- b2Vec2 d = Q - P;
- float32 dd = b2Dot(d, d);
- if (dd > radius * radius)
- {
- return;
- }
-
- // Is there an edge connected to A?
- if (edgeA->m_hasVertex0)
- {
- b2Vec2 A1 = edgeA->m_vertex0;
- b2Vec2 B1 = A;
- b2Vec2 e1 = B1 - A1;
- float32 u1 = b2Dot(e1, B1 - Q);
-
- // Is the circle in Region AB of the previous edge?
- if (u1 > 0.0f)
- {
- return;
- }
- }
-
- cf.indexA = 0;
- cf.typeA = b2ContactFeature::e_vertex;
- manifold->pointCount = 1;
- manifold->type = b2Manifold::e_circles;
- manifold->localNormal.SetZero();
- manifold->localPoint = P;
- manifold->points[0].id.key = 0;
- manifold->points[0].id.cf = cf;
- manifold->points[0].localPoint = circleB->m_p;
- return;
- }
-
- // Region B
- if (u <= 0.0f)
- {
- b2Vec2 P = B;
- b2Vec2 d = Q - P;
- float32 dd = b2Dot(d, d);
- if (dd > radius * radius)
- {
- return;
- }
-
- // Is there an edge connected to B?
- if (edgeA->m_hasVertex3)
- {
- b2Vec2 B2 = edgeA->m_vertex3;
- b2Vec2 A2 = B;
- b2Vec2 e2 = B2 - A2;
- float32 v2 = b2Dot(e2, Q - A2);
-
- // Is the circle in Region AB of the next edge?
- if (v2 > 0.0f)
- {
- return;
- }
- }
-
- cf.indexA = 1;
- cf.typeA = b2ContactFeature::e_vertex;
- manifold->pointCount = 1;
- manifold->type = b2Manifold::e_circles;
- manifold->localNormal.SetZero();
- manifold->localPoint = P;
- manifold->points[0].id.key = 0;
- manifold->points[0].id.cf = cf;
- manifold->points[0].localPoint = circleB->m_p;
- return;
- }
-
- // Region AB
- float32 den = b2Dot(e, e);
- b2Assert(den > 0.0f);
- b2Vec2 P = (1.0f / den) * (u * A + v * B);
- b2Vec2 d = Q - P;
- float32 dd = b2Dot(d, d);
- if (dd > radius * radius)
- {
- return;
- }
-
- b2Vec2 n(-e.y, e.x);
- if (b2Dot(n, Q - A) < 0.0f)
- {
- n.Set(-n.x, -n.y);
- }
- n.Normalize();
-
- cf.indexA = 0;
- cf.typeA = b2ContactFeature::e_face;
- manifold->pointCount = 1;
- manifold->type = b2Manifold::e_faceA;
- manifold->localNormal = n;
- manifold->localPoint = A;
- manifold->points[0].id.key = 0;
- manifold->points[0].id.cf = cf;
- manifold->points[0].localPoint = circleB->m_p;
- }
- // This structure is used to keep track of the best separating axis.
- struct b2EPAxis
- {
- enum Type
- {
- e_unknown,
- e_edgeA,
- e_edgeB
- };
-
- Type type;
- int32 index;
- float32 separation;
- };
- // This holds polygon B expressed in frame A.
- struct b2TempPolygon
- {
- b2Vec2 vertices[b2_maxPolygonVertices];
- b2Vec2 normals[b2_maxPolygonVertices];
- int32 count;
- };
- // Reference face used for clipping
- struct b2ReferenceFace
- {
- int32 i1, i2;
-
- b2Vec2 v1, v2;
-
- b2Vec2 normal;
-
- b2Vec2 sideNormal1;
- float32 sideOffset1;
-
- b2Vec2 sideNormal2;
- float32 sideOffset2;
- };
- // This class collides and edge and a polygon, taking into account edge adjacency.
- struct b2EPCollider
- {
- void Collide(b2Manifold* manifold, const b2EdgeShape* edgeA, const b2Transform& xfA,
- const b2PolygonShape* polygonB, const b2Transform& xfB);
- b2EPAxis ComputeEdgeSeparation();
- b2EPAxis ComputePolygonSeparation();
-
- enum VertexType
- {
- e_isolated,
- e_concave,
- e_convex
- };
-
- b2TempPolygon m_polygonB;
-
- b2Transform m_xf;
- b2Vec2 m_centroidB;
- b2Vec2 m_v0, m_v1, m_v2, m_v3;
- b2Vec2 m_normal0, m_normal1, m_normal2;
- b2Vec2 m_normal;
- VertexType m_type1, m_type2;
- b2Vec2 m_lowerLimit, m_upperLimit;
- float32 m_radius;
- bool m_front;
- };
- // Algorithm:
- // 1. Classify v1 and v2
- // 2. Classify polygon centroid as front or back
- // 3. Flip normal if necessary
- // 4. Initialize normal range to [-pi, pi] about face normal
- // 5. Adjust normal range according to adjacent edges
- // 6. Visit each separating axes, only accept axes within the range
- // 7. Return if _any_ axis indicates separation
- // 8. Clip
- void b2EPCollider::Collide(b2Manifold* manifold, const b2EdgeShape* edgeA, const b2Transform& xfA,
- const b2PolygonShape* polygonB, const b2Transform& xfB)
- {
- m_xf = b2MulT(xfA, xfB);
-
- m_centroidB = b2Mul(m_xf, polygonB->m_centroid);
-
- m_v0 = edgeA->m_vertex0;
- m_v1 = edgeA->m_vertex1;
- m_v2 = edgeA->m_vertex2;
- m_v3 = edgeA->m_vertex3;
-
- bool hasVertex0 = edgeA->m_hasVertex0;
- bool hasVertex3 = edgeA->m_hasVertex3;
-
- b2Vec2 edge1 = m_v2 - m_v1;
- edge1.Normalize();
- m_normal1.Set(edge1.y, -edge1.x);
- float32 offset1 = b2Dot(m_normal1, m_centroidB - m_v1);
- float32 offset0 = 0.0f, offset2 = 0.0f;
- bool convex1 = false, convex2 = false;
-
- // Is there a preceding edge?
- if (hasVertex0)
- {
- b2Vec2 edge0 = m_v1 - m_v0;
- edge0.Normalize();
- m_normal0.Set(edge0.y, -edge0.x);
- convex1 = b2Cross(edge0, edge1) >= 0.0f;
- offset0 = b2Dot(m_normal0, m_centroidB - m_v0);
- }
-
- // Is there a following edge?
- if (hasVertex3)
- {
- b2Vec2 edge2 = m_v3 - m_v2;
- edge2.Normalize();
- m_normal2.Set(edge2.y, -edge2.x);
- convex2 = b2Cross(edge1, edge2) > 0.0f;
- offset2 = b2Dot(m_normal2, m_centroidB - m_v2);
- }
-
- // Determine front or back collision. Determine collision normal limits.
- if (hasVertex0 && hasVertex3)
- {
- if (convex1 && convex2)
- {
- m_front = offset0 >= 0.0f || offset1 >= 0.0f || offset2 >= 0.0f;
- if (m_front)
- {
- m_normal = m_normal1;
- m_lowerLimit = m_normal0;
- m_upperLimit = m_normal2;
- }
- else
- {
- m_normal = -m_normal1;
- m_lowerLimit = -m_normal1;
- m_upperLimit = -m_normal1;
- }
- }
- else if (convex1)
- {
- m_front = offset0 >= 0.0f || (offset1 >= 0.0f && offset2 >= 0.0f);
- if (m_front)
- {
- m_normal = m_normal1;
- m_lowerLimit = m_normal0;
- m_upperLimit = m_normal1;
- }
- else
- {
- m_normal = -m_normal1;
- m_lowerLimit = -m_normal2;
- m_upperLimit = -m_normal1;
- }
- }
- else if (convex2)
- {
- m_front = offset2 >= 0.0f || (offset0 >= 0.0f && offset1 >= 0.0f);
- if (m_front)
- {
- m_normal = m_normal1;
- m_lowerLimit = m_normal1;
- m_upperLimit = m_normal2;
- }
- else
- {
- m_normal = -m_normal1;
- m_lowerLimit = -m_normal1;
- m_upperLimit = -m_normal0;
- }
- }
- else
- {
- m_front = offset0 >= 0.0f && offset1 >= 0.0f && offset2 >= 0.0f;
- if (m_front)
- {
- m_normal = m_normal1;
- m_lowerLimit = m_normal1;
- m_upperLimit = m_normal1;
- }
- else
- {
- m_normal = -m_normal1;
- m_lowerLimit = -m_normal2;
- m_upperLimit = -m_normal0;
- }
- }
- }
- else if (hasVertex0)
- {
- if (convex1)
- {
- m_front = offset0 >= 0.0f || offset1 >= 0.0f;
- if (m_front)
- {
- m_normal = m_normal1;
- m_lowerLimit = m_normal0;
- m_upperLimit = -m_normal1;
- }
- else
- {
- m_normal = -m_normal1;
- m_lowerLimit = m_normal1;
- m_upperLimit = -m_normal1;
- }
- }
- else
- {
- m_front = offset0 >= 0.0f && offset1 >= 0.0f;
- if (m_front)
- {
- m_normal = m_normal1;
- m_lowerLimit = m_normal1;
- m_upperLimit = -m_normal1;
- }
- else
- {
- m_normal = -m_normal1;
- m_lowerLimit = m_normal1;
- m_upperLimit = -m_normal0;
- }
- }
- }
- else if (hasVertex3)
- {
- if (convex2)
- {
- m_front = offset1 >= 0.0f || offset2 >= 0.0f;
- if (m_front)
- {
- m_normal = m_normal1;
- m_lowerLimit = -m_normal1;
- m_upperLimit = m_normal2;
- }
- else
- {
- m_normal = -m_normal1;
- m_lowerLimit = -m_normal1;
- m_upperLimit = m_normal1;
- }
- }
- else
- {
- m_front = offset1 >= 0.0f && offset2 >= 0.0f;
- if (m_front)
- {
- m_normal = m_normal1;
- m_lowerLimit = -m_normal1;
- m_upperLimit = m_normal1;
- }
- else
- {
- m_normal = -m_normal1;
- m_lowerLimit = -m_normal2;
- m_upperLimit = m_normal1;
- }
- }
- }
- else
- {
- m_front = offset1 >= 0.0f;
- if (m_front)
- {
- m_normal = m_normal1;
- m_lowerLimit = -m_normal1;
- m_upperLimit = -m_normal1;
- }
- else
- {
- m_normal = -m_normal1;
- m_lowerLimit = m_normal1;
- m_upperLimit = m_normal1;
- }
- }
-
- // Get polygonB in frameA
- m_polygonB.count = polygonB->m_count;
- for (int32 i = 0; i < polygonB->m_count; ++i)
- {
- m_polygonB.vertices[i] = b2Mul(m_xf, polygonB->m_vertices[i]);
- m_polygonB.normals[i] = b2Mul(m_xf.q, polygonB->m_normals[i]);
- }
-
- m_radius = 2.0f * b2_polygonRadius;
-
- manifold->pointCount = 0;
-
- b2EPAxis edgeAxis = ComputeEdgeSeparation();
-
- // If no valid normal can be found than this edge should not collide.
- if (edgeAxis.type == b2EPAxis::e_unknown)
- {
- return;
- }
-
- if (edgeAxis.separation > m_radius)
- {
- return;
- }
-
- b2EPAxis polygonAxis = ComputePolygonSeparation();
- if (polygonAxis.type != b2EPAxis::e_unknown && polygonAxis.separation > m_radius)
- {
- return;
- }
-
- // Use hysteresis for jitter reduction.
- const float32 k_relativeTol = 0.98f;
- const float32 k_absoluteTol = 0.001f;
-
- b2EPAxis primaryAxis;
- if (polygonAxis.type == b2EPAxis::e_unknown)
- {
- primaryAxis = edgeAxis;
- }
- else if (polygonAxis.separation > k_relativeTol * edgeAxis.separation + k_absoluteTol)
- {
- primaryAxis = polygonAxis;
- }
- else
- {
- primaryAxis = edgeAxis;
- }
-
- b2ClipVertex ie[2];
- b2ReferenceFace rf;
- if (primaryAxis.type == b2EPAxis::e_edgeA)
- {
- manifold->type = b2Manifold::e_faceA;
-
- // Search for the polygon normal that is most anti-parallel to the edge normal.
- int32 bestIndex = 0;
- float32 bestValue = b2Dot(m_normal, m_polygonB.normals[0]);
- for (int32 i = 1; i < m_polygonB.count; ++i)
- {
- float32 value = b2Dot(m_normal, m_polygonB.normals[i]);
- if (value < bestValue)
- {
- bestValue = value;
- bestIndex = i;
- }
- }
-
- int32 i1 = bestIndex;
- int32 i2 = i1 + 1 < m_polygonB.count ? i1 + 1 : 0;
-
- ie[0].v = m_polygonB.vertices[i1];
- ie[0].id.cf.indexA = 0;
- ie[0].id.cf.indexB = static_cast<uint8>(i1);
- ie[0].id.cf.typeA = b2ContactFeature::e_face;
- ie[0].id.cf.typeB = b2ContactFeature::e_vertex;
-
- ie[1].v = m_polygonB.vertices[i2];
- ie[1].id.cf.indexA = 0;
- ie[1].id.cf.indexB = static_cast<uint8>(i2);
- ie[1].id.cf.typeA = b2ContactFeature::e_face;
- ie[1].id.cf.typeB = b2ContactFeature::e_vertex;
-
- if (m_front)
- {
- rf.i1 = 0;
- rf.i2 = 1;
- rf.v1 = m_v1;
- rf.v2 = m_v2;
- rf.normal = m_normal1;
- }
- else
- {
- rf.i1 = 1;
- rf.i2 = 0;
- rf.v1 = m_v2;
- rf.v2 = m_v1;
- rf.normal = -m_normal1;
- }
- }
- else
- {
- manifold->type = b2Manifold::e_faceB;
-
- ie[0].v = m_v1;
- ie[0].id.cf.indexA = 0;
- ie[0].id.cf.indexB = static_cast<uint8>(primaryAxis.index);
- ie[0].id.cf.typeA = b2ContactFeature::e_vertex;
- ie[0].id.cf.typeB = b2ContactFeature::e_face;
-
- ie[1].v = m_v2;
- ie[1].id.cf.indexA = 0;
- ie[1].id.cf.indexB = static_cast<uint8>(primaryAxis.index);
- ie[1].id.cf.typeA = b2ContactFeature::e_vertex;
- ie[1].id.cf.typeB = b2ContactFeature::e_face;
-
- rf.i1 = primaryAxis.index;
- rf.i2 = rf.i1 + 1 < m_polygonB.count ? rf.i1 + 1 : 0;
- rf.v1 = m_polygonB.vertices[rf.i1];
- rf.v2 = m_polygonB.vertices[rf.i2];
- rf.normal = m_polygonB.normals[rf.i1];
- }
-
- rf.sideNormal1.Set(rf.normal.y, -rf.normal.x);
- rf.sideNormal2 = -rf.sideNormal1;
- rf.sideOffset1 = b2Dot(rf.sideNormal1, rf.v1);
- rf.sideOffset2 = b2Dot(rf.sideNormal2, rf.v2);
-
- // Clip incident edge against extruded edge1 side edges.
- b2ClipVertex clipPoints1[2];
- b2ClipVertex clipPoints2[2];
- int32 np;
-
- // Clip to box side 1
- np = b2ClipSegmentToLine(clipPoints1, ie, rf.sideNormal1, rf.sideOffset1, rf.i1);
-
- if (np < b2_maxManifoldPoints)
- {
- return;
- }
-
- // Clip to negative box side 1
- np = b2ClipSegmentToLine(clipPoints2, clipPoints1, rf.sideNormal2, rf.sideOffset2, rf.i2);
-
- if (np < b2_maxManifoldPoints)
- {
- return;
- }
-
- // Now clipPoints2 contains the clipped points.
- if (primaryAxis.type == b2EPAxis::e_edgeA)
- {
- manifold->localNormal = rf.normal;
- manifold->localPoint = rf.v1;
- }
- else
- {
- manifold->localNormal = polygonB->m_normals[rf.i1];
- manifold->localPoint = polygonB->m_vertices[rf.i1];
- }
-
- int32 pointCount = 0;
- for (int32 i = 0; i < b2_maxManifoldPoints; ++i)
- {
- float32 separation;
-
- separation = b2Dot(rf.normal, clipPoints2[i].v - rf.v1);
-
- if (separation <= m_radius)
- {
- b2ManifoldPoint* cp = manifold->points + pointCount;
-
- if (primaryAxis.type == b2EPAxis::e_edgeA)
- {
- cp->localPoint = b2MulT(m_xf, clipPoints2[i].v);
- cp->id = clipPoints2[i].id;
- }
- else
- {
- cp->localPoint = clipPoints2[i].v;
- cp->id.cf.typeA = clipPoints2[i].id.cf.typeB;
- cp->id.cf.typeB = clipPoints2[i].id.cf.typeA;
- cp->id.cf.indexA = clipPoints2[i].id.cf.indexB;
- cp->id.cf.indexB = clipPoints2[i].id.cf.indexA;
- }
-
- ++pointCount;
- }
- }
-
- manifold->pointCount = pointCount;
- }
- b2EPAxis b2EPCollider::ComputeEdgeSeparation()
- {
- b2EPAxis axis;
- axis.type = b2EPAxis::e_edgeA;
- axis.index = m_front ? 0 : 1;
- axis.separation = FLT_MAX;
-
- for (int32 i = 0; i < m_polygonB.count; ++i)
- {
- float32 s = b2Dot(m_normal, m_polygonB.vertices[i] - m_v1);
- if (s < axis.separation)
- {
- axis.separation = s;
- }
- }
-
- return axis;
- }
- b2EPAxis b2EPCollider::ComputePolygonSeparation()
- {
- b2EPAxis axis;
- axis.type = b2EPAxis::e_unknown;
- axis.index = -1;
- axis.separation = -FLT_MAX;
- b2Vec2 perp(-m_normal.y, m_normal.x);
- for (int32 i = 0; i < m_polygonB.count; ++i)
- {
- b2Vec2 n = -m_polygonB.normals[i];
-
- float32 s1 = b2Dot(n, m_polygonB.vertices[i] - m_v1);
- float32 s2 = b2Dot(n, m_polygonB.vertices[i] - m_v2);
- float32 s = b2Min(s1, s2);
-
- if (s > m_radius)
- {
- // No collision
- axis.type = b2EPAxis::e_edgeB;
- axis.index = i;
- axis.separation = s;
- return axis;
- }
-
- // Adjacency
- if (b2Dot(n, perp) >= 0.0f)
- {
- if (b2Dot(n - m_upperLimit, m_normal) < -b2_angularSlop)
- {
- continue;
- }
- }
- else
- {
- if (b2Dot(n - m_lowerLimit, m_normal) < -b2_angularSlop)
- {
- continue;
- }
- }
-
- if (s > axis.separation)
- {
- axis.type = b2EPAxis::e_edgeB;
- axis.index = i;
- axis.separation = s;
- }
- }
-
- return axis;
- }
- void b2CollideEdgeAndPolygon( b2Manifold* manifold,
- const b2EdgeShape* edgeA, const b2Transform& xfA,
- const b2PolygonShape* polygonB, const b2Transform& xfB)
- {
- b2EPCollider collider;
- collider.Collide(manifold, edgeA, xfA, polygonB, xfB);
- }
|