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