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

XFEEDToolBox

【WinUI】XFE精英危险工具箱

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

namespace XFEEDToolBox.Controls;

public sealed partial class GalaxyMapView
{
    private sealed record MarkerSample(GalacticSystem System,string Asset,double HorizontalOffset);
    private sealed class MarkerVisual(Image icon,TranslateTransform position)
    {
        internal Image Icon=icon;
        internal string Asset="";
        internal TranslateTransform Position=position;
        internal bool Visible;
    }
    private MarkerSample[] worldMarkerSamples=[];
    private readonly List<MarkerVisual> worldMarkerVisuals=[];
    private readonly List<(GalacticSystem System,Rect Bounds)> worldMarkerHits=[];
    private readonly HashSet<string> worldMarkerRenderedNames=new(StringComparer.OrdinalIgnoreCase);
    private readonly List<(TextBlock Text,TranslateTransform Position)> nebulaNameLabels=[];
    private Image? currentLocationIcon;
    private readonly TranslateTransform currentLocationPosition=new();
    private (GalacticSystem System,Rect Bounds)? currentLocationHit;
    internal bool VisibleCurrentLocationMarker=>currentLocationHit is not null;
    internal int VisibleNebulaNameLabels {get;private set;}
    internal int VisibleWorldMarkers=>worldMarkerHits.Count;
    internal IReadOnlyList<(GalacticSystem System,Rect Bounds)> WorldMarkerBoundsForProbe=>worldMarkerHits;

    // Current mapui Icons frames retain their original colours, borders and
    // alpha. Legacy bookmark-editor IDs never select current world glyphs.
    private static string? WorldMarkerIcon(string key)
    {
        var frame=key switch
        {
            "engineer"=>"GM_DisplayOptions_Engineers","mission"=>"GM_DisplayOptions_Missions",
            "fleetcarrier"=>"GM_DisplayOptions_CarrierLocation","drydock"=>"GM_DisplayOptions_DryDock",
            "conflictzone"=>"GM_DisplayOptions_CZ","starterzone"=>"GM_DisplayOptions_StarterBookmarks",
            "navigation"=>"GM_DisplayOptions_Navigation","rescuemegaship"=>"GM_Marker_RescueMegaship",
            "bookmark"=>"GM_DisplayOptions_Bookmarks","squadronbookmark"=>"GM_DisplayOptions_BookmarksSquadron",
            "ship"=>"GM_DisplayOptions_StoredShips","communitygoal"=>"GM_DisplayOptions_CommunityGoals",
            "friend"=>"GM_DisplayOptions_Friends","wing"=>"GM_DisplayOptions_Wings",
            "damaged"=>"GM_DisplayOptions_StationDamaged","repairing"=>"GM_DisplayOptions_StationRepairing",
            "underattack"=>"GM_Marker_StationUnderAttack","abandoned"=>"GM_Marker_StationAbandoned",
            _=>null
        };
        return frame is not null&&GalaxyOriginalIcons.Map(frame) is {} icon?"ms-appx:///"+icon.AssetPath:null;
    }
    private void RebuildWorldMarkers()
    {
        if(Filter?.ShowMarkers==false){worldMarkerSamples=[];return;}
        var values=Filter is {IsQueryActive:true,Category:GalaxyFilterCategory.Markers}?Filter.Values:null;
        IEnumerable<MarkerSample> Samples(GalacticSystem system)
        {
            IEnumerable<string> Assets(string key)
            {
                if(key=="mission"&&system.Data.NativeAccountMarkers?.MissionMarkerTypes is {Count:>0} types)
                {
                    foreach(var frame in types.Select(GalaxyMissionMarkerStyles.OriginalFrameFor).OfType<string>().Distinct())
                        if(GalaxyOriginalIcons.Map(frame) is {} icon)yield return "ms-appx:///"+icon.AssetPath;
                }
                else if(WorldMarkerIcon(key) is {} asset)yield return asset;
            }
            var assets=(GalaxyMapFilter.Keys(system,GalaxyFilterCategory.Markers)??[]).Where(k=>(values is null||values.Contains(k))&&Filter?.IsMarkerVisible(k)!=false).SelectMany(Assets).Distinct().ToArray();
            // Separate actual types at the same system, including simultaneous
            // active and completed missions. They must not cover each other.
            for(var index=0;index<assets.Length;index++)yield return new(system,assets[index],28*(index-(assets.Length-1)*.5));
        }
        worldMarkerSamples=ordered.SelectMany(s=>Samples(s.System)).DistinctBy(s=>(s.System.Name,s.Asset)).Take(4096).ToArray();
    }
    private void DrawWorldMarkers(GalaxyCamera camera)
    {
        worldMarkerHits.Clear();worldMarkerRenderedNames.Clear();
        DrawCurrentLocationMarker(camera);
        var count=0;
        if(worldMarkerSamples.Length>0&&Filter?.ShowMarkers!=false)
        {
            if(StellarLodActive)for(var i=0;i<hitCount;i++)worldMarkerRenderedNames.Add(hits[i].System.Name);
            foreach(var sample in worldMarkerSamples)
            {
                if(count>=256)break;
                var projected=camera.ProjectForCatalogue(sample.System.Position);
                if(!projected.IsVisible||StellarLodActive&&!worldMarkerRenderedNames.Contains(sample.System.Name))continue;
                var bounds=new Rect(projected.X-13+sample.HorizontalOffset,projected.Y-(sample.System.Name.Equals(CurrentSystem,StringComparison.OrdinalIgnoreCase)?76:38),26,26);
                if(bounds.Left<0||bounds.Right>camera.Width||bounds.Top<0||bounds.Bottom>camera.Height)continue;
                if(currentLocationHit is {} current&&current.Bounds.Left<bounds.Right&&current.Bounds.Right>bounds.Left&&current.Bounds.Top<bounds.Bottom&&current.Bounds.Bottom>bounds.Top)continue;
                var collides=false;foreach(var placed in worldMarkerHits)if(placed.Bounds.Left<bounds.Right&&placed.Bounds.Right>bounds.Left&&placed.Bounds.Top<bounds.Bottom&&placed.Bounds.Bottom>bounds.Top){collides=true;break;}
                if(collides)continue;
                if(worldMarkerVisuals.Count==count)
                {
                    var position=new TranslateTransform();var icon=new Image{Width=26,Height=26,Stretch=Stretch.Uniform,IsHitTestVisible=false,RenderTransform=position,Opacity=0};
                    labels.Children.Add(icon);worldMarkerVisuals.Add(new(icon,position));
                }
                var visual=worldMarkerVisuals[count++];
                if(visual.Asset!=sample.Asset){visual.Asset=sample.Asset;visual.Icon.Source=sample.Asset.EndsWith(".svg",StringComparison.OrdinalIgnoreCase)?new SvgImageSource(new Uri(sample.Asset)):new BitmapImage(new Uri(sample.Asset));}
                visual.Position.X=bounds.X;visual.Position.Y=bounds.Y;
                if(!visual.Visible){visual.Visible=true;visual.Icon.Opacity=1;}
                worldMarkerHits.Add((sample.System,bounds));
            }
        }
        for(var i=count;i<worldMarkerVisuals.Count;i++)if(worldMarkerVisuals[i].Visible)
        {worldMarkerVisuals[i].Visible=false;worldMarkerVisuals[i].Icon.Opacity=0;}
        DrawOriginalRegionNames(camera);DrawNebulaNames(camera);
    }
    private void DrawCurrentLocationMarker(GalaxyCamera camera)
    {
        currentLocationHit=null;
        if(Filter?.ShowMarkers!=false&&!string.IsNullOrEmpty(CurrentSystem))for(var i=0;i<hitCount;i++)
        {
            var system=hits[i].System;if(!system.Name.Equals(CurrentSystem,StringComparison.OrdinalIgnoreCase))continue;
            var p=camera.ProjectForCatalogue(system.Position);if(!p.IsVisible)break;
            var bounds=new Rect(p.X-18,p.Y-47,36,36);
            if(bounds.Left<0||bounds.Right>camera.Width||bounds.Top<0||bounds.Bottom>camera.Height)break;
            if(currentLocationIcon is null&&GalaxyOriginalIcons.Map("CurrentLocationMarker") is {} asset)
            {
                currentLocationIcon=new Image{Width=36,Height=36,Stretch=Stretch.Uniform,IsHitTestVisible=false,RenderTransform=currentLocationPosition,
                    Source=new SvgImageSource(new Uri("ms-appx:///"+asset.AssetPath)),Opacity=0};
                labels.Children.Add(currentLocationIcon);
            }
            if(currentLocationIcon is null)break;
            currentLocationPosition.X=bounds.X;currentLocationPosition.Y=bounds.Y;currentLocationIcon.Opacity=1;
            currentLocationHit=(system,bounds);return;
        }
        if(currentLocationIcon is not null)currentLocationIcon.Opacity=0;
    }
    private void DrawNebulaNames(GalaxyCamera camera)
    {
        var show=Filter?.ShowNebulaNames==true;
        var count=0;var boxes=annotationBounds;
        if(show)foreach(var nebula in EliteNebulae.Reference.Nebulae.OrderBy(n=>n.Position.DistanceTo(camera.Focus)))
        {
            if(count>=24)break;var p=camera.Project(nebula.Position);if(!p.IsVisible)continue;
            var size=MeasureAnnotation(nebula.Name,12);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(nebulaNameLabels.Count==count)
            {
                var position=new TranslateTransform();var text=new TextBlock{FontSize=12,Foreground=Brush(170,184,202),RenderTransform=position,IsHitTestVisible=false};
                labels.Children.Add(text);nebulaNameLabels.Add((text,position));
            }
            var label=nebulaNameLabels[count++];if(label.Text.Text!=nebula.Name)label.Text.Text=nebula.Name;
            label.Position.X=box.X;label.Position.Y=box.Y;label.Text.Opacity=1;boxes.Add(box);
        }
        for(var i=count;i<nebulaNameLabels.Count;i++)nebulaNameLabels[i].Text.Opacity=0;
        VisibleNebulaNameLabels=count;
        emptyText.Opacity=ordered.Length==0&&count==0&&VisibleRegionNameLabels==0&&VisibleGalacticZoneLabels==0?1:0;
    }
    private GalacticSystem? FindWorldMarkerAt(Point point)
    {
        if(Filter?.ShowMarkers==false)return null;
        if(currentLocationHit is {} current&&current.Bounds.Contains(point)&&Filter?.Matches(current.System)!=false)return current.System;
        foreach(var marker in worldMarkerHits)if(marker.Bounds.Contains(point)&&Filter?.Matches(marker.System)!=false)return marker.System;
        return null;
    }
}