using Microsoft.Xna.Framework; using MrGameEng.Graphics; using Xunit; namespace MrGameEng.Graphics.Tests; public class CullingTests { [Fact] public void BoundingCircle_UnrotatedTopLeftOrigin_CenterIsRegionCenter() { var transform = Transform2D.At(new Vector2(100f, 200f)); var (center, radius) = CullingMath.SpriteBoundingCircle(transform, 32f, 32f, Vector2.Zero); Assert.Equal(new Vector2(116f, 216f), center); Assert.Equal(0.5f * MathF.Sqrt(32f * 32f * 2f), radius, 3); } [Fact] public void BoundingCircle_CenterOrigin_CenterIsPosition() { var transform = Transform2D.At(new Vector2(50f, 50f)); var (center, _) = CullingMath.SpriteBoundingCircle( transform, 64f, 32f, new Vector2(32f, 16f) ); Assert.Equal(new Vector2(50f, 50f), center); } [Fact] public void BoundingCircle_Scale_GrowsRadius() { var transform = new Transform2D(Vector2.Zero, scale: new Vector2(2f, 2f)); var (_, radius) = CullingMath.SpriteBoundingCircle(transform, 10f, 10f, Vector2.Zero); Assert.Equal(0.5f * MathF.Sqrt(800f), radius, 3); } [Fact] public void BoundingCircle_RegionOverload_MatchesSizeOverload_ForUniformScale() { var region = new Texture2DRegion(null!, new Rectangle(0, 0, 48, 24)); var transform = new Transform2D( new Vector2(10f, 20f), rotation: 0.6f, scale: new Vector2(1.5f, 1.5f) ); var origin = new Vector2(5f, 7f); var (centerA, radiusA) = CullingMath.SpriteBoundingCircle(transform, 48f, 24f, origin); var (centerB, radiusB) = CullingMath.SpriteBoundingCircle(in transform, region, origin); Assert.Equal(centerA.X, centerB.X, 3); Assert.Equal(centerA.Y, centerB.Y, 3); Assert.Equal(radiusA, radiusB, 3); } [Fact] public void BoundingCircle_RegionOverload_IsConservative_ForNonUniformScale() { var region = new Texture2DRegion(null!, new Rectangle(0, 0, 100, 10)); var transform = new Transform2D(Vector2.Zero, scale: new Vector2(1f, 3f)); var (_, exact) = CullingMath.SpriteBoundingCircle(transform, 100f, 10f, Vector2.Zero); var (_, conservative) = CullingMath.SpriteBoundingCircle( in transform, region, Vector2.Zero ); Assert.True(conservative >= exact); } [Theory] [InlineData(50f, 50f, true)] // inside [InlineData(-4f, 50f, true)] // touching from the left (radius 5) [InlineData(-20f, 50f, false)] // far left [InlineData(50f, 130f, false)] // far below public void CircleIntersectsRect_DetectsOverlap(float x, float y, bool expected) { var rect = new RectF(0f, 0f, 100f, 100f); Assert.Equal(expected, CullingMath.CircleIntersectsRect(new Vector2(x, y), 5f, rect)); } }