File indexing completed on 2025-08-03 03:49:56
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/b2TimeOfImpact.h> 0021 #include <Box2D/Collision/Shapes/b2CircleShape.h> 0022 #include <Box2D/Collision/Shapes/b2PolygonShape.h> 0023 0024 #include <cstdio> 0025 using namespace std; 0026 0027 int32 b2_toiCalls, b2_toiIters, b2_toiMaxIters; 0028 int32 b2_toiRootIters, b2_toiMaxRootIters; 0029 0030 struct b2SeparationFunction 0031 { 0032 enum Type 0033 { 0034 e_points, 0035 e_faceA, 0036 e_faceB 0037 }; 0038 0039 // TODO_ERIN might not need to return the separation 0040 0041 qreal Initialize(const b2SimplexCache* cache, 0042 const b2DistanceProxy* proxyA, const b2Sweep& sweepA, 0043 const b2DistanceProxy* proxyB, const b2Sweep& sweepB, 0044 qreal t1) 0045 { 0046 m_proxyA = proxyA; 0047 m_proxyB = proxyB; 0048 int32 count = cache->count; 0049 b2Assert(0 < count && count < 3); 0050 0051 m_sweepA = sweepA; 0052 m_sweepB = sweepB; 0053 0054 b2Transform xfA, xfB; 0055 m_sweepA.GetTransform(&xfA, t1); 0056 m_sweepB.GetTransform(&xfB, t1); 0057 0058 if (count == 1) 0059 { 0060 m_type = e_points; 0061 b2Vec2 localPointA = m_proxyA->GetVertex(cache->indexA[0]); 0062 b2Vec2 localPointB = m_proxyB->GetVertex(cache->indexB[0]); 0063 b2Vec2 pointA = b2Mul(xfA, localPointA); 0064 b2Vec2 pointB = b2Mul(xfB, localPointB); 0065 m_axis = pointB - pointA; 0066 qreal s = m_axis.Normalize(); 0067 return s; 0068 } 0069 else if (cache->indexA[0] == cache->indexA[1]) 0070 { 0071 // Two points on B and one on A. 0072 m_type = e_faceB; 0073 b2Vec2 localPointB1 = proxyB->GetVertex(cache->indexB[0]); 0074 b2Vec2 localPointB2 = proxyB->GetVertex(cache->indexB[1]); 0075 0076 m_axis = b2Cross(localPointB2 - localPointB1, 1.0f); 0077 m_axis.Normalize(); 0078 b2Vec2 normal = b2Mul(xfB.R, m_axis); 0079 0080 m_localPoint = 0.5f * (localPointB1 + localPointB2); 0081 b2Vec2 pointB = b2Mul(xfB, m_localPoint); 0082 0083 b2Vec2 localPointA = proxyA->GetVertex(cache->indexA[0]); 0084 b2Vec2 pointA = b2Mul(xfA, localPointA); 0085 0086 qreal s = b2Dot(pointA - pointB, normal); 0087 if (s < 0.0f) 0088 { 0089 m_axis = -m_axis; 0090 s = -s; 0091 } 0092 return s; 0093 } 0094 else 0095 { 0096 // Two points on A and one or two points on B. 0097 m_type = e_faceA; 0098 b2Vec2 localPointA1 = m_proxyA->GetVertex(cache->indexA[0]); 0099 b2Vec2 localPointA2 = m_proxyA->GetVertex(cache->indexA[1]); 0100 0101 m_axis = b2Cross(localPointA2 - localPointA1, 1.0f); 0102 m_axis.Normalize(); 0103 b2Vec2 normal = b2Mul(xfA.R, m_axis); 0104 0105 m_localPoint = 0.5f * (localPointA1 + localPointA2); 0106 b2Vec2 pointA = b2Mul(xfA, m_localPoint); 0107 0108 b2Vec2 localPointB = m_proxyB->GetVertex(cache->indexB[0]); 0109 b2Vec2 pointB = b2Mul(xfB, localPointB); 0110 0111 qreal s = b2Dot(pointB - pointA, normal); 0112 if (s < 0.0f) 0113 { 0114 m_axis = -m_axis; 0115 s = -s; 0116 } 0117 return s; 0118 } 0119 } 0120 0121 qreal FindMinSeparation(int32* indexA, int32* indexB, qreal t) const 0122 { 0123 b2Transform xfA, xfB; 0124 m_sweepA.GetTransform(&xfA, t); 0125 m_sweepB.GetTransform(&xfB, t); 0126 0127 switch (m_type) 0128 { 0129 case e_points: 0130 { 0131 b2Vec2 axisA = b2MulT(xfA.R, m_axis); 0132 b2Vec2 axisB = b2MulT(xfB.R, -m_axis); 0133 0134 *indexA = m_proxyA->GetSupport(axisA); 0135 *indexB = m_proxyB->GetSupport(axisB); 0136 0137 b2Vec2 localPointA = m_proxyA->GetVertex(*indexA); 0138 b2Vec2 localPointB = m_proxyB->GetVertex(*indexB); 0139 0140 b2Vec2 pointA = b2Mul(xfA, localPointA); 0141 b2Vec2 pointB = b2Mul(xfB, localPointB); 0142 0143 qreal separation = b2Dot(pointB - pointA, m_axis); 0144 return separation; 0145 } 0146 0147 case e_faceA: 0148 { 0149 b2Vec2 normal = b2Mul(xfA.R, m_axis); 0150 b2Vec2 pointA = b2Mul(xfA, m_localPoint); 0151 0152 b2Vec2 axisB = b2MulT(xfB.R, -normal); 0153 0154 *indexA = -1; 0155 *indexB = m_proxyB->GetSupport(axisB); 0156 0157 b2Vec2 localPointB = m_proxyB->GetVertex(*indexB); 0158 b2Vec2 pointB = b2Mul(xfB, localPointB); 0159 0160 qreal separation = b2Dot(pointB - pointA, normal); 0161 return separation; 0162 } 0163 0164 case e_faceB: 0165 { 0166 b2Vec2 normal = b2Mul(xfB.R, m_axis); 0167 b2Vec2 pointB = b2Mul(xfB, m_localPoint); 0168 0169 b2Vec2 axisA = b2MulT(xfA.R, -normal); 0170 0171 *indexB = -1; 0172 *indexA = m_proxyA->GetSupport(axisA); 0173 0174 b2Vec2 localPointA = m_proxyA->GetVertex(*indexA); 0175 b2Vec2 pointA = b2Mul(xfA, localPointA); 0176 0177 qreal separation = b2Dot(pointA - pointB, normal); 0178 return separation; 0179 } 0180 0181 default: 0182 b2Assert(false); 0183 *indexA = -1; 0184 *indexB = -1; 0185 return 0.0f; 0186 } 0187 } 0188 0189 qreal Evaluate(int32 indexA, int32 indexB, qreal t) const 0190 { 0191 b2Transform xfA, xfB; 0192 m_sweepA.GetTransform(&xfA, t); 0193 m_sweepB.GetTransform(&xfB, t); 0194 0195 switch (m_type) 0196 { 0197 case e_points: 0198 { 0199 //b2Vec2 axisA = b2MulT(xfA.R, m_axis); 0200 //b2Vec2 axisB = b2MulT(xfB.R, -m_axis); 0201 0202 b2Vec2 localPointA = m_proxyA->GetVertex(indexA); 0203 b2Vec2 localPointB = m_proxyB->GetVertex(indexB); 0204 0205 b2Vec2 pointA = b2Mul(xfA, localPointA); 0206 b2Vec2 pointB = b2Mul(xfB, localPointB); 0207 qreal separation = b2Dot(pointB - pointA, m_axis); 0208 0209 return separation; 0210 } 0211 0212 case e_faceA: 0213 { 0214 b2Vec2 normal = b2Mul(xfA.R, m_axis); 0215 b2Vec2 pointA = b2Mul(xfA, m_localPoint); 0216 0217 //b2Vec2 axisB = b2MulT(xfB.R, -normal); 0218 0219 b2Vec2 localPointB = m_proxyB->GetVertex(indexB); 0220 b2Vec2 pointB = b2Mul(xfB, localPointB); 0221 0222 qreal separation = b2Dot(pointB - pointA, normal); 0223 return separation; 0224 } 0225 0226 case e_faceB: 0227 { 0228 b2Vec2 normal = b2Mul(xfB.R, m_axis); 0229 b2Vec2 pointB = b2Mul(xfB, m_localPoint); 0230 0231 //b2Vec2 axisA = b2MulT(xfA.R, -normal); 0232 0233 b2Vec2 localPointA = m_proxyA->GetVertex(indexA); 0234 b2Vec2 pointA = b2Mul(xfA, localPointA); 0235 0236 qreal separation = b2Dot(pointA - pointB, normal); 0237 return separation; 0238 } 0239 0240 default: 0241 b2Assert(false); 0242 return 0.0f; 0243 } 0244 } 0245 0246 const b2DistanceProxy* m_proxyA; 0247 const b2DistanceProxy* m_proxyB; 0248 b2Sweep m_sweepA, m_sweepB; 0249 Type m_type; 0250 b2Vec2 m_localPoint; 0251 b2Vec2 m_axis; 0252 }; 0253 0254 // CCD via the local separating axis method. This seeks progression 0255 // by computing the largest time at which separation is maintained. 0256 void b2TimeOfImpact(b2TOIOutput* output, const b2TOIInput* input) 0257 { 0258 ++b2_toiCalls; 0259 0260 output->state = b2TOIOutput::e_unknown; 0261 output->t = input->tMax; 0262 0263 const b2DistanceProxy* proxyA = &input->proxyA; 0264 const b2DistanceProxy* proxyB = &input->proxyB; 0265 0266 b2Sweep sweepA = input->sweepA; 0267 b2Sweep sweepB = input->sweepB; 0268 0269 // Large rotations can make the root finder fail, so we normalize the 0270 // sweep angles. 0271 sweepA.Normalize(); 0272 sweepB.Normalize(); 0273 0274 qreal tMax = input->tMax; 0275 0276 qreal totalRadius = proxyA->m_radius + proxyB->m_radius; 0277 qreal target = b2Max(b2_linearSlop, totalRadius - 3.0f * b2_linearSlop); 0278 qreal tolerance = 0.25f * b2_linearSlop; 0279 b2Assert(target > tolerance); 0280 0281 qreal t1 = 0.0f; 0282 const int32 k_maxIterations = 20; // TODO_ERIN b2Settings 0283 int32 iter = 0; 0284 0285 // Prepare input for distance query. 0286 b2SimplexCache cache; 0287 cache.count = 0; 0288 b2DistanceInput distanceInput; 0289 distanceInput.proxyA = input->proxyA; 0290 distanceInput.proxyB = input->proxyB; 0291 distanceInput.useRadii = false; 0292 0293 // The outer loop progressively attempts to compute new separating axes. 0294 // This loop terminates when an axis is repeated (no progress is made). 0295 for(;;) 0296 { 0297 b2Transform xfA, xfB; 0298 sweepA.GetTransform(&xfA, t1); 0299 sweepB.GetTransform(&xfB, t1); 0300 0301 // Get the distance between shapes. We can also use the results 0302 // to get a separating axis. 0303 distanceInput.transformA = xfA; 0304 distanceInput.transformB = xfB; 0305 b2DistanceOutput distanceOutput; 0306 b2Distance(&distanceOutput, &cache, &distanceInput); 0307 0308 // If the shapes are overlapped, we give up on continuous collision. 0309 if (distanceOutput.distance <= 0.0f) 0310 { 0311 // Failure! 0312 output->state = b2TOIOutput::e_overlapped; 0313 output->t = 0.0f; 0314 break; 0315 } 0316 0317 if (distanceOutput.distance < target + tolerance) 0318 { 0319 // Victory! 0320 output->state = b2TOIOutput::e_touching; 0321 output->t = t1; 0322 break; 0323 } 0324 0325 // Initialize the separating axis. 0326 b2SeparationFunction fcn; 0327 fcn.Initialize(&cache, proxyA, sweepA, proxyB, sweepB, t1); 0328 #if 0 0329 // Dump the curve seen by the root finder 0330 { 0331 const int32 N = 100; 0332 qreal dx = 1.0f / N; 0333 qreal xs[N+1]; 0334 qreal fs[N+1]; 0335 0336 qreal x = 0.0f; 0337 0338 for (int32 i = 0; i <= N; ++i) 0339 { 0340 sweepA.GetTransform(&xfA, x); 0341 sweepB.GetTransform(&xfB, x); 0342 qreal f = fcn.Evaluate(xfA, xfB) - target; 0343 0344 printf("%g %g\n", x, f); 0345 0346 xs[i] = x; 0347 fs[i] = f; 0348 0349 x += dx; 0350 } 0351 } 0352 #endif 0353 0354 // Compute the TOI on the separating axis. We do this by successively 0355 // resolving the deepest point. This loop is bounded by the number of vertices. 0356 bool done = false; 0357 qreal t2 = tMax; 0358 int32 pushBackIter = 0; 0359 for (;;) 0360 { 0361 // Find the deepest point at t2. Store the witness point indices. 0362 int32 indexA, indexB; 0363 qreal s2 = fcn.FindMinSeparation(&indexA, &indexB, t2); 0364 0365 // Is the final configuration separated? 0366 if (s2 > target + tolerance) 0367 { 0368 // Victory! 0369 output->state = b2TOIOutput::e_separated; 0370 output->t = tMax; 0371 done = true; 0372 break; 0373 } 0374 0375 // Has the separation reached tolerance? 0376 if (s2 > target - tolerance) 0377 { 0378 // Advance the sweeps 0379 t1 = t2; 0380 break; 0381 } 0382 0383 // Compute the initial separation of the witness points. 0384 qreal s1 = fcn.Evaluate(indexA, indexB, t1); 0385 0386 // Check for initial overlap. This might happen if the root finder 0387 // runs out of iterations. 0388 if (s1 < target - tolerance) 0389 { 0390 output->state = b2TOIOutput::e_failed; 0391 output->t = t1; 0392 done = true; 0393 break; 0394 } 0395 0396 // Check for touching 0397 if (s1 <= target + tolerance) 0398 { 0399 // Victory! t1 should hold the TOI (could be 0.0). 0400 output->state = b2TOIOutput::e_touching; 0401 output->t = t1; 0402 done = true; 0403 break; 0404 } 0405 0406 // Compute 1D root of: f(x) - target = 0 0407 int32 rootIterCount = 0; 0408 qreal a1 = t1, a2 = t2; 0409 for (;;) 0410 { 0411 // Use a mix of the secant rule and bisection. 0412 qreal t; 0413 if (rootIterCount & 1) 0414 { 0415 // Secant rule to improve convergence. 0416 t = a1 + (target - s1) * (a2 - a1) / (s2 - s1); 0417 } 0418 else 0419 { 0420 // Bisection to guarantee progress. 0421 t = 0.5f * (a1 + a2); 0422 } 0423 0424 qreal s = fcn.Evaluate(indexA, indexB, t); 0425 0426 if (b2Abs(s - target) < tolerance) 0427 { 0428 // t2 holds a tentative value for t1 0429 t2 = t; 0430 break; 0431 } 0432 0433 // Ensure we continue to bracket the root. 0434 if (s > target) 0435 { 0436 a1 = t; 0437 s1 = s; 0438 } 0439 else 0440 { 0441 a2 = t; 0442 s2 = s; 0443 } 0444 0445 ++rootIterCount; 0446 ++b2_toiRootIters; 0447 0448 if (rootIterCount == 50) 0449 { 0450 break; 0451 } 0452 } 0453 0454 b2_toiMaxRootIters = b2Max(b2_toiMaxRootIters, rootIterCount); 0455 0456 ++pushBackIter; 0457 0458 if (pushBackIter == b2_maxPolygonVertices) 0459 { 0460 break; 0461 } 0462 } 0463 0464 ++iter; 0465 ++b2_toiIters; 0466 0467 if (done) 0468 { 0469 break; 0470 } 0471 0472 if (iter == k_maxIterations) 0473 { 0474 // Root finder got stuck. Semi-victory. 0475 output->state = b2TOIOutput::e_failed; 0476 output->t = t1; 0477 break; 0478 } 0479 } 0480 0481 b2_toiMaxIters = b2Max(b2_toiMaxIters, iter); 0482 }