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

XFEEDToolBox

【WinUI】XFE精英危险工具箱

公开
关注 0 Fork 0 Star 0
UTF-8
using XFEEDToolBox.Core.Models;

namespace XFEEDToolBox.Core.Galaxy;

/// <summary>
/// The ordinary original catalogue descriptor (3A81510/3C14AF0). Radii select
/// whole mass-code cells, not individual stars inside a point-distance sphere.
/// </summary>
public sealed class GalaxyCatalogueLodDescriptor
{
    private readonly IReadOnlyList<double> radii;
    internal GalaxyCatalogueLodDescriptor(Coordinates centre,double density,uint qualityStars,double modifier,
        double multiplier,double expansion,double baseRadius,double innerRadius,double outerRadius)
    {
        Centre=centre;SurfaceDensity=density;QualityStarCount=qualityStars;Dd4Modifier=modifier;
        RuntimeRangeMultiplier=multiplier;SparseExpansionFactor=expansion;BaseRadius=baseRadius;
        InnerRadius=innerRadius;OuterRadius=outerRadius;
        radii=Array.AsReadOnly(new[]{innerRadius,innerRadius,innerRadius,innerRadius,innerRadius,innerRadius,innerRadius,outerRadius});
    }
    public Coordinates Centre {get;}
    public double SurfaceDensity {get;}
    public uint QualityStarCount {get;}
    public uint EffectiveQualityStarCount=>Math.Max(500u,QualityStarCount);
    public double Dd4Modifier {get;}
    public double RuntimeRangeMultiplier {get;}
    public double SparseExpansionFactor {get;}
    public double BaseRadius {get;}
    public double InnerRadius {get;}
    public double OuterRadius {get;}
    public IReadOnlyList<double> MassRadii=>radii;

    public double RadiusForMassCode(int massCode)=>massCode is >=0 and <8?radii[massCode]:
        throw new ArgumentOutOfRangeException(nameof(massCode));

    /// <summary>3C353C0: strict sphere/cell-AABB intersection, including boundary cells in full.</summary>
    public bool IntersectsCell(GalaxyQueryBounds cell,int massCode)
    {
        ArgumentNullException.ThrowIfNull(cell);
        var radius=RadiusForMassCode(massCode);
        if(cell.Minimum is not{IsValid:true}||cell.Maximum is not{IsValid:true}||
           cell.Minimum.X>cell.Maximum.X||cell.Minimum.Y>cell.Maximum.Y||cell.Minimum.Z>cell.Maximum.Z)
            throw new ArgumentException("星区边界无效。",nameof(cell));
        static double Axis(double p,double minimum,double maximum)=>Math.Max(Math.Max(minimum-p,p-maximum),0);
        var x=Axis(Centre.X,cell.Minimum.X,cell.Maximum.X);var y=Axis(Centre.Y,cell.Minimum.Y,cell.Maximum.Y);
        var z=Axis(Centre.Z,cell.Minimum.Z,cell.Maximum.Z);
        // The original divides distance by radius and tests 1-result>0. At
        // radius zero this rejects even a cell containing the centre (0/0).
        return radius>0&&Math.Sqrt(x*x+y*y+z*z)<radius;
    }

    public bool IntersectsCell(ulong cellAddress,Coordinates absoluteOrigin)=>
        IntersectsCell(GalaxyOriginalCatalogueLod.CellBounds(cellAddress,absoluteOrigin),(int)(cellAddress&7));
}

/// <summary>
/// Pure double-precision port of the recovered ordinary range formula. No
/// current quality level, DD4 mode/fade, or runtime global is guessed here.
/// </summary>
public static class GalaxyOriginalCatalogueLod
{
    // Preserve the actual single-precision source operands in double arithmetic.
    public const double DensityThreshold=(double).3f;
    public const double FourPi=(double)12.566370964050293f;
    public const double DensityCoefficient=(double).2864789068698883f;
    public const double CubeRootExponent=(double).3333333432674408f;
    public const double MaximumBaseRadius=200;
    public const double ConstructorSparseExpansionFactor=4;

    public static double BaseRadius(double surfaceDensity,uint qualityStarCount)
    {
        Finite(surfaceDensity,nameof(surfaceDensity));
        var density=Math.Clamp(surfaceDensity,0,1);
        var stars=Math.Max(500d,qualityStarCount);
        var numerator=3*stars;
        var minimum=numerator/(Math.Pow(MaximumBaseRadius,3)*FourPi);
        var densityTerm=Math.Pow(density,3.5)*DensityCoefficient;
        return Math.Pow(numerator/(Math.Max(minimum,densityTerm)*FourPi),CubeRootExponent);
    }

    public static GalaxyCatalogueLodDescriptor Create(Coordinates centre,double surfaceDensity,uint qualityStarCount,
        double dd4Modifier,double runtimeRangeMultiplier,double sparseExpansionFactor)
    {
        if(centre is not{IsValid:true})throw new ArgumentException("星图目录中心无效。",nameof(centre));
        Finite(surfaceDensity,nameof(surfaceDensity));Nonnegative(dd4Modifier,nameof(dd4Modifier));
        Nonnegative(runtimeRangeMultiplier,nameof(runtimeRangeMultiplier));Nonnegative(sparseExpansionFactor,nameof(sparseExpansionFactor));
        var basic=BaseRadius(surfaceDensity,qualityStarCount);
        var inner=basic*dd4Modifier*runtimeRangeMultiplier;
        var fraction=Math.Clamp((surfaceDensity-DensityThreshold)/DensityThreshold,0,1);
        var outer=inner+(1-fraction*fraction)*sparseExpansionFactor*10*128;
        if(!double.IsFinite(inner)||!double.IsFinite(outer))throw new ArgumentOutOfRangeException(nameof(runtimeRangeMultiplier));
        return new(centre,surfaceDensity,qualityStarCount,dd4Modifier,runtimeRangeMultiplier,sparseExpansionFactor,basic,inner,outer);
    }

