XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFEEDToolBox

【WinUI】XFE精英危险工具箱

公开
关注 0 Fork 0 Star 0
UTF-8
using XFEEDToolBox.Core.Navigation;
using XFEEDToolBox.Core.Rendering;
using XFEExtension.NetCore.XUnit.Attributes;

namespace XFEEDToolBox.Test;

[TestFixture]
public sealed class LocalSystemOrbitGeometryTests
{
    private static (CurrentSystemSnapshot Snapshot,SystemOrbit Orbit) KnownOrbit()
    {
        var parent=new SystemLocation(1,11,"Parent star","",0,SystemLocationCategory.Star,null,new(1e11,2e10,0),null,false,"parent-star");
        var child=new SystemLocation(2,22,"Child planet","",0,SystemLocationCategory.Planet,null,new(1.6e11,2e10,0),null,false,"child-planet");
        var orbit=new SystemOrbit{SystemAddress=123,ChildWorldAddress=22,ChildIdentity=child.Identity,ParentWorldAddress=11,ParentIdentity=parent.Identity,
            SemiMajorAxisMetres=1e11,Eccentricity=.4,PeriapsisDirection=new(1,0,0),TransverseDirection=new(0,0,1),
            Quality=SystemOrbitQuality.NativeVerified,Source="Explicit geometry fixture",UnavailableReason=""};
        return(new(123,"Geometry fixture",1,1,DateTimeOffset.UtcNow,[parent,child]){Orbits=[orbit]},orbit);
    }
    [Test]public void ParentIsTheFocusRatherThanTheCentreAndPolylineClosesExactly()
    {
        var parent=new Vector3d(100,200,300);
        Check.That(LocalSystemOrbitGeometry.TrySampleEllipse(parent,1000,.6,new(1,0,0),new(0,1,0),out var points,out _));
        Check.Equal(129,points.Count);Check.Equal(points[0],points[^1]);
        Check.That((points[0]-(parent+new Vector3d(400,0,0))).Length<1e-9);
        Check.That((points[64]-(parent+new Vector3d(-1600,0,0))).Length<1e-9);
        // At eccentric anomaly pi/2, x=-a*e and y=a*sqrt(1-e^2).
        Check.That((points[32]-(parent+new Vector3d(-600,800,0))).Length<1e-9);
    }
    [Test]public void RealThreeDimensionalBasisIsPreservedWithoutChoosingAScreenPlane()
    {
        var parent=new Vector3d(9e9,-2e9,7e9);var x=new Vector3d(0,0,1);var y=new Vector3d(1,0,0);
        Check.That(LocalSystemOrbitGeometry.TrySampleEllipse(parent,1e8,0,x,y,out var points,out _));
        Check.That((points[0]-parent-x*1e8).Length<1e-6);
        Check.That((points[32]-parent-y*1e8).Length<1e-6);
        Check.That(points.All(point=>Math.Abs((point-parent).Length-1e8)<1e-5&&point.Y==parent.Y));
    }
    [Test]public void MissingNonOrthogonalAndNonUnitPlanesAreRejectedRatherThanInvented()
    {
        foreach(var axes in new[]{(X:Vector3d.Zero,Y:new Vector3d(0,1,0)),(X:new Vector3d(1,0,0),Y:new Vector3d(1,0,0)),
            (X:new Vector3d(2,0,0),Y:new Vector3d(0,1,0)),(X:new Vector3d(double.NaN,0,0),Y:new Vector3d(0,1,0))})
        {Check.That(!LocalSystemOrbitGeometry.TrySampleEllipse(Vector3d.Zero,10,.1,axes.X,axes.Y,out var points,out _));Check.Equal(0,points.Count);}
    }
    [Test]public void NonEllipticAndOverflowingParametersCannotReachTheRenderer()
    {
        foreach(var value in new[]{(A:0d,E:0d),(A:-1d,E:.5),(A:1d,E:-.1),(A:1d,E:1d),(A:1d,E:double.NaN),
            (A:double.PositiveInfinity,E:0d),(A:double.MaxValue,E:.9)})
        {Check.That(!LocalSystemOrbitGeometry.TrySampleEllipse(Vector3d.Zero,value.A,value.E,new(1,0,0),new(0,1,0),out var points,out _));Check.Equal(0,points.Count);}
    }
    [Test]public void DistanceCompressionIsAppliedAfterSamplingAroundAnOffsetParent()
    {
        var star=new SystemLocation(1,1,"Star","",0,SystemLocationCategory.Star,null,new(0,0,0),null,false,"star");
        var parent=new SystemLocation(2,2,"Planet","",0,SystemLocationCategory.Planet,null,new(2e11,0,0),null,false,"parent");
        var scene=new LocalSystemScene(new(1,"System",1,1,DateTimeOffset.UtcNow,[star,parent]));
        Check.That(LocalSystemOrbitGeometry.TrySampleEllipse(parent.Position!.Value,1e11,.4,new(1,0,0),new(0,0,1),out var actual,out _));
        var transformed=actual.Select(scene.Transform).ToArray();
        var compressedParent=scene.Transform(parent.Position.Value);
        // A circle drawn around the already compressed parent would wrongly
        // give equal radial offsets; the actual radial log transform does not.
        Check.That(Math.Abs((transformed[0]-compressedParent).Length-(transformed[64]-compressedParent).Length)>.01);
        Check.That(transformed.All(point=>point.Finite));
    }
    [Test]public void SceneUsesOneTransformForBodyOrbitAndParentFocus()
    {
        var (snapshot,_)=KnownOrbit();var scene=new LocalSystemScene(snapshot);var path=scene.Orbits.Single();
        Check.Equal(1,scene.VerifiedOrbitCount);Check.Equal(0,scene.UnavailableOrbitCount);
        Check.Equal(scene.Nodes.Single(node=>node.Location.Identity==path.Child.Identity).Position,path.Points[0]);
        // The display scale is affine within this parent family, rather than
        // applying a radial log that bends offset ellipses into crescents.
        var centre=(path.Points[0]+path.Points[64])/2;var x=(path.Points[0]-path.Points[64])/2;var z=(path.Points[32]-path.Points[96])/2;
        Check.That(path.Points.All(point=>Math.Abs(Math.Pow(Vector3d.Dot(point-centre,x)/Vector3d.Dot(x,x),2)
            +Math.Pow(Vector3d.Dot(point-centre,z)/Vector3d.Dot(z,z),2)-1)<1e-9));
        Check.That(path.Points.All(point=>point.Length<=4.4+1e-9));
        var camera=LocalSystemCamera.Overview;var before=camera.Project(path.Points[32],900,600)!.Value;
        var after=camera.Rotate(80,20).Project(path.Points[32],900,600)!.Value;
        Check.That(Math.Abs(before.X-after.X)>1||Math.Abs(before.Y-after.Y)>1);
    }
    [Test]public void UnknownPlanesWrongSystemsAndAmbiguousParentsNeverBecomeOrbits()
    {
        var (snapshot,orbit)=KnownOrbit();
        foreach(var invalid in new[]{orbit with{Quality=SystemOrbitQuality.ElementsOnly},orbit with{SystemAddress=124},
            orbit with{ParentIdentity="wrong"},orbit with{ParentWorldAddress=99},orbit with{ChildIdentity="wrong"},
            orbit with{PeriapsisDirection=null},orbit with{Source=""}})
        {var scene=new LocalSystemScene(snapshot with{Orbits=[invalid]});Check.Equal(0,scene.Orbits.Count);Check.Equal(1,scene.UnavailableOrbitCount);}
        var ambiguous=new LocalSystemScene(snapshot with{Locations=[..snapshot.Locations,snapshot.Locations[0]]});
        Check.Equal(0,ambiguous.Orbits.Count);Check.Equal(1,ambiguous.UnavailableOrbitCount);
    }
    [Test]public void FilteringKeepsOrbitTransformAndCanHideParentWithoutChangingItsFocus()
    {
        var (snapshot,_)=KnownOrbit();var all=new LocalSystemScene(snapshot);var childOnly=new LocalSystemScene(snapshot,[snapshot.Locations[1]]);
        Check.Equal(1,childOnly.Orbits.Count);Check.Equal(all.ExtentMetres,childOnly.ExtentMetres);
        Check.That(all.Orbits[0].Points.SequenceEqual(childOnly.Orbits[0].Points));
        Check.Equal("parent-star",childOnly.Orbits[0].ParentIdentity);
        var parentOnly=new LocalSystemScene(snapshot,[snapshot.Locations[0]]);
        Check.Equal(0,parentOnly.Orbits.Count);Check.Equal(1,parentOnly.VerifiedOrbitCount);
    }
    [Test]public void DuplicateChildOrbitRecordsAreReportedInsteadOfPickingOne()
    {
        var (snapshot,orbit)=KnownOrbit();var scene=new LocalSystemScene(snapshot with{Orbits=[orbit,orbit with{Eccentricity=.2}]});
        Check.Equal(0,scene.Orbits.Count);Check.Equal(0,scene.VerifiedOrbitCount);Check.Equal(2,scene.UnavailableOrbitCount);
        Check.That(scene.OrbitUnavailableReasons.Any(reason=>reason.Contains("多个",StringComparison.Ordinal)));
    }
    [Test]public void VerifiedNativeBarycentreCanBeAFocusWithoutInventingACatalogueNode()
    {
        var (snapshot,orbit)=KnownOrbit();var focus=snapshot.Locations[0].Position;
        var scene=new LocalSystemScene(snapshot with{Locations=[snapshot.Locations[1]],Orbits=[orbit with{ParentPosition=focus,ParentName="Actual native barycentre",ParentIsBarycentre=true}]});
        Check.Equal(1,scene.Nodes.Count);Check.Equal(1,scene.Orbits.Count);Check.That(scene.Orbits[0].Parent is null);
        Check.Equal(focus!.Value,scene.Orbits[0].FocusPosition);Check.Equal("parent-star",scene.Orbits[0].ParentIdentity);
        var unknown=new LocalSystemScene(snapshot with{Locations=[snapshot.Locations[1]],Orbits=[orbit with{ParentPosition=focus,Quality=SystemOrbitQuality.ElementsOnly}]});
        Check.Equal(0,unknown.Orbits.Count);
        var conflict=new LocalSystemScene(snapshot with{Orbits=[orbit with{ParentPosition=new(8e15,0,0)}]});
        Check.Equal(0,conflict.Orbits.Count);
    }
    [Test]public void SharedWorldCarriersAndPoiDoNotConflictWithAVerifiedPhysicalFocus()
    {
        var (snapshot,orbit)=KnownOrbit();var parent=snapshot.Locations[0];
        var carrier=new SystemLocation(3,parent.WorldAddress,"Carrier","",0,SystemLocationCategory.Carrier,123,new(1e11,1e8,0),null,false,"shared-world-carrier");
        var poi=carrier with{Key=4,Name="POI",Category=SystemLocationCategory.PointOfInterest,Identity="shared-world-poi",MarketId=null};
        var signal=poi with{Key=5,Name="Signal",Category=SystemLocationCategory.Signal,Identity="shared-world-signal"};
        // The native focus is real even when it has no selectable physical row.
        var recorded=snapshot with{Locations=[snapshot.Locations[1],carrier,poi,signal],Orbits=[orbit with{ParentPosition=parent.Position}]};
        var scene=new LocalSystemScene(recorded);Check.Equal(1,scene.Orbits.Count);Check.Equal(0,scene.UnavailableOrbitCount);
        Check.Equal(parent.Position!.Value,scene.Orbits[0].FocusPosition);Check.That(scene.Orbits[0].Parent is null);
        // Another physical body claiming that world still is a real conflict.
        var wrongParent=parent with{Identity="different-physical-parent"};
        Check.Equal(0,new LocalSystemScene(recorded with{Locations=[..recorded.Locations,wrongParent]}).Orbits.Count);
        // A carrier may share IDs but cannot become an orbiting physical child.
        var wrongChild=carrier with{WorldAddress=orbit.ChildWorldAddress,Identity=orbit.ChildIdentity};
        Check.Equal(0,new LocalSystemScene(recorded with{Locations=[wrongChild,poi,signal]}).Orbits.Count);
    }
}