< Summary

Information
Class: Trailblazer.Navigation.GuidedVolumeExitPlanner
Assembly: Trailblazer
File(s): /home/runner/work/Trailblazer/Trailblazer/src/Trailblazer/Navigation/Navigator/Guidance/VolumeExit/GuidedVolumeExitPlanner.cs
Line coverage
98%
Covered lines: 170
Uncovered lines: 2
Coverable lines: 172
Total lines: 356
Line coverage: 98.8%
Branch coverage
90%
Covered branches: 60
Total branches: 66
Branch coverage: 90.9%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
TryPlan(...)90%101098.21%
TryPlanWithTransitions(...)75%1616100%
GetLocalDirectedTransitions(...)100%44100%
CreateHandoff(...)100%11100%
TryGetChartLegCost(...)95%202097.29%
TryGetDirectAStarCost(...)100%22100%
TryGetDirectFlowFieldCost(...)100%88100%
TryGetTransitionAwareChartCost(...)100%11100%
TryGetTransitionAwareChartCost(...)100%11100%
TryGetTransitionAwareChartCost(...)100%66100%
TryAssignChartCost(...)100%11100%

File(s)

/home/runner/work/Trailblazer/Trailblazer/src/Trailblazer/Navigation/Navigator/Guidance/VolumeExit/GuidedVolumeExitPlanner.cs

