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))); } }