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

XFEEDToolBox

【WinUI】XFE精英危险工具箱

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

namespace XFEEDToolBox.Core.Galaxy;

public readonly record struct GalaxyCatalogueCell(ulong Address,int MassCode,GalaxyQueryBounds Bounds);

/// <summary>
/// Exact original mass-cell predicate intersected with the current geometric
/// frustum. Queue limits are tool scheduling bounds, never coverage evidence.
/// </summary>
public sealed class GalaxyCellQueryPlan
{
    public const int DefaultMaximumCells=131072;
    public IReadOnlyList<GalaxyCatalogueCell> Cells {get;}
    public bool Truncated {get;}
    public long OmittedCandidates {get;}
    public GalaxyCatalogueLodDescriptor Descriptor {get;}
    public Coordinates AbsoluteOrigin {get;}

    public GalaxyCellQueryPlan(GalaxyCamera camera,GalaxyCatalogueLodDescriptor descriptor,Coordinates absoluteOrigin,
        int maximumCells=DefaultMaximumCells,int maximumCandidates=2000000)
    {
        ArgumentNullException.ThrowIfNull(camera);ArgumentNullException.ThrowIfNull(descriptor);
        if(!absoluteOrigin.IsValid||maximumCells<1||maximumCandidates<maximumCells)
            throw new ArgumentException("原星区查询计划的坐标或调度上限无效。");
        Descriptor=descriptor;AbsoluteOrigin=absoluteOrigin;
        var cells=new List<GalaxyCatalogueCell>();long candidates=0,omitted=0;var truncated=false;
        for(var mass=0;mass<8;mass++)
        {
            var radius=descriptor.RadiusForMassCode(mass);if(radius<=0)continue;
            var size=10d*(1<<mass);var centre=descriptor.Centre;
            var maxX=(1<<(14-mass))-1;var maxY=(1<<(13-mass))-1;
            static long Index(double value,double size,long low,long high)=>(long)Math.Clamp(Math.Floor(value/size),low,high);
            var minX=Index(centre.X+absoluteOrigin.X-radius,size,0,maxX+1);
            var minY=Index(centre.Y+absoluteOrigin.Y-radius,size,0,maxY+1);
            var minZ=Index(centre.Z+absoluteOrigin.Z-radius,size,0,maxX+1);
            var endX=Index(centre.X+absoluteOrigin.X+radius,size,-1,maxX);
            var endY=Index(centre.Y+absoluteOrigin.Y+radius,size,-1,maxY);
            var endZ=Index(centre.Z+absoluteOrigin.Z+radius,size,-1,maxX);
            if(minX>endX||minY>endY||minZ>endZ)continue;
            var count=checked((endX-minX+1)*(endY-minY+1)*(endZ-minZ+1));
            if(count>maximumCandidates-candidates)
            {
                // Do not enumerate billions of cells after an unsupported
                // runtime override, or report the omitted region as covered.
                omitted+=count;truncated=true;continue;
            }
            candidates+=count;
            for(var x=minX;x<=endX;x++)for(var y=minY;y<=endY;y++)for(var z=minZ;z<=endZ;z++)
            {
                var address=Encode(mass,(int)x,(int)y,(int)z);
                var bounds=GalaxyOriginalCatalogueLod.CellBounds(address,absoluteOrigin);
                if(!descriptor.IntersectsCell(bounds,mass)||!IntersectsFrustum(camera,bounds))continue;
                if(cells.Count>=maximumCells){truncated=true;omitted++;continue;}
                cells.Add(new(address,mass,bounds));
            }
        }
        // This near-first ordering is a tool scheduling policy. The exact
        // requested cell set does not depend on the order or camera QueryFar.
        double Priority(GalaxyCatalogueCell cell)
        {
            var p=cell.Bounds.Centre;
            var delta=new Vector3d(p.X-camera.Eye.X,p.Y-camera.Eye.Y,p.Z-camera.Eye.Z);
            return Math.Max(0,Vector3d.Dot(delta,camera.Forward));
        }
        Cells=Array.AsReadOnly(cells.OrderBy(Priority).ThenBy(c=>c.Address).ToArray());
        Truncated=truncated;OmittedCandidates=omitted;
    }

    public static ulong Encode(int massCode,int x,int y,int z)
    {
        if(massCode is <0 or >7||x<0||y<0||z<0||x>=(1<<(14-massCode))||y>=(1<<(13-massCode))||z>=(1<<(14-massCode)))
            throw new ArgumentOutOfRangeException(nameof(massCode),"原星区编号越界。");
        return ((ulong)x<<(30-2*massCode))|((ulong)y<<(17-massCode))|((ulong)z<<3)|(uint)massCode;
    }

    /// <summary>Strip only the system ordinal, preserving all mass-dependent cell bits.</summary>
    public static ulong CellAddress(ulong systemAddress)
    {
        if(systemAddress>=1UL<<55)throw new ArgumentOutOfRangeException(nameof(systemAddress));
        var mass=(int)(systemAddress&7);
        return systemAddress&((1UL<<(44-3*mass))-1);
    }

    public static bool IntersectsFrustum(GalaxyCamera camera,GalaxyQueryBounds bounds,double margin=1)
    {
        if(!double.IsFinite(margin)||margin<1)throw new ArgumentOutOfRangeException(nameof(margin));
        var centre=bounds.Centre;
        var extent=new Vector3d((bounds.Maximum.X-bounds.Minimum.X)/2,(bounds.Maximum.Y-bounds.Minimum.Y)/2,(bounds.Maximum.Z-bounds.Minimum.Z)/2);
        var delta=new Vector3d(centre.X-camera.Eye.X,centre.Y-camera.Eye.Y,centre.Z-camera.Eye.Z);
        double Support(Vector3d n)=>Math.Abs(n.X)*extent.X+Math.Abs(n.Y)*extent.Y+Math.Abs(n.Z)*extent.Z;
        if(Vector3d.Dot(delta,camera.Forward)+Support(camera.Forward)<camera.Near)return false;
        var tx=camera.Width/(2*camera.FocalLength)*margin;var ty=camera.Height/(2*camera.FocalLength)*margin;
        foreach(var normal in new[]{camera.Forward*tx+camera.Right,camera.Forward*tx-camera.Right,
            camera.Forward*ty+camera.Up,camera.Forward*ty-camera.Up})
            if(Vector3d.Dot(delta,normal)+Support(normal)<0)return false;
        return true;
    }
}