using System.Numerics; using AcDream.App.Rendering; using Xunit; namespace AcDream.App.Tests.Rendering; // Regression tests for the indoor-render HANG (2026-06-06): the portal-visibility flood // re-queues a cell whenever its CellView grows, so it only terminates when CellView.Add's // dedup catches a duplicate. Across BFS rounds the same region comes back float-drifted, // vertex-rotated, or with a ±1 vertex count; the old exact index-by-index SamePolygon // (eps 1e-4) missed all three, so the region grew forever -> CPU-spin hang in CellView.Add. // A drift-tolerant, rotation-invariant dedup makes the key space finite, so the flood // converges (and these duplicates collapse). public class CellViewDedupTests { private static ViewPolygon Quad(float ox, float oy) => new(new[] { new Vector2(ox - 0.5f, oy - 0.5f), new Vector2(ox + 0.5f, oy - 0.5f), new Vector2(ox + 0.5f, oy + 0.5f), new Vector2(ox - 0.5f, oy + 0.5f), }); [Fact] public void Add_DropsSubGridDriftDuplicate() { var v = new CellView(); Assert.True(v.Add(Quad(0f, 0f))); // Same quad, every vertex nudged 3e-4 — beyond the old 1e-4 SamePolygon eps, // within the 1e-3 dedup grid. This is the per-round float drift that caused the hang. var drifted = new ViewPolygon(new[] { new Vector2(-0.5f + 3e-4f, -0.5f - 3e-4f), new Vector2(0.5f + 3e-4f, -0.5f - 3e-4f), new Vector2(0.5f + 3e-4f, 0.5f - 3e-4f), new Vector2(-0.5f + 3e-4f, 0.5f - 3e-4f), }); Assert.False(v.Add(drifted)); Assert.Single(v.Polygons); } [Fact] public void Add_DropsRotatedStartDuplicate() { var v = new CellView(); Assert.True(v.Add(Quad(0f, 0f))); // Same 4 corners (same CCW cycle), but emitted starting at the 2nd vertex — what // Sutherland-Hodgman can do when the subject order differs across rounds. var rotated = new ViewPolygon(new[] { new Vector2(0.5f, -0.5f), new Vector2(0.5f, 0.5f), new Vector2(-0.5f, 0.5f), new Vector2(-0.5f, -0.5f), }); Assert.False(v.Add(rotated)); Assert.Single(v.Polygons); } [Fact] public void Add_KeepsGenuinelyDistinctPolygons() { // The fix must NOT over-merge: two regions 0.4 NDC apart (far beyond the 1e-3 grid) // remain distinct, so a real second portal opening is not silently dropped. var v = new CellView(); Assert.True(v.Add(Quad(0f, 0f))); Assert.True(v.Add(Quad(0.4f, 0f))); Assert.Equal(2, v.Polygons.Count); } }