Dart Documentationbox2dTimeOfImpact

TimeOfImpact class

class TimeOfImpact {
 static const int MAX_ITERATIONS = 1000;

 static int toiCalls;
 static int toiIters;
 static int toiMaxIters;
 static int toiRootIters;
 static int toiMaxRootIters;

 /** Pool variables */
 final SimplexCache cache;
 final DistanceInput distanceInput;
 final Transform xfA;
 final Transform xfB;
 final DistanceOutput distanceOutput;
 final SeparationFunction fcn;
 final List<int> indexes;
 final Sweep sweepA;
 final Sweep sweepB;

 DefaultWorldPool pool;

 TimeOfImpact._construct(DefaultWorldPool argPool) :
   pool = argPool,
   cache = new SimplexCache(),
   distanceInput = new DistanceInput(),
   xfA = new Transform(),
   xfB = new Transform(),
   distanceOutput = new DistanceOutput(),
   fcn = new SeparationFunction(),
   indexes = new List<int>(2),
   sweepA = new Sweep(),
   sweepB = new Sweep() {
   indexes[0] = 0;
   indexes[1] = 0;
   toiCalls = 0;
   toiIters = 0;
   toiMaxIters = 0;
   toiRootIters = 0;
   toiMaxRootIters = 0;
 }

 /**
  * Compute the upper bound on time before two shapes penetrate. Time is
  * represented as a fraction between [0,tMax]. This uses a swept separating
  * axis and may miss some intermediate, non-tunneling collision. If you
  * change the time interval, you should call this function again.
  * Note: use Distance to compute the contact point and normal at the time
  * of impact.
  */
 void timeOfImpact(TimeOfImpactOutput output, TimeOfImpactInput input) {
   // CCD via the local separating axis method. This seeks progression
   // by computing the largest time at which separation is maintained.
   ++toiCalls;

   output.state = TimeOfImpactOutputState.UNKNOWN;
   output.t = input.tMax;

   DistanceProxy proxyA = input.proxyA;
   DistanceProxy proxyB = input.proxyB;

   sweepA.setFrom(input.sweepA);
   sweepB.setFrom(input.sweepB);

   // Large rotations can make the root finder fail, so we normalize the
   // sweep angles.
   sweepA.normalize();
   sweepB.normalize();

   double tMax = input.tMax;

   double totalRadius = proxyA.radius + proxyB.radius;
   double target = Math.max(Settings.LINEAR_SLOP,
       totalRadius - 3.0 * Settings.LINEAR_SLOP);
   double tolerance = 0.25 * Settings.LINEAR_SLOP;

   assert (target > tolerance);

   double t1 = 0.0;
   int iter = 0;

   cache.count = 0;
   distanceInput.proxyA = input.proxyA;
   distanceInput.proxyB = input.proxyB;
   distanceInput.useRadii = false;

   // The outer loop progressively attempts to compute new separating axes.
   // This loop terminates when an axis is repeated (no progress is made).
   while (true) {
     sweepA.getTransform(xfA, t1);
     sweepB.getTransform(xfB, t1);
     // Get the distance between shapes. We can also use the results
     // to get a separating axis
     distanceInput.transformA = xfA;
     distanceInput.transformB = xfB;
     pool.distance.distance(distanceOutput, cache, distanceInput);

     // If the shapes are overlapped, we give up on continuous collision.
     if (distanceOutput.distance <= 0) {
       // Failure!
       output.state = TimeOfImpactOutputState.OVERLAPPED;
       output.t = 0;
       break;
     }

     if (distanceOutput.distance < target + tolerance) {
       // Victory!
       output.state = TimeOfImpactOutputState.TOUCHING;
       output.t = t1;
       break;
     }

     // Initialize the separating axis.
     fcn.initialize(cache, proxyA, sweepA, proxyB, sweepB, t1);

     // Compute the TimeOfImpact on the separating axis. We do this by successively
     // resolving the deepest point. This loop is bounded by the number of
     // vertices.
     bool done = false;
     num t2 = tMax;
     int pushBackIter = 0;
     while (true) {

       // Find the deepest point at t2. Store the witness point indices.
       num s2 = fcn.findMinSeparation(indexes, t2);
       // Is the configuration separated?
       if (s2 > target + tolerance) {
         // Victory!
         output.state = TimeOfImpactOutputState.SEPARATED;
         output.t = tMax;
         done = true;
         break;
       }

       // Has the separation reached tolerance?
       if (s2 > target - tolerance) {
         // Advance the sweeps
         t1 = t2;
         break;
       }

       // Compute the initial separation of the witness points.
       num s1 = fcn.evaluate(indexes[0], indexes[1], t1);
       // Check for initial overlap. This might happen if the root finder
       // runs out of iterations.
       if (s1 < target - tolerance) {
         output.state = TimeOfImpactOutputState.FAILED;
         output.t = t1;
         done = true;
         break;
       }

       // Check for touching
       if (s1 <= target + tolerance) {
         // Victory! t1 should hold the TimeOfImpact (could be 0.0).
         output.state = TimeOfImpactOutputState.TOUCHING;
         output.t = t1;
         done = true;
         break;
       }

       // Compute 1D root of: f(x) - target = 0
       int rootIterCount = 0;
       num a1 = t1, a2 = t2;
       while (true) {
         // Use a mix of the secant rule and bisection.
         num t;
         if ((rootIterCount & 1) == 1) {
           // Secant rule to improve convergence.
           t = a1 + (target - s1) * (a2 - a1) / (s2 - s1);
         } else {
           // Bisection to guarantee progress.
           t = 0.5 * (a1 + a2);
         }

         num s = fcn.evaluate(indexes[0], indexes[1], t);

         if ((s - target).abs() < tolerance) {
           // t2 holds a tentative value for t1
           t2 = t;
           break;
         }

         // Ensure we continue to bracket the root.
         if (s > target) {
           a1 = t;
           s1 = s;
         } else {
           a2 = t;
           s2 = s;
         }

         ++rootIterCount;
         ++toiRootIters;

         // djm: whats with this? put in settings?
         if (rootIterCount == 50) {
           break;
         }
       }

       toiMaxRootIters = Math.max(toiMaxRootIters, rootIterCount);

       ++pushBackIter;

       if (pushBackIter == Settings.MAX_POLYGON_VERTICES) {
         break;
       }
     }

     ++iter;
     ++toiIters;

     if (done)
       break;

     if (iter == MAX_ITERATIONS) {
       // Root finder got stuck. Semi-victory.
       output.state = TimeOfImpactOutputState.FAILED;
       output.t = t1;
       break;
     }
   }

   toiMaxIters = Math.max(toiMaxIters, iter);
 }
}

