raycast method

void raycast (TreeRayCastCallback callback, RayCastInput input)
override

Ray-cast against the proxies in the tree. This relies on the callback to perform a exact ray-cast in the case were the proxy contains a shape. The callback also performs the any collision filtering. This has performance roughly equal to k * log(n), where k is the number of collisions and n is the number of proxies in the tree.

@param input the ray-cast input data. The ray extends from p1 to p1 + maxFraction * (p2 - p1). @param callback a callback class that is called for each proxy that is hit by the ray.

Implementation

void raycast(TreeRayCastCallback callback, RayCastInput input) {
  final Vector2 p1 = input.p1;
  final Vector2 p2 = input.p2;
  double p1x = p1.x, p2x = p2.x, p1y = p1.y, p2y = p2.y;
  double vx, vy;
  double rx, ry;
  double absVx, absVy;
  double cx, cy;
  double hx, hy;
  double tempx, tempy;
  _r.x = p2x - p1x;
  _r.y = p2y - p1y;
  assert((_r.x * _r.x + _r.y * _r.y) > 0.0);
  _r.normalize();
  rx = _r.x;
  ry = _r.y;

  // v is perpendicular to the segment.
  vx = -1.0 * ry;
  vy = 1.0 * rx;
  absVx = vx.abs();
  absVy = vy.abs();

  // Separating axis for segment (Gino, p80).
  // |dot(v, p1 - c)| > dot(|v|, h)

  double maxFraction = input.maxFraction;

  // Build a bounding box for the segment.
  final AABB segAABB = _aabbTemp;
  // Vec2 t = p1 + maxFraction * (p2 - p1);
  // before inline
  // temp.set(p2).subLocal(p1).mulLocal(maxFraction).addLocal(p1);
  // Vec2.minToOut(p1, temp, segAABB.lowerBound);
  // Vec2.maxToOut(p1, temp, segAABB.upperBound);
  tempx = (p2x - p1x) * maxFraction + p1x;
  tempy = (p2y - p1y) * maxFraction + p1y;
  segAABB.lowerBound.x = p1x < tempx ? p1x : tempx;
  segAABB.lowerBound.y = p1y < tempy ? p1y : tempy;
  segAABB.upperBound.x = p1x > tempx ? p1x : tempx;
  segAABB.upperBound.y = p1y > tempy ? p1y : tempy;
  // end inline

  _nodeStackIndex = 0;
  _nodeStack[_nodeStackIndex++] = _root;
  while (_nodeStackIndex > 0) {
    int node = _nodeStack[--_nodeStackIndex] = _root;
    if (node == NULL_NODE) {
      continue;
    }

    final AABB nodeAABB = _aabb[node];
    if (!AABB.testOverlap(nodeAABB, segAABB)) {
      continue;
    }

    // Separating axis for segment (Gino, p80).
    // |dot(v, p1 - c)| > dot(|v|, h)
    // node.aabb.getCenterToOut(c);
    // node.aabb.getExtentsToOut(h);
    cx = (nodeAABB.lowerBound.x + nodeAABB.upperBound.x) * .5;
    cy = (nodeAABB.lowerBound.y + nodeAABB.upperBound.y) * .5;
    hx = (nodeAABB.upperBound.x - nodeAABB.lowerBound.x) * .5;
    hy = (nodeAABB.upperBound.y - nodeAABB.lowerBound.y) * .5;
    tempx = p1x - cx;
    tempy = p1y - cy;
    double separation =
        (vx * tempx + vy * tempy).abs() - (absVx * hx + absVy * hy);
    if (separation > 0.0) {
      continue;
    }

    int child1 = _child1[node];
    if (child1 == NULL_NODE) {
      _subInput.p1.x = p1x;
      _subInput.p1.y = p1y;
      _subInput.p2.x = p2x;
      _subInput.p2.y = p2y;
      _subInput.maxFraction = maxFraction;

      double value = callback.raycastCallback(_subInput, node);

      if (value == 0.0) {
        // The client has terminated the ray cast.
        return;
      }

      if (value > 0.0) {
        // Update segment bounding box.
        maxFraction = value;
        // temp.set(p2).subLocal(p1).mulLocal(maxFraction).addLocal(p1);
        // Vec2.minToOut(p1, temp, segAABB.lowerBound);
        // Vec2.maxToOut(p1, temp, segAABB.upperBound);
        tempx = (p2x - p1x) * maxFraction + p1x;
        tempy = (p2y - p1y) * maxFraction + p1y;
        segAABB.lowerBound.x = p1x < tempx ? p1x : tempx;
        segAABB.lowerBound.y = p1y < tempy ? p1y : tempy;
        segAABB.upperBound.x = p1x > tempx ? p1x : tempx;
        segAABB.upperBound.y = p1y > tempy ? p1y : tempy;
      }
    } else {
      _nodeStack[_nodeStackIndex++] = child1;
      _nodeStack[_nodeStackIndex++] = _child2[node];
    }
  }
}