    /// <summary>Unknown source values remain unknown; null is never silently replaced with 500 or image defaults.</summary>
    public static bool TryCreate(Coordinates centre,double? surfaceDensity,uint? qualityStarCount,double? dd4Modifier,
        double? runtimeRangeMultiplier,double? sparseExpansionFactor,out GalaxyCatalogueLodDescriptor? descriptor)
    {
        descriptor=null;
        if(centre is not{IsValid:true}||surfaceDensity is not{} d||qualityStarCount is not{} n||
           dd4Modifier is not{} modifier||runtimeRangeMultiplier is not{} multiplier||sparseExpansionFactor is not{} expansion||
           !double.IsFinite(d)||!double.IsFinite(modifier)||modifier<0||!double.IsFinite(multiplier)||multiplier<0||
           !double.IsFinite(expansion)||expansion<0)return false;
        try{descriptor=Create(centre,d,n,modifier,multiplier,expansion);return true;}
        catch(ArgumentException){return false;}
    }

    /// <summary>3B8E040: unavailable/default settings use Eye; a ready nonzero original view mode uses Focus.</summary>
    public static Coordinates QueryCentre(GalaxyCamera camera,int? originalViewMode)=>
        originalViewMode.GetValueOrDefault()!=0?camera.Focus:camera.Eye;

    /// <summary>3B46FD0 clamps the absolute float input before publishing the ordinary descriptor centre.</summary>
    public static Coordinates ClampCentre(Coordinates relativeInput, Coordinates absoluteOrigin)
    {
        if (!relativeInput.IsValid || !absoluteOrigin.IsValid) throw new ArgumentException("原星图坐标无效。");
        var x = (float)(relativeInput.X + absoluteOrigin.X);
        var y = (float)(relativeInput.Y + absoluteOrigin.Y);
        var z = (float)(relativeInput.Z + absoluteOrigin.Z);
        if (!float.IsFinite(x) || !float.IsFinite(y) || !float.IsFinite(z)) throw new ArgumentOutOfRangeException(nameof(relativeInput));
        return new(Math.Clamp(x, 100f, 163738f) - absoluteOrigin.X,
            Math.Clamp(y, 100f, 81818f) - absoluteOrigin.Y, Math.Clamp(z, 100f, 163738f) - absoluteOrigin.Z);
    }

    /// <summary>
    /// The ordinary DD4 range fade uses the raw input, not the clamped centre.
    /// The debug region replaces the descriptor callback and is deliberately
    /// rejected here rather than being passed through the ordinary formula.
    /// </summary>
    public static bool TryOrdinaryModifier(Coordinates relativeInput, Coordinates absoluteOrigin, bool? smallMode,
        Coordinates? referenceAbsolutePosition, bool? debugRangeEnabled, int? originalViewMode, out double modifier)
    {
        modifier = 0;
        if (!relativeInput.IsValid || !absoluteOrigin.IsValid || smallMode is null) return false;
        if (smallMode == true) { modifier = 1; return true; }
        if (debugRangeEnabled is null || debugRangeEnabled == true && originalViewMode is null || referenceAbsolutePosition is not { IsValid: true } reference) return false;
        var x = (float)(relativeInput.X + absoluteOrigin.X);
        var y = (float)(relativeInput.Y + absoluteOrigin.Y);
        var z = (float)(relativeInput.Z + absoluteOrigin.Z);
        if (!float.IsFinite(x) || !float.IsFinite(y) || !float.IsFinite(z)) return false;
        static float Square(float value) => value * value;
        if (debugRangeEnabled == true && originalViewMode == 2 &&
            Square(x - 49985f) + Square(y - 40985f) + Square(z - 24105f) < 4000000f) return false;
        var distance = MathF.Sqrt(Square(x - (float)reference.X) + Square(y - (float)reference.Y) + Square(z - (float)reference.Z));
        var fade = (distance - 1000f) / -500f;
        if (!float.IsFinite(fade)) return false;
        modifier = Math.Clamp(fade, 0, 1);
        return true;
    }

    /// <summary>Cell coordinates are absolute galactic ly; absoluteOrigin converts them to the caller's coordinate frame.</summary>
    public static GalaxyQueryBounds CellBounds(ulong cellAddress,Coordinates absoluteOrigin)
    {
        if(absoluteOrigin is not{IsValid:true})throw new ArgumentException("银河坐标原点无效。",nameof(absoluteOrigin));
        if(cellAddress>=1UL<<55)throw new ArgumentOutOfRangeException(nameof(cellAddress));
        var mass=(int)(cellAddress&7);var size=10d*(1<<mass);
        var x=(((cellAddress>>(30-2*mass))<<mass)&0x3FFF)*10d-absoluteOrigin.X;
        var y=(((cellAddress>>(17-mass))<<mass)&0x1FFF)*10d-absoluteOrigin.Y;
        var z=(((cellAddress>>3)<<mass)&0x3FFF)*10d-absoluteOrigin.Z;
        return new(new(x,y,z),new(x+size,y+size,z+size));
    }

    private static void Finite(double value,string name)
    {if(!double.IsFinite(value))throw new ArgumentOutOfRangeException(name);}
    private static void Nonnegative(double value,string name)
    {Finite(value,name);if(value<0)throw new ArgumentOutOfRangeException(name);}
}