Static Properties

const int MAX_ITERATIONS #

static const int MAX_ITERATIONS = 1000

int toiCalls #

static int toiCalls

int toiIters #

static int toiIters

int toiMaxIters #

static int toiMaxIters

int toiMaxRootIters #

static int toiMaxRootIters

int toiRootIters #

static int toiRootIters

Properties

final SimplexCache cache #

Pool variables

final SimplexCache cache

final DistanceInput distanceInput #

final DistanceInput distanceInput

final DistanceOutput distanceOutput #

final DistanceOutput distanceOutput

final SeparationFunction fcn #

final SeparationFunction fcn

final List<int> indexes #

final List<int> indexes

DefaultWorldPool pool #

DefaultWorldPool pool

final Sweep sweepA #

final Sweep sweepA

final Sweep sweepB #

final Sweep sweepB

final Transform xfA #

final Transform xfA

final Transform xfB #

final Transform xfB

Methods

void timeOfImpact(TimeOfImpactOutput output, TimeOfImpactInput input) #

Compute the upper bound on time before two shapes penetrate. Time is represented as a fraction between 0,tMax. This uses a swept separating axis and may miss some intermediate, non-tunneling collision. If you change the time interval, you should call this function again. Note: use Distance to compute the contact point and normal at the time of impact.

