< Summary

Information
Class: Trailblazer.Pathing.NavigationMap
Assembly: Trailblazer
File(s): /home/runner/work/Trailblazer/Trailblazer/src/Trailblazer/Pathing/Map/NavigationMap.cs
Line coverage
100%
Covered lines: 136
Uncovered lines: 0
Coverable lines: 136
Total lines: 271
Line coverage: 100%
Branch coverage
100%
Covered branches: 58
Total branches: 58
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
get_Cells()100%11100%
get_Connections()100%11100%
get_Transitions()100%11100%
get_TransitionRules()100%11100%
get_DefaultCell()100%11100%
get_CellSpan()100%11100%
get_ConnectionSpan()100%11100%
get_TransitionSpan()100%11100%
get_TransitionRuleSpan()100%11100%
get_NativePortalTemplateCount()100%11100%
get_NativePortalTemplateRetainedBytes()100%11100%
.ctor(...)100%11100%
Equals(...)100%3434100%
Equals(...)100%11100%
GetHashCode()100%88100%
ContainsCell(...)100%11100%
FindCellIndex(...)100%66100%
GetNativePortalTemplate(...)100%11100%
GetNativeSurfaceDirectionCount(...)100%22100%
GetNativeSurfaceOffset(...)100%22100%
CompileNativePortalTemplates(...)100%66100%

File(s)

/home/runner/work/Trailblazer/Trailblazer/src/Trailblazer/Pathing/Map/NavigationMap.cs

