rayIntersection method

  1. @override
RaycastResult<ShapeHitbox>? rayIntersection(
  1. Ray2 ray, {
  2. 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,
}) {
  var isInsideHitbox = false;
  _temporaryLineSegment.from.setFrom(ray.origin);
  // Adding a small value to the origin to avoid the ray to be on the edge
  // of the circle and then directly intersecting and causing the reflecting
  // ray to go in the wrong direction.
  _temporaryOrigin.setValues(
    ray.origin.x + ray.direction.x * 0.00001,
    ray.origin.y + ray.direction.y * 0.00001,
  );
  _temporaryAbsoluteCenter.setFrom(absoluteCenter);
  _temporaryCenter
    ..setFrom(_temporaryAbsoluteCenter)
    ..sub(ray.origin);

  if (_temporaryCenter.isZero()) {
    // If _temporaryCenter is zero, it's projection onto ray.direction
    // will be zero. In that case, directly use ray.direction as temp
    // end point of line segment.
    _temporaryLineSegment.to.setFrom(ray.direction);
  } else {
    _temporaryCenter.projection(ray.direction, out: _temporaryLineSegment.to);
    _temporaryLineSegment.to
      ..x *= (ray.direction.x.sign * _temporaryLineSegment.to.x.sign)
      ..y *= (ray.direction.y.sign * _temporaryLineSegment.to.y.sign);
  }

  if (_temporaryOrigin.distanceToSquared(_temporaryAbsoluteCenter) <
      radius * radius) {
    _temporaryLineSegment.to.scaleTo(2 * radius);
    isInsideHitbox = true;
  }
  _temporaryLineSegment.to.add(ray.origin);
  final intersections = lineSegmentIntersections(_temporaryLineSegment).where(
    (i) => i.distanceToSquared(ray.origin) > 0.0000001,
  );
  if (intersections.isEmpty) {
    out?.reset();
    return null;
  } else {
    final result = out ?? RaycastResult();
    final intersectionPoint = intersections.first;
    _temporaryNormal
      ..setFrom(intersectionPoint)
      ..sub(_temporaryAbsoluteCenter)
      ..normalize();
    if (isInsideHitbox) {
      _temporaryNormal.invert();
    }
    final reflectionDirection =
        (out?.reflectionRay?.direction ?? Vector2.zero())
          ..setFrom(ray.direction)
          ..reflect(_temporaryNormal);

    final reflectionRay = (out?.reflectionRay
          ?..setWith(
            origin: intersectionPoint,
            direction: reflectionDirection,
          )) ??
        Ray2(
          origin: intersectionPoint,
          direction: reflectionDirection,
        );

    result.setWith(
      hitbox: this,
      reflectionRay: reflectionRay,
      normal: _temporaryNormal,
      distance: ray.origin.distanceTo(intersectionPoint),
      isInsideHitbox: isInsideHitbox,
    );
    return result;
  }
}