File indexing completed on 2025-08-03 03:49:55
0001 /* 0002 * Copyright (c) 2006-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/b2PolygonShape.h> 0021 0022 // Find the separation between poly1 and poly2 for a give edge normal on poly1. 0023 static qreal b2EdgeSeparation(const b2PolygonShape* poly1, const b2Transform& xf1, int32 edge1, 0024 const b2PolygonShape* poly2, const b2Transform& xf2) 0025 { 0026 int32 count1 = poly1->m_vertexCount; 0027 const b2Vec2* vertices1 = poly1->m_vertices; 0028 const b2Vec2* normals1 = poly1->m_normals; 0029 0030 int32 count2 = poly2->m_vertexCount; 0031 const b2Vec2* vertices2 = poly2->m_vertices; 0032 0033 b2Assert(0 <= edge1 && edge1 < count1); 0034 0035 // Convert normal from poly1's frame into poly2's frame. 0036 b2Vec2 normal1World = b2Mul(xf1.R, normals1[edge1]); 0037 b2Vec2 normal1 = b2MulT(xf2.R, normal1World); 0038 0039 // Find support vertex on poly2 for -normal. 0040 int32 index = 0; 0041 qreal minDot = b2_maxFloat; 0042 0043 for (int32 i = 0; i < count2; ++i) 0044 { 0045 qreal dot = b2Dot(vertices2[i], normal1); 0046 if (dot < minDot) 0047 { 0048 minDot = dot; 0049 index = i; 0050 } 0051 } 0052 0053 b2Vec2 v1 = b2Mul(xf1, vertices1[edge1]); 0054 b2Vec2 v2 = b2Mul(xf2, vertices2[index]); 0055 qreal separation = b2Dot(v2 - v1, normal1World); 0056 return separation; 0057 } 0058 0059 // Find the max separation between poly1 and poly2 using edge normals from poly1. 0060 static qreal b2FindMaxSeparation(int32* edgeIndex, 0061 const b2PolygonShape* poly1, const b2Transform& xf1, 0062 const b2PolygonShape* poly2, const b2Transform& xf2) 0063 { 0064 int32 count1 = poly1->m_vertexCount; 0065 const b2Vec2* normals1 = poly1->m_normals; 0066 0067 // Vector pointing from the centroid of poly1 to the centroid of poly2. 0068 b2Vec2 d = b2Mul(xf2, poly2->m_centroid) - b2Mul(xf1, poly1->m_centroid); 0069 b2Vec2 dLocal1 = b2MulT(xf1.R, d); 0070 0071 // Find edge normal on poly1 that has the largest projection onto d. 0072 int32 edge = 0; 0073 qreal maxDot = -b2_maxFloat; 0074 for (int32 i = 0; i < count1; ++i) 0075 { 0076 qreal dot = b2Dot(normals1[i], dLocal1); 0077 if (dot > maxDot) 0078 { 0079 maxDot = dot; 0080 edge = i; 0081 } 0082 } 0083 0084 // Get the separation for the edge normal. 0085 qreal s = b2EdgeSeparation(poly1, xf1, edge, poly2, xf2); 0086 0087 // Check the separation for the previous edge normal. 0088 int32 prevEdge = edge - 1 >= 0 ? edge - 1 : count1 - 1; 0089 qreal sPrev = b2EdgeSeparation(poly1, xf1, prevEdge, poly2, xf2); 0090 0091 // Check the separation for the next edge normal. 0092 int32 nextEdge = edge + 1 < count1 ? edge + 1 : 0; 0093 qreal sNext = b2EdgeSeparation(poly1, xf1, nextEdge, poly2, xf2); 0094 0095 // Find the best edge and the search direction. 0096 int32 bestEdge; 0097 qreal bestSeparation; 0098 int32 increment; 0099 if (sPrev > s && sPrev > sNext) 0100 { 0101 increment = -1; 0102 bestEdge = prevEdge; 0103 bestSeparation = sPrev; 0104 } 0105 else if (sNext > s) 0106 { 0107 increment = 1; 0108 bestEdge = nextEdge; 0109 bestSeparation = sNext; 0110 } 0111 else 0112 { 0113 *edgeIndex = edge; 0114 return s; 0115 } 0116 0117 // Perform a local search for the best edge normal. 0118 for ( ; ; ) 0119 { 0120 if (increment == -1) 0121 edge = bestEdge - 1 >= 0 ? bestEdge - 1 : count1 - 1; 0122 else 0123 edge = bestEdge + 1 < count1 ? bestEdge + 1 : 0; 0124 0125 s = b2EdgeSeparation(poly1, xf1, edge, poly2, xf2); 0126 0127 if (s > bestSeparation) 0128 { 0129 bestEdge = edge; 0130 bestSeparation = s; 0131 } 0132 else 0133 { 0134 break; 0135 } 0136 } 0137 0138 *edgeIndex = bestEdge; 0139 return bestSeparation; 0140 } 0141 0142 static void b2FindIncidentEdge(b2ClipVertex c[2], 0143 const b2PolygonShape* poly1, const b2Transform& xf1, int32 edge1, 0144 const b2PolygonShape* poly2, const b2Transform& xf2) 0145 { 0146 int32 count1 = poly1->m_vertexCount; 0147 const b2Vec2* normals1 = poly1->m_normals; 0148 0149 int32 count2 = poly2->m_vertexCount; 0150 const b2Vec2* vertices2 = poly2->m_vertices; 0151 const b2Vec2* normals2 = poly2->m_normals; 0152 0153 b2Assert(0 <= edge1 && edge1 < count1); 0154 0155 // Get the normal of the reference edge in poly2's frame. 0156 b2Vec2 normal1 = b2MulT(xf2.R, b2Mul(xf1.R, normals1[edge1])); 0157 0158 // Find the incident edge on poly2. 0159 int32 index = 0; 0160 qreal minDot = b2_maxFloat; 0161 for (int32 i = 0; i < count2; ++i) 0162 { 0163 qreal dot = b2Dot(normal1, normals2[i]); 0164 if (dot < minDot) 0165 { 0166 minDot = dot; 0167 index = i; 0168 } 0169 } 0170 0171 // Build the clip vertices for the incident edge. 0172 int32 i1 = index; 0173 int32 i2 = i1 + 1 < count2 ? i1 + 1 : 0; 0174 0175 c[0].v = b2Mul(xf2, vertices2[i1]); 0176 c[0].id.cf.indexA = (uint8)edge1; 0177 c[0].id.cf.indexB = (uint8)i1; 0178 c[0].id.cf.typeA = b2ContactFeature::e_face; 0179 c[0].id.cf.typeB = b2ContactFeature::e_vertex; 0180 0181 c[1].v = b2Mul(xf2, vertices2[i2]); 0182 c[1].id.cf.indexA = (uint8)edge1; 0183 c[1].id.cf.indexB = (uint8)i2; 0184 c[1].id.cf.typeA = b2ContactFeature::e_face; 0185 c[1].id.cf.typeB = b2ContactFeature::e_vertex; 0186 } 0187 0188 // Find edge normal of max separation on A - return if separating axis is found 0189 // Find edge normal of max separation on B - return if separation axis is found 0190 // Choose reference edge as min(minA, minB) 0191 // Find incident edge 0192 // Clip 0193 0194 // The normal points from 1 to 2 0195 void b2CollidePolygons(b2Manifold* manifold, 0196 const b2PolygonShape* polyA, const b2Transform& xfA, 0197 const b2PolygonShape* polyB, const b2Transform& xfB) 0198 { 0199 manifold->pointCount = 0; 0200 qreal totalRadius = polyA->m_radius + polyB->m_radius; 0201 0202 int32 edgeA = 0; 0203 qreal separationA = b2FindMaxSeparation(&edgeA, polyA, xfA, polyB, xfB); 0204 if (separationA > totalRadius) 0205 return; 0206 0207 int32 edgeB = 0; 0208 qreal separationB = b2FindMaxSeparation(&edgeB, polyB, xfB, polyA, xfA); 0209 if (separationB > totalRadius) 0210 return; 0211 0212 const b2PolygonShape* poly1; // reference polygon 0213 const b2PolygonShape* poly2; // incident polygon 0214 b2Transform xf1, xf2; 0215 int32 edge1; // reference edge 0216 uint8 flip; 0217 const qreal k_relativeTol = 0.98f; 0218 const qreal k_absoluteTol = 0.001f; 0219 0220 if (separationB > k_relativeTol * separationA + k_absoluteTol) 0221 { 0222 poly1 = polyB; 0223 poly2 = polyA; 0224 xf1 = xfB; 0225 xf2 = xfA; 0226 edge1 = edgeB; 0227 manifold->type = b2Manifold::e_faceB; 0228 flip = 1; 0229 } 0230 else 0231 { 0232 poly1 = polyA; 0233 poly2 = polyB; 0234 xf1 = xfA; 0235 xf2 = xfB; 0236 edge1 = edgeA; 0237 manifold->type = b2Manifold::e_faceA; 0238 flip = 0; 0239 } 0240 0241 b2ClipVertex incidentEdge[2]; 0242 b2FindIncidentEdge(incidentEdge, poly1, xf1, edge1, poly2, xf2); 0243 0244 int32 count1 = poly1->m_vertexCount; 0245 const b2Vec2* vertices1 = poly1->m_vertices; 0246 0247 int32 iv1 = edge1; 0248 int32 iv2 = edge1 + 1 < count1 ? edge1 + 1 : 0; 0249 0250 b2Vec2 v11 = vertices1[iv1]; 0251 b2Vec2 v12 = vertices1[iv2]; 0252 0253 b2Vec2 localTangent = v12 - v11; 0254 localTangent.Normalize(); 0255 0256 b2Vec2 localNormal = b2Cross(localTangent, 1.0f); 0257 b2Vec2 planePoint = 0.5f * (v11 + v12); 0258 0259 b2Vec2 tangent = b2Mul(xf1.R, localTangent); 0260 b2Vec2 normal = b2Cross(tangent, 1.0f); 0261 0262 v11 = b2Mul(xf1, v11); 0263 v12 = b2Mul(xf1, v12); 0264 0265 // Face offset. 0266 qreal frontOffset = b2Dot(normal, v11); 0267 0268 // Side offsets, extended by polytope skin thickness. 0269 qreal sideOffset1 = -b2Dot(tangent, v11) + totalRadius; 0270 qreal sideOffset2 = b2Dot(tangent, v12) + totalRadius; 0271 0272 // Clip incident edge against extruded edge1 side edges. 0273 b2ClipVertex clipPoints1[2]; 0274 b2ClipVertex clipPoints2[2]; 0275 int np; 0276 0277 // Clip to box side 1 0278 np = b2ClipSegmentToLine(clipPoints1, incidentEdge, -tangent, sideOffset1, iv1); 0279 0280 if (np < 2) 0281 return; 0282 0283 // Clip to negative box side 1 0284 np = b2ClipSegmentToLine(clipPoints2, clipPoints1, tangent, sideOffset2, iv2); 0285 0286 if (np < 2) 0287 { 0288 return; 0289 } 0290 0291 // Now clipPoints2 contains the clipped points. 0292 manifold->localNormal = localNormal; 0293 manifold->localPoint = planePoint; 0294 0295 int32 pointCount = 0; 0296 for (int32 i = 0; i < b2_maxManifoldPoints; ++i) 0297 { 0298 qreal separation = b2Dot(normal, clipPoints2[i].v) - frontOffset; 0299 0300 if (separation <= totalRadius) 0301 { 0302 b2ManifoldPoint* cp = manifold->points + pointCount; 0303 cp->localPoint = b2MulT(xf2, clipPoints2[i].v); 0304 cp->id = clipPoints2[i].id; 0305 if (flip) 0306 { 0307 // Swap features 0308 b2ContactFeature cf = cp->id.cf; 0309 cp->id.cf.indexA = cf.indexB; 0310 cp->id.cf.indexB = cf.indexA; 0311 cp->id.cf.typeA = cf.typeB; 0312 cp->id.cf.typeB = cf.typeA; 0313 } 0314 ++pointCount; 0315 } 0316 } 0317 0318 manifold->pointCount = pointCount; 0319 }