using System; using System.Numerics; using AcDream.App.Rendering; using Xunit; namespace AcDream.App.Tests.Rendering; /// /// #130: the doorway-slice scissor must be a CONSERVATIVE outer bound of its /// NDC AABB (AD-17: over-inclusion safe, under-inclusion is the bug class). /// The old Floor(origin)+Ceiling(size) form put the far edge at /// floor(min)+ceil(max−min), up to one pixel short of the true max edge — /// the doorway top-edge background strip. /// public class NdcScissorRectTests { /// Containment property: every pixel whose CENTER lies inside the /// NDC box is inside the scissor box, across a dense grid of fractional /// alignments at two framebuffer sizes. [Theory] [InlineData(1920, 1080)] [InlineData(2560, 1440)] public void EveryCenterInsidePixel_IsInsideTheBox(int fbW, int fbH) { for (int i = 0; i < 251; i++) { // Sweep fractional alignments of all four edges. float f = i / 251f; float minX = -0.83f + f * 0.0031f; float minY = -0.71f + f * 0.0047f; float maxX = 0.339f + f * 0.0043f; float maxY = 0.7938f + f * 0.0029f; var box = NdcScissorRect.ToPixels(new Vector4(minX, minY, maxX, maxY), fbW, fbH); // Pixel-space extremes of center-inside pixels. float x0 = (minX * 0.5f + 0.5f) * fbW, x1 = (maxX * 0.5f + 0.5f) * fbW; float y0 = (minY * 0.5f + 0.5f) * fbH, y1 = (maxY * 0.5f + 0.5f) * fbH; int loX = (int)MathF.Ceiling(x0 - 0.5f), hiX = (int)MathF.Floor(x1 - 0.5f); int loY = (int)MathF.Ceiling(y0 - 0.5f), hiY = (int)MathF.Floor(y1 - 0.5f); Assert.True(box.X <= loX, $"left cut: box.X={box.X} > loX={loX} (minX={minX})"); Assert.True(box.Y <= loY, $"bottom cut: box.Y={box.Y} > loY={loY} (minY={minY})"); Assert.True(box.X + box.Width > hiX, $"right cut: box ends {box.X + box.Width} <= hiX={hiX} (maxX={maxX})"); Assert.True(box.Y + box.Height > hiY, $"top cut: box ends {box.Y + box.Height} <= hiY={hiY} (maxY={maxY})"); // Over-inclusion stays bounded (≤1 px per edge). Assert.True(box.X >= loX - 1 && box.Y >= loY - 1); Assert.True(box.X + box.Width <= hiX + 2 && box.Y + box.Height <= hiY + 2); } } [Fact] public void CapturedRegression_TopEdgeRow968_At1080p() { // Issue130DoorwayStripTests live capture: aperture top y=0.7938 → // pixel row 968 (center 968.5 < 968.65). The old formula ended the box // at row 967 — the visible strip. var box = NdcScissorRect.ToPixels(new Vector4(-0.339f, -0.743f, 0.339f, 0.7938f), 1920, 1080); Assert.True(box.Y + box.Height > 968, $"top row 968 cut: box ends at {box.Y + box.Height}"); } [Fact] public void CapturedRegression_RightColumn1296_At1920() { // Issue130DoorwayStripTests live capture: gate right edge x=0.3507 → // pixel column 1296 admitted by the plane gate; the old formula ended // the box at column 1295. var box = NdcScissorRect.ToPixels(new Vector4(-0.2845f, -1.0f, 0.3507f, 0.2630f), 1920, 1080); Assert.True(box.X + box.Width > 1296, $"right column 1296 cut: box ends at {box.X + box.Width}"); } [Fact] public void DegenerateAndOffscreenBoxes_StayValid() { // Past-the-edge regions clamp to the screen and keep min 1 px size. var box = NdcScissorRect.ToPixels(new Vector4(0.999f, 0.999f, 1.5f, 1.5f), 1920, 1080); Assert.True(box.Width >= 1 && box.Height >= 1); var inverted = NdcScissorRect.ToPixels(new Vector4(1f, 1f, -1f, -1f), 1920, 1080); Assert.True(inverted.Width >= 1 && inverted.Height >= 1); } }