< Summary

Information
Class: Trailblazer.Pathing.NavigationVolumeAnchorEvaluator
Assembly: Trailblazer
File(s): /home/runner/work/Trailblazer/Trailblazer/src/Trailblazer/Pathing/Graph/NavigationVolumeAnchorEvaluator.cs
Line coverage
100%
Covered lines: 166
Uncovered lines: 0
Coverable lines: 166
Total lines: 303
Line coverage: 100%
Branch coverage
100%
Covered branches: 51
Total branches: 51
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
Evaluate(...)100%22100%
EvaluateSegment(...)100%11100%
Trace(...)100%2222100%
IsTraceGenerationCurrent(...)100%22100%
ResolveTraceStatus(...)100%1515100%
ResolveFinalStatus(...)100%22100%
FilterPassableMedia(...)100%88100%

File(s)

/home/runner/work/Trailblazer/Trailblazer/src/Trailblazer/Pathing/Graph/NavigationVolumeAnchorEvaluator.cs

#LineLine coverage
 1//=======================================================================
 2// NavigationVolumeAnchorEvaluator.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 FixedMathSharp;
 10using GridForge.Grids;
 11using GridForge.Spatial;
 12using GridForge.Utility;
 13
 14namespace Trailblazer.Pathing;
 15
 16internal enum NavigationVolumeAnchorStatus : byte
 17{
 18    Success,
 19    Unavailable,
 20    BudgetExceeded,
 21    CostOverflow,
 22    CapacityExceeded,
 23    Stale
 24}
 25
 26/// <summary>Certifies one centered volume-body placement through GridForge's prism union.</summary>
 27internal readonly struct NavigationVolumeAnchorEvaluator
 28{
 29    private readonly GridWorld _world;
 30    private readonly NavigationWorldGraph _graph;
 31    private readonly NavigationAgentProfile _profile;
 32    private readonly NavigationAreaPolicy _areaPolicy;
 33    private readonly NavigationRayWorkspace _workspace;
 34
 35    internal NavigationVolumeAnchorEvaluator(
 36        GridWorld world,
 37        NavigationWorldGraph graph,
 38        NavigationAgentProfile profile,
 39        NavigationAreaPolicy areaPolicy,
 40        NavigationRayWorkspace workspace)
 41    {
 302542        _world = world;
 302543        _graph = graph;
 302544        _profile = profile;
 302545        _areaPolicy = areaPolicy;
 302546        _workspace = workspace;
 302547    }
 48
 49    internal NavigationVolumeAnchorStatus Evaluate(
 50        NavigationNodeRef node,
 51        TraversalMedia requestedMedia,
 52        NavigationWorkMeter meter,
 53        NavigationDependencyWorkspace dependencies,
 54        out Vector3d footAnchor,
 55        out TraversalMedia qualifyingMedia)
 56    {
 43157        footAnchor = default;
 43158        qualifyingMedia = TraversalMedia.None;
 43159        bool hasAddress = _graph.TryGetNodeAddress(
 43160            node,
 43161            out NavigationCellAddress address);
 43162        bool hasState = _graph.TryGetRawNodeState(node, out NavigationNodeState state);
 63        System.Diagnostics.Debug.Assert(hasAddress && hasState,
 64            "Volume anchor evaluation receives a node owned by its immutable graph.");
 43165        if (!state.TryGetCenteredVolumeFootAnchor(
 43166                _profile.Shape.Height,
 43167                out footAnchor))
 68        {
 169            return NavigationVolumeAnchorStatus.CostOverflow;
 70        }
 71
 43072        return Trace(
 43073            address,
 43074            address,
 43075            footAnchor,
 43076            footAnchor,
 43077            requestedMedia,
 43078            meter,
 43079            dependencies,
 43080            out qualifyingMedia);
 81    }
 82
 83    internal NavigationVolumeAnchorStatus EvaluateSegment(
 84        NavigationMediumStateRef source,
 85        NavigationMediumStateRef target,
 86        Vector3d sourceFootAnchor,
 87        Vector3d targetFootAnchor,
 88        NavigationWorkMeter meter,
 89        NavigationDependencyWorkspace dependencies)
 90    {
 167091        bool hasSourceAddress = _graph.TryGetNodeAddress(
 167092            source.Node,
 167093            out NavigationCellAddress sourceAddress);
 167094        bool hasTargetAddress = _graph.TryGetNodeAddress(
 167095            target.Node,
 167096            out NavigationCellAddress targetAddress);
 97        System.Diagnostics.Debug.Assert(
 98            source.Medium == target.Medium && hasSourceAddress && hasTargetAddress);
 99
 1670100        return Trace(
 1670101            sourceAddress,
 1670102            targetAddress,
 1670103            sourceFootAnchor,
 1670104            targetFootAnchor,
 1670105            NavigationCell.ToMedia(source.Medium),
 1670106            meter,
 1670107            dependencies,
 1670108            out _);
 109    }
 110
 111    private NavigationVolumeAnchorStatus Trace(
 112        NavigationCellAddress sourceAddress,
 113        NavigationCellAddress targetAddress,
 114        Vector3d sourceFootAnchor,
 115        Vector3d targetFootAnchor,
 116        TraversalMedia requestedMedia,
 117        NavigationWorkMeter meter,
 118        NavigationDependencyWorkspace dependencies,
 119        out TraversalMedia qualifyingMedia)
 120    {
 2100121        qualifyingMedia = TraversalMedia.None;
 2100122        ulong before = _world.ChangeSequence;
 2100123        _graph.TryGetMap(sourceAddress.MapId, out NavigationMapInstance? sourceInstance);
 2100124        _graph.TryGetMap(targetAddress.MapId, out NavigationMapInstance? targetInstance);
 125        System.Diagnostics.Debug.Assert(sourceInstance != null && targetInstance != null);
 126
 2100127        NavigationGridGenerationIdentity sourceIdentity = sourceInstance!.GridIdentity;
 2100128        NavigationGridGenerationIdentity targetIdentity = targetInstance!.GridIdentity;
 2100129        var sourceCell = new WorldVoxelIndex(
 2100130            sourceIdentity.WorldSpawnToken,
 2100131            sourceIdentity.GridIndex,
 2100132            sourceIdentity.GridSpawnToken,
 2100133            sourceAddress.Index);
 2100134        var targetCell = new WorldVoxelIndex(
 2100135            targetIdentity.WorldSpawnToken,
 2100136            targetIdentity.GridIndex,
 2100137            targetIdentity.GridSpawnToken,
 2100138            targetAddress.Index);
 2100139        int gridLimit = Math.Min(_workspace.MapCapacity, meter.RemainingLookupProbes);
 2100140        int addressLimit = Math.Min(
 2100141            _workspace.CoveredAddressCapacity,
 2100142            meter.RemainingCoveredVoxelIntervals);
 2100143        long candidateWorkLimit = meter.RemainingGridCandidateWork;
 2100144        meter.RecordVolumeUnionCheck();
 2100145        GridNavigationBodyTraceReport report = GridTracer.TraceNavigationBodyInto(
 2100146            _world,
 2100147            sourceCell,
 2100148            targetCell,
 2100149            sourceFootAnchor,
 2100150            targetFootAnchor,
 2100151            _profile.Shape.Radius,
 2100152            _profile.Shape.Height,
 2100153            _workspace.BodyTraceCells,
 2100154            _workspace.BodyTraceScratch,
 2100155            gridLimit,
 2100156            addressLimit,
 2100157            _workspace.CoveredAddressCapacity,
 2100158            candidateWorkLimit);
 2100159        bool consumedLookups = meter.TryConsumeLookupProbes(report.GridCandidateCount);
 2100160        bool consumedAddresses =
 2100161            meter.TryConsumeCoveredVoxelIntervals(report.AddressCandidateCount);
 162        System.Diagnostics.Debug.Assert(consumedLookups && consumedAddresses);
 163        System.Diagnostics.Debug.Assert(
 164            report.Status != GridNavigationBodyTraceStatus.OutputLimitExceeded,
 165            "the address ceiling never exceeds the shared body-trace output capacity");
 2100166        NavigationVolumeAnchorStatus traceStatus = ResolveTraceStatus(
 2100167            before,
 2100168            _world.ChangeSequence,
 2100169            report.Status,
 2100170            gridLimit,
 2100171            _workspace.MapCapacity,
 2100172            addressLimit,
 2100173            _workspace.CoveredAddressCapacity);
 2100174        if (traceStatus is not NavigationVolumeAnchorStatus.Success
 2100175            and not NavigationVolumeAnchorStatus.Unavailable)
 176        {
 27177            return traceStatus;
 178        }
 179
 2073180        qualifyingMedia = requestedMedia & TraversalMedia.AnyVolume;
 21234181        for (int i = 0; i < _workspace.BodyTraceCells.Count; i++)
 182        {
 8554183            GridNavigationBodyTraceCell traceCell = _workspace.BodyTraceCells[i];
 8554184            if (!_graph.TryGetMapId(traceCell.ConfigurationKey, out string mapId))
 1185                return NavigationVolumeAnchorStatus.Stale;
 8553186            bool hasTraceInstance = _graph.TryGetMap(
 8553187                mapId,
 8553188                out NavigationMapInstance traceInstance);
 189            System.Diagnostics.Debug.Assert(
 190                hasTraceInstance,
 191                "the immutable configuration index and map directory are built and replaced together");
 8553192            if (!IsTraceGenerationCurrent(
 8553193                    traceInstance.GridIdentity.Matches(
 8553194                        traceCell.Cell.WorldSpawnToken,
 8553195                        traceCell.Cell.GridIndex,
 8553196                        traceCell.Cell.GridSpawnToken),
 8553197                    traceInstance.GridLastChangeSequence,
 8553198                    traceCell.GridLastChangeSequence))
 199            {
 3200                return NavigationVolumeAnchorStatus.Stale;
 201            }
 202
 8550203            var traceAddress = new NavigationCellAddress(mapId, traceCell.Cell.VoxelIndex);
 8550204            if (!_graph.TryGetNodeRef(traceAddress, out NavigationNodeRef traceNode))
 5205                return NavigationVolumeAnchorStatus.Stale;
 8545206            if (!dependencies.TryRecordPage(
 8545207                    mapId,
 8545208                    traceNode.CellSlot / NavigationSemanticPage.SlotCount))
 209            {
 1210                return NavigationVolumeAnchorStatus.CapacityExceeded;
 211            }
 8544212            if (traceCell.Role != GridNavigationBodyTraceCellRole.RequiredCoverage)
 213                continue;
 8543214            if (!traceCell.IsPhysicallyPresent)
 215            {
 5216                qualifyingMedia = TraversalMedia.None;
 5217                continue;
 218            }
 219
 8538220            qualifyingMedia = FilterPassableMedia(traceNode, qualifyingMedia);
 221        }
 2063222        NavigationVolumeAnchorStatus result = traceStatus == NavigationVolumeAnchorStatus.Unavailable
 2063223            || qualifyingMedia == TraversalMedia.None
 2063224            ? NavigationVolumeAnchorStatus.Unavailable
 2063225            : NavigationVolumeAnchorStatus.Success;
 2063226        return ResolveFinalStatus(before, _world.ChangeSequence, result);
 227    }
 228
 229    internal static bool IsTraceGenerationCurrent(
 230        bool identityMatches,
 231        ulong instanceLastChangeSequence,
 232        ulong traceLastChangeSequence) =>
 8557233        identityMatches && instanceLastChangeSequence == traceLastChangeSequence;
 234
 235    internal static NavigationVolumeAnchorStatus ResolveTraceStatus(
 236        ulong worldSequenceBefore,
 237        ulong worldSequenceAfter,
 238        GridNavigationBodyTraceStatus status,
 239        int gridLimit,
 240        int mapCapacity,
 241        int addressLimit,
 242        int coveredAddressCapacity)
 243    {
 2110244        if (worldSequenceBefore != worldSequenceAfter)
 1245            return NavigationVolumeAnchorStatus.Stale;
 2109246        return status switch
 2109247        {
 2109248            GridNavigationBodyTraceStatus.Complete =>
 2023249                NavigationVolumeAnchorStatus.Success,
 2109250            GridNavigationBodyTraceStatus.IncompletePhysicalCoverage =>
 10251                NavigationVolumeAnchorStatus.Unavailable,
 2109252            GridNavigationBodyTraceStatus.InvalidOrUnrepresentableGeometry =>
 43253                NavigationVolumeAnchorStatus.Unavailable,
 2109254            GridNavigationBodyTraceStatus.ArithmeticOverflow =>
 4255                NavigationVolumeAnchorStatus.CostOverflow,
 2109256            GridNavigationBodyTraceStatus.GridCandidateLimitExceeded =>
 13257                gridLimit < mapCapacity
 13258                    ? NavigationVolumeAnchorStatus.BudgetExceeded
 13259                    : NavigationVolumeAnchorStatus.CapacityExceeded,
 2109260            GridNavigationBodyTraceStatus.AddressLimitExceeded =>
 14261                addressLimit < coveredAddressCapacity
 14262                    ? NavigationVolumeAnchorStatus.BudgetExceeded
 14263                    : NavigationVolumeAnchorStatus.CapacityExceeded,
 2264            _ => NavigationVolumeAnchorStatus.BudgetExceeded
 2109265        };
 266    }
 267
 268    internal static NavigationVolumeAnchorStatus ResolveFinalStatus(
 269        ulong worldSequenceBefore,
 270        ulong worldSequenceAfter,
 271        NavigationVolumeAnchorStatus result) =>
 2065272        worldSequenceBefore == worldSequenceAfter
 2065273            ? result
 2065274            : NavigationVolumeAnchorStatus.Stale;
 275
 276    private TraversalMedia FilterPassableMedia(
 277        NavigationNodeRef node,
 278        TraversalMedia media)
 279    {
 8538280        if ((media & TraversalMedia.Gas) != 0
 8538281            && !new TraversalEvaluator(
 8538282                    _graph,
 8538283                    _profile,
 8538284                    _areaPolicy,
 8538285                    TraversalMedium.Gas)
 8538286                .TryGetPassableNodeState(node, out _))
 287        {
 6288            media &= ~TraversalMedia.Gas;
 289        }
 8538290        if ((media & TraversalMedia.Liquid) != 0
 8538291            && !new TraversalEvaluator(
 8538292                    _graph,
 8538293                    _profile,
 8538294                    _areaPolicy,
 8538295                    TraversalMedium.Liquid)
 8538296                .TryGetPassableNodeState(node, out _))
 297        {
 5298            media &= ~TraversalMedia.Liquid;
 299        }
 8538300        return media;
 301    }
 302
 303}