using XFEExtension.NetCore.XUnit.Attributes;
using XFEEDToolBox.Core.Galaxy;
using XFEEDToolBox.Core.Models;
namespace XFEEDToolBox.Test;
[TestFixture]
public class GalaxyOriginalProjectionTests
{
static void Near(double expected, double actual) => Check.That(Math.Abs(expected - actual) < 1e-10,
$"Expected {expected}, actual {actual}");
[Test]
public void FlatViewPreservesWideScreenVerticalAngleAndNarrowViewPreservesHorizontalAngle()
{
var aspect = GalaxyOriginalProjection.ReferenceAspectRatio;
var original = GalaxyOriginalProjection.StackFieldOfViewRadians;
Near(original, GalaxyOriginalProjection.VerticalFieldOfView(aspect));
Near(original, GalaxyOriginalProjection.VerticalFieldOfView(21d / 9));
var narrow = GalaxyOriginalProjection.VerticalFieldOfView(4d / 3);
Check.That(narrow > original);
Near(Math.Tan(original / 2) * aspect, Math.Tan(narrow / 2) * 4d / 3);
Near(original, GalaxyOriginalProjection.VerticalFieldOfView(4d / 3, compensateNarrowViewport: false));
}
[Test]
public void FocalLengthProjectsVerticalEdgeAndScalesWithActualViewportPixels()
{
var vertical = GalaxyOriginalProjection.VerticalFieldOfView(1920d / 1080);
var focal = GalaxyOriginalProjection.FocalLength(1920, 1080);
var depth = 17.75;
var topWorldOffset = Math.Tan(vertical / 2) * depth;
Near(540, topWorldOffset * focal / depth);
Near(focal * 2, GalaxyOriginalProjection.FocalLength(3840, 2160));
var narrowFocal = GalaxyOriginalProjection.FocalLength(1440, 1080);
Near(focal * .75, narrowFocal);
}
[Test]
public void InfiniteReverseDepthDoesNotConfuseStoredFarMetadataWithProjectionOrQueryCoverage()
{
Near(1, GalaxyOriginalProjection.ReverseDepth(GalaxyOriginalProjection.NearPlane));
Near(.01, GalaxyOriginalProjection.ReverseDepth(GalaxyOriginalProjection.NearPlane * 100));
var beyondStoredFar = GalaxyOriginalProjection.StoredFarPlane * 2;
Check.That(GalaxyOriginalProjection.ReverseDepth(beyondStoredFar) > 0);
Near(GalaxyOriginalProjection.NearPlane / beyondStoredFar, GalaxyOriginalProjection.ReverseDepth(beyondStoredFar));
}
[Test]
public void InvalidLensAndBehindCameraInputsAreRejected()
{
foreach (var bad in new[] { double.NaN, double.PositiveInfinity, 0d, -1d })
{
var rejected = 0;
try { GalaxyOriginalProjection.VerticalFieldOfView(bad); } catch (ArgumentOutOfRangeException) { rejected++; }
try { GalaxyOriginalProjection.FocalLength(bad, 1080); } catch (ArgumentOutOfRangeException) { rejected++; }
try { GalaxyOriginalProjection.ReverseDepth(bad); } catch (ArgumentOutOfRangeException) { rejected++; }
Check.That(rejected == 3);
}
}
[Test]
public void ProductionCameraUsesRecoveredLensAndNearPlaneAtEveryViewportAspect()
{
foreach (var width in new[] { 1440d, 1920d, 2560d })
{
var focal = GalaxyOriginalProjection.FocalLength(width, 1080);
var camera = GalaxyCamera.Create(new(0, 0, 0), width, 1080, 0, 0, focal / 17.75);
Near(GalaxyOriginalProjection.VerticalFieldOfView(width / 1080), camera.VerticalFieldOfView);
Near(focal, camera.FocalLength);
Near(17.75, camera.Distance);
Near(GalaxyOriginalProjection.NearPlane, camera.Near);
var beforeNear = new Coordinates(camera.Eye.X, camera.Eye.Y, camera.Eye.Z + camera.Near * .5);
var afterNear = new Coordinates(camera.Eye.X, camera.Eye.Y, camera.Eye.Z + camera.Near * 1.01);
Check.That(!camera.Project(beforeNear).IsVisible && !camera.ToViewport().Contains(beforeNear));
Check.That(camera.Project(afterNear).IsVisible && camera.ToViewport().Contains(afterNear));
}
}
[Test]
public void OriginalLensDoesNotExpandNearViewCatalogueQueriesToStoredFarOrWholeGalaxy()
{
var focal = GalaxyOriginalProjection.FocalLength(1920, 1080);
var camera = GalaxyCamera.Create(new(0, 0, 0), 1920, 1080, 0, 0, focal / 6);
var viewport = camera.ToViewport();
var bounds = GalaxyQueryBounds.FromViewport(viewport);
Near(24, camera.QueryFar);
Near(camera.QueryFar, bounds.Maximum.Z - camera.Eye.Z);
Check.That(double.IsPositiveInfinity(camera.ProjectionFar));
Check.That(viewport.Radius < 100 && bounds.Radius < 100);
Check.That(!bounds.Contains(new(0, 0, 26000)) && !viewport.Contains(new(0, 0, 26000)));
var plan = new GalaxyQueryPlan(viewport);
var count = 0;
while (plan.TryTake(out var tile))
{
Check.That(tile.Radius < 100);
Check.That(++count < 128, "A6ly camera view must remain a bounded local query after lens integration.");
}
Check.That(count > 0 && plan.OmittedTiles == 0);
}
[Test]
public void InfiniteVisualProjectionCannotPublishOrSelectAStarOutsideTheFiniteCatalogueView()
{
var focal=GalaxyOriginalProjection.FocalLength(1920,1080);
var camera=GalaxyCamera.Create(new(0,0,0),1920,1080,0,0,focal/6);
var distant=new Coordinates(0,0,camera.Eye.Z+GalaxyOriginalProjection.StoredFarPlane*2);
var projected=camera.Project(distant);
Check.That(projected.IsVisible&&projected.Depth>GalaxyOriginalProjection.StoredFarPlane);
Near(960,projected.X);Near(540,projected.Y);
Check.That(!camera.ProjectForCatalogue(distant).IsVisible);
Check.That(!camera.Contains(distant)&&!camera.ToViewport().Contains(distant));
Check.That(!GalaxyQueryBounds.FromViewport(camera.ToViewport()).Contains(distant));
Check.That(camera.TryProjectSegment(distant,new(10,0,distant.Z+100),out var a,out var b));
Check.That(double.IsFinite(a.X+a.Y+b.X+b.Y));
}
}
using XFEExtension.NetCore.XUnit.Attributes;
using XFEEDToolBox.Core.Galaxy;
using XFEEDToolBox.Core.Models;
namespace XFEEDToolBox.Test;
[TestFixture]
public class GalaxyOriginalProjectionTests
{
static void Near(double expected, double actual) => Check.That(Math.Abs(expected - actual) < 1e-10,
$"Expected {expected}, actual {actual}");
[Test]
public void FlatViewPreservesWideScreenVerticalAngleAndNarrowViewPreservesHorizontalAngle()
{
var aspect = GalaxyOriginalProjection.ReferenceAspectRatio;
var original = GalaxyOriginalProjection.StackFieldOfViewRadians;
Near(original, GalaxyOriginalProjection.VerticalFieldOfView(aspect));
Near(original, GalaxyOriginalProjection.VerticalFieldOfView(21d / 9));
var narrow = GalaxyOriginalProjection.VerticalFieldOfView(4d / 3);
Check.That(narrow > original);
Near(Math.Tan(original / 2) * aspect, Math.Tan(narrow / 2) * 4d / 3);
Near(original, GalaxyOriginalProjection.VerticalFieldOfView(4d / 3, compensateNarrowViewport: false));
}
[Test]
public void FocalLengthProjectsVerticalEdgeAndScalesWithActualViewportPixels()
{
var vertical = GalaxyOriginalProjection.VerticalFieldOfView(1920d / 1080);
var focal = GalaxyOriginalProjection.FocalLength(1920, 1080);
var depth = 17.75;
var topWorldOffset = Math.Tan(vertical / 2) * depth;
Near(540, topWorldOffset * focal / depth);
Near(focal * 2, GalaxyOriginalProjection.FocalLength(3840, 2160));
var narrowFocal = GalaxyOriginalProjection.FocalLength(1440, 1080);
Near(focal * .75, narrowFocal);
}
[Test]
public void InfiniteReverseDepthDoesNotConfuseStoredFarMetadataWithProjectionOrQueryCoverage()
{
Near(1, GalaxyOriginalProjection.ReverseDepth(GalaxyOriginalProjection.NearPlane));
Near(.01, GalaxyOriginalProjection.ReverseDepth(GalaxyOriginalProjection.NearPlane * 100));
var beyondStoredFar = GalaxyOriginalProjection.StoredFarPlane * 2;
Check.That(GalaxyOriginalProjection.ReverseDepth(beyondStoredFar) > 0);
Near(GalaxyOriginalProjection.NearPlane / beyondStoredFar, GalaxyOriginalProjection.ReverseDepth(beyondStoredFar));
}
[Test]
public void InvalidLensAndBehindCameraInputsAreRejected()
{
foreach (var bad in new[] { double.NaN, double.PositiveInfinity, 0d, -1d })
{
var rejected = 0;
try { GalaxyOriginalProjection.VerticalFieldOfView(bad); } catch (ArgumentOutOfRangeException) { rejected++; }
try { GalaxyOriginalProjection.FocalLength(bad, 1080); } catch (ArgumentOutOfRangeException) { rejected++; }
try { GalaxyOriginalProjection.ReverseDepth(bad); } catch (ArgumentOutOfRangeException) { rejected++; }
Check.That(rejected == 3);
}
}
[Test]
public void ProductionCameraUsesRecoveredLensAndNearPlaneAtEveryViewportAspect()
{
foreach (var width in new[] { 1440d, 1920d, 2560d })
{
var focal = GalaxyOriginalProjection.FocalLength(width, 1080);
var camera = GalaxyCamera.Create(new(0, 0, 0), width, 1080, 0, 0, focal / 17.75);
Near(GalaxyOriginalProjection.VerticalFieldOfView(width / 1080), camera.VerticalFieldOfView);
Near(focal, camera.FocalLength);
Near(17.75, camera.Distance);
Near(GalaxyOriginalProjection.NearPlane, camera.Near);
var beforeNear = new Coordinates(camera.Eye.X, camera.Eye.Y, camera.Eye.Z + camera.Near * .5);
var afterNear = new Coordinates(camera.Eye.X, camera.Eye.Y, camera.Eye.Z + camera.Near * 1.01);
Check.That(!camera.Project(beforeNear).IsVisible && !camera.ToViewport().Contains(beforeNear));
Check.That(camera.Project(afterNear).IsVisible && camera.ToViewport().Contains(afterNear));
}
}
[Test]
public void OriginalLensDoesNotExpandNearViewCatalogueQueriesToStoredFarOrWholeGalaxy()
{
var focal = GalaxyOriginalProjection.FocalLength(1920, 1080);
var camera = GalaxyCamera.Create(new(0, 0, 0), 1920, 1080, 0, 0, focal / 6);
var viewport = camera.ToViewport();
var bounds = GalaxyQueryBounds.FromViewport(viewport);
Near(24, camera.QueryFar);
Near(camera.QueryFar, bounds.Maximum.Z - camera.Eye.Z);
Check.That(double.IsPositiveInfinity(camera.ProjectionFar));
Check.That(viewport.Radius < 100 && bounds.Radius < 100);
Check.That(!bounds.Contains(new(0, 0, 26000)) && !viewport.Contains(new(0, 0, 26000)));
var plan = new GalaxyQueryPlan(viewport);
var count = 0;
while (plan.TryTake(out var tile))
{
Check.That(tile.Radius < 100);
Check.That(++count < 128, "A6ly camera view must remain a bounded local query after lens integration.");
}
Check.That(count > 0 && plan.OmittedTiles == 0);
}
[Test]
public void InfiniteVisualProjectionCannotPublishOrSelectAStarOutsideTheFiniteCatalogueView()
{
var focal=GalaxyOriginalProjection.FocalLength(1920,1080);
var camera=GalaxyCamera.Create(new(0,0,0),1920,1080,0,0,focal/6);
var distant=new Coordinates(0,0,camera.Eye.Z+GalaxyOriginalProjection.StoredFarPlane*2);
var projected=camera.Project(distant);
Check.That(projected.IsVisible&&projected.Depth>GalaxyOriginalProjection.StoredFarPlane);
Near(960,projected.X);Near(540,projected.Y);
Check.That(!camera.ProjectForCatalogue(distant).IsVisible);
Check.That(!camera.Contains(distant)&&!camera.ToViewport().Contains(distant));
Check.That(!GalaxyQueryBounds.FromViewport(camera.ToViewport()).Contains(distant));
Check.That(camera.TryProjectSegment(distant,new(10,0,distant.Z+100),out var a,out var b));
Check.That(double.IsFinite(a.X+a.Y+b.X+b.Y));
}
}