< Summary

Information
Class: Trailblazer.Pathing.AlternativeVoxelFinder
Assembly: Trailblazer
File(s): /home/runner/work/Trailblazer/Trailblazer/src/Trailblazer/Pathing/Search/VoxelResolution/AlternativeVoxelFinder.cs
Line coverage
95%
Covered lines: 71
Uncovered lines: 3
Coverable lines: 74
Total lines: 169
Line coverage: 95.9%
Branch coverage
91%
Covered branches: 44
Total branches: 48
Branch coverage: 91.6%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
SetQuery(...)100%11100%
GetVoxel(...)88.46%262691.42%
AdvanceRotation()100%1414100%
IsSearchCandidate(...)50%22100%
InitializeDirection()100%66100%

File(s)

/home/runner/work/Trailblazer/Trailblazer/src/Trailblazer/Pathing/Search/VoxelResolution/AlternativeVoxelFinder.cs

#LineLine coverage
 1using FixedMathSharp;
 2using GridForge.Grids;
 3using System.Diagnostics.CodeAnalysis;
 4
 5namespace Trailblazer.Pathing;
 6
 7/// <summary>
 8/// Performs a bounded same-layer fallback scan around a query position and returns
 9/// the first unblocked voxel found in deterministic star/ring order.
 10/// </summary>
 11public class AlternativeVoxelFinder
 12{
 13    private Vector3d _worldPos;
 14
 15    private Vector3d _anchorVoxelPosition;
 16
 17    private GridWorld? _world;
 18
 19    private Fixed64 _voxelSize;
 20
 21    private int _maxTestDistance;
 22
 23    private (int x, int z) _direction;
 24
 25    private int _layer;
 26
 27    /// <summary>
 28    /// Configures the fallback search around the given world-space query point for one explicit context.
 29    /// </summary>
 30    public void SetQuery(
 31        TrailblazerWorldContext context,
 32        Vector3d worldPos,
 33        Voxel anchorVoxel,
 34        int maxTestDistance)
 35    {
 1136        PathRequestContextResolver.ThrowIfUnusable(context);
 1137        _world = context.World;
 1138        _voxelSize = context.VoxelSize;
 1139        _worldPos = worldPos;
 1140        _anchorVoxelPosition = anchorVoxel.WorldPosition;
 1141        _maxTestDistance = maxTestDistance;
 1142        _layer = 1;
 1143    }
 44
 45    /// <summary>
 46    /// Attempts to find the first unblocked voxel in deterministic star/ring order on the query layer.
 47    /// </summary>
 48    public bool GetVoxel([MaybeNullWhen(false)] out Voxel nextVoxel)
 49    {
 1150        nextVoxel = null;
 1151        InitializeDirection();
 52
 1153        int layerStartX = _direction.x;
 1154        int layerStartZ = _direction.z;
 55
 1156        int iterations = 0; // <- this is for debugging
 6257        for (_layer = 1; _layer <= _maxTestDistance;)
 58        {
 5059            Vector3d checkPosition = new(
 5060                _worldPos.x + _direction.x,
 5061                _worldPos.y,
 5062                _worldPos.z + _direction.z);
 5063            if (_world != null
 5064                && _world.TryGetVoxel(checkPosition, out Voxel? checkVoxel)
 5065                && checkVoxel != null
 5066                && IsSearchCandidate(checkVoxel))
 67            {
 1068                nextVoxel = checkVoxel;
 1069                return true;
 70            }
 71
 4072            AdvanceRotation();
 73            // If we make a full loop
 4074            if (layerStartX == _direction.x && layerStartZ == _direction.z)
 75            {
 576                _layer++;
 77                // Advance a layer instead of rotation
 578                if (_direction.x > 0)
 179                    _direction.x = _layer;
 480                else if (_direction.x < 0)
 281                    _direction.x = -_layer;
 82
 583                if (_direction.z > 0)
 184                    _direction.z = _layer;
 485                else if (_direction.z < 0)
 186                    _direction.z = -_layer;
 87
 588                layerStartX = _direction.x;
 589                layerStartZ = _direction.z;
 90            }
 91
 4092            iterations++;
 4093            if (iterations > 500)
 94            {
 095                TrailblazerLogger.Channel.Error(
 096                    $"Alternative voxel search exceeded the iteration safety cap for query {_worldPos} at layer {_layer}
 097                break;
 98            }
 99        }
 100
 1101        return false;
 102    }
 103
 104    /// <summary>
 105    /// Advances the rotation clockwise
 106    /// </summary>
 107    private void AdvanceRotation()
 108    {
 109        // sides
 40110        if (_direction.x == 0)
 111        {
 10112            if (_direction.z == 1)  // up
 5113                _direction.x = _layer;
 114            else  // down
 5115                _direction.x = -_layer;
 116
 5117            return;
 118        }
 119
 30120        if (_direction.z == 0)
 121        {
 122
 10123            if (_direction.x == 1)  // right
 5124                _direction.z = -_layer;
 125            else  // left
 5126                _direction.z = _layer;
 127
 5128            return;
 129        }
 130
 131        // corners
 20132        if (_direction.x > 0)
 133        {
 134
 10135            if (_direction.z > 0)  // top-right
 5136                _direction.z = 0;
 137            else
 5138                _direction.x = 0;  // bottom-right
 139
 5140            return;
 141        }
 142
 10143        if (_direction.z > 0)  // top-left
 5144            _direction.x = 0;
 145        else
 5146            _direction.z = 0;  // bottom-left
 5147    }
 148
 149    private static bool IsSearchCandidate(Voxel voxel) =>
 50150        voxel != null
 50151        && !voxel.IsBlocked;
 152
 153    private void InitializeDirection()
 154    {
 11155        Fixed64 halfVoxel = _voxelSize * Fixed64.Half;
 11156        Fixed64 xOffsetFromCenter = _worldPos.x - (_anchorVoxelPosition.x + halfVoxel);
 11157        Fixed64 zOffsetFromCenter = _worldPos.z - (_anchorVoxelPosition.z + halfVoxel);
 158
 11159        if (xOffsetFromCenter.Abs() >= zOffsetFromCenter.Abs())
 160        {
 9161            _direction.x = xOffsetFromCenter < Fixed64.Zero ? -1 : 1;
 9162            _direction.z = 0;
 9163            return;
 164        }
 165
 2166        _direction.x = 0;
 2167        _direction.z = zOffsetFromCenter < Fixed64.Zero ? -1 : 1;
 2168    }
 169}