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

XFEEDToolBox

【WinUI】XFE精英危险工具箱

公开
关注 0 Fork 0 Star 0
UTF-8
using Microsoft.UI.Xaml.Media;
using Windows.Foundation;
using XFEEDToolBox.Core.Galaxy;
using XFEEDToolBox.Core.Rendering;

namespace XFEEDToolBox.Controls;

public sealed partial class GalaxyMapView
{
    private readonly List<(TextBlock Text,TranslateTransform Position)> regionNameVisuals=[];
    private readonly List<(TextBlock Text,TranslateTransform Position)> galacticZoneNameVisuals=[];
    private readonly List<Rect> annotationBounds=[];
    private readonly Dictionary<(string Text,double FontSize),Size> annotationSizes=[];
    private TextBlock? annotationMeasure;
    internal int VisibleRegionNameLabels {get;private set;}
    internal int VisibleGalacticZoneLabels {get;private set;}
    internal bool GalacticZonesPlaneReadyForProbe=>graphics?.GalacticZonesPlaneReady==true;
    internal bool GalacticZonesPlaneDrawnForProbe=>graphics?.GalacticZonesPlaneDrawn==true;
    internal object AnnotationMetricsForProbe=>new{width=ActualWidth,height=ActualHeight,scale=XamlRoot?.RasterizationScale,
        sizes=annotationSizes.Select(s=>new{text=s.Key.Text,fontSize=s.Key.FontSize,width=s.Value.Width,height=s.Value.Height}).ToArray(),
        visible=regionNameVisuals.Where(s=>s.Text.Opacity>0).Select(s=>new{text=s.Text.Text,x=s.Position.X,y=s.Position.Y,width=s.Text.ActualWidth,height=s.Text.ActualHeight,desiredWidth=s.Text.DesiredSize.Width}).ToArray()};
    private void DrawOriginalRegionNames(GalaxyCamera camera)
    {
        annotationBounds.Clear();DrawGalacticZoneNames(camera);var count=0;
        if(Filter?.ShowRegionNames==true)foreach(var region in GalaxyOriginalRegions.Reference.Regions.OrderBy(r=>r.Spheres.Min(s=>s.Position.DistanceTo(camera.Focus))))
        {
            if(count>=32)break;
            foreach(var sphere in region.Spheres.OrderBy(s=>s.Position.DistanceTo(camera.Focus)))
            {
                var p=camera.Project(sphere.Position);if(!p.IsVisible)continue;
                var size=MeasureAnnotation(region.Name,14);var box=new Rect(p.X-size.Width/2,p.Y-size.Height/2,size.Width,size.Height);
                if(box.Left<4||box.Right>camera.Width-4||box.Top<4||box.Bottom>camera.Height-4||AnnotationBlocked(box))continue;
                if(regionNameVisuals.Count==count)
                {
                    var position=new TranslateTransform();var text=new TextBlock{FontSize=14,Foreground=Brush(160,177,188),RenderTransform=position,IsHitTestVisible=false};
                    labels.Children.Add(text);regionNameVisuals.Add((text,position));
                }
                var label=regionNameVisuals[count++];if(label.Text.Text!=region.Name)label.Text.Text=region.Name;
                label.Position.X=box.X;label.Position.Y=box.Y;label.Text.Opacity=1;annotationBounds.Add(box);break;
            }
        }
        for(var i=count;i<regionNameVisuals.Count;i++)regionNameVisuals[i].Text.Opacity=0;
        VisibleRegionNameLabels=count;
    }
    private void DrawGalacticZoneNames(GalaxyCamera camera)
    {
        var count=0;
        // Text anchors are area centroids calculated from the original GZM;
        // the resource contains names and boundaries, no authored 3D labels.
        if(Filter?.ShowGalacticZoneNames==true&&camera.Scale<=.06)
        foreach(var zone in GalaxyOriginalZones.Reference.Zones.OrderBy(z=>z.Position.DistanceTo(camera.Focus)))
        {
            var p=camera.Project(zone.Position);if(!p.IsVisible)continue;
            var size=MeasureAnnotation(zone.Name,18);var box=new Rect(p.X-size.Width/2,p.Y-size.Height/2,size.Width,size.Height);
            if(box.Left<4||box.Right>camera.Width-4||box.Top<4||box.Bottom>camera.Height-4||AnnotationBlocked(box))continue;
            if(galacticZoneNameVisuals.Count==count)
            {
                var position=new TranslateTransform();var text=new TextBlock{FontSize=18,Foreground=Brush(142,180,194),RenderTransform=position,IsHitTestVisible=false};
                labels.Children.Add(text);galacticZoneNameVisuals.Add((text,position));
            }
            var label=galacticZoneNameVisuals[count++];if(label.Text.Text!=zone.Name)label.Text.Text=zone.Name;
            label.Position.X=box.X;label.Position.Y=box.Y;label.Text.Opacity=1;annotationBounds.Add(box);
        }
        for(var i=count;i<galacticZoneNameVisuals.Count;i++)galacticZoneNameVisuals[i].Text.Opacity=0;
        VisibleGalacticZoneLabels=count;
    }
    private Size MeasureAnnotation(string text,double fontSize)
    {
        var key=(text,fontSize);if(annotationSizes.TryGetValue(key,out var size))return size;
        // A detached WinUI TextBlock can report an empty DesiredSize. Attach
        // the invisible measurer so it receives the same root/font metrics.
        if(annotationMeasure is null)
        {
            annotationMeasure=new TextBlock{IsHitTestVisible=false,Opacity=0};labels.Children.Add(annotationMeasure);
        }
        annotationMeasure.Text=text;annotationMeasure.FontSize=fontSize;annotationMeasure.InvalidateMeasure();
        annotationMeasure.Measure(new(100000,100000));
        var measured=annotationMeasure.DesiredSize;
        if(measured.Width<=0||measured.Height<=0)
            return new(text.Length*fontSize+8,fontSize*2+4);
        size=new(Math.Ceiling(measured.Width)+6,Math.Ceiling(measured.Height)+4);annotationSizes.Add(key,size);return size;
    }
    private bool AnnotationBlocked(Rect box)
    {
        static bool Intersects(Rect a,Rect b)=>a.Left<b.Right&&a.Right>b.Left&&a.Top<b.Bottom&&a.Bottom>b.Top;
        if(annotationBounds.Any(b=>Intersects(b,box))||worldMarkerHits.Any(m=>Intersects(m.Bounds,box))||currentLocationHit is {} current&&Intersects(current.Bounds,box))return true;
        foreach(var label in names)if(label.Visible&&Intersects(new Rect(label.Transform.X,label.Transform.Y,label.BoxWidth,label.BoxHeight),box))return true;
        return false;
    }
}