void timeOfImpact(TimeOfImpactOutput output, TimeOfImpactInput input) {
 // CCD via the local separating axis method. This seeks progression
 // by computing the largest time at which separation is maintained.
 ++toiCalls;

 output.state = TimeOfImpactOutputState.UNKNOWN;
 output.t = input.tMax;

 DistanceProxy proxyA = input.proxyA;
 DistanceProxy proxyB = input.proxyB;

 sweepA.setFrom(input.sweepA);
 sweepB.setFrom(input.sweepB);

 // Large rotations can make the root finder fail, so we normalize the
 // sweep angles.
 sweepA.normalize();
 sweepB.normalize();

 double tMax = input.tMax;

 double totalRadius = proxyA.radius + proxyB.radius;
 double target = Math.max(Settings.LINEAR_SLOP,
     totalRadius - 3.0 * Settings.LINEAR_SLOP);
 double tolerance = 0.25 * Settings.LINEAR_SLOP;

 assert (target > tolerance);

 double t1 = 0.0;
 int iter = 0;

 cache.count = 0;
 distanceInput.proxyA = input.proxyA;
 distanceInput.proxyB = input.proxyB;
 distanceInput.useRadii = false;

 // The outer loop progressively attempts to compute new separating axes.
 // This loop terminates when an axis is repeated (no progress is made).
 while (true) {
   sweepA.getTransform(xfA, t1);
   sweepB.getTransform(xfB, t1);
   // Get the distance between shapes. We can also use the results
   // to get a separating axis
   distanceInput.transformA = xfA;
   distanceInput.transformB = xfB;
   pool.distance.distance(distanceOutput, cache, distanceInput);

   // If the shapes are overlapped, we give up on continuous collision.
   if (distanceOutput.distance <= 0) {
     // Failure!
     output.state = TimeOfImpactOutputState.OVERLAPPED;
     output.t = 0;
     break;
   }

   if (distanceOutput.distance < target + tolerance) {
     // Victory!
     output.state = TimeOfImpactOutputState.TOUCHING;
     output.t = t1;
     break;
   }

   // Initialize the separating axis.
   fcn.initialize(cache, proxyA, sweepA, proxyB, sweepB, t1);

   // Compute the TimeOfImpact on the separating axis. We do this by successively
   // resolving the deepest point. This loop is bounded by the number of
   // vertices.
   bool done = false;
   num t2 = tMax;
   int pushBackIter = 0;
   while (true) {

     // Find the deepest point at t2. Store the witness point indices.
     num s2 = fcn.findMinSeparation(indexes, t2);
     // Is the configuration separated?
     if (s2 > target + tolerance) {
       // Victory!
       output.state = TimeOfImpactOutputState.SEPARATED;
       output.t = tMax;
       done = true;
       break;
     }

     // Has the separation reached tolerance?
     if (s2 > target - tolerance) {
       // Advance the sweeps
       t1 = t2;
       break;
     }

     // Compute the initial separation of the witness points.
     num s1 = fcn.evaluate(indexes[0], indexes[1], t1);
     // Check for initial overlap. This might happen if the root finder
     // runs out of iterations.
     if (s1 < target - tolerance) {
       output.state = TimeOfImpactOutputState.FAILED;
       output.t = t1;
       done = true;
       break;
     }

     // Check for touching
     if (s1 <= target + tolerance) {
       // Victory! t1 should hold the TimeOfImpact (could be 0.0).
       output.state = TimeOfImpactOutputState.TOUCHING;
       output.t = t1;
       done = true;
       break;
     }

     // Compute 1D root of: f(x) - target = 0
     int rootIterCount = 0;
     num a1 = t1, a2 = t2;
     while (true) {
       // Use a mix of the secant rule and bisection.
       num t;
       if ((rootIterCount & 1) == 1) {
         // Secant rule to improve convergence.
         t = a1 + (target - s1) * (a2 - a1) / (s2 - s1);
       } else {
         // Bisection to guarantee progress.
         t = 0.5 * (a1 + a2);
       }

       num s = fcn.evaluate(indexes[0], indexes[1], t);

       if ((s - target).abs() < tolerance) {
         // t2 holds a tentative value for t1
         t2 = t;
         break;
       }

       // Ensure we continue to bracket the root.
       if (s > target) {
         a1 = t;
         s1 = s;
       } else {
         a2 = t;
         s2 = s;
       }

       ++rootIterCount;
       ++toiRootIters;

       // djm: whats with this? put in settings?
       if (rootIterCount == 50) {
         break;
       }
     }

     toiMaxRootIters = Math.max(toiMaxRootIters, rootIterCount);

     ++pushBackIter;

     if (pushBackIter == Settings.MAX_POLYGON_VERTICES) {
       break;
     }
   }

   ++iter;
   ++toiIters;

   if (done)
     break;

   if (iter == MAX_ITERATIONS) {
     // Root finder got stuck. Semi-victory.
     output.state = TimeOfImpactOutputState.FAILED;
     output.t = t1;
     break;
   }
 }

 toiMaxIters = Math.max(toiMaxIters, iter);
}