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