#LineLine coverage
 1//=======================================================================
 2// NavigationMap.cs
 3//=======================================================================
 4// MIT License, Copyright (c) 2024-present David Oravsky (mrdav30)
 5// See LICENSE file in the project root for full license information.
 6//=======================================================================
 7
 8using System;
 9using System.Collections.Generic;
 10using System.Collections.ObjectModel;
 11using System.Runtime.CompilerServices;
 12using FixedMathSharp;
 13using GridForge.Configuration;
 14using GridForge.Grids.Topology;
 15using GridForge.Spatial;
 16using SwiftCollections.Utility;
 17
 18namespace Trailblazer.Pathing;
 19
 20/// <summary>
 21/// Stores one immutable, canonically ordered navigation bake for one normalized grid.
 22/// </summary>
 23public sealed class NavigationMap : IEquatable<NavigationMap>
 24{
 125    private static readonly HexDirection[] NativeHexDirections =
 126    {
 127        HexDirection.QNegative,
 128        HexDirection.QNegativeRPositive,
 129        HexDirection.RNegative,
 130        HexDirection.RPositive,
 131        HexDirection.QPositiveRNegative,
 132        HexDirection.QPositive
 133    };
 34
 135    private static readonly RectangularDirection[] NativeRectangularDirections =
 136    {
 137        RectangularDirection.West,
 138        RectangularDirection.South,
 139        RectangularDirection.North,
 140        RectangularDirection.East
 141    };
 42
 43    private readonly NavigationCellEntry[] _cells;
 44    private readonly NavigationConnection[] _connections;
 45    private readonly TraversalTransitionDefinition[] _transitions;
 46    private readonly TraversalTransitionRule[] _transitionRules;
 47    private readonly NavigationCell? _defaultCell;
 48    private readonly GridNavigationPortal[] _nativePortalTemplates;
 49    private readonly ReadOnlyCollection<NavigationCellEntry> _cellView;
 50    private readonly ReadOnlyCollection<NavigationConnection> _connectionView;
 51    private readonly ReadOnlyCollection<TraversalTransitionDefinition> _transitionView;
 52    private readonly ReadOnlyCollection<TraversalTransitionRule> _transitionRuleView;
 53
 54    /// <summary>
 55    /// The stable host-owned map identifier.
 56    /// </summary>
 57    public string MapId { get; }
 58
 59    /// <summary>
 60    /// The normalized storage-neutral GridForge configuration binding.
 61    /// </summary>
 62    public NormalizedGridConfiguration GridBinding { get; }
 63
 64    /// <summary>
 65    /// Authored cells in stable lexicographic topology-local index order.
 66    /// </summary>
 334567    public IReadOnlyList<NavigationCellEntry> Cells => _cellView;
 68
 69    /// <summary>
 70    /// Directed source-owned connections in ordinal ID order.
 71    /// </summary>
 258672    public IReadOnlyList<NavigationConnection> Connections => _connectionView;
 73
 74    /// <summary>
 75    /// Directed source-owned semantic transitions in ordinal ID order.
 76    /// </summary>
 232677    public IReadOnlyList<TraversalTransitionDefinition> Transitions => _transitionView;
 78
 79    /// <summary>
 80    /// Bounded procedural semantic transition rules in canonical ordinal ID order.
 81    /// </summary>
 282    public IReadOnlyList<TraversalTransitionRule> TransitionRules => _transitionRuleView;
 83
 84    /// <summary>
 85    /// Gets the optional complete fallback cell used when no explicit cell wins.
 86    /// </summary>
 8186187    public NavigationCell? DefaultCell => _defaultCell;
 88
 2272419389    internal ReadOnlySpan<NavigationCellEntry> CellSpan => _cells;
 90
 615691    internal ReadOnlySpan<NavigationConnection> ConnectionSpan => _connections;
 92
 3460793    internal ReadOnlySpan<TraversalTransitionDefinition> TransitionSpan => _transitions;
 94
 1826395    internal ReadOnlySpan<TraversalTransitionRule> TransitionRuleSpan => _transitionRules;
 96
 497    internal int NativePortalTemplateCount => _nativePortalTemplates.Length;
 98
 201599    internal long NativePortalTemplateRetainedBytes => checked(
 2015100        24L + ((long)_nativePortalTemplates.Length * Unsafe.SizeOf<GridNavigationPortal>()));
 101
 2037102    internal NavigationMap(
 2037103        string mapId,
 2037104        NormalizedGridConfiguration gridBinding,
 2037105        NavigationCellEntry[] cells,
 2037106        NavigationConnection[] connections,
 2037107        TraversalTransitionDefinition[] transitions,
 2037108        TraversalTransitionRule[] transitionRules,
 2037109        NavigationCell? defaultCell)
 110    {
 2037111        MapId = mapId;
 2037112        GridBinding = gridBinding;
 2037113        _cells = cells;
 2037114        _connections = connections;
 2037115        _transitions = transitions;
 2037116        _transitionRules = transitionRules;
 2037117        _defaultCell = defaultCell;
 2037118        _nativePortalTemplates = CompileNativePortalTemplates(gridBinding);
 2037119        _cellView = Array.AsReadOnly(_cells);
 2037120        _connectionView = Array.AsReadOnly(_connections);
 2037121        _transitionView = Array.AsReadOnly(_transitions);
 2037122        _transitionRuleView = Array.AsReadOnly(_transitionRules);
 2037123    }
 124
 125    /// <inheritdoc/>
 126    public bool Equals(NavigationMap? other)
 127    {
 20128        if (ReferenceEquals(this, other))
 1129            return true;
 19130        if (other is null
 19131            || !string.Equals(MapId, other.MapId, StringComparison.Ordinal)
 19132            || GridBinding.Key != other.GridBinding.Key
 19133            || !_defaultCell.Equals(other._defaultCell)
 19134            || _cells.Length != other._cells.Length
 19135            || _connections.Length != other._connections.Length
 19136            || _transitions.Length != other._transitions.Length
 19137            || _transitionRules.Length != other._transitionRules.Length)
 138        {
 8139            return false;
 140        }
 141
 52142        for (int i = 0; i < _cells.Length; i++)
 143        {
 16144            if (!_cells[i].Equals(other._cells[i]))
 1145                return false;
 146        }
 147
 26148        for (int i = 0; i < _connections.Length; i++)
 149        {
 4150            if (!_connections[i].Equals(other._connections[i]))
 1151                return false;
 152        }
 153
 26154        for (int i = 0; i < _transitions.Length; i++)
 155        {
 5156            if (!_transitions[i].Equals(other._transitions[i]))
 1157                return false;
 158        }
 159
 22160        for (int i = 0; i < _transitionRules.Length; i++)
 161        {
 4162            if (!_transitionRules[i].Equals(other._transitionRules[i]))
 1163                return false;
 164        }
 165
 7166        return true;
 167    }
 168
 169    /// <inheritdoc/>
 18170    public override bool Equals(object? obj) => Equals(obj as NavigationMap);
 171
 172    /// <inheritdoc/>
 173    public override int GetHashCode()
 174    {
 12175        int mapHash = SwiftHashTools.GetDeterministicStringEqualityComparer().GetHashCode(MapId);
 12176        int hash = SwiftHashTools.CombineHashCodes(mapHash, GridBinding.Key.GetHashCode());
 12177        hash = SwiftHashTools.CombineHashCodes(hash, GridBinding.Width);
 12178        hash = SwiftHashTools.CombineHashCodes(hash, GridBinding.Height);
 12179        hash = SwiftHashTools.CombineHashCodes(hash, GridBinding.Length);
 12180        hash = SwiftHashTools.CombineHashCodes(hash, _defaultCell.GetHashCode());
 52181        for (int i = 0; i < _cells.Length; i++)
 14182            hash = SwiftHashTools.CombineHashCodes(hash, _cells[i].GetHashCode());
 32183        for (int i = 0; i < _connections.Length; i++)
 4184            hash = SwiftHashTools.CombineHashCodes(hash, _connections[i].GetHashCode());
 36185        for (int i = 0; i < _transitions.Length; i++)
 6186            hash = SwiftHashTools.CombineHashCodes(hash, _transitions[i].GetHashCode());
 32187        for (int i = 0; i < _transitionRules.Length; i++)
 4188            hash = SwiftHashTools.CombineHashCodes(hash, _transitionRules[i].GetHashCode());
 12189        return hash;
 190    }
 191
 192    internal bool ContainsCell(GridForge.Spatial.VoxelIndex index) =>
 17213193        FindCellIndex(index) >= 0;
 194
 195    internal int FindCellIndex(GridForge.Spatial.VoxelIndex index)
 196    {
 3738866197        int low = 0;
 3738866198        int high = _cells.Length - 1;
 7798721199        while (low <= high)
 200        {
 4300673201            int middle = low + ((high - low) >> 1);
 4300673202            int comparison = _cells[middle].Index.CompareTo(index);
 4300673203            if (comparison == 0)
 240818204                return middle;
 4059855205            if (comparison < 0)
 3761889206                low = middle + 1;
 207            else
 297966208                high = middle - 1;
 209        }
 210
 3498048211        return -1;
 212    }
 213
 214    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 215    internal GridNavigationPortal GetNativePortalTemplate(int directionIndex) =>
 788973216        _nativePortalTemplates[directionIndex];
 217
 218    internal static int GetNativeSurfaceDirectionCount(GridTopologyKind topology) =>
 1668688219        topology == GridTopologyKind.HexPrism
 1668688220            ? NativeHexDirections.Length
 1668688221            : NativeRectangularDirections.Length;
 222
 223    internal static VoxelIndex GetNativeSurfaceOffset(
 224        GridTopologyKind topology,
 225        int directionIndex)
 226    {
 1851904227        if (topology == GridTopologyKind.HexPrism)
 5456228            return HexDirectionUtility.GetOffset(NativeHexDirections[directionIndex]);
 229
 1846448230        RectangularDirection rectangular = NativeRectangularDirections[directionIndex];
 1846448231        (int x, int y, int z) offset = RectangularDirectionUtility.Offsets[(int)rectangular];
 1846448232        return new VoxelIndex(offset.x, offset.y, offset.z);
 233    }
 234
 235    private static GridNavigationPortal[] CompileNativePortalTemplates(
 236        NormalizedGridConfiguration binding)
 237    {
 2037238        GridTopologyKind topology = binding.Configuration.TopologyKind;
 2037239        int count = GetNativeSurfaceDirectionCount(topology);
 2037240        var templates = new GridNavigationPortal[count];
 20606241        for (int directionIndex = 0; directionIndex < count; directionIndex++)
 242        {
 8266243            VoxelIndex offset = GetNativeSurfaceOffset(topology, directionIndex);
 8266244            var sourceIndex = new VoxelIndex(
 8266245                offset.x < 0 ? 1 : 0,
 8266246                0,
 8266247                offset.z < 0 ? 1 : 0);
 8266248            var targetIndex = new VoxelIndex(
 8266249                sourceIndex.x + offset.x,
 8266250                sourceIndex.y + offset.y,
 8266251                sourceIndex.z + offset.z);
 8266252            if (!binding.IsValidIndex(sourceIndex) || !binding.IsValidIndex(targetIndex))
 253                continue;
 254
 5510255            bool hasSource = binding.TryGetCellPrism(sourceIndex, out GridCellPrism source);
 5510256            bool hasTarget = binding.TryGetCellPrism(targetIndex, out GridCellPrism target);
 5510257            bool hasPortal = GridCellGeometry.TryCreateNavigationPortal(
 5510258                source,
 5510259                target,
 5510260                out GridNavigationPortal portal);
 5510261            bool hasTranslation = Vector3d.TrySubtract(
 5510262                Vector3d.Zero,
 5510263                source.Center,
 5510264                out Vector3d translation);
 5510265            bool translated = portal.TryTranslate(translation, out templates[directionIndex]);
 266            System.Diagnostics.Debug.Assert(
 267                hasSource && hasTarget && hasPortal && hasTranslation && translated);
 268        }
 2037269        return templates;
 270    }
 271}