using System.Numerics; using TheLivingWorld.Core.Geo; using TheLivingWorld.Core.Worlds; namespace TheLivingWorld.Tests; public class LocalProjectionTests { // Robert Lee, Texas - the town the first worlds were generated around. private static readonly GeoPoint Origin = new(31.8966010, -100.4858591); [Fact] public void Origin_projects_to_world_zero() { var projection = new LocalProjection(Origin); var projected = projection.Project(Origin); Assert.Equal(0f, projected.X, 3); Assert.Equal(0f, projected.Y, 3); } [Fact] public void One_kilometre_north_lands_one_kilometre_up() { var projection = new LocalProjection(Origin); var north = new GeoPoint(Origin.Latitude + 1000.0 / LocalProjection.MetersPerDegreeLatitude, Origin.Longitude); var projected = projection.Project(north); Assert.Equal(0f, projected.X, 2); Assert.Equal(1000f, projected.Y, 1); } [Fact] public void Unproject_reverses_project() { var projection = new LocalProjection(Origin); var point = new Vector2(-4321.5f, 6789.25f); var roundTripped = projection.Project(projection.Unproject(point)); Assert.Equal(point.X, roundTripped.X, 1); Assert.Equal(point.Y, roundTripped.Y, 1); } [Fact] public void Rejects_coordinates_outside_the_globe() { Assert.Throws(() => new LocalProjection(new GeoPoint(91, 0))); } } public class GeoBoundsTests { [Theory] [InlineData(31.8966010, -100.4858591, 10_000)] [InlineData(60.0, 30.3, 20_000)] [InlineData(-33.87, 151.21, 5_000)] public void FromCenter_produces_a_square_of_the_requested_size(double lat, double lon, double sizeMeters) { var center = new GeoPoint(lat, lon); var bounds = GeoBounds.FromCenter(center, sizeMeters); var projection = new LocalProjection(center); var southWest = projection.Project(bounds.South, bounds.West); var northEast = projection.Project(bounds.North, bounds.East); // Half a metre of slack over a 20 km span is the float rounding in the projection. Assert.Equal(sizeMeters, northEast.X - southWest.X, 0.5); Assert.Equal(sizeMeters, northEast.Y - southWest.Y, 0.5); } [Fact] public void Overpass_bbox_is_south_west_north_east() { var bounds = new GeoBounds(South: 1, West: 2, North: 3, East: 4); Assert.Equal("1.0000000,2.0000000,3.0000000,4.0000000", bounds.ToOverpassBbox()); } } public class ChunkGridTests { [Fact] public void Covers_the_whole_world_square() { var grid = new ChunkGrid(worldSizeMeters: 2048, chunkSizeMeters: 512); Assert.Equal(4, grid.CountX); Assert.Equal(4, grid.CountY); Assert.Equal(-1024f, grid.MinX); } [Fact] public void Rounds_the_count_up_when_the_size_does_not_divide_evenly() { var grid = new ChunkGrid(worldSizeMeters: 10_000, chunkSizeMeters: 512); Assert.Equal(20, grid.CountX); } [Fact] public void Maps_points_to_the_chunk_containing_them() { var grid = new ChunkGrid(worldSizeMeters: 2048, chunkSizeMeters: 512); Assert.Equal(new ChunkCoord(0, 0), grid.CoordOf(new Vector2(-1024, -1024))); Assert.Equal(new ChunkCoord(2, 2), grid.CoordOf(new Vector2(1, 1))); Assert.Equal(new ChunkCoord(3, 3), grid.CoordOf(new Vector2(1023, 1023))); } [Fact] public void Clamps_points_that_fall_outside_the_world() { var grid = new ChunkGrid(worldSizeMeters: 2048, chunkSizeMeters: 512); Assert.Equal(new ChunkCoord(0, 0), grid.CoordOf(new Vector2(-99_999, -99_999))); Assert.Equal(new ChunkCoord(3, 3), grid.CoordOf(new Vector2(99_999, 99_999))); } [Fact] public void Chunk_bounds_line_up_with_the_grid() { var grid = new ChunkGrid(worldSizeMeters: 2048, chunkSizeMeters: 512); var bounds = grid.BoundsOf(new ChunkCoord(1, 2)); Assert.Equal(-512f, bounds.MinX); Assert.Equal(0f, bounds.MinY); Assert.Equal(0f, bounds.MaxX); Assert.Equal(512f, bounds.MaxY); } }