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 }