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(); num tMax = input.tMax; num totalRadius = proxyA.radius + proxyB.radius; num target = Math.max(Settings.LINEAR_SLOP, totalRadius - 3.0 * Settings.LINEAR_SLOP); num tolerance = 0.25 * Settings.LINEAR_SLOP; assert (target > tolerance); num t1 = 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;
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(); num tMax = input.tMax; num totalRadius = proxyA.radius + proxyB.radius; num target = Math.max(Settings.LINEAR_SLOP, totalRadius - 3.0 * Settings.LINEAR_SLOP); num tolerance = 0.25 * Settings.LINEAR_SLOP; assert (target > tolerance); num t1 = 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); }