using XFEEDToolBox.Core.Navigation;
namespace XFEEDToolBox.Core.Rendering;
public sealed record LocalSystemOrbitPath(SystemOrbit Orbit,SystemLocation Child,SystemLocation? Parent,Vector3d FocusPosition,IReadOnlyList<Vector3d> ActualPoints,IReadOnlyList<Vector3d> Points)
{
public string ParentIdentity=>Parent?.Identity??Orbit.ParentIdentity!;
public string ParentDisplayName=>Parent?.DisplayName??(!string.IsNullOrWhiteSpace(Orbit.ParentName)?Orbit.ParentName:Orbit.ParentIsBarycentre?"原生父质心":"原生父天体");
}
/// <summary>Samples a focus-centred Kepler ellipse in the supplied, verified system-space basis, in metres.</summary>
public static class LocalSystemOrbitGeometry
{
public const int DefaultSegments=128;
private static bool PhysicalBody(SystemLocation location)=>location.Category is SystemLocationCategory.Star or SystemLocationCategory.Planet or SystemLocationCategory.LandablePlanet;
public static bool TrySampleOrbit(SystemOrbit orbit,CurrentSystemSnapshot snapshot,out SystemLocation? child,out SystemLocation? parent,
out IReadOnlyList<Vector3d> points,out string? issue)
{
child=parent=null;points=[];issue=null;
if(orbit.SystemAddress!=snapshot.SystemAddress){issue="轨道所属星系与当前目录不一致。";return false;}
if(string.IsNullOrWhiteSpace(orbit.Source)){issue="轨道参数来源尚未确认。";return false;}
if(!orbit.Renderable){issue=string.IsNullOrWhiteSpace(orbit.UnavailableReason)?"轨道形状、父子关系或星系空间平面尚未核实。":orbit.UnavailableReason;return false;}
var physical=snapshot.Locations.Where(PhysicalBody).ToArray();
var children=physical.Where(location=>location.WorldAddress==orbit.ChildWorldAddress&&location.Identity==orbit.ChildIdentity).ToArray();
var parents=physical.Where(location=>location.WorldAddress==orbit.ParentWorldAddress&&location.Identity==orbit.ParentIdentity).ToArray();
if(children.Length!=1||parents.Length>1||parents.Length==1&&children[0].Identity==parents[0].Identity)
{issue="轨道父子天体无法与当前目录唯一对应。";return false;}
child=children[0];parent=parents.SingleOrDefault();
// A verified native barycentre need not be a selectable catalogue row.
// A conflicting row is never bypassed by substituting that extra focus.
if(parents.Length==0&&physical.Any(location=>location.WorldAddress==orbit.ParentWorldAddress||location.Identity==orbit.ParentIdentity))
{issue="原生父天体身份与目录不一致,未替换焦点。";return false;}
var focus=parent?.Position??orbit.ParentPosition;
if(child.Position is not{Finite:true} childPoint||!double.IsFinite(childPoint.Length)||focus is not{Finite:true} focusPoint)
{issue="轨道父子天体的真实坐标尚未确认。";return false;}
if(parent is not null&&orbit.ParentPosition is {} nativeParent&&(!nativeParent.Finite||!double.IsFinite(nativeParent.Length)
||(nativeParent-focusPoint).Length>1000+focusPoint.Length*1e-9))
{issue="原生父天体焦点与当前目录坐标不一致。";return false;}
return TrySampleEllipse(focusPoint,orbit.SemiMajorAxisMetres!.Value,orbit.Eccentricity!.Value,
orbit.PeriapsisDirection!.Value,orbit.TransverseDirection!.Value,out points,out issue);
}
public static bool TrySampleEllipse(Vector3d focus,double semiMajorAxisMetres,double eccentricity,Vector3d periapsisAxis,Vector3d transverseAxis,
out IReadOnlyList<Vector3d> points,out string? issue,int segments=DefaultSegments)
{
points=[];issue=null;
if(!focus.Finite||!double.IsFinite(focus.Length)){issue="parent-position-invalid";return false;}
if(!double.IsFinite(semiMajorAxisMetres)||semiMajorAxisMetres<=0||!double.IsFinite(eccentricity)||eccentricity is <0 or >=1)
{issue="ellipse-elements-invalid";return false;}
if(segments is <16 or >512){issue="sample-budget-invalid";return false;}
var xLength=periapsisAxis.Length;var yLength=transverseAxis.Length;
if(!periapsisAxis.Finite||!transverseAxis.Finite||!double.IsFinite(xLength)||!double.IsFinite(yLength)
||Math.Abs(xLength-1)>.001||Math.Abs(yLength-1)>.001||Math.Abs(Vector3d.Dot(periapsisAxis,transverseAxis))>.001)
{issue="system-plane-basis-invalid";return false;}
// Normalize only the rounding error of an already unit, orthogonal basis.
// Its direction and periapsis reference must be established by the source.
var x=periapsisAxis/xLength;var y=transverseAxis/yLength;
var b=semiMajorAxisMetres*Math.Sqrt((1-eccentricity)*(1+eccentricity));
if(!double.IsFinite(b)){issue="ellipse-size-overflow";return false;}
var sampled=new Vector3d[segments+1];
for(var index=0;index<segments;index++)
{
var anomaly=2*Math.PI*index/segments;
var point=focus+x*(semiMajorAxisMetres*(Math.Cos(anomaly)-eccentricity))+y*(b*Math.Sin(anomaly));
if(!point.Finite||!double.IsFinite(point.Length)){issue="ellipse-position-overflow";return false;}
sampled[index]=point;
}
sampled[^1]=sampled[0];points=sampled;return true;
}
}
using XFEEDToolBox.Core.Navigation;
namespace XFEEDToolBox.Core.Rendering;
public sealed record LocalSystemOrbitPath(SystemOrbit Orbit,SystemLocation Child,SystemLocation? Parent,Vector3d FocusPosition,IReadOnlyList<Vector3d> ActualPoints,IReadOnlyList<Vector3d> Points)
{
public string ParentIdentity=>Parent?.Identity??Orbit.ParentIdentity!;
public string ParentDisplayName=>Parent?.DisplayName??(!string.IsNullOrWhiteSpace(Orbit.ParentName)?Orbit.ParentName:Orbit.ParentIsBarycentre?"原生父质心":"原生父天体");
}
/// <summary>Samples a focus-centred Kepler ellipse in the supplied, verified system-space basis, in metres.</summary>
public static class LocalSystemOrbitGeometry
{
public const int DefaultSegments=128;
private static bool PhysicalBody(SystemLocation location)=>location.Category is SystemLocationCategory.Star or SystemLocationCategory.Planet or SystemLocationCategory.LandablePlanet;
public static bool TrySampleOrbit(SystemOrbit orbit,CurrentSystemSnapshot snapshot,out SystemLocation? child,out SystemLocation? parent,
out IReadOnlyList<Vector3d> points,out string? issue)
{
child=parent=null;points=[];issue=null;
if(orbit.SystemAddress!=snapshot.SystemAddress){issue="轨道所属星系与当前目录不一致。";return false;}
if(string.IsNullOrWhiteSpace(orbit.Source)){issue="轨道参数来源尚未确认。";return false;}
if(!orbit.Renderable){issue=string.IsNullOrWhiteSpace(orbit.UnavailableReason)?"轨道形状、父子关系或星系空间平面尚未核实。":orbit.UnavailableReason;return false;}
var physical=snapshot.Locations.Where(PhysicalBody).ToArray();
var children=physical.Where(location=>location.WorldAddress==orbit.ChildWorldAddress&&location.Identity==orbit.ChildIdentity).ToArray();
var parents=physical.Where(location=>location.WorldAddress==orbit.ParentWorldAddress&&location.Identity==orbit.ParentIdentity).ToArray();
if(children.Length!=1||parents.Length>1||parents.Length==1&&children[0].Identity==parents[0].Identity)
{issue="轨道父子天体无法与当前目录唯一对应。";return false;}
child=children[0];parent=parents.SingleOrDefault();
// A verified native barycentre need not be a selectable catalogue row.
// A conflicting row is never bypassed by substituting that extra focus.
if(parents.Length==0&&physical.Any(location=>location.WorldAddress==orbit.ParentWorldAddress||location.Identity==orbit.ParentIdentity))
{issue="原生父天体身份与目录不一致,未替换焦点。";return false;}
var focus=parent?.Position??orbit.ParentPosition;
if(child.Position is not{Finite:true} childPoint||!double.IsFinite(childPoint.Length)||focus is not{Finite:true} focusPoint)
{issue="轨道父子天体的真实坐标尚未确认。";return false;}
if(parent is not null&&orbit.ParentPosition is {} nativeParent&&(!nativeParent.Finite||!double.IsFinite(nativeParent.Length)
||(nativeParent-focusPoint).Length>1000+focusPoint.Length*1e-9))
{issue="原生父天体焦点与当前目录坐标不一致。";return false;}
return TrySampleEllipse(focusPoint,orbit.SemiMajorAxisMetres!.Value,orbit.Eccentricity!.Value,
orbit.PeriapsisDirection!.Value,orbit.TransverseDirection!.Value,out points,out issue);
}
public static bool TrySampleEllipse(Vector3d focus,double semiMajorAxisMetres,double eccentricity,Vector3d periapsisAxis,Vector3d transverseAxis,
out IReadOnlyList<Vector3d> points,out string? issue,int segments=DefaultSegments)
{
points=[];issue=null;
if(!focus.Finite||!double.IsFinite(focus.Length)){issue="parent-position-invalid";return false;}
if(!double.IsFinite(semiMajorAxisMetres)||semiMajorAxisMetres<=0||!double.IsFinite(eccentricity)||eccentricity is <0 or >=1)
{issue="ellipse-elements-invalid";return false;}
if(segments is <16 or >512){issue="sample-budget-invalid";return false;}
var xLength=periapsisAxis.Length;var yLength=transverseAxis.Length;
if(!periapsisAxis.Finite||!transverseAxis.Finite||!double.IsFinite(xLength)||!double.IsFinite(yLength)
||Math.Abs(xLength-1)>.001||Math.Abs(yLength-1)>.001||Math.Abs(Vector3d.Dot(periapsisAxis,transverseAxis))>.001)
{issue="system-plane-basis-invalid";return false;}
// Normalize only the rounding error of an already unit, orthogonal basis.
// Its direction and periapsis reference must be established by the source.
var x=periapsisAxis/xLength;var y=transverseAxis/yLength;
var b=semiMajorAxisMetres*Math.Sqrt((1-eccentricity)*(1+eccentricity));
if(!double.IsFinite(b)){issue="ellipse-size-overflow";return false;}
var sampled=new Vector3d[segments+1];
for(var index=0;index<segments;index++)
{
var anomaly=2*Math.PI*index/segments;
var point=focus+x*(semiMajorAxisMetres*(Math.Cos(anomaly)-eccentricity))+y*(b*Math.Sin(anomaly));
if(!point.Finite||!double.IsFinite(point.Length)){issue="ellipse-position-overflow";return false;}
sampled[index]=point;
}
sampled[^1]=sampled[0];points=sampled;return true;
}
}