Dart Documentationbox2d_consoleTimeOfImpact

TimeOfImpact class

Class used for computing the time of impact. This class should not be constructed usually, just retrieve from the SingletonPool.getTimeOfImpact().

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);
  }
}

Constructors

new TimeOfImpact._construct(DefaultWorldPool argPool) #

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;
}

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 Type runtimeType #

inherited from Object

A representation of the runtime type of the object.

external Type get runtimeType;

final Sweep sweepA #

final Sweep sweepA;

final Sweep sweepB #

final Sweep sweepB;

final Transform xfA #

final Transform xfA;

final Transform xfB #

final Transform xfB;

Operators

bool operator ==(other) #

inherited from Object

The equality operator.

The default behavior for all Objects is to return true if and only if this and other are the same object.

If a subclass overrides the equality operator it should override the hashCode method as well to maintain consistency.

bool operator ==(other) => identical(this, other);

Methods

int hashCode() #

inherited from Object

Get a hash code for this object.

All objects have hash codes. Hash codes are guaranteed to be the same for objects that are equal when compared using the equality operator ==. Other than that there are no guarantees about the hash codes. They will not be consistent between runs and there are no distribution guarantees.

If a subclass overrides hashCode it should override the equality operator as well to maintain consistency.

external int hashCode();

noSuchMethod(String name, List args) #

inherited from Object

noSuchMethod is invoked when users invoke a non-existant method on an object. The name of the method and the arguments of the invocation are passed to noSuchMethod. If noSuchMethod returns a value, that value becomes the result of the original invocation.

The default behavior of noSuchMethod is to throw a noSuchMethodError.

external Dynamic noSuchMethod(String name, List args);

const Object() #

inherited from Object

Creates a new Object instance.

Object instances have no meaningful state, and are only useful through their identity. An Object instance is equal to itself only.

const Object();

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);
}

new TimeOfImpact._construct(DefaultWorldPool argPool) #

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;
}

String toString() #

inherited from Object

Returns a string representation of this object.

external String toString();