#LineLine coverage
 1using FixedMathSharp;
 2using GridForge.Grids;
 3using System;
 4using System.Diagnostics.CodeAnalysis;
 5using Trailblazer.Pathing;
 6
 7namespace Trailblazer.Navigation;
 8
 9/// <summary>
 10/// Plans a bounded volume-first handoff into chart-backed traversal for navigators.
 11/// </summary>
 12internal static class GuidedVolumeExitPlanner
 13{
 14    /// <summary>
 15    /// Attempts to plan a volume exit path from the origin to a target position, followed by a chart-backed leg to the 
 16    /// </summary>
 17    /// <param name="context">The world context that owns the request and transition registry.</param>
 18    /// <param name="origin"></param>
 19    /// <param name="targetPosition"></param>
 20    /// <param name="unitSize"></param>
 21    /// <param name="medium"></param>
 22    /// <param name="chartPathMode"></param>
 23    /// <param name="allowUnwalkableEndpoints"></param>
 24    /// <param name="allowTraversalTransitions"></param>
 25    /// <param name="maxClimbHeight"></param>
 26    /// <param name="aStarHeuristic"></param>
 27    /// <param name="flowFieldExtraFloodRange"></param>
 28    /// <param name="request"></param>
 29    /// <param name="handoff"></param>
 30    /// <param name="totalPathCost"></param>
 31    /// <returns>True if a valid path was found; otherwise, false.</returns>
 32    public static bool TryPlan(
 33        TrailblazerWorldContext context,
 34        Vector3d origin,
 35        Vector3d targetPosition,
 36        Fixed64 unitSize,
 37        TraversalMedium medium,
 38        SolidPathAlgorithm chartPathMode,
 39        bool allowUnwalkableEndpoints,
 40        bool allowTraversalTransitions,
 41        Fixed64 maxClimbHeight,
 42        HeuristicMethod aStarHeuristic,
 43        int flowFieldExtraFloodRange,
 44        [NotNullWhen(true)] out VolumePathRequest? request,
 45        out GuidedVolumeExitHandoff? handoff,
 46        out int totalPathCost)
 47    {
 3348        PathRequestContextResolver.ThrowIfUnusable(context);
 3349        request = null;
 3350        handoff = null;
 3351        totalPathCost = 0;
 52
 3353        if (chartPathMode != SolidPathAlgorithm.AStar
 3354            && chartPathMode != SolidPathAlgorithm.FlowField)
 55        {
 156            return false;
 57        }
 58
 3259        TraversalTransition bestTransition = default;
 3260        VolumePathRequest? bestRequest = null;
 3261        int bestTotalCost = int.MaxValue;
 62
 3263        if (!TryPlanWithTransitions(
 3264                GetLocalDirectedTransitions(context, targetPosition, medium),
 3265                context,
 3266                origin,
 3267                targetPosition,
 3268                unitSize,
 3269                chartPathMode,
 3270                allowUnwalkableEndpoints,
 3271                allowTraversalTransitions,
 3272                maxClimbHeight,
 3273                aStarHeuristic,
 3274                flowFieldExtraFloodRange,
 3275                ref bestTransition,
 3276                ref bestRequest,
 3277                ref bestTotalCost)
 3278            && !TryPlanWithTransitions(
 3279                context.Transitions.GetDirectedTransitions(medium, TraversalMedium.Solid),
 3280                context,
 3281                origin,
 3282                targetPosition,
 3283                unitSize,
 3284                chartPathMode,
 3285                allowUnwalkableEndpoints,
 3286                allowTraversalTransitions,
 3287                maxClimbHeight,
 3288                aStarHeuristic,
 3289                flowFieldExtraFloodRange,
 3290                ref bestTransition,
 3291                ref bestRequest,
 3292                ref bestTotalCost))
 93        {
 594            return false;
 95        }
 96
 2797        if (bestRequest == null)
 098            return false;
 99
 27100        request = bestRequest;
 27101        totalPathCost = bestTotalCost;
 27102        handoff = CreateHandoff(
 27103            bestTransition,
 27104            context,
 27105            targetPosition,
 27106            chartPathMode,
 27107            allowUnwalkableEndpoints,
 27108            allowTraversalTransitions,
 27109            maxClimbHeight,
 27110            aStarHeuristic,
 27111            flowFieldExtraFloodRange);
 27112        return true;
 113    }
 114
 115
 116    private static bool TryPlanWithTransitions(
 117        TraversalTransition[] transitions,
 118        TrailblazerWorldContext context,
 119        Vector3d origin,
 120        Vector3d targetPosition,
 121        Fixed64 unitSize,
 122        SolidPathAlgorithm chartPathMode,
 123        bool allowUnwalkableEndpoints,
 124        bool allowTraversalTransitions,
 125        Fixed64 maxClimbHeight,
 126        HeuristicMethod aStarHeuristic,
 127        int flowFieldExtraFloodRange,
 128        ref TraversalTransition bestTransition,
 129        ref VolumePathRequest? bestRequest,
 130        ref int bestTotalCost)
 131    {
 37132        if (transitions == null || transitions.Length == 0)
 5133            return false;
 134
 32135        bool foundPlan = false;
 136
 128137        for (int i = 0; i < transitions.Length; i++)
 138        {
 32139            TraversalTransition transition = transitions[i];
 32140            VolumePathRequest? volumeRequest = VolumePathRequest.Create(
 32141                context,
 32142                origin,
 32143                transition.Source.Position,
 32144                unitSize,
 32145                aStarHeuristic,
 32146                allowUnwalkableEndpoints,
 32147                transition.Source.Medium);
 32148            if (volumeRequest == null)
 149                continue;
 150
 32151            int volumeCost = 0;
 32152            if (!volumeRequest.HasZeroDisplacement)
 153            {
 31154                VolumeSurveyResult volumeResult = context.Pathing.State.GuideState.VolumeSurveyor.FindPath(volumeRequest
 31155                if (!volumeResult.HasPath)
 156                    continue;
 157
 31158                volumeCost = volumeResult.Waypoints![^1].PathCost;
 159            }
 160
 32161            if (!TryGetChartLegCost(
 32162                context,
 32163                transition.Destination.Position,
 32164                targetPosition,
 32165                unitSize,
 32166                chartPathMode,
 32167                allowUnwalkableEndpoints,
 32168                allowTraversalTransitions,
 32169                maxClimbHeight,
 32170                aStarHeuristic,
 32171                flowFieldExtraFloodRange,
 32172                out int chartCost))
 173            {
 174                continue;
 175            }
 176
 27177            int totalCost = volumeCost + transition.PathCostModifier + chartCost;
 27178            if (totalCost >= bestTotalCost)
 179                continue;
 180
 27181            bestTotalCost = totalCost;
 27182            bestTransition = transition;
 27183            bestRequest = volumeRequest;
 27184            foundPlan = true;
 185        }
 186
 32187        return foundPlan;
 188    }
 189
 190    private static TraversalTransition[] GetLocalDirectedTransitions(
 191        TrailblazerWorldContext context,
 192        Vector3d targetPosition,
 193        TraversalMedium medium)
 194    {
 32195        if (!context.World.TryGetVoxel(targetPosition, out Voxel? targetVoxel)
 32196            || targetVoxel == null)
 1197            return Array.Empty<TraversalTransition>();
 198
 31199        return context.Transitions.GetDirectedTransitionsToDestinationGrid(
 31200            targetVoxel.GridIndex,
 31201            medium,
 31202            TraversalMedium.Solid);
 203    }
 204
 205    private static GuidedVolumeExitHandoff CreateHandoff(
 206        TraversalTransition transition,
 207        TrailblazerWorldContext context,
 208        Vector3d targetPosition,
 209        SolidPathAlgorithm chartPathMode,
 210        bool allowUnwalkableEndpoints,
 211        bool allowTraversalTransitions,
 212        Fixed64 maxClimbHeight,
 213        HeuristicMethod aStarHeuristic,
 214        int flowFieldExtraFloodRange)
 215    {
 27216        return new GuidedVolumeExitHandoff
 27217        {
 27218            TransitionId = transition.Id,
 27219            Context = context,
 27220            ChartOriginPosition = transition.Destination.Position,
 27221            TargetPosition = targetPosition,
 27222            ChartPathMode = chartPathMode,
 27223            AllowUnwalkableEndpoints = allowUnwalkableEndpoints,
 27224            AllowTraversalTransitions = allowTraversalTransitions,
 27225            MaxClimbHeight = maxClimbHeight,
 27226            AStarHeuristic = aStarHeuristic,
 27227            FlowFieldExtraFloodRange = flowFieldExtraFloodRange,
 27228            IsRequestingClimb = transition.PreserveClimbIntentOnFollowup
 27229        };
 230    }
 231
 232    private static bool TryGetChartLegCost(
 233        TrailblazerWorldContext context,
 234        Vector3d origin,
 235        Vector3d targetPosition,
 236        Fixed64 unitSize,
 237        SolidPathAlgorithm chartPathMode,
 238        bool allowUnwalkableEndpoints,
 239        bool allowTraversalTransitions,
 240        Fixed64 maxClimbHeight,
 241        HeuristicMethod aStarHeuristic,
 242        int flowFieldExtraFloodRange,
 243        out int chartCost)
 244    {
 32245        chartCost = 0;
 246
 247        switch (chartPathMode)
 248        {
 249            case SolidPathAlgorithm.FlowField:
 12250                FlowFieldPathRequest? flowFieldRequest = FlowFieldPathRequest.Create(
 12251                    context,
 12252                    origin,
 12253                    targetPosition,
 12254                    unitSize,
 12255                    allowUnwalkableEndpoints,
 12256                    allowTraversalTransitions);
 12257                if (flowFieldRequest == null)
 0258                    return false;
 259
 12260                flowFieldRequest.MaxClimbHeight = maxClimbHeight;
 12261                flowFieldRequest.ExtraFloodRange = flowFieldExtraFloodRange;
 12262                if (flowFieldRequest.HasZeroDisplacement)
 1263                    return true;
 264
 11265                if (TryGetDirectFlowFieldCost(flowFieldRequest, out chartCost))
 8266                    return true;
 267
 3268                if (!allowTraversalTransitions)
 2269                    return false;
 270
 1271                return TryGetTransitionAwareChartCost(flowFieldRequest, out chartCost);
 272
 273            case SolidPathAlgorithm.AStar:
 274            default:
 20275                AStarPathRequest? aStarRequest = AStarPathRequest.Create(
 20276                    context,
 20277                    origin,
 20278                    targetPosition,
 20279                    unitSize,
 20280                    aStarHeuristic,
 20281                    allowUnwalkableEndpoints,
 20282                    allowTraversalTransitions);
 20283                if (aStarRequest == null)
 1284                    return false;
 285
 19286                aStarRequest.MaxClimbHeight = maxClimbHeight;
 19287                if (aStarRequest.HasZeroDisplacement)
 4288                    return true;
 289
 15290                if (TryGetDirectAStarCost(aStarRequest, out chartCost))
 1291                    return true;
 292
 14293                if (!allowTraversalTransitions)
 2294                    return false;
 295
 12296                return TryGetTransitionAwareChartCost(aStarRequest, out chartCost);
 297        }
 298    }
 299
 300    private static bool TryGetDirectAStarCost(
 301        AStarPathRequest request,
 302        out int chartCost)
 303    {
 15304        chartCost = 0;
 305
 15306        AStarSurveyResult aStarResult = request.Context.Pathing.State.GuideState.AStarSurveyor.FindPath(request);
 15307        return aStarResult.HasPath
 15308            && TryAssignChartCost(aStarResult.Waypoints[^1].PathCost, out chartCost);
 309    }
 310
 311    private static bool TryGetDirectFlowFieldCost(
 312        FlowFieldPathRequest request,
 313        out int chartCost)
 314    {
 11315        chartCost = 0;
 316
 11317        FlowFieldSurveyResult flowFieldResult = request.Context.Pathing.State.GuideState.FlowFieldSurveyor.FindPath(requ
 11318        return flowFieldResult.HasPath
 11319            && flowFieldResult.Fields != null
 11320            && request.StartNode != null
 11321            && flowFieldResult.Fields.TryGetValue(request.StartNode.WorldIndex, out FlowField startField)
 11322            && TryAssignChartCost(startField.PathCost, out chartCost);
 323    }
 324
 325    private static bool TryGetTransitionAwareChartCost(
 326        AStarPathRequest request,
 327        out int chartCost)
 328    {
 12329        return TryGetTransitionAwareChartCost(HybridPathRequest.CreateFromAStar(request), out chartCost);
 330    }
 331
 332    private static bool TryGetTransitionAwareChartCost(
 333        FlowFieldPathRequest request,
 334        out int chartCost)
 335    {
 1336        return TryGetTransitionAwareChartCost(HybridPathRequest.CreateFromFlowField(request), out chartCost);
 337    }
 338
 339    internal static bool TryGetTransitionAwareChartCost(
 340        HybridPathRequest? hybridRequest,
 341        out int chartCost)
 342    {
 16343        chartCost = 0;
 344
 16345        HybridRoutePlan? routePlan = hybridRequest?.RoutePlan;
 16346        return routePlan != null
 16347            && routePlan.DirectedTransitions.Length > 0
 16348            && TryAssignChartCost(routePlan.TotalPathCost, out chartCost);
 349    }
 350
 351    private static bool TryAssignChartCost(int cost, out int chartCost)
 352    {
 23353        chartCost = cost;
 23354        return true;
 355    }
 356}

Methods/Properties

TryPlan(Trailblazer.TrailblazerWorldContext,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,Trailblazer.TraversalMedium,Trailblazer.Navigation.SolidPathAlgorithm,System.Boolean,System.Boolean,FixedMathSharp.Fixed64,Trailblazer.Pathing.HeuristicMethod,System.Int32,Trailblazer.Pathing.VolumePathRequest&,Trailblazer.Navigation.GuidedVolumeExitHandoff&,System.Int32&)
TryPlanWithTransitions(Trailblazer.Pathing.TraversalTransition[],Trailblazer.TrailblazerWorldContext,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,Trailblazer.Navigation.SolidPathAlgorithm,System.Boolean,System.Boolean,FixedMathSharp.Fixed64,Trailblazer.Pathing.HeuristicMethod,System.Int32,Trailblazer.Pathing.TraversalTransition&,Trailblazer.Pathing.VolumePathRequest&,System.Int32&)
GetLocalDirectedTransitions(Trailblazer.TrailblazerWorldContext,FixedMathSharp.Vector3d,Trailblazer.TraversalMedium)
CreateHandoff(Trailblazer.Pathing.TraversalTransition,Trailblazer.TrailblazerWorldContext,FixedMathSharp.Vector3d,Trailblazer.Navigation.SolidPathAlgorithm,System.Boolean,System.Boolean,FixedMathSharp.Fixed64,Trailblazer.Pathing.HeuristicMethod,System.Int32)
TryGetChartLegCost(Trailblazer.TrailblazerWorldContext,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64,Trailblazer.Navigation.SolidPathAlgorithm,System.Boolean,System.Boolean,FixedMathSharp.Fixed64,Trailblazer.Pathing.HeuristicMethod,System.Int32,System.Int32&)
TryGetDirectAStarCost(Trailblazer.Pathing.AStarPathRequest,System.Int32&)
TryGetDirectFlowFieldCost(Trailblazer.Pathing.FlowFieldPathRequest,System.Int32&)
TryGetTransitionAwareChartCost(Trailblazer.Pathing.AStarPathRequest,System.Int32&)
TryGetTransitionAwareChartCost(Trailblazer.Pathing.FlowFieldPathRequest,System.Int32&)
TryGetTransitionAwareChartCost(Trailblazer.Pathing.HybridPathRequest,System.Int32&)
TryAssignChartCost(System.Int32,System.Int32&)