using System.Numerics;
using DatReaderWriter.Enums;
using DatReaderWriter.Types;
using AcDream.Core.Physics;
using Xunit;
namespace AcDream.Core.Tests.Physics;
///
/// Tests for — the radius-
/// aware sibling of PointInsideCellBsp. Pins issue #90's fix semantics:
/// a sphere whose center is JUST outside the cell volume but whose
/// radius extends back into the cell should still register as
/// overlapping.
///
/// Retail oracle: BSPTREE::sphere_intersects_cell_bsp at
/// acclient_2013_pseudo_c.txt:323267 / BSPNODE::sphere_intersects_cell_bsp
/// at :325546.
///
public class SphereIntersectsCellBspTests
{
///
/// One splitting plane at x=0 with positive normal +X (so the
/// "inside" half-space is x ≥ 0). Pos-leaf representing the cell
/// interior.
///
private static CellBSPNode SinglePlaneTree()
{
var leaf = new CellBSPNode { Type = BSPNodeType.Leaf };
// Internal nodes don't set Type (default is non-Leaf). The production
// PointInsideCellBsp / SphereIntersectsCellBsp both only branch on
// `Type == Leaf` and otherwise treat the node as internal.
return new CellBSPNode
{
SplittingPlane = new Plane(new Vector3(1f, 0f, 0f), 0f), // x ≥ 0 is inside
PosNode = leaf,
};
}
[Fact]
public void NullRoot_ReturnsTrue()
{
Assert.True(BSPQuery.SphereIntersectsCellBsp(null, Vector3.Zero, 0.5f));
}
[Fact]
public void Leaf_ReturnsTrue()
{
var leaf = new CellBSPNode { Type = BSPNodeType.Leaf };
Assert.True(BSPQuery.SphereIntersectsCellBsp(leaf, Vector3.Zero, 0.5f));
}
[Fact]
public void SphereCenterInsideHalfSpace_ReturnsTrue()
{
var root = SinglePlaneTree();
// Sphere center at x=0.5 (inside).
Assert.True(BSPQuery.SphereIntersectsCellBsp(root, new Vector3(0.5f, 0f, 0f), 0.5f));
}
[Fact]
public void SphereCenterOnPlane_ReturnsTrue()
{
var root = SinglePlaneTree();
// Sphere center exactly at x=0 (on the plane).
Assert.True(BSPQuery.SphereIntersectsCellBsp(root, new Vector3(0f, 0f, 0f), 0.5f));
}
[Fact]
public void SphereCenterOutside_ButRadiusReachesIn_ReturnsTrue()
{
// Issue #90's core case. Sphere center at x=-0.3 (outside the
// x≥0 cell), but radius 0.5 → reach to x=+0.2 (inside). The
// sphere STRADDLES the splitting plane — must return true.
// Pre-#90, PointInsideCellBsp would have returned false here,
// causing CellId to flip out → wall ping-pong.
var root = SinglePlaneTree();
Assert.True(BSPQuery.SphereIntersectsCellBsp(root, new Vector3(-0.3f, 0f, 0f), 0.5f));
}
[Fact]
public void SphereFullyOutside_ReturnsFalse()
{
var root = SinglePlaneTree();
// Sphere center at x=-1.0 with radius 0.5 → reach to x=-0.5.
// Fully behind the splitting plane (with the 0.01 retail epsilon
// accounted for, still fully outside).
Assert.False(BSPQuery.SphereIntersectsCellBsp(root, new Vector3(-1.0f, 0f, 0f), 0.5f));
}
[Fact]
public void SphereTangentToPlane_ReturnsTrue()
{
// Tangent boundary: sphere center at x=-0.5, radius=0.5 →
// reaches exactly x=0 (touches the plane). Retail's +0.01 epsilon
// applied to the radius means -0.5 > -(0.5+0.01) → returns true.
var root = SinglePlaneTree();
Assert.True(BSPQuery.SphereIntersectsCellBsp(root, new Vector3(-0.5f, 0f, 0f), 0.5f));
}
[Fact]
public void PointInsideCellBsp_PointJustOutside_ReturnsFalse_ProvesRegression()
{
// Confirms the OLD point-only behavior would have returned false
// for the same input that SphereIntersectsCellBsp returns true
// for above. This is the "ping-pong cause" pin.
var root = SinglePlaneTree();
Assert.False(BSPQuery.PointInsideCellBsp(root, new Vector3(-0.3f, 0f, 0f)));
}
}