raycast method

  1. @override
void raycast(
  1. TreeRayCastCallback callback,
  2. 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.

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

Implementation

@override
void raycast(TreeRayCastCallback callback, RayCastInput input) {
  final p1 = input.p1;
  final p2 = input.p2;
  final p1x = p1.x;
  final p2x = p2.x;
  final p1y = p1.y;
  final p2y = p2.y;
  double vx;
  double vy;
  double rx;
  double ry;
  double absVx;
  double absVy;
  double cx;
  double cy;
  double hx;
  double hy;
  double tempX;
  double 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)

  var maxFraction = input.maxFraction;

  // Build a bounding box for the segment.
  final segAABB = _aabb;
  tempX = (p2x - p1x) * maxFraction + p1x;
  tempY = (p2y - p1y) * maxFraction + p1y;
  segAABB.lowerBound.x = min(p1x, tempX);
  segAABB.lowerBound.y = min(p1y, tempY);
  segAABB.upperBound.x = max(p1x, tempX);
  segAABB.upperBound.y = max(p1y, tempY);

  nodeStackIndex = 0;
  _nodeStack[nodeStackIndex++] = _root;
  while (nodeStackIndex > 0) {
    final node = _nodeStack[--nodeStackIndex];
    if (node == null) {
      continue;
    }

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

    // Separating axis for segment (Gino, p80).
    // |dot(v, p1 - c)| > dot(|v|, 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;
    final separation =
        (vx * tempX + vy * tempY).abs() - (absVx * hx + absVy * hy);
    if (separation > 0.0) {
      continue;
    }

    if (node.child1 == null) {
      _subInput.p1.x = p1x;
      _subInput.p1.y = p1y;
      _subInput.p2.x = p2x;
      _subInput.p2.y = p2y;
      _subInput.maxFraction = maxFraction;

      final value = callback.raycastCallback(_subInput, node.id);

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

      if (value > 0.0) {
        // Update segment bounding box.
        maxFraction = value;
        tempX = (p2x - p1x) * maxFraction + p1x;
        tempY = (p2y - p1y) * maxFraction + p1y;
        segAABB.lowerBound.x = min(p1x, tempX);
        segAABB.lowerBound.y = min(p1y, tempY);
        segAABB.upperBound.x = max(p1x, tempX);
        segAABB.upperBound.y = max(p1y, tempY);
      }
    } else {
      if (_nodeStack.length - nodeStackIndex - 2 <= 0) {
        final previousSize = _nodeStack.length;
        _nodeStack = _nodeStack +
            List.generate(
              previousSize,
              (i) => DynamicTreeNode(previousSize + i),
            );
      }
      _nodeStack[nodeStackIndex++] = node.child1;
      _nodeStack[nodeStackIndex++] = node.child2;
    }
  }
}