rayIntersection method
RaycastResult<ShapeHitbox> ?
rayIntersection(
- Ray2 ray, {
- RaycastResult<
ShapeHitbox> ? out,
override
Returns information about how the ray intersects the shape.
If you are only interested in the intersection point use RaycastResult.intersectionPoint of the result.
Implementation
@override
RaycastResult<ShapeHitbox>? rayIntersection(
Ray2 ray, {
RaycastResult<ShapeHitbox>? out,
}) {
final effectiveRadius = scaledRadius;
_temporaryAbsoluteCenter.setFrom(absoluteCenter);
// Solve ray-circle intersection analytically.
// Ray: P + t*D where |D| = 1, Circle: |X - C|² = r²
// Substituting: t² + bt + c = 0
_temporaryCenter
..setFrom(ray.origin)
..sub(_temporaryAbsoluteCenter); // P - C
final b = 2 * _temporaryCenter.dot(ray.direction);
final c = _temporaryCenter.length2 - effectiveRadius * effectiveRadius;
final discriminant = b * b - 4 * c;
if (discriminant < 0) {
out?.reset();
return null;
}
final sqrtDiscriminant = sqrt(discriminant);
final t1 = (-b - sqrtDiscriminant) / 2;
final t2 = (-b + sqrtDiscriminant) / 2;
// Use a radius-relative epsilon. Vector2 stores components in Float32List,
// so coordinates near the boundary carry ~6 digits of precision. After
// reflecting, the stored origin can be off by up to r*1e-4, producing a
// spurious near-zero t from recomputing against the same circle.
final epsilon = effectiveRadius * 1e-4;
final double t;
final bool isInsideHitbox;
if (t1 > epsilon) {
t = t1;
isInsideHitbox = false;
} else if (t2 > epsilon) {
t = t2;
isInsideHitbox = true;
} else {
out?.reset();
return null;
}
// Intersection point = origin + t * direction.
_temporaryCenter
..setFrom(ray.direction)
..scale(t)
..add(ray.origin);
// Normal at intersection: direction from center to hit point.
_temporaryNormal
..setFrom(_temporaryCenter)
..sub(_temporaryAbsoluteCenter)
..normalize();
// Snap intersection to exact boundary to prevent numerical drift.
_temporaryCenter
..setFrom(_temporaryNormal)
..scale(effectiveRadius)
..add(_temporaryAbsoluteCenter);
if (isInsideHitbox) {
_temporaryNormal.invert();
}
final result = out ?? RaycastResult();
final reflectionDirection =
(out?.reflectionRay?.direction ?? Vector2.zero())
..setFrom(ray.direction)
..reflect(_temporaryNormal);
reflectionDirection.normalize();
final reflectionRay =
(out?.reflectionRay?..setWith(
origin: _temporaryCenter,
direction: reflectionDirection,
)) ??
Ray2(
origin: _temporaryCenter,
direction: reflectionDirection,
);
result.setWith(
hitbox: this,
reflectionRay: reflectionRay,
normal: _temporaryNormal,
distance: t, // |D| = 1, so parametric t equals Euclidean distance
isInsideHitbox: isInsideHitbox,
);
return result;
}