< Summary

Line coverage
100%
Covered lines: 1329
Uncovered lines: 0
Coverable lines: 1329
Total lines: 3027
Line coverage: 100%
Branch coverage
100%
Covered branches: 576
Total branches: 576
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
File 1: TryBeginCoveredAddresses(...)100%11100%
File 1: TryBeginCoveredAddresses(...)100%11100%
File 1: AdvanceCoveredAddresses(...)100%44100%
File 1: TryBeginCoveredAddressesCore(...)100%22100%
File 1: AdvanceCoveredAddressesCore(...)100%4444100%
File 1: TryBindCoveredAddressGeneration(...)100%2020100%
File 1: IsCoveredAddressCursorCurrent(...)100%66100%
File 1: IsCoveredAddress(...)100%1212100%
File 1: AdvanceCoveredAddress(...)100%66100%
File 1: ConsumeLookupProbe(...)100%22100%
File 1: ConsumeAddressProbe(...)100%22100%
File 2: .cctor()100%11100%
File 2: get_ChangeSequence()100%11100%
File 2: EnterReadLock()100%11100%
File 2: ExitReadLock()100%11100%
File 2: get_IsWriteLockHeld()100%11100%
File 2: .ctor(...)100%11100%
File 2: add_OnActiveGridAdded(...)100%11100%
File 2: remove_OnActiveGridAdded(...)100%11100%
File 2: add_OnActiveGridRemoved(...)100%11100%
File 2: remove_OnActiveGridRemoved(...)100%11100%
File 2: add_OnActiveGridChange(...)100%11100%
File 2: remove_OnActiveGridChange(...)100%11100%
File 2: add_OnChangeCommitted(...)100%11100%
File 2: remove_OnChangeCommitted(...)100%11100%
File 2: add_OnReset(...)100%11100%
File 2: remove_OnReset(...)100%11100%
File 2: Reset(...)100%1010100%
File 2: NotifyResetHandlers()100%66100%
File 2: ReleaseActiveGrids()100%22100%
File 2: Dispose()100%11100%
File 2: AllocateObstacleToken()100%22100%
File 2: TryCaptureNavigationBaseline(...)100%44100%
File 2: ExecuteNavigationMaintenanceSnapshot(...)100%22100%
File 2: BeginBoundaryContacts(...)100%22100%
File 2: TryBeginBoundaryContacts(...)100%44100%
File 2: AdvanceBoundaryContacts(...)100%22100%
File 2: AdvanceBoundaryContacts(...)100%22100%
File 2: AdvanceBoundaryContactsUnderGate(...)100%22100%
File 2: AdvanceBoundaryContactsCore(...)100%4848100%
File 2: IsBoundaryContactCursorCurrent(...)100%1616100%
File 2: TryAdvanceFilteredBoundaryContactPair(...)100%2222100%
File 2: BindBoundaryContactPair(...)100%11100%
File 2: ProbeBoundaryContactTarget(...)100%44100%
File 2: AdvanceBoundaryContactAddress(...)100%66100%
File 2: TryCreateTopologyPrism(...)100%11100%
File 2: GetTrailingZeroCount(...)100%22100%
File 2: ConsumeBoundaryContactProbe(...)100%22100%
File 2: ThrowIfNavigationMaintenanceUnavailable()100%11100%
File 2: TryEnterNavigationMaintenanceSnapshot()100%22100%
File 2: ExitNavigationMaintenanceSnapshot()100%11100%
File 2: WaitForPublishedChangePrefix()100%22100%
File 2: TryCaptureNavigationBaselineCore(...)100%88100%
File 2: AreNavigationBaselineAddressesValid(...)100%88100%
File 2: TryAddGrid(...)100%11100%
File 2: TryAddGrid(...)100%11100%
File 2: TryAddGrid(...)100%11100%
File 2: TryAddGridCore(...)100%1414100%
File 2: TryRemoveGrid(...)100%1010100%
File 2: CanAddGrid()100%88100%
File 2: TryPrepareConfiguredVoxels(...)100%44100%
File 2: TryPrepareConfiguredVoxelMask(...)100%2626100%
File 2: TryPrepareConfiguredVoxelIndices(...)100%1212100%
File 2: IsConfiguredVoxelInBounds(...)100%44100%
File 2: CompactPreparedVoxels(...)100%88100%
File 2: UpdateMaxTopologyCellEdge(...)100%22100%
File 2: RecalculateMaxTopologyCellEdgeIfNeeded(...)100%66100%
File 2: TryFindExistingGridUnsafe(...)100%44100%
File 2: RegisterGrid(...)100%1212100%
File 2: UnregisterGrid(...)100%11100%
File 2: TryCreateBoundaryContactEnvelope(...)100%44100%
File 2: InsertBoundaryContactPair(...)100%66100%
File 2: RemoveBoundaryContactPairs(...)100%88100%
File 2: RemoveBoundaryContactIncident(...)100%44100%
File 2: GetOrCreateBoundaryContactRow(...)100%22100%
File 2: InsertSorted(...)100%11100%
File 2: RemoveSorted(...)100%11100%
File 2: FindSortedIndex(...)100%44100%
File 2: SetBoundaryContactSource(...)100%44100%
File 2: ClearBoundaryContactSource(...)100%66100%
File 2: ReleaseBoundaryContactPairs()100%44100%
File 2: UnlinkGridNeighbors(...)100%66100%
File 2: CollectGridCandidates(...)100%1212100%
File 2: CreateExpandedBounds(...)100%11100%
File 2: TryGetGrid(...)100%22100%
File 2: TryGetGrid(...)100%66100%
File 2: TryGetGrid(...)100%11100%
File 2: TryGetGrid(...)100%11100%
File 2: TryGetClosestGrid(...)100%1212100%
File 2: TryGetClosestGrid(...)100%11100%
File 2: TryGetClosestGrid(...)100%11100%
File 2: TryGetGrid(...)100%66100%
File 2: TryGetGridAndVoxel(...)100%22100%
File 2: TryGetGridAndVoxel(...)100%11100%
File 2: TryGetGridAndVoxel(...)100%11100%
File 2: TryGetClosestGridAndVoxel(...)100%2424100%
File 2: TryGetClosestGridAndVoxel(...)100%11100%
File 2: TryGetClosestGridAndVoxel(...)100%11100%
File 2: TryGetGridAndVoxel(...)100%22100%
File 2: TryGetVoxel(...)100%22100%
File 2: TryGetVoxel(...)100%11100%
File 2: TryGetVoxel(...)100%11100%
File 2: TryGetClosestVoxel(...)100%22100%
File 2: TryGetClosestVoxel(...)100%11100%
File 2: TryGetClosestVoxel(...)100%11100%
File 2: TryGetVoxel(...)100%22100%
File 2: IncrementGridVersion(...)100%88100%
File 2: FindOverlappingGrids(...)100%11100%
File 2: FindOverlappingGridsInto(...)100%66100%
File 2: MatchesTopologyKind(...)100%22100%
File 2: IsBetterClosestVoxel(...)100%66100%
File 2: GetDistanceSquaredToBounds(...)100%11100%
File 2: GetAxisDistanceToBounds(...)100%44100%
File 2: CanResolveGrid(...)100%66100%
File 2: CanResolveActiveGrid()100%44100%
File 2: IsGridIndexInActiveRange(...)100%22100%
File 2: IsGridIndexAllocated(...)100%44100%
File 2: CanResolvePosition()100%44100%
File 2: TryGetContainingGrid(...)100%44100%
File 2: TryAddOverlappingGrid(...)100%22100%
File 2: ResolveSpatialGridCellSize(...)100%44100%
File 2: CreateGridEventInfo(...)100%11100%
File 2: CreateGridEventInfo(...)100%11100%
File 2: AllocateChangeStamp()100%11100%
File 2: EnqueueCommittedChange(...)100%22100%
File 2: DrainCommittedChanges()100%88100%
File 2: NotifyActiveGridAdded(...)100%66100%
File 2: NotifyActiveGridRemoved(...)100%66100%
File 2: NotifyActiveGridChange(...)100%66100%
File 2: NotifyChangeCommitted(...)100%66100%

File(s)

/home/runner/work/GridForge/GridForge/src/GridForge/Grids/Managers/GridWorld.CoveredAddresses.cs

#LineLine coverage
 1//=======================================================================
 2// GridWorld.CoveredAddresses.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.Configuration;
 11using GridForge.Grids.Topology;
 12using GridForge.Spatial;
 13using SwiftCollections.Utility;
 14
 15namespace GridForge.Grids;
 16
 17public sealed partial class GridWorld
 18{
 19    /// <summary>
 20    /// Begins a bounded covered-address query for a declared number of exact eligible grid generations.
 21    /// </summary>
 22    /// <param name="cursor">The caller-owned cursor and preallocated generation storage.</param>
 23    /// <param name="boundsMin">One world-space corner of the coverage bounds.</param>
 24    /// <param name="boundsMax">The opposite world-space corner of the coverage bounds.</param>
 25    /// <param name="eligibleGenerationCount">The exact number of canonical generation inputs that will follow.</param>
 26    /// <returns>True when the declared count fits the cursor; otherwise false and the cursor is stale.</returns>
 27    public bool TryBeginCoveredAddresses(
 28        GridCoveredAddressCursor cursor,
 29        Vector3d boundsMin,
 30        Vector3d boundsMax,
 31        int eligibleGenerationCount) =>
 2732        TryBeginCoveredAddressesCore(
 2733            cursor,
 2734            boundsMin,
 2735            boundsMax,
 2736            eligibleGenerationCount,
 2737            hasConfigurationFilter: false,
 2738            default);
 39
 40    /// <summary>
 41    /// Begins a bounded covered-address query filtered to one durable grid configuration before address yield.
 42    /// </summary>
 43    /// <param name="cursor">The caller-owned cursor and preallocated generation storage.</param>
 44    /// <param name="boundsMin">One world-space corner of the coverage bounds.</param>
 45    /// <param name="boundsMax">The opposite world-space corner of the coverage bounds.</param>
 46    /// <param name="eligibleGenerationCount">The exact number of canonical generation inputs that will follow.</param>
 47    /// <param name="configurationFilter">The only configuration eligible to produce addresses.</param>
 48    /// <returns>True when the declared count fits the cursor; otherwise false and the cursor is stale.</returns>
 49    public bool TryBeginCoveredAddresses(
 50        GridCoveredAddressCursor cursor,
 51        Vector3d boundsMin,
 52        Vector3d boundsMax,
 53        int eligibleGenerationCount,
 54        GridConfigurationKey configurationFilter) =>
 255        TryBeginCoveredAddressesCore(
 256            cursor,
 257            boundsMin,
 258            boundsMax,
 259            eligibleGenerationCount,
 260            hasConfigurationFilter: true,
 261            configurationFilter);
 62
 63    /// <summary>
 64    /// Advances generation binding and covered topology-address enumeration under one maintenance snapshot.
 65    /// </summary>
 66    /// <param name="cursor">The cursor previously begun through this world.</param>
 67    /// <param name="generationInputs">The next strictly ascending remaining generation slice, or empty after binding co
 68    /// <param name="results">Caller-owned output storage.</param>
 69    /// <param name="lookupProbeLimit">Maximum generation-validation and spatial lookup probes for this chunk.</param>
 70    /// <param name="addressProbeLimit">Maximum topology-address probes for this chunk.</param>
 71    /// <param name="outputLimit">Maximum covered addresses to write for this chunk.</param>
 72    /// <param name="lookupProbesConsumed">Exact lookup probes consumed by this chunk.</param>
 73    /// <param name="addressProbesConsumed">Exact topology-address probes consumed by this chunk.</param>
 74    /// <param name="generationInputsConsumed">Generation values consumed from the supplied slice.</param>
 75    /// <param name="outputCount">Covered addresses written to <paramref name="results"/>.</param>
 76    /// <returns>The resulting cursor state.</returns>
 77    /// <remarks>
 78    /// No address can be emitted until every declared generation has been validated and copied. A stale
 79    /// result writes no output, clears the run stamp, and requires every prior result from the run to be discarded.
 80    /// Address enumeration does not resolve physical voxel presence or obstacle state. Hex-prism coverage
 81    /// includes cells touching the inclusive query bounds at either vertical face.
 82    /// </remarks>
 83    public GridCoveredAddressCursorStatus AdvanceCoveredAddresses(
 84        GridCoveredAddressCursor cursor,
 85        ReadOnlySpan<GridCoveredAddressGeneration> generationInputs,
 86        Span<GridCoveredAddress> results,
 87        int lookupProbeLimit,
 88        int addressProbeLimit,
 89        int outputLimit,
 90        out int lookupProbesConsumed,
 91        out int addressProbesConsumed,
 92        out int generationInputsConsumed,
 93        out int outputCount)
 94    {
 3495        SwiftThrowHelper.ThrowIfNull(cursor, nameof(cursor));
 3496        SwiftThrowHelper.ThrowIfNegative(lookupProbeLimit, nameof(lookupProbeLimit));
 3497        SwiftThrowHelper.ThrowIfNegative(addressProbeLimit, nameof(addressProbeLimit));
 3498        SwiftThrowHelper.ThrowIfNegative(outputLimit, nameof(outputLimit));
 3499        if (outputLimit > results.Length)
 1100            throw new ArgumentOutOfRangeException(nameof(outputLimit));
 101
 33102        ThrowIfNavigationMaintenanceUnavailable();
 32103        lookupProbesConsumed = 0;
 32104        addressProbesConsumed = 0;
 32105        generationInputsConsumed = 0;
 32106        outputCount = 0;
 1107        while (true)
 108        {
 33109            if (TryEnterNavigationMaintenanceSnapshot())
 110            {
 111                try
 112                {
 32113                    return AdvanceCoveredAddressesCore(
 32114                        cursor,
 32115                        generationInputs,
 32116                        results,
 32117                        lookupProbeLimit,
 32118                        addressProbeLimit,
 32119                        outputLimit,
 32120                        out lookupProbesConsumed,
 32121                        out addressProbesConsumed,
 32122                        out generationInputsConsumed,
 32123                        out outputCount);
 124                }
 125                finally
 126                {
 32127                    ExitNavigationMaintenanceSnapshot();
 32128                }
 129            }
 130
 1131            WaitForPublishedChangePrefix();
 132        }
 32133    }
 134
 135    private bool TryBeginCoveredAddressesCore(
 136        GridCoveredAddressCursor cursor,
 137        Vector3d boundsMin,
 138        Vector3d boundsMax,
 139        int eligibleGenerationCount,
 140        bool hasConfigurationFilter,
 141        GridConfigurationKey configurationFilter)
 142    {
 29143        SwiftThrowHelper.ThrowIfNull(cursor, nameof(cursor));
 29144        ThrowIfNavigationMaintenanceUnavailable();
 145
 28146        Vector3d queryMinimum = new Vector3d(
 28147            FixedMath.Min(boundsMin.X, boundsMax.X),
 28148            FixedMath.Min(boundsMin.Y, boundsMax.Y),
 28149            FixedMath.Min(boundsMin.Z, boundsMax.Z));
 28150        Vector3d queryMaximum = new Vector3d(
 28151            FixedMath.Max(boundsMin.X, boundsMax.X),
 28152            FixedMath.Max(boundsMin.Y, boundsMax.Y),
 28153            FixedMath.Max(boundsMin.Z, boundsMax.Z));
 154
 1155        while (true)
 156        {
 29157            if (TryEnterNavigationMaintenanceSnapshot())
 158            {
 159                try
 160                {
 28161                    return cursor.Begin(
 28162                        SpawnToken,
 28163                        Version,
 28164                        _changeSequence,
 28165                        queryMinimum,
 28166                        queryMaximum,
 28167                        eligibleGenerationCount,
 28168                        hasConfigurationFilter,
 28169                        configurationFilter);
 170                }
 171                finally
 172                {
 28173                    ExitNavigationMaintenanceSnapshot();
 28174                }
 175            }
 176
 1177            WaitForPublishedChangePrefix();
 178        }
 28179    }
 180
 181    private GridCoveredAddressCursorStatus AdvanceCoveredAddressesCore(
 182        GridCoveredAddressCursor cursor,
 183        ReadOnlySpan<GridCoveredAddressGeneration> generationInputs,
 184        Span<GridCoveredAddress> results,
 185        int lookupProbeLimit,
 186        int addressProbeLimit,
 187        int outputLimit,
 188        out int lookupProbesConsumed,
 189        out int addressProbesConsumed,
 190        out int generationInputsConsumed,
 191        out int outputCount)
 192    {
 32193        lookupProbesConsumed = 0;
 32194        addressProbesConsumed = 0;
 32195        generationInputsConsumed = 0;
 32196        outputCount = 0;
 197
 32198        if (!IsCoveredAddressCursorCurrent(cursor))
 2199            return cursor.MarkStale();
 200
 30201        int remainingInputs = cursor.ExpectedGenerationCount - cursor.BoundGenerationCount;
 30202        if (generationInputs.Length > remainingInputs)
 1203            return cursor.MarkStale();
 29204        if (generationInputs.IsEmpty && remainingInputs != 0)
 1205            return cursor.MarkStale();
 206
 78207        while (generationInputsConsumed < generationInputs.Length)
 208        {
 54209            if (lookupProbesConsumed == lookupProbeLimit)
 1210                return GridCoveredAddressCursorStatus.More;
 211
 53212            GridCoveredAddressGeneration generation = generationInputs[generationInputsConsumed++];
 53213            ConsumeLookupProbe(cursor, ref lookupProbesConsumed);
 53214            if ((cursor.HasLastBoundGeneration
 53215                    && cursor.LastBoundGeneration.CompareTo(generation) >= 0)
 53216                || !TryBindCoveredAddressGeneration(cursor, generation, out GridCoveredAddressCursor.BoundGeneration bou
 217            {
 3218                return cursor.MarkStale();
 219            }
 220
 50221            cursor.LastBoundGeneration = generation;
 50222            cursor.HasLastBoundGeneration = true;
 50223            cursor.BoundGenerationCount++;
 50224            if (bound.HasRange)
 17225                cursor.Generations[cursor.RangeGenerationCount++] = bound;
 226        }
 227
 24228        if (cursor.BoundGenerationCount < cursor.ExpectedGenerationCount)
 2229            return GridCoveredAddressCursorStatus.More;
 230
 22231        if (cursor.CurrentStatus == GridCoveredAddressCursorStatus.Complete)
 1232            return cursor.CurrentStatus;
 233
 26234        while (true)
 235        {
 49236            if (cursor.HasPendingOutput)
 237            {
 27238                if (outputCount == outputLimit)
 1239                    return GridCoveredAddressCursorStatus.More;
 240
 26241                results[outputCount++] = cursor.PendingOutput;
 26242                cursor.PendingOutput = default;
 26243                cursor.HasPendingOutput = false;
 26244                if (cursor.OutputOrdinal != ulong.MaxValue)
 25245                    cursor.OutputOrdinal++;
 26246                if (outputCount == outputLimit)
 247                {
 14248                    if (cursor.GenerationOrdinal >= cursor.RangeGenerationCount)
 249                    {
 11250                        cursor.CurrentStatus = GridCoveredAddressCursorStatus.Complete;
 11251                        return cursor.CurrentStatus;
 252                    }
 253
 3254                    return GridCoveredAddressCursorStatus.More;
 255                }
 256            }
 257
 34258            if (!cursor.HasCurrentAddress)
 259            {
 19260                if (cursor.GenerationOrdinal >= cursor.RangeGenerationCount)
 261                {
 4262                    cursor.CurrentStatus = GridCoveredAddressCursorStatus.Complete;
 4263                    return cursor.CurrentStatus;
 264                }
 265
 15266                cursor.CurrentAddress = cursor.Generations[cursor.GenerationOrdinal].Minimum;
 15267                cursor.HasCurrentAddress = true;
 268            }
 269
 30270            if (addressProbesConsumed == addressProbeLimit)
 2271                return GridCoveredAddressCursorStatus.More;
 272
 28273            GridCoveredAddressCursor.BoundGeneration current =
 28274                cursor.Generations[cursor.GenerationOrdinal];
 28275            VoxelIndex address = cursor.CurrentAddress;
 28276            cursor.HasCurrentAddress = AdvanceCoveredAddress(
 28277                ref cursor.CurrentAddress,
 28278                current.Minimum,
 28279                current.Maximum);
 28280            if (!cursor.HasCurrentAddress)
 15281                cursor.GenerationOrdinal++;
 282
 28283            ConsumeAddressProbe(cursor, ref addressProbesConsumed);
 28284            if (IsCoveredAddress(cursor, current.Generation, address))
 285            {
 26286                cursor.PendingOutput = new GridCoveredAddress(current.Generation, address);
 26287                cursor.HasPendingOutput = true;
 288            }
 289        }
 290    }
 291
 292    private bool TryBindCoveredAddressGeneration(
 293        GridCoveredAddressCursor cursor,
 294        GridCoveredAddressGeneration generation,
 295        out GridCoveredAddressCursor.BoundGeneration bound)
 296    {
 52297        bound = default;
 52298        if (!BoundsTracker.TryGetValue(generation.ConfigurationKey, out ushort gridIndex)
 52299            || gridIndex != generation.GridIndex)
 300        {
 1301            return false;
 302        }
 303
 51304        VoxelGrid grid = ActiveGrids[gridIndex];
 51305        if (grid.SpawnToken != generation.GridSpawnToken
 51306            || grid.LastChangeSequence != generation.GridLastChangeSequence)
 307        {
 1308            return false;
 309        }
 310
 50311        bool eligible = !cursor.HasConfigurationFilter
 50312            || cursor.FilterConfigurationKey == generation.ConfigurationKey;
 50313        VoxelIndex minimum = default;
 50314        VoxelIndex maximum = default;
 50315        Vector3d queryMinimum = cursor.QueryMinimum;
 50316        Vector3d queryMaximum = cursor.QueryMaximum;
 50317        if (eligible && grid.Topology.Kind == GridTopologyKind.HexPrism)
 318        {
 6319            Fixed64 halfLayerHeight = generation.ConfigurationKey.TopologyMetrics.LayerHeight
 6320                * Fixed64.Half;
 6321            queryMinimum = new Vector3d(
 6322                queryMinimum.X,
 6323                queryMinimum.Y - halfLayerHeight,
 6324                queryMinimum.Z);
 6325            queryMaximum = new Vector3d(
 6326                queryMaximum.X,
 6327                queryMaximum.Y + halfLayerHeight,
 6328                queryMaximum.Z);
 329        }
 330
 50331        bool hasRange = eligible && TopologyVoxelRangeUtility.TryGetCandidateRange(
 50332            grid,
 50333            queryMinimum,
 50334            queryMaximum,
 50335            out minimum,
 50336            out maximum);
 50337        bound = new GridCoveredAddressCursor.BoundGeneration(
 50338            generation,
 50339            hasRange ? minimum : default,
 50340            hasRange ? maximum : default,
 50341            hasRange);
 50342        return true;
 343    }
 344
 345    private bool IsCoveredAddressCursorCurrent(GridCoveredAddressCursor cursor) =>
 32346        cursor.CurrentStatus != GridCoveredAddressCursorStatus.Stale
 32347        && cursor.WorldSpawnToken == SpawnToken
 32348        && cursor.WorldVersion == Version
 32349        && cursor.WorldChangeSequence == _changeSequence;
 350
 351    private bool IsCoveredAddress(
 352        GridCoveredAddressCursor cursor,
 353        GridCoveredAddressGeneration generation,
 354        VoxelIndex address)
 355    {
 28356        if (generation.ConfigurationKey.TopologyKind != GridTopologyKind.HexPrism)
 20357            return true;
 358
 8359        VoxelGrid grid = ActiveGrids[generation.GridIndex];
 8360        Vector3d center = grid.GetWorldPosition(address);
 8361        Fixed64 radius = generation.ConfigurationKey.TopologyMetrics.CellRadius;
 8362        Fixed64 halfLayerHeight = generation.ConfigurationKey.TopologyMetrics.LayerHeight
 8363            * Fixed64.Half;
 8364        return center.X >= cursor.QueryMinimum.X - radius
 8365            && center.X <= cursor.QueryMaximum.X + radius
 8366            && center.Y >= cursor.QueryMinimum.Y - halfLayerHeight
 8367            && center.Y <= cursor.QueryMaximum.Y + halfLayerHeight
 8368            && center.Z >= cursor.QueryMinimum.Z - radius
 8369            && center.Z <= cursor.QueryMaximum.Z + radius;
 370    }
 371
 372    private static bool AdvanceCoveredAddress(
 373        ref VoxelIndex address,
 374        VoxelIndex minimum,
 375        VoxelIndex maximum)
 376    {
 28377        if (address.z < maximum.z)
 378        {
 4379            address.z++;
 4380            return true;
 381        }
 382
 24383        address.z = minimum.z;
 24384        if (address.y < maximum.y)
 385        {
 4386            address.y++;
 4387            return true;
 388        }
 389
 20390        address.y = minimum.y;
 20391        if (address.x < maximum.x)
 392        {
 5393            address.x++;
 5394            return true;
 395        }
 396
 15397        return false;
 398    }
 399
 400    private static void ConsumeLookupProbe(
 401        GridCoveredAddressCursor cursor,
 402        ref int lookupProbesConsumed)
 403    {
 53404        lookupProbesConsumed++;
 53405        if (cursor.LookupProbeOrdinal != ulong.MaxValue)
 52406            cursor.LookupProbeOrdinal++;
 53407    }
 408
 409    private static void ConsumeAddressProbe(
 410        GridCoveredAddressCursor cursor,
 411        ref int addressProbesConsumed)
 412    {
 28413        addressProbesConsumed++;
 28414        if (cursor.AddressProbeOrdinal != ulong.MaxValue)
 27415            cursor.AddressProbeOrdinal++;
 28416    }
 417}

/home/runner/work/GridForge/GridForge/src/GridForge/Grids/Managers/GridWorld.cs

#LineLine coverage
 1//=======================================================================
 2// GridWorld.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.Diagnostics;
 11using System.Runtime.CompilerServices;
 12using System.Threading;
 13using FixedMathSharp;
 14using GridForge.Configuration;
 15using GridForge.Grids.Storage;
 16using GridForge.Grids.Topology;
 17using GridForge.Spatial;
 18using SwiftCollections;
 19using SwiftCollections.Pool;
 20using SwiftCollections.Query;
 21
 22namespace GridForge.Grids;
 23
 24/// <summary>
 25/// Owns the mutable runtime state for one GridForge world.
 26/// </summary>
 27public sealed partial class GridWorld : IDisposable
 28{
 29    #region Constants
 30
 31    /// <summary>
 32    /// Maximum number of grids that can be managed within a world.
 33    /// </summary>
 34    public const ushort MaxGrids = ushort.MaxValue - 1;
 35
 36    /// <summary>
 37    /// The default rectangular cell edge in world units.
 38    /// </summary>
 139    public static readonly Fixed64 DefaultRectangularCellSize = Fixed64.One;
 40
 41    /// <summary>
 42    /// The default cell size used to tune ordinary-grid lookup.
 43    /// Oversized grids are indexed automatically outside this tier.
 44    /// </summary>
 45    public const int DefaultSpatialGridCellSize = 50;
 46
 47    private const int BoundaryContactSourceWordCount = (MaxGrids + 63) / 64;
 48    private const int BoundaryContactSourceSummaryWordCount =
 49        (BoundaryContactSourceWordCount + 63) / 64;
 50
 51    #endregion
 52
 53    #region Properties
 54
 155    private static readonly Comparison<VoxelIndex> CompareVoxelIndices =
 156        static (left, right) => left.CompareTo(right);
 57
 58    /// <summary>
 59    /// The cell size used to tune ordinary-grid lookup in this world.
 60    /// Oversized grids are indexed automatically outside this tier.
 61    /// </summary>
 62    public int SpatialGridCellSize { get; }
 63
 64    /// <summary>
 65    /// Collection of all active grids owned by this world.
 66    /// </summary>
 67    public SwiftBucket<VoxelGrid> ActiveGrids { get; }
 68
 69    /// <summary>
 70    /// Dictionary mapping exact grid configuration keys to grid indices to prevent duplicate grids.
 71    /// </summary>
 72    public SwiftDictionary<GridConfigurationKey, ushort> BoundsTracker { get; }
 73
 74    /// <summary>
 75    /// Nonzero process-unique 64-bit runtime allocation token for this active world.
 76    /// Zero indicates an inactive world.
 77    /// </summary>
 78    public long SpawnToken { get; private set; }
 79
 80    /// <summary>
 81    /// The current version of the world, incremented on major changes.
 82    /// </summary>
 83    public uint Version { get; private set; }
 84
 85    /// <summary>
 86    /// The most recent world-local committed change sequence.
 87    /// </summary>
 88    public ulong ChangeSequence
 89    {
 90        get
 91        {
 4792            lock (ChangeSyncRoot)
 4793                return _changeSequence;
 4794        }
 95    }
 96
 97    /// <summary>
 98    /// Indicates whether this world is currently active.
 99    /// </summary>
 100    public bool IsActive { get; private set; }
 101
 102    internal Fixed64 MaxTopologyCellEdge { get; private set; }
 103
 1327104    internal void EnterReadLock() => _gridLock.EnterReadLock();
 105
 1327106    internal void ExitReadLock() => _gridLock.ExitReadLock();
 107
 565108    internal bool IsWriteLockHeld => _gridLock.IsWriteLockHeld;
 109
 110    private static long s_worldAllocationCounter;
 111    private static long s_obstacleRegistrationCounter;
 112
 785113    private readonly ReaderWriterLockSlim _gridLock = new();
 114    internal object ChangeSyncRoot { get; } = new object();
 785115    private readonly SwiftQueue<GridCommittedChange> _committedChanges = new SwiftQueue<GridCommittedChange>();
 785116    private readonly SwiftList<ushort> _gridCandidates = new();
 785117    private readonly SwiftDictionary<ushort, SwiftList<ushort>> _boundaryContactTargetsBySource = new();
 785118    private readonly SwiftDictionary<ushort, SwiftList<ushort>> _boundaryContactSourcesByTarget = new();
 119    private readonly GridSpatialIndex _spatialIndex;
 120    private ulong[]? _boundaryContactSourceWords;
 121    private ulong[]? _boundaryContactSourceSummaryWords;
 122    private int _boundaryContactSourceSummaryLength;
 123    private long _gridGenerationCounter;
 124    private ulong _changeSequence;
 125    private ulong _publishedChangeSequence;
 126    private bool _isPublishingCommittedChanges;
 127    private volatile bool _isDisposed;
 128    private int _committedPublicationOwnerThreadId;
 129    private int _navigationMaintenanceOwnerThreadId;
 130
 131    #endregion
 132
 133    #region Events
 134
 135    private Action<GridEventInfo>? _onActiveGridAdded;
 136    private Action<GridEventInfo>? _onActiveGridRemoved;
 137    private Action<GridEventInfo>? _onActiveGridChange;
 138    private Action<GridEventInfo>? _onChangeCommitted;
 139    private Action? _onReset;
 140
 141    /// <summary>
 142    /// Event triggered when a new grid is added to this world.
 143    /// </summary>
 144    public event Action<GridEventInfo> OnActiveGridAdded
 145    {
 146        add
 147        {
 171148            lock (ChangeSyncRoot)
 171149                _onActiveGridAdded += value;
 171150        }
 151        remove
 152        {
 168153            lock (ChangeSyncRoot)
 168154                _onActiveGridAdded -= value;
 168155        }
 156    }
 157
 158    /// <summary>
 159    /// Event triggered when a grid is removed from this world.
 160    /// </summary>
 161    public event Action<GridEventInfo> OnActiveGridRemoved
 162    {
 163        add
 164        {
 171165            lock (ChangeSyncRoot)
 171166                _onActiveGridRemoved += value;
 171167        }
 168        remove
 169        {
 168170            lock (ChangeSyncRoot)
 168171                _onActiveGridRemoved -= value;
 168172        }
 173    }
 174
 175    /// <summary>
 176    /// Event triggered when a grid in this world undergoes a significant change.
 177    /// </summary>
 178    public event Action<GridEventInfo> OnActiveGridChange
 179    {
 180        add
 181        {
 175182            lock (ChangeSyncRoot)
 175183                _onActiveGridChange += value;
 175184        }
 185        remove
 186        {
 169187            lock (ChangeSyncRoot)
 169188                _onActiveGridChange -= value;
 169189        }
 190    }
 191
 192    /// <summary>
 193    /// Receives every committed grid lifecycle, sparse-presence, and obstacle mutation in
 194    /// ascending <see cref="GridEventInfo.ChangeSequence"/> order.
 195    /// </summary>
 196    public event Action<GridEventInfo> OnChangeCommitted
 197    {
 198        add
 199        {
 13200            lock (ChangeSyncRoot)
 13201                _onChangeCommitted += value;
 13202        }
 203        remove
 204        {
 7205            lock (ChangeSyncRoot)
 7206                _onChangeCommitted -= value;
 7207        }
 208    }
 209
 210    /// <summary>
 211    /// Event triggered when this world is reset.
 212    /// </summary>
 213    public event Action OnReset
 214    {
 215        add
 216        {
 171217            lock (ChangeSyncRoot)
 171218                _onReset += value;
 171219        }
 220        remove
 221        {
 168222            lock (ChangeSyncRoot)
 168223                _onReset -= value;
 168224        }
 225    }
 226
 227    #endregion
 228
 229    /// <summary>
 230    /// Initializes a new world with optional ordinary-grid lookup tuning.
 231    /// </summary>
 232    /// <param name="spatialGridCellSize">Optional ordinary-grid lookup cell size for this world.</param>
 785233    public GridWorld(int spatialGridCellSize = DefaultSpatialGridCellSize)
 234    {
 785235        ActiveGrids = new SwiftBucket<VoxelGrid>();
 785236        BoundsTracker = new SwiftDictionary<GridConfigurationKey, ushort>();
 237
 785238        SpatialGridCellSize = ResolveSpatialGridCellSize(spatialGridCellSize);
 785239        _spatialIndex = new GridSpatialIndex(SpatialGridCellSize);
 785240        SpawnToken = RuntimeIdentityAllocator.Allocate(ref s_worldAllocationCounter);
 785241        Version = 1;
 785242        IsActive = true;
 785243    }
 244
 245    #region Lifecycle
 246
 247    /// <summary>
 248    /// Clears all grids and spatial data owned by this world.
 249    /// </summary>
 250    /// <param name="deactivate">If true, marks the world inactive and releases its event handlers.</param>
 251    public void Reset(bool deactivate = false)
 252    {
 801253        if (!IsActive)
 254        {
 9255            GridForgeLogger.Channel.Warn($"Grid world not active. Cannot reset an inactive world.");
 9256            return;
 257        }
 258
 792259        NotifyResetHandlers();
 260        bool drainCommittedChanges;
 792261        _gridLock.EnterWriteLock();
 262        try
 263        {
 792264            lock (ChangeSyncRoot)
 265            {
 792266                bool wasPublishingCommittedChanges = _isPublishingCommittedChanges;
 792267                ReleaseActiveGrids();
 792268                GridOccupantManager.ClearTrackedOccupancies(this);
 792269                Version++;
 270
 792271                GridEventInfo resetEvent = new GridEventInfo(
 792272                    SpawnToken,
 792273                    ushort.MaxValue,
 792274                    0,
 792275                    default,
 792276                    0,
 792277                    GridEventKind.WorldReset,
 792278                    changeStamp: AllocateChangeStamp());
 792279                EnqueueCommittedChange(new GridCommittedChange(resetEvent));
 792280                drainCommittedChanges = !wasPublishingCommittedChanges;
 281
 792282                if (deactivate)
 283                {
 781284                    GridOccupantManager.ReleaseTrackedOccupancies(this);
 781285                    IsActive = false;
 286                }
 792287            }
 288        }
 289        finally
 290        {
 792291            _gridLock.ExitWriteLock();
 792292        }
 293
 792294        if (drainCommittedChanges)
 791295            DrainCommittedChanges();
 296
 792297        if (!deactivate)
 11298            return;
 299
 781300        lock (ChangeSyncRoot)
 301        {
 781302            SpawnToken = 0;
 781303            _onActiveGridAdded = null;
 781304            _onActiveGridRemoved = null;
 781305            _onActiveGridChange = null;
 781306            _onChangeCommitted = null;
 781307            _onReset = null;
 781308        }
 781309    }
 310
 311    private void NotifyResetHandlers()
 312    {
 792313        Action? resetHandlers = _onReset;
 792314        if (resetHandlers == null)
 770315            return;
 316
 22317        var handlerDelegates = resetHandlers.GetInvocationList();
 312318        for (int i = 0; i < handlerDelegates.Length; i++)
 319        {
 320            try
 321            {
 134322                ((Action)handlerDelegates[i])();
 132323            }
 2324            catch (Exception ex)
 325            {
 2326                GridForgeLogger.Channel.Error($"World reset notification error: {ex.Message}");
 2327            }
 328        }
 22329    }
 330
 331    private void ReleaseActiveGrids()
 332    {
 792333        _spatialIndex.Clear();
 792334        ReleaseBoundaryContactPairs();
 335
 3302336        foreach (VoxelGrid grid in ActiveGrids)
 859337            Pools.GridPool.Release(grid);
 338
 792339        ActiveGrids.Clear();
 792340        BoundsTracker.Clear();
 792341        MaxTopologyCellEdge = Fixed64.Zero;
 792342    }
 343
 344    /// <inheritdoc />
 345    public void Dispose()
 346    {
 785347        Reset(deactivate: true);
 785348        _isDisposed = true;
 785349        _gridLock.Dispose();
 785350        GC.SuppressFinalize(this);
 785351    }
 352
 353    #endregion
 354
 355    #region Grid Management
 356
 357    /// <summary>
 358    /// Allocates a nonzero process-unique identity for one obstacle registration lifetime.
 359    /// </summary>
 360    /// <returns>A fresh opaque obstacle token.</returns>
 361    /// <exception cref="InvalidOperationException">The world is inactive or its token space is exhausted.</exception>
 362    public ObstacleToken AllocateObstacleToken()
 363    {
 459364        if (!IsActive)
 1365            throw new InvalidOperationException("Cannot allocate an obstacle token from an inactive world.");
 366
 458367        return new ObstacleToken(RuntimeIdentityAllocator.Allocate(ref s_obstacleRegistrationCounter));
 368    }
 369
 370    /// <summary>
 371    /// Captures presence and obstacle state for a sorted requested address span without
 372    /// enumerating unrelated grids or unrequested physical voxels.
 373    /// </summary>
 374    /// <param name="configurationKey">The exact normalized configuration identity to resolve.</param>
 375    /// <param name="requestedVoxels">Strictly ascending, unique, in-bounds topology-local addresses.</param>
 376    /// <param name="baseline">The atomic baseline on success.</param>
 377    /// <returns>True when the requested active grid generation was captured; otherwise false.</returns>
 378    public bool TryCaptureNavigationBaseline(
 379        GridConfigurationKey configurationKey,
 380        ReadOnlySpan<VoxelIndex> requestedVoxels,
 381        out GridNavigationBaseline? baseline)
 382    {
 11383        baseline = null;
 11384        if (!IsActive)
 1385            return false;
 386
 10387        if (Volatile.Read(ref _navigationMaintenanceOwnerThreadId)
 10388            == Environment.CurrentManagedThreadId)
 389        {
 1390            return TryCaptureNavigationBaselineCore(configurationKey, requestedVoxels, out baseline);
 391        }
 392
 9393        _gridLock.EnterReadLock();
 394        try
 395        {
 9396            lock (ChangeSyncRoot)
 9397                return TryCaptureNavigationBaselineCore(configurationKey, requestedVoxels, out baseline);
 398        }
 399        finally
 400        {
 9401            _gridLock.ExitReadLock();
 9402        }
 9403    }
 404
 405    /// <summary>
 406    /// Executes one short navigation maintenance snapshot while grid mutations are frozen.
 407    /// Committed-change prefix detachment and all required navigation baseline captures can
 408    /// therefore observe one deterministic world state.
 409    /// </summary>
 410    /// <param name="maintenance">The non-mutating maintenance callback to execute.</param>
 411    /// <remarks>
 412    /// The callback may call <see cref="TryCaptureNavigationBaseline"/> without lock recursion.
 413    /// It must not mutate this world, wait for code that may mutate this world, or retain live
 414    /// grid/voxel references beyond the callback. This method must not be called from a committed-
 415    /// change notification handler. Those handlers remain outside the mutation lock and may enqueue
 416    /// represented events after this snapshot completes.
 417    /// </remarks>
 418    public void ExecuteNavigationMaintenanceSnapshot(Action maintenance)
 419    {
 3420        SwiftThrowHelper.ThrowIfNull(maintenance, nameof(maintenance));
 3421        ThrowIfNavigationMaintenanceUnavailable();
 422
 1423        while (true)
 424        {
 3425            if (TryEnterNavigationMaintenanceSnapshot())
 426            {
 427                try
 428                {
 2429                    maintenance();
 2430                    return;
 431                }
 432                finally
 433                {
 2434                    ExitNavigationMaintenanceSnapshot();
 2435                }
 436            }
 437
 1438            WaitForPublishedChangePrefix();
 439        }
 2440    }
 441
 442    /// <summary>
 443    /// Begins or restarts a bounded exact boundary-contact query against the current committed world state.
 444    /// </summary>
 445    /// <param name="cursor">The caller-owned cursor to reset and bind.</param>
 446    public void BeginBoundaryContacts(GridBoundaryContactCursor cursor)
 447    {
 26448        SwiftThrowHelper.ThrowIfNull(cursor, nameof(cursor));
 26449        ThrowIfNavigationMaintenanceUnavailable();
 450
 1451        while (true)
 452        {
 27453            if (TryEnterNavigationMaintenanceSnapshot())
 454            {
 455                try
 456                {
 26457                    cursor.Begin(SpawnToken, Version, _changeSequence);
 26458                    return;
 459                }
 460                finally
 461                {
 26462                    ExitNavigationMaintenanceSnapshot();
 26463                }
 464            }
 465
 1466            WaitForPublishedChangePrefix();
 467        }
 26468    }
 469
 470    /// <summary>
 471    /// Begins or restarts a bounded exact boundary-contact query restricted to one active grid.
 472    /// </summary>
 473    /// <param name="configurationKey">The exact normalized configuration identity to resolve.</param>
 474    /// <param name="cursor">The caller-owned cursor to reset and bind.</param>
 475    /// <returns>True when the requested active grid was bound; otherwise false.</returns>
 476    public bool TryBeginBoundaryContacts(
 477        GridConfigurationKey configurationKey,
 478        GridBoundaryContactCursor cursor)
 479    {
 14480        SwiftThrowHelper.ThrowIfNull(cursor, nameof(cursor));
 14481        ThrowIfNavigationMaintenanceUnavailable();
 482
 1483        while (true)
 484        {
 15485            if (TryEnterNavigationMaintenanceSnapshot())
 486            {
 487                try
 488                {
 14489                    if (!BoundsTracker.TryGetValue(configurationKey, out ushort gridIndex))
 490                    {
 1491                        cursor.MarkStale();
 1492                        return false;
 493                    }
 494
 13495                    VoxelGrid grid = ActiveGrids[gridIndex];
 13496                    cursor.BeginFiltered(
 13497                        SpawnToken,
 13498                        Version,
 13499                        _changeSequence,
 13500                        gridIndex,
 13501                        grid.SpawnToken,
 13502                        grid.LastChangeSequence);
 13503                    return true;
 504                }
 505                finally
 506                {
 14507                    ExitNavigationMaintenanceSnapshot();
 14508                }
 509            }
 510
 1511            WaitForPublishedChangePrefix();
 512        }
 14513    }
 514
 515    /// <summary>
 516    /// Advances a bounded exact boundary-contact query under one short navigation-maintenance snapshot.
 517    /// </summary>
 518    /// <param name="cursor">The caller-owned cursor previously begun through this world.</param>
 519    /// <param name="results">Caller-owned storage for contacts emitted by this chunk.</param>
 520    /// <param name="candidateProbeLimit">The maximum pair, source-address, and target probes for this chunk.</param>
 521    /// <param name="outputLimit">The maximum contacts to write during this chunk.</param>
 522    /// <param name="candidateProbesConsumed">The exact number of candidate probes consumed by this chunk.</param>
 523    /// <param name="outputCount">The number of contacts written to <paramref name="results"/>.</param>
 524    /// <returns>The resulting cursor state.</returns>
 525    /// <remarks>
 526    /// A <see cref="GridBoundaryContactCursorStatus.Stale"/> result writes no contacts and resets the
 527    /// cursor ordinal. The caller must discard every contact returned since the preceding begin.
 528    /// Completed cursors remain bound and are revalidated on every later call, including zero-budget calls.
 529    /// </remarks>
 530    public GridBoundaryContactCursorStatus AdvanceBoundaryContacts(
 531        GridBoundaryContactCursor cursor,
 532        Span<VoxelContactManifold> results,
 533        int candidateProbeLimit,
 534        int outputLimit,
 535        out int candidateProbesConsumed,
 536        out int outputCount)
 537    {
 107538        SwiftThrowHelper.ThrowIfNull(cursor, nameof(cursor));
 107539        SwiftThrowHelper.ThrowIfNegative(candidateProbeLimit, nameof(candidateProbeLimit));
 107540        SwiftThrowHelper.ThrowIfNegative(outputLimit, nameof(outputLimit));
 107541        if (outputLimit > results.Length)
 1542            throw new ArgumentOutOfRangeException(nameof(outputLimit));
 543
 106544        return AdvanceBoundaryContactsUnderGate(
 106545            cursor,
 106546            results,
 106547            default,
 106548            includeConfigurationKeys: false,
 106549            candidateProbeLimit,
 106550            outputLimit,
 106551            out candidateProbesConsumed,
 106552            out outputCount);
 553    }
 554
 555    /// <summary>
 556    /// Advances a bounded exact boundary-contact query and emits durable grid identities with each contact.
 557    /// </summary>
 558    /// <param name="cursor">The caller-owned cursor previously begun through this world.</param>
 559    /// <param name="results">Caller-owned storage for contacts and their normalized grid identities.</param>
 560    /// <param name="candidateProbeLimit">The maximum pair, source-address, and target probes for this chunk.</param>
 561    /// <param name="outputLimit">The maximum contacts to write during this chunk.</param>
 562    /// <param name="candidateProbesConsumed">The exact number of candidate probes consumed by this chunk.</param>
 563    /// <param name="outputCount">The number of contacts written to <paramref name="results"/>.</param>
 564    /// <returns>The resulting cursor state.</returns>
 565    /// <remarks>
 566    /// Every emitted identity belongs to <see cref="GridBoundaryContactCursor.RunStamp"/>.
 567    /// A stale result writes no contacts and resets that stamp to its default value.
 568    /// </remarks>
 569    public GridBoundaryContactCursorStatus AdvanceBoundaryContacts(
 570        GridBoundaryContactCursor cursor,
 571        Span<GridBoundaryContact> results,
 572        int candidateProbeLimit,
 573        int outputLimit,
 574        out int candidateProbesConsumed,
 575        out int outputCount)
 576    {
 9577        SwiftThrowHelper.ThrowIfNull(cursor, nameof(cursor));
 9578        SwiftThrowHelper.ThrowIfNegative(candidateProbeLimit, nameof(candidateProbeLimit));
 9579        SwiftThrowHelper.ThrowIfNegative(outputLimit, nameof(outputLimit));
 9580        if (outputLimit > results.Length)
 1581            throw new ArgumentOutOfRangeException(nameof(outputLimit));
 582
 8583        return AdvanceBoundaryContactsUnderGate(
 8584            cursor,
 8585            default,
 8586            results,
 8587            includeConfigurationKeys: true,
 8588            candidateProbeLimit,
 8589            outputLimit,
 8590            out candidateProbesConsumed,
 8591            out outputCount);
 592    }
 593
 594    private GridBoundaryContactCursorStatus AdvanceBoundaryContactsUnderGate(
 595        GridBoundaryContactCursor cursor,
 596        Span<VoxelContactManifold> manifoldResults,
 597        Span<GridBoundaryContact> boundResults,
 598        bool includeConfigurationKeys,
 599        int candidateProbeLimit,
 600        int outputLimit,
 601        out int candidateProbesConsumed,
 602        out int outputCount)
 603    {
 114604        ThrowIfNavigationMaintenanceUnavailable();
 114605        candidateProbesConsumed = 0;
 114606        outputCount = 0;
 1607        while (true)
 608        {
 115609            if (TryEnterNavigationMaintenanceSnapshot())
 610            {
 611                try
 612                {
 114613                    return AdvanceBoundaryContactsCore(
 114614                        cursor,
 114615                        manifoldResults,
 114616                        boundResults,
 114617                        includeConfigurationKeys,
 114618                        candidateProbeLimit,
 114619                        outputLimit,
 114620                        out candidateProbesConsumed,
 114621                        out outputCount);
 622                }
 623                finally
 624                {
 114625                    ExitNavigationMaintenanceSnapshot();
 114626                }
 627            }
 628
 1629            WaitForPublishedChangePrefix();
 630        }
 114631    }
 632
 633    private GridBoundaryContactCursorStatus AdvanceBoundaryContactsCore(
 634        GridBoundaryContactCursor cursor,
 635        Span<VoxelContactManifold> manifoldResults,
 636        Span<GridBoundaryContact> boundResults,
 637        bool includeConfigurationKeys,
 638        int candidateProbeLimit,
 639        int outputLimit,
 640        out int candidateProbesConsumed,
 641        out int outputCount)
 642    {
 114643        candidateProbesConsumed = 0;
 114644        outputCount = 0;
 114645        if (!IsBoundaryContactCursorCurrent(cursor))
 10646            return cursor.MarkStale();
 647
 104648        if (cursor.CurrentStatus == GridBoundaryContactCursorStatus.Complete)
 2649            return cursor.CurrentStatus;
 650
 651        while (true)
 652        {
 320653            if (cursor.HasPendingContact)
 654            {
 52655                if (outputCount == outputLimit)
 1656                    return GridBoundaryContactCursorStatus.More;
 657
 51658                if (includeConfigurationKeys)
 659                {
 8660                    boundResults[outputCount++] = new GridBoundaryContact(
 8661                        cursor.SourceConfigurationKey,
 8662                        cursor.TargetConfigurationKey,
 8663                        cursor.PendingContact);
 664                }
 665                else
 666                {
 43667                    manifoldResults[outputCount++] = cursor.PendingContact;
 668                }
 51669                cursor.PendingContact = default;
 51670                cursor.HasPendingContact = false;
 51671                if (outputCount == outputLimit)
 40672                    return GridBoundaryContactCursorStatus.More;
 673            }
 674
 279675            switch (cursor.Stage)
 676            {
 677                case GridBoundaryContactCursor.TraversalStage.Pair:
 122678                    if (cursor.IsFiltered)
 679                    {
 23680                        if (!TryAdvanceFilteredBoundaryContactPair(
 23681                                cursor,
 23682                                candidateProbeLimit,
 23683                                ref candidateProbesConsumed,
 23684                                out ushort filteredSource,
 23685                                out ushort filteredTarget))
 686                        {
 12687                            return cursor.CurrentStatus;
 688                        }
 689
 11690                        BindBoundaryContactPair(cursor, filteredSource, filteredTarget);
 11691                        cursor.Stage = GridBoundaryContactCursor.TraversalStage.Source;
 11692                        continue;
 693                    }
 694
 99695                    if (!cursor.HasPairSource)
 696                    {
 69697                        if (cursor.PairSourceWord != 0)
 698                        {
 17699                            int sourceBit = GetTrailingZeroCount(cursor.PairSourceWord);
 17700                            cursor.PairSourceWord &= cursor.PairSourceWord - 1UL;
 17701                            cursor.PairSourceGridIndex = (ushort)(
 17702                                (cursor.PairSourceWordIndex << 6) + sourceBit);
 17703                            cursor.PairTargetOrdinal = 0;
 17704                            cursor.HasPairSource = true;
 17705                            continue;
 706                        }
 707
 52708                        if (cursor.PairSourceSummaryWord != 0)
 709                        {
 19710                            if (candidateProbesConsumed == candidateProbeLimit)
 2711                                return GridBoundaryContactCursorStatus.More;
 712
 17713                            int wordBit = GetTrailingZeroCount(cursor.PairSourceSummaryWord);
 17714                            cursor.PairSourceSummaryWord &= cursor.PairSourceSummaryWord - 1UL;
 17715                            cursor.PairSourceWordIndex =
 17716                                ((cursor.PairSourceSummaryWordIndex - 1) << 6) + wordBit;
 17717                            cursor.PairSourceWord = _boundaryContactSourceWords![cursor.PairSourceWordIndex];
 17718                            ConsumeBoundaryContactProbe(cursor, ref candidateProbesConsumed);
 17719                            continue;
 720                        }
 721
 33722                        if (_boundaryContactSourceSummaryWords == null
 33723                            || cursor.PairSourceSummaryWordIndex
 33724                                >= _boundaryContactSourceSummaryLength)
 725                        {
 14726                            cursor.CurrentStatus = GridBoundaryContactCursorStatus.Complete;
 14727                            return cursor.CurrentStatus;
 728                        }
 729
 19730                        if (candidateProbesConsumed == candidateProbeLimit)
 1731                            return GridBoundaryContactCursorStatus.More;
 732
 18733                        cursor.PairSourceSummaryWord = _boundaryContactSourceSummaryWords[
 18734                            cursor.PairSourceSummaryWordIndex++];
 18735                        ConsumeBoundaryContactProbe(cursor, ref candidateProbesConsumed);
 18736                        continue;
 737                    }
 738
 30739                    bool foundPairTargets = _boundaryContactTargetsBySource.TryGetValue(
 30740                        cursor.PairSourceGridIndex,
 30741                        out SwiftList<ushort>? pairTargets);
 742                    Debug.Assert(foundPairTargets && pairTargets != null);
 30743                    if (cursor.PairTargetOrdinal >= pairTargets.Count)
 744                    {
 12745                        cursor.HasPairSource = false;
 12746                        continue;
 747                    }
 748
 18749                    if (candidateProbesConsumed == candidateProbeLimit)
 1750                        return GridBoundaryContactCursorStatus.More;
 751
 17752                    ushort pairTarget = pairTargets[cursor.PairTargetOrdinal++];
 17753                    ConsumeBoundaryContactProbe(cursor, ref candidateProbesConsumed);
 17754                    BindBoundaryContactPair(cursor, cursor.PairSourceGridIndex, pairTarget);
 17755                    cursor.Stage = GridBoundaryContactCursor.TraversalStage.Source;
 17756                    continue;
 757
 758                case GridBoundaryContactCursor.TraversalStage.Source:
 77759                    VoxelGrid sourceGrid = ActiveGrids[cursor.SourceGridIndex];
 77760                    VoxelGrid targetGrid = ActiveGrids[cursor.TargetGridIndex];
 77761                    if (!cursor.HasSourceRange)
 762                    {
 22763                        cursor.ClearPairProgress();
 22764                        continue;
 765                    }
 766
 55767                    if (candidateProbesConsumed == candidateProbeLimit)
 4768                        return GridBoundaryContactCursorStatus.More;
 769
 51770                    VoxelIndex sourceIndex = cursor.SourceAddress;
 51771                    cursor.HasSourceRange = AdvanceBoundaryContactAddress(
 51772                        ref cursor.SourceAddress,
 51773                        cursor.SourceMinimum,
 51774                        cursor.SourceMaximum);
 51775                    ConsumeBoundaryContactProbe(cursor, ref candidateProbesConsumed);
 51776                    if (!TryCreateTopologyPrism(sourceGrid, sourceIndex, out cursor.SourcePrism)
 51777                        || !TopologyVoxelRangeUtility.TryGetCandidateRange(
 51778                            targetGrid,
 51779                            cursor.SourcePrism.GetAabb().Expand(targetGrid.Topology.MaxCellEdge),
 51780                            out cursor.TargetMinimum,
 51781                            out cursor.TargetMaximum))
 782                    {
 783                        continue;
 784                    }
 785
 51786                    cursor.TargetAddress = cursor.TargetMinimum;
 51787                    cursor.Stage = GridBoundaryContactCursor.TraversalStage.Target;
 51788                    continue;
 789
 790                default:
 80791                    if (candidateProbesConsumed == candidateProbeLimit)
 27792                        return GridBoundaryContactCursorStatus.More;
 793
 53794                    ProbeBoundaryContactTarget(cursor, ref candidateProbesConsumed);
 53795                    continue;
 796            }
 797        }
 798    }
 799
 800    private bool IsBoundaryContactCursorCurrent(GridBoundaryContactCursor cursor)
 801    {
 114802        if (cursor.CurrentStatus == GridBoundaryContactCursorStatus.Stale
 114803            || cursor.WorldSpawnToken != SpawnToken
 114804            || cursor.WorldVersion != Version
 114805            || cursor.WorldChangeSequence != _changeSequence)
 806        {
 7807            return false;
 808        }
 809
 107810        if (cursor.IsFiltered
 107811            && ActiveGrids[cursor.FilterGridIndex].LastChangeSequence
 107812                != cursor.FilterGridLastChangeSequence)
 813        {
 1814            return false;
 815        }
 816
 106817        if (cursor.SourceGridSpawnToken == 0)
 36818            return true;
 819
 70820        return ActiveGrids[cursor.SourceGridIndex].LastChangeSequence
 70821                == cursor.SourceGridLastChangeSequence
 70822            && ActiveGrids[cursor.TargetGridIndex].LastChangeSequence
 70823                == cursor.TargetGridLastChangeSequence;
 824    }
 825
 826    private bool TryAdvanceFilteredBoundaryContactPair(
 827        GridBoundaryContactCursor cursor,
 828        int candidateProbeLimit,
 829        ref int candidateProbesConsumed,
 830        out ushort sourceGridIndex,
 831        out ushort targetGridIndex)
 832    {
 23833        sourceGridIndex = 0;
 23834        targetGridIndex = 0;
 45835        while (cursor.FilteredPairPhase < 2)
 836        {
 36837            SwiftDictionary<ushort, SwiftList<ushort>> rows = cursor.FilteredPairPhase == 0
 36838                ? _boundaryContactSourcesByTarget
 36839                : _boundaryContactTargetsBySource;
 36840            if (!cursor.HasFilteredPairRow)
 841            {
 21842                if (candidateProbesConsumed == candidateProbeLimit)
 1843                    return false;
 844
 20845                cursor.FilteredPairRowCount = rows.TryGetValue(
 20846                    cursor.FilterGridIndex,
 20847                    out SwiftList<ushort>? row)
 20848                    ? row.Count
 20849                    : 0;
 20850                cursor.FilteredPairRowOrdinal = 0;
 20851                if (cursor.FilteredPairRowCount != 0)
 852                {
 8853                    cursor.PendingFilteredGridIndex = row![0];
 8854                    cursor.FilteredPairRowOrdinal = 1;
 8855                    cursor.HasPendingFilteredPair = true;
 856                }
 20857                cursor.HasFilteredPairRow = true;
 20858                ConsumeBoundaryContactProbe(cursor, ref candidateProbesConsumed);
 859            }
 860
 35861            if (cursor.HasPendingFilteredPair)
 862            {
 12863                if (candidateProbesConsumed == candidateProbeLimit)
 1864                    return false;
 865
 11866                ushort incidentGridIndex = cursor.PendingFilteredGridIndex;
 11867                cursor.PendingFilteredGridIndex = 0;
 11868                cursor.HasPendingFilteredPair = false;
 11869                ConsumeBoundaryContactProbe(cursor, ref candidateProbesConsumed);
 11870                if (cursor.FilteredPairPhase == 0)
 871                {
 3872                    sourceGridIndex = incidentGridIndex;
 3873                    targetGridIndex = cursor.FilterGridIndex;
 874                }
 875                else
 876                {
 8877                    sourceGridIndex = cursor.FilterGridIndex;
 8878                    targetGridIndex = incidentGridIndex;
 879                }
 880
 11881                return true;
 882            }
 883
 23884            if (cursor.FilteredPairRowOrdinal < cursor.FilteredPairRowCount)
 885            {
 4886                if (candidateProbesConsumed == candidateProbeLimit)
 1887                    return false;
 888
 3889                bool foundRow = rows.TryGetValue(
 3890                    cursor.FilterGridIndex,
 3891                    out SwiftList<ushort>? row);
 892                Debug.Assert(foundRow && row != null);
 3893                cursor.PendingFilteredGridIndex = row[cursor.FilteredPairRowOrdinal++];
 3894                cursor.HasPendingFilteredPair = true;
 3895                ConsumeBoundaryContactProbe(cursor, ref candidateProbesConsumed);
 3896                continue;
 897            }
 898
 19899            cursor.FilteredPairPhase++;
 19900            cursor.FilteredPairRowCount = 0;
 19901            cursor.FilteredPairRowOrdinal = 0;
 19902            cursor.HasFilteredPairRow = false;
 903        }
 904
 9905        cursor.CurrentStatus = GridBoundaryContactCursorStatus.Complete;
 9906        return false;
 907    }
 908
 909    private void BindBoundaryContactPair(
 910        GridBoundaryContactCursor cursor,
 911        ushort sourceGridIndex,
 912        ushort targetGridIndex)
 913    {
 28914        VoxelGrid sourceGrid = ActiveGrids[sourceGridIndex];
 28915        VoxelGrid targetGrid = ActiveGrids[targetGridIndex];
 28916        cursor.SourceGridIndex = sourceGridIndex;
 28917        cursor.TargetGridIndex = targetGridIndex;
 28918        cursor.SourceGridSpawnToken = sourceGrid.SpawnToken;
 28919        cursor.TargetGridSpawnToken = targetGrid.SpawnToken;
 28920        cursor.SourceGridLastChangeSequence = sourceGrid.LastChangeSequence;
 28921        cursor.TargetGridLastChangeSequence = targetGrid.LastChangeSequence;
 28922        cursor.SourceConfigurationKey = sourceGrid.Configuration.ToGridKey();
 28923        cursor.TargetConfigurationKey = targetGrid.Configuration.ToGridKey();
 924
 28925        bool createdEnvelope = TryCreateBoundaryContactEnvelope(
 28926            targetGrid,
 28927            out FixedBoundVolume targetEnvelope);
 28928        bool createdSourcePrism = TryCreateTopologyPrism(
 28929            sourceGrid,
 28930            default,
 28931            out GridCellPrism firstSourcePrism);
 932        Debug.Assert(createdEnvelope && createdSourcePrism);
 933
 28934        TopologyVoxelAabb firstSourceBounds = firstSourcePrism.GetAabb();
 28935        Vector3d lowerExtent = sourceGrid.BoundsMin - firstSourceBounds.Min;
 28936        Vector3d upperExtent = firstSourceBounds.Max - sourceGrid.BoundsMin;
 28937        var sourceCandidateBounds = new TopologyVoxelAabb(
 28938            targetEnvelope.Min - upperExtent,
 28939            targetEnvelope.Max + lowerExtent);
 28940        cursor.HasSourceRange = TopologyVoxelRangeUtility.TryGetCandidateRange(
 28941            sourceGrid,
 28942            sourceCandidateBounds,
 28943            out cursor.SourceMinimum,
 28944            out cursor.SourceMaximum);
 28945        cursor.SourceAddress = cursor.SourceMinimum;
 28946    }
 947
 948    private void ProbeBoundaryContactTarget(
 949        GridBoundaryContactCursor cursor,
 950        ref int candidateProbesConsumed)
 951    {
 53952        VoxelGrid targetGrid = ActiveGrids[cursor.TargetGridIndex];
 53953        VoxelIndex targetIndex = cursor.TargetAddress;
 53954        if (!AdvanceBoundaryContactAddress(
 53955                ref cursor.TargetAddress,
 53956                cursor.TargetMinimum,
 53957                cursor.TargetMaximum))
 958        {
 51959            cursor.Stage = GridBoundaryContactCursor.TraversalStage.Source;
 960        }
 53961        ConsumeBoundaryContactProbe(cursor, ref candidateProbesConsumed);
 962
 53963        bool createdTargetPrism = TryCreateTopologyPrism(
 53964            targetGrid,
 53965            targetIndex,
 53966            out GridCellPrism targetPrism);
 967        Debug.Assert(createdTargetPrism);
 968
 53969        VoxelContactManifold contact = GridCellGeometry.GetContact(cursor.SourcePrism, targetPrism);
 53970        if (contact.Kind != VoxelContactKind.Separated)
 971        {
 51972            cursor.PendingContact = contact;
 51973            cursor.HasPendingContact = true;
 974        }
 53975    }
 976
 977    private static bool AdvanceBoundaryContactAddress(
 978        ref VoxelIndex address,
 979        VoxelIndex minimum,
 980        VoxelIndex maximum)
 981    {
 104982        if (address.z < maximum.z)
 983        {
 18984            address.z++;
 18985            return true;
 986        }
 987
 86988        address.z = minimum.z;
 86989        if (address.y < maximum.y)
 990        {
 6991            address.y++;
 6992            return true;
 993        }
 994
 80995        address.y = minimum.y;
 80996        if (address.x < maximum.x)
 997        {
 4998            address.x++;
 4999            return true;
 1000        }
 1001
 761002        return false;
 1003    }
 1004
 1005    private bool TryCreateTopologyPrism(
 1006        VoxelGrid grid,
 1007        VoxelIndex index,
 1008        out GridCellPrism prism)
 1009    {
 1321010        return GridCellGeometry.TryCreatePrism(
 1321011            grid.Configuration.TopologyKind,
 1321012            grid.Configuration.TopologyMetrics,
 1321013            grid.GetWorldPosition(index),
 1321014            new WorldVoxelIndex(SpawnToken, grid.GridIndex, grid.SpawnToken, index),
 1321015            out prism);
 1016    }
 1017
 1018    private static int GetTrailingZeroCount(ulong value)
 1019    {
 341020        int count = 0;
 661021        while ((value & 1UL) == 0)
 1022        {
 321023            value >>= 1;
 321024            count++;
 1025        }
 1026
 341027        return count;
 1028    }
 1029
 1030    private static void ConsumeBoundaryContactProbe(
 1031        GridBoundaryContactCursor cursor,
 1032        ref int candidateProbesConsumed)
 1033    {
 1901034        candidateProbesConsumed++;
 1901035        if (cursor.CandidateOrdinal != ulong.MaxValue)
 1891036            cursor.CandidateOrdinal++;
 1901037    }
 1038
 1039    private void ThrowIfNavigationMaintenanceUnavailable()
 1040    {
 2191041        SwiftThrowHelper.ThrowIfTrue(
 2191042            !IsActive,
 2191043            message: "Cannot capture navigation maintenance state from an inactive world.");
 2171044        SwiftThrowHelper.ThrowIfTrue(
 2171045            Volatile.Read(ref _committedPublicationOwnerThreadId)
 2171046                == Environment.CurrentManagedThreadId,
 2171047            message: "Cannot enter navigation maintenance from a committed-change notification handler.");
 2161048    }
 1049
 1050    private bool TryEnterNavigationMaintenanceSnapshot()
 1051    {
 2221052        _gridLock.EnterReadLock();
 2221053        Monitor.Enter(ChangeSyncRoot);
 2221054        if (_publishedChangeSequence != _changeSequence)
 1055        {
 61056            Monitor.Exit(ChangeSyncRoot);
 61057            _gridLock.ExitReadLock();
 61058            return false;
 1059        }
 1060
 2161061        _navigationMaintenanceOwnerThreadId = Environment.CurrentManagedThreadId;
 2161062        return true;
 1063    }
 1064
 1065    private void ExitNavigationMaintenanceSnapshot()
 1066    {
 2161067        _navigationMaintenanceOwnerThreadId = 0;
 2161068        Monitor.Exit(ChangeSyncRoot);
 2161069        _gridLock.ExitReadLock();
 2161070    }
 1071
 1072    private void WaitForPublishedChangePrefix()
 1073    {
 1074        // A committed handler may legally perform a reentrant structural mutation. Never
 1075        // wait for that handler while holding the read lock it needs to promote past.
 61076        lock (ChangeSyncRoot)
 1077        {
 121078            while (_publishedChangeSequence != _changeSequence)
 61079                Monitor.Wait(ChangeSyncRoot);
 61080        }
 61081    }
 1082
 1083    private bool TryCaptureNavigationBaselineCore(
 1084        GridConfigurationKey configurationKey,
 1085        ReadOnlySpan<VoxelIndex> requestedVoxels,
 1086        out GridNavigationBaseline? baseline)
 1087    {
 1088        Debug.Assert(Monitor.IsEntered(ChangeSyncRoot));
 101089        baseline = null;
 101090        if (!BoundsTracker.TryGetValue(configurationKey, out ushort gridIndex))
 1091        {
 11092            return false;
 1093        }
 1094
 91095        VoxelGrid grid = ActiveGrids[gridIndex];
 91096        if (!AreNavigationBaselineAddressesValid(grid, requestedVoxels))
 1097        {
 31098            return false;
 1099        }
 1100
 61101        NavigationBaselineVoxelState[] states = new NavigationBaselineVoxelState[requestedVoxels.Length];
 281102        for (int i = 0; i < requestedVoxels.Length; i++)
 1103        {
 81104            VoxelIndex requestedVoxel = requestedVoxels[i];
 81105            bool isPresent = grid.TryGetVoxel(requestedVoxel, out Voxel? voxel);
 81106            states[i] = new NavigationBaselineVoxelState(
 81107                requestedVoxel,
 81108                isPresent,
 81109                isPresent ? voxel!.ObstacleCount : (byte)0);
 1110        }
 1111
 61112        baseline = new GridNavigationBaseline(
 61113            _changeSequence,
 61114            SpawnToken,
 61115            grid.SpawnToken,
 61116            grid.LastChangeSequence,
 61117            grid.GridIndex,
 61118            configurationKey,
 61119            states);
 61120        return true;
 1121    }
 1122
 1123    private static bool AreNavigationBaselineAddressesValid(
 1124        VoxelGrid grid,
 1125        ReadOnlySpan<VoxelIndex> requestedVoxels)
 1126    {
 381127        for (int i = 0; i < requestedVoxels.Length; i++)
 1128        {
 131129            VoxelIndex requestedVoxel = requestedVoxels[i];
 131130            if (!grid.IsValidVoxelIndex(requestedVoxel.x, requestedVoxel.y, requestedVoxel.z)
 131131                || (i > 0 && requestedVoxels[i - 1].CompareTo(requestedVoxel) >= 0))
 1132            {
 31133                return false;
 1134            }
 1135        }
 1136
 61137        return true;
 1138    }
 1139
 1140    /// <summary>
 1141    /// Adds a new grid to this world and registers it in the spatial index.
 1142    /// </summary>
 1143    /// <param name="configuration">The grid configuration to normalize and register.</param>
 1144    /// <param name="allocatedIndex">The allocated world-local grid slot on success.</param>
 1145    /// <returns>True if the grid was added; otherwise false.</returns>
 1146    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1147    public bool TryAddGrid(GridConfiguration configuration, out ushort allocatedIndex) =>
 8401148        TryAddGridCore(configuration, null, null, out allocatedIndex);
 1149
 1150    /// <summary>
 1151    /// Adds a new grid to this world and materializes the supplied sparse voxel indices when sparse storage is configur
 1152    /// Dense grids ignore the configured voxel input and materialize every in-bounds voxel.
 1153    /// </summary>
 1154    /// <param name="configuration">The grid configuration to normalize and register.</param>
 1155    /// <param name="configuredVoxels">Grid-local voxel indices to materialize for sparse storage.</param>
 1156    /// <param name="allocatedIndex">The allocated world-local grid slot on success.</param>
 1157    /// <returns>True if the grid was added; otherwise false.</returns>
 1158    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1159    public bool TryAddGrid(
 1160        GridConfiguration configuration,
 1161        IEnumerable<VoxelIndex>? configuredVoxels,
 1162        out ushort allocatedIndex) =>
 971163        TryAddGridCore(configuration, configuredVoxels, null, out allocatedIndex);
 1164
 1165    /// <summary>
 1166    /// Adds a new grid to this world and materializes true cells from the supplied sparse voxel mask when sparse storag
 1167    /// Dense grids ignore the configured voxel input and materialize every in-bounds voxel.
 1168    /// </summary>
 1169    /// <param name="configuration">The grid configuration to normalize and register.</param>
 1170    /// <param name="configuredVoxels">A [x, y, z] mask whose true values identify sparse voxels to materialize. Sparse 
 1171    /// <param name="allocatedIndex">The allocated world-local grid slot on success.</param>
 1172    /// <returns>True if the grid was added; otherwise false.</returns>
 1173    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1174    public bool TryAddGrid(
 1175        GridConfiguration configuration,
 1176        bool[,,]? configuredVoxels,
 1177        out ushort allocatedIndex) =>
 71178        TryAddGridCore(configuration, null, configuredVoxels, out allocatedIndex);
 1179
 1180    private bool TryAddGridCore(
 1181        GridConfiguration configuration,
 1182        IEnumerable<VoxelIndex>? configuredVoxels,
 1183        bool[,,]? configuredVoxelMask,
 1184        out ushort allocatedIndex)
 1185    {
 9441186        allocatedIndex = ushort.MaxValue;
 1187
 9441188        if (!configuration.TryNormalize(out NormalizedGridConfiguration descriptor))
 61189            return false;
 1190
 9381191        GridConfiguration normalizedConfiguration = descriptor.Configuration;
 9381192        IGridTopology topology = descriptor.Topology!;
 9381193        GridDimensions dimensions = descriptor.Dimensions;
 1194
 9381195        if (!TryPrepareConfiguredVoxels(
 9381196            normalizedConfiguration,
 9381197            dimensions,
 9381198            configuredVoxels,
 9381199            configuredVoxelMask,
 9381200            out VoxelIndex[] preparedVoxels))
 1201        {
 121202            return false;
 1203        }
 1204
 9261205        if (_isDisposed)
 1206        {
 31207            GridForgeLogger.Channel.Error($"Grid world not active. Cannot add grids to an inactive world.");
 31208            return false;
 1209        }
 1210
 9231211        GridConfigurationKey boundsKey = descriptor.Key;
 9231212        VoxelGrid? newGrid = null;
 9231213        GridEventInfo addedGridInfo = default;
 1214        bool drainCommittedChanges;
 1215
 9231216        _gridLock.EnterWriteLock();
 1217        try
 1218        {
 9231219            lock (ChangeSyncRoot)
 1220            {
 9231221                if (!CanAddGrid() || TryFindExistingGridUnsafe(boundsKey, out allocatedIndex))
 81222                    return false;
 1223
 9151224                long gridGeneration = RuntimeIdentityAllocator.Allocate(ref _gridGenerationCounter);
 9151225                newGrid = Pools.GridPool.Rent();
 1226
 9151227                allocatedIndex = (ushort)ActiveGrids.Add(newGrid);
 9151228                BoundsTracker.Add(boundsKey, allocatedIndex);
 1229
 9151230                newGrid.Initialize(this, allocatedIndex, gridGeneration, normalizedConfiguration, topology, preparedVoxe
 9151231                UpdateMaxTopologyCellEdge(newGrid.Topology.MaxCellEdge);
 9151232                RegisterGrid(newGrid, allocatedIndex);
 1233
 9151234                Version++;
 9151235                addedGridInfo = CreateGridEventInfo(
 9151236                    newGrid,
 9151237                    GridEventKind.GridAdded,
 9151238                    AllocateChangeStamp());
 9151239                drainCommittedChanges = EnqueueCommittedChange(new GridCommittedChange(addedGridInfo));
 9151240            }
 1241        }
 1242        finally
 1243        {
 9231244            _gridLock.ExitWriteLock();
 9231245        }
 1246
 9151247        if (drainCommittedChanges)
 9141248            DrainCommittedChanges();
 9151249        return true;
 81250    }
 1251
 1252    /// <summary>
 1253    /// Removes a grid from this world and updates all references to ensure integrity.
 1254    /// </summary>
 1255    /// <param name="removeIndex">The world-local grid slot to remove.</param>
 1256    /// <returns>True if the grid was removed; otherwise false.</returns>
 1257    public bool TryRemoveGrid(ushort removeIndex)
 1258    {
 581259        if (!IsActive)
 11260            return false;
 1261
 571262        VoxelGrid? gridToRemove = null;
 571263        GridEventInfo removedGridInfo = default;
 1264        bool drainCommittedChanges;
 1265
 571266        _gridLock.EnterWriteLock();
 1267        try
 1268        {
 571269            lock (ChangeSyncRoot)
 1270            {
 571271                if (!IsActive || !ActiveGrids.IsAllocated(removeIndex))
 11272                    return false;
 1273
 561274                gridToRemove = ActiveGrids[removeIndex];
 561275                Fixed64 removedMaxCellEdge = gridToRemove.Topology.MaxCellEdge;
 561276                UnregisterGrid(gridToRemove, removeIndex);
 561277                BoundsTracker.Remove(gridToRemove.Configuration.ToGridKey());
 561278                ActiveGrids.RemoveAt(removeIndex);
 561279                RecalculateMaxTopologyCellEdgeIfNeeded(removedMaxCellEdge);
 1280
 561281                Version++;
 561282                removedGridInfo = CreateGridEventInfo(
 561283                    gridToRemove,
 561284                    GridEventKind.GridRemoved,
 561285                    AllocateChangeStamp());
 561286                drainCommittedChanges = EnqueueCommittedChange(new GridCommittedChange(removedGridInfo));
 561287            }
 1288        }
 1289        finally
 1290        {
 571291            _gridLock.ExitWriteLock();
 571292        }
 1293
 561294        Pools.GridPool.Release(gridToRemove!);
 561295        if (drainCommittedChanges)
 551296            DrainCommittedChanges();
 1297
 561298        if (ActiveGrids.Count == 0)
 291299            ActiveGrids.TrimExcessCapacity();
 1300
 561301        return true;
 11302    }
 1303
 1304    #endregion
 1305
 1306    private bool CanAddGrid()
 1307    {
 9231308        if (!IsActive)
 1309        {
 21310            GridForgeLogger.Channel.Error($"Grid world not active. Cannot add grids to an inactive world.");
 21311            return false;
 1312        }
 1313
 9211314        if ((uint)ActiveGrids.Count >= MaxGrids)
 1315        {
 21316            GridForgeLogger.Channel.Warn($"No more grids can be added at this time.");
 21317            return false;
 1318        }
 1319
 9191320        return true;
 1321    }
 1322
 1323    private static bool TryPrepareConfiguredVoxels(
 1324        GridConfiguration configuration,
 1325        GridDimensions dimensions,
 1326        IEnumerable<VoxelIndex>? configuredVoxels,
 1327        bool[,,]? configuredVoxelMask,
 1328        out VoxelIndex[] preparedVoxels)
 1329    {
 9381330        preparedVoxels = Array.Empty<VoxelIndex>();
 9381331        if (configuration.StorageKind != GridStorageKind.Sparse)
 8011332            return true;
 1333
 1371334        if (configuredVoxelMask != null)
 71335            return TryPrepareConfiguredVoxelMask(configuredVoxelMask, dimensions, out preparedVoxels);
 1336
 1301337        return TryPrepareConfiguredVoxelIndices(configuredVoxels, dimensions, out preparedVoxels);
 1338    }
 1339
 1340    private static bool TryPrepareConfiguredVoxelMask(
 1341        bool[,,] configuredVoxelMask,
 1342        GridDimensions dimensions,
 1343        out VoxelIndex[] preparedVoxels)
 1344    {
 71345        preparedVoxels = Array.Empty<VoxelIndex>();
 1346
 71347        if (configuredVoxelMask.GetLength(0) != dimensions.Width
 71348            || configuredVoxelMask.GetLength(1) != dimensions.Height
 71349            || configuredVoxelMask.GetLength(2) != dimensions.Length)
 1350        {
 51351            GridForgeLogger.Channel.Warn($"Sparse voxel mask dimensions must match normalized grid dimensions.");
 51352            return false;
 1353        }
 1354
 21355        int configuredCount = 0;
 121356        for (int x = 0; x < dimensions.Width; x++)
 1357        {
 161358            for (int y = 0; y < dimensions.Height; y++)
 1359            {
 241360                for (int z = 0; z < dimensions.Length; z++)
 1361                {
 81362                    if (configuredVoxelMask[x, y, z])
 21363                        configuredCount++;
 1364                }
 1365            }
 1366        }
 1367
 21368        if (configuredCount == 0)
 11369            return true;
 1370
 11371        preparedVoxels = new VoxelIndex[configuredCount];
 11372        int index = 0;
 61373        for (int x = 0; x < dimensions.Width; x++)
 1374        {
 81375            for (int y = 0; y < dimensions.Height; y++)
 1376            {
 121377                for (int z = 0; z < dimensions.Length; z++)
 1378                {
 41379                    if (configuredVoxelMask[x, y, z])
 21380                        preparedVoxels[index++] = new VoxelIndex(x, y, z);
 1381                }
 1382            }
 1383        }
 1384
 11385        return true;
 1386    }
 1387
 1388    private static bool TryPrepareConfiguredVoxelIndices(
 1389        IEnumerable<VoxelIndex>? configuredVoxels,
 1390        GridDimensions dimensions,
 1391        out VoxelIndex[] preparedVoxels)
 1392    {
 1301393        preparedVoxels = Array.Empty<VoxelIndex>();
 1301394        if (configuredVoxels == null)
 341395            return true;
 1396
 961397        SwiftList<VoxelIndex> indices = configuredVoxels is ICollection<VoxelIndex> collection
 961398            ? new SwiftList<VoxelIndex>(collection.Count)
 961399            : new SwiftList<VoxelIndex>();
 1400
 4691401        foreach (VoxelIndex configuredVoxel in configuredVoxels)
 1402        {
 1421403            if (!IsConfiguredVoxelInBounds(configuredVoxel, dimensions))
 1404            {
 71405                GridForgeLogger.Channel.Warn($"Sparse voxel index {configuredVoxel} is outside normalized grid dimension
 71406                return false;
 1407            }
 1408
 1351409            indices.Add(configuredVoxel);
 1410        }
 1411
 891412        if (indices.Count == 0)
 81413            return true;
 1414
 811415        preparedVoxels = indices.ToArray();
 811416        Array.Sort(preparedVoxels, CompareVoxelIndices);
 811417        CompactPreparedVoxels(ref preparedVoxels);
 811418        return true;
 71419    }
 1420
 1421    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1422    private static bool IsConfiguredVoxelInBounds(VoxelIndex voxelIndex, GridDimensions dimensions) =>
 1421423        (uint)voxelIndex.x < (uint)dimensions.Width
 1421424        && (uint)voxelIndex.y < (uint)dimensions.Height
 1421425        && (uint)voxelIndex.z < (uint)dimensions.Length;
 1426
 1427    private static void CompactPreparedVoxels(ref VoxelIndex[] preparedVoxels)
 1428    {
 811429        if (preparedVoxels.Length < 2)
 521430            return;
 1431
 291432        int writeIndex = 1;
 291433        VoxelIndex previous = preparedVoxels[0];
 1661434        for (int readIndex = 1; readIndex < preparedVoxels.Length; readIndex++)
 1435        {
 541436            VoxelIndex current = preparedVoxels[readIndex];
 541437            if (current == previous)
 1438                continue;
 1439
 521440            preparedVoxels[writeIndex++] = current;
 521441            previous = current;
 1442        }
 1443
 291444        if (writeIndex != preparedVoxels.Length)
 21445            Array.Resize(ref preparedVoxels, writeIndex);
 291446    }
 1447
 1448    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1449    private void UpdateMaxTopologyCellEdge(Fixed64 candidate)
 1450    {
 9151451        if (candidate > MaxTopologyCellEdge)
 6241452            MaxTopologyCellEdge = candidate;
 9151453    }
 1454
 1455    private void RecalculateMaxTopologyCellEdgeIfNeeded(Fixed64 removedMaxCellEdge)
 1456    {
 561457        if (removedMaxCellEdge < MaxTopologyCellEdge)
 31458            return;
 1459
 531460        Fixed64 maxCellEdge = Fixed64.Zero;
 1641461        foreach (VoxelGrid grid in ActiveGrids)
 1462        {
 291463            if (grid.Topology.MaxCellEdge > maxCellEdge)
 241464                maxCellEdge = grid.Topology.MaxCellEdge;
 1465        }
 1466
 531467        MaxTopologyCellEdge = maxCellEdge;
 531468    }
 1469
 1470    private bool TryFindExistingGridUnsafe(GridConfigurationKey boundsKey, out ushort allocatedIndex)
 1471    {
 9191472        if (BoundsTracker.TryGetValue(boundsKey, out allocatedIndex))
 1473        {
 41474            GridForgeLogger.Channel.Warn($"A grid with these bounds has already been allocated.");
 41475            return true;
 1476        }
 1477
 9151478        allocatedIndex = ushort.MaxValue;
 9151479        return false;
 1480    }
 1481
 1482    private void RegisterGrid(VoxelGrid newGrid, ushort allocatedIndex)
 1483    {
 9151484        bool hasContactEnvelope = TryCreateBoundaryContactEnvelope(
 9151485            newGrid,
 9151486            out FixedBoundVolume contactEnvelope);
 9151487        _spatialIndex.Insert(
 9151488            allocatedIndex,
 9151489            new FixedBoundVolume(newGrid.BoundsMin, newGrid.BoundsMax),
 9151490            hasContactEnvelope ? contactEnvelope : null);
 1491
 9151492        if (hasContactEnvelope)
 1493        {
 9021494            _spatialIndex.CollectContactCandidates(contactEnvelope, _gridCandidates);
 40741495            for (int i = 0; i < _gridCandidates.Count; i++)
 1496            {
 11351497                ushort candidateIndex = _gridCandidates[i];
 11351498                if (candidateIndex != allocatedIndex)
 2331499                    InsertBoundaryContactPair(allocatedIndex, candidateIndex);
 1500            }
 1501        }
 1502
 9151503        _spatialIndex.CollectCandidates(
 9151504            CreateExpandedBounds(
 9151505                newGrid.BoundsMin,
 9151506                newGrid.BoundsMax,
 9151507                newGrid.Topology.OverlapTolerance),
 9151508            ActiveGrids,
 9151509            _gridCandidates);
 1510
 39201511        for (int candidateIndex = 0; candidateIndex < _gridCandidates.Count; candidateIndex++)
 1512        {
 10451513            ushort neighborIndex = _gridCandidates[candidateIndex];
 10451514            if (neighborIndex == allocatedIndex)
 1515                continue;
 1516
 1301517            VoxelGrid neighborGrid = ActiveGrids[neighborIndex];
 1301518            newGrid.TryAddGridNeighbor(neighborGrid);
 1301519            neighborGrid.TryAddGridNeighbor(newGrid);
 1520        }
 9151521    }
 1522
 1523    private void UnregisterGrid(VoxelGrid gridToRemove, ushort removeIndex)
 1524    {
 561525        _spatialIndex.Remove(removeIndex);
 561526        RemoveBoundaryContactPairs(removeIndex);
 561527        UnlinkGridNeighbors(gridToRemove);
 561528    }
 1529
 1530    private static bool TryCreateBoundaryContactEnvelope(
 1531        VoxelGrid grid,
 1532        out FixedBoundVolume envelope)
 1533    {
 9431534        envelope = default;
 9431535        if (!GridCellGeometry.TryCreatePrism(
 9431536                grid.Configuration.TopologyKind,
 9431537                grid.Configuration.TopologyMetrics,
 9431538                grid.BoundsMin,
 9431539                default,
 9431540                out GridCellPrism minimumPrism)
 9431541            || !GridCellGeometry.TryCreatePrism(
 9431542                grid.Configuration.TopologyKind,
 9431543                grid.Configuration.TopologyMetrics,
 9431544                grid.BoundsMax,
 9431545                default,
 9431546                out GridCellPrism maximumPrism))
 1547        {
 131548            return false;
 1549        }
 1550
 9301551        TopologyVoxelAabb minimum = minimumPrism.GetAabb();
 9301552        TopologyVoxelAabb maximum = maximumPrism.GetAabb();
 9301553        envelope = new FixedBoundVolume(
 9301554            new Vector3d(
 9301555                FixedMath.Min(minimum.Min.X, maximum.Min.X),
 9301556                FixedMath.Min(minimum.Min.Y, maximum.Min.Y),
 9301557                FixedMath.Min(minimum.Min.Z, maximum.Min.Z)),
 9301558            new Vector3d(
 9301559                FixedMath.Max(minimum.Max.X, maximum.Max.X),
 9301560                FixedMath.Max(minimum.Max.Y, maximum.Max.Y),
 9301561                FixedMath.Max(minimum.Max.Z, maximum.Max.Z)));
 9301562        return true;
 1563    }
 1564
 1565    private void InsertBoundaryContactPair(ushort firstGridIndex, ushort secondGridIndex)
 1566    {
 2331567        ushort source = firstGridIndex < secondGridIndex ? firstGridIndex : secondGridIndex;
 2331568        ushort target = firstGridIndex < secondGridIndex ? secondGridIndex : firstGridIndex;
 2331569        SwiftList<ushort> targets = GetOrCreateBoundaryContactRow(
 2331570            _boundaryContactTargetsBySource,
 2331571            source,
 2331572            out bool addedSourceRow);
 2331573        InsertSorted(targets, target);
 1574
 2331575        InsertSorted(
 2331576            GetOrCreateBoundaryContactRow(_boundaryContactSourcesByTarget, target, out _),
 2331577            source);
 2331578        if (addedSourceRow)
 1851579            SetBoundaryContactSource(source);
 2331580    }
 1581
 1582    private void RemoveBoundaryContactPairs(ushort gridIndex)
 1583    {
 561584        if (_boundaryContactTargetsBySource.TryGetValue(
 561585                gridIndex,
 561586                out SwiftList<ushort>? targets))
 1587        {
 441588            for (int i = 0; i < targets.Count; i++)
 1589            {
 151590                RemoveBoundaryContactIncident(
 151591                    _boundaryContactSourcesByTarget,
 151592                    targets[i],
 151593                    gridIndex,
 151594                    clearSourceBit: false);
 1595            }
 1596
 71597            _boundaryContactTargetsBySource.Remove(gridIndex);
 71598            SwiftListPool<ushort>.Shared.Release(targets);
 71599            ClearBoundaryContactSource(gridIndex);
 1600        }
 1601
 561602        if (_boundaryContactSourcesByTarget.TryGetValue(
 561603                gridIndex,
 561604                out SwiftList<ushort>? sources))
 1605        {
 601606            for (int i = 0; i < sources.Count; i++)
 1607            {
 161608                RemoveBoundaryContactIncident(
 161609                    _boundaryContactTargetsBySource,
 161610                    sources[i],
 161611                    gridIndex,
 161612                    clearSourceBit: true);
 1613            }
 1614
 141615            _boundaryContactSourcesByTarget.Remove(gridIndex);
 141616            SwiftListPool<ushort>.Shared.Release(sources);
 1617        }
 561618    }
 1619
 1620    private void RemoveBoundaryContactIncident(
 1621        SwiftDictionary<ushort, SwiftList<ushort>> rows,
 1622        ushort rowIndex,
 1623        ushort incidentIndex,
 1624        bool clearSourceBit)
 1625    {
 311626        bool foundRow = rows.TryGetValue(rowIndex, out SwiftList<ushort>? row);
 1627        Debug.Assert(foundRow && row != null);
 311628        RemoveSorted(row, incidentIndex);
 311629        if (row.Count != 0)
 121630            return;
 1631
 191632        rows.Remove(rowIndex);
 191633        SwiftListPool<ushort>.Shared.Release(row);
 191634        if (clearSourceBit)
 131635            ClearBoundaryContactSource(rowIndex);
 191636    }
 1637
 1638    private static SwiftList<ushort> GetOrCreateBoundaryContactRow(
 1639        SwiftDictionary<ushort, SwiftList<ushort>> rows,
 1640        ushort rowIndex,
 1641        out bool added)
 1642    {
 4661643        if (rows.TryGetValue(rowIndex, out SwiftList<ushort>? row))
 1644        {
 971645            added = false;
 971646            return row;
 1647        }
 1648
 3691649        row = SwiftListPool<ushort>.Shared.Rent();
 3691650        rows.Add(rowIndex, row);
 3691651        added = true;
 3691652        return row;
 1653    }
 1654
 1655    private static void InsertSorted(SwiftList<ushort> row, ushort value)
 1656    {
 4661657        int index = FindSortedIndex(row, value);
 4661658        row.Insert(index, value);
 4661659    }
 1660
 1661    private static void RemoveSorted(SwiftList<ushort> row, ushort value)
 1662    {
 311663        int index = FindSortedIndex(row, value);
 1664        Debug.Assert(index < row.Count && row[index] == value);
 311665        row.RemoveAt(index);
 311666    }
 1667
 1668    private static int FindSortedIndex(SwiftList<ushort> row, ushort value)
 1669    {
 4971670        int minimum = 0;
 4971671        int maximum = row.Count;
 6751672        while (minimum < maximum)
 1673        {
 1781674            int middle = minimum + ((maximum - minimum) >> 1);
 1781675            if (row[middle] < value)
 1171676                minimum = middle + 1;
 1677            else
 611678                maximum = middle;
 1679        }
 1680
 4971681        return minimum;
 1682    }
 1683
 1684    private void SetBoundaryContactSource(ushort source)
 1685    {
 1851686        _boundaryContactSourceWords ??= new ulong[BoundaryContactSourceWordCount];
 1851687        _boundaryContactSourceSummaryWords ??= new ulong[BoundaryContactSourceSummaryWordCount];
 1851688        int wordIndex = source >> 6;
 1851689        _boundaryContactSourceWords[wordIndex] |= 1UL << (source & 63);
 1851690        _boundaryContactSourceSummaryWords[wordIndex >> 6] |= 1UL << (wordIndex & 63);
 1851691        _boundaryContactSourceSummaryLength = Math.Max(
 1851692            _boundaryContactSourceSummaryLength,
 1851693            (wordIndex >> 6) + 1);
 1851694    }
 1695
 1696    private void ClearBoundaryContactSource(ushort source)
 1697    {
 1698        Debug.Assert(_boundaryContactSourceWords != null && _boundaryContactSourceSummaryWords != null);
 201699        int wordIndex = source >> 6;
 201700        _boundaryContactSourceWords![wordIndex] &= ~(1UL << (source & 63));
 201701        if (_boundaryContactSourceWords[wordIndex] == 0)
 161702            _boundaryContactSourceSummaryWords![wordIndex >> 6] &= ~(1UL << (wordIndex & 63));
 1703
 361704        while (_boundaryContactSourceSummaryLength > 0
 361705            && _boundaryContactSourceSummaryWords[_boundaryContactSourceSummaryLength - 1] == 0)
 1706        {
 161707            _boundaryContactSourceSummaryLength--;
 1708        }
 1709
 201710    }
 1711
 1712    private void ReleaseBoundaryContactPairs()
 1713    {
 19141714        foreach (SwiftList<ushort> row in _boundaryContactTargetsBySource.Values)
 1651715            SwiftListPool<ushort>.Shared.Release(row);
 19121716        foreach (SwiftList<ushort> row in _boundaryContactSourcesByTarget.Values)
 1641717            SwiftListPool<ushort>.Shared.Release(row);
 1718
 7921719        _boundaryContactTargetsBySource.Clear();
 7921720        _boundaryContactSourcesByTarget.Clear();
 7921721        _boundaryContactSourceWords = null;
 7921722        _boundaryContactSourceSummaryWords = null;
 7921723        _boundaryContactSourceSummaryLength = 0;
 7921724    }
 1725
 1726    private void UnlinkGridNeighbors(VoxelGrid gridToRemove)
 1727    {
 561728        if (!gridToRemove.IsConjoined)
 441729            return;
 1730
 121731        var neighborSets = gridToRemove.Neighbors!.DenseValues;
 121732        int neighborSetCount = gridToRemove.Neighbors.Count;
 541733        for (int neighborSetIndex = 0; neighborSetIndex < neighborSetCount; neighborSetIndex++)
 1734        {
 701735            foreach (int neighborIndex in neighborSets[neighborSetIndex])
 1736            {
 201737                VoxelGrid neighborGrid = ActiveGrids[neighborIndex];
 201738                neighborGrid.TryRemoveGridNeighbor(gridToRemove);
 1739            }
 1740        }
 121741    }
 1742
 1743    internal bool CollectGridCandidates(
 1744        Vector3d boundsMin,
 1745        Vector3d boundsMax,
 1746        SwiftList<ushort> candidates,
 1747        int candidateLimit)
 1748    {
 9901749        SwiftThrowHelper.ThrowIfNegative(candidateLimit, nameof(candidateLimit));
 9901750        candidates.Clear();
 9901751        if (ActiveGrids.Count == 0)
 61752            return true;
 1753
 9841754        FixedBoundVolume queryBounds = new(boundsMin, boundsMax);
 9841755        if (ActiveGrids.Count <= candidateLimit)
 1756        {
 9581757            _spatialIndex.CollectCandidates(queryBounds, ActiveGrids, candidates);
 9581758            return true;
 1759        }
 1760
 2231761        foreach (VoxelGrid grid in ActiveGrids)
 1762        {
 891763            FixedBoundVolume gridBounds = new(grid.BoundsMin, grid.BoundsMax);
 891764            if (!gridBounds.Intersects(queryBounds))
 1765                continue;
 691766            if (candidates.Count >= candidateLimit)
 71767                return false;
 1768
 621769            candidates.Add(grid.GridIndex);
 1770        }
 1771
 191772        if (candidates.Count > 1)
 181773            candidates.SortInPlace();
 191774        return true;
 71775    }
 1776
 1777    private static FixedBoundVolume CreateExpandedBounds(
 1778        Vector3d boundsMin,
 1779        Vector3d boundsMax,
 1780        Fixed64 padding)
 1781    {
 9191782        Vector3d expansion = new(padding, padding, padding);
 9191783        return new FixedBoundVolume(boundsMin - expansion, boundsMax + expansion);
 1784    }
 1785
 1786    #region Lookup
 1787
 1788    /// <summary>
 1789    /// Retrieves a grid by its world-local index.
 1790    /// </summary>
 1791    /// <param name="index">The world-local grid slot to resolve.</param>
 1792    /// <param name="outGrid">The resolved grid, if found.</param>
 1793    /// <returns>True if the grid was resolved; otherwise false.</returns>
 1794    public bool TryGetGrid(int index, out VoxelGrid? outGrid)
 1795    {
 15701796        outGrid = null;
 15701797        if (!CanResolveGrid(index))
 181798            return false;
 1799
 15521800        outGrid = ActiveGrids[index];
 15521801        return true;
 1802    }
 1803
 1804    /// <summary>
 1805    /// Retrieves the grid containing a given world position.
 1806    /// </summary>
 1807    /// <param name="position">The world position to resolve.</param>
 1808    /// <param name="outGrid">The resolved grid, if found.</param>
 1809    /// <returns>True if a containing grid was found; otherwise false.</returns>
 1810    public bool TryGetGrid(Vector3d position, out VoxelGrid? outGrid)
 1811    {
 1541812        outGrid = null;
 1541813        if (!CanResolvePosition())
 21814            return false;
 1815
 1521816        _spatialIndex.CollectPointCandidates(position, _gridCandidates);
 1521817        if (TryGetContainingGrid(position, _gridCandidates, out outGrid))
 641818            return true;
 1819
 881820        GridForgeLogger.Channel.Info($"No grid contains position {position}.");
 881821        return false;
 1822    }
 1823
 1824    /// <summary>
 1825    /// Retrieves the grid containing a 2D XZ-plane world position on the default world Y layer.
 1826    /// </summary>
 1827    /// <param name="position">The 2D position whose X component maps to world X and Y component maps to world Z.</param
 1828    /// <param name="outGrid">The resolved grid, if found.</param>
 1829    /// <returns>True if a containing grid was found; otherwise false.</returns>
 1830    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1831    public bool TryGetGrid(Vector2d position, out VoxelGrid? outGrid) =>
 11832         TryGetGrid(position, default, out outGrid);
 1833
 1834    /// <summary>
 1835    /// Retrieves the grid containing a 2D XZ-plane world position on the supplied world Y layer.
 1836    /// </summary>
 1837    /// <param name="position">The 2D position whose X component maps to world X and Y component maps to world Z.</param
 1838    /// <param name="layerY">The world Y layer to resolve. Defaults to zero when omitted by paired overloads.</param>
 1839    /// <param name="outGrid">The resolved grid, if found.</param>
 1840    /// <returns>True if a containing grid was found; otherwise false.</returns>
 1841    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1842    public bool TryGetGrid(Vector2d position, Fixed64 layerY, out VoxelGrid? outGrid) =>
 31843        TryGetGrid(GridPlane2d.ToWorld(position, layerY), out outGrid);
 1844
 1845    /// <summary>
 1846    /// Retrieves the active grid whose bounds are nearest to the supplied world position.
 1847    /// </summary>
 1848    /// <param name="position">The world position to resolve.</param>
 1849    /// <param name="outGrid">The closest grid, if found.</param>
 1850    /// <param name="topologyKind">Optional topology filter. When supplied, only grids using the requested topology are 
 1851    /// <returns>True if a closest active grid was resolved; otherwise false.</returns>
 1852    public bool TryGetClosestGrid(
 1853        Vector3d position,
 1854        out VoxelGrid? outGrid,
 1855        GridTopologyKind? topologyKind = null)
 1856    {
 271857        outGrid = null;
 271858        if (!CanResolveActiveGrid())
 11859            return false;
 1860
 261861        Fixed64 closestDistanceSquared = Fixed64.MaxValue;
 1241862        foreach (VoxelGrid candidateGrid in ActiveGrids)
 1863        {
 361864            if (!candidateGrid.IsActive
 361865                || !MatchesTopologyKind(candidateGrid, topologyKind))
 1866            {
 1867                continue;
 1868            }
 1869
 301870            Fixed64 distanceSquared = GetDistanceSquaredToBounds(position, candidateGrid.BoundsMin, candidateGrid.Bounds
 301871            if (outGrid == null || distanceSquared < closestDistanceSquared)
 1872            {
 231873                outGrid = candidateGrid;
 231874                closestDistanceSquared = distanceSquared;
 1875            }
 1876        }
 1877
 261878        return outGrid != null;
 1879    }
 1880
 1881    /// <summary>
 1882    /// Retrieves the active grid whose bounds are nearest to a 2D XZ-plane world position on the default world Y layer.
 1883    /// </summary>
 1884    /// <param name="position">The 2D position whose X component maps to world X and Y component maps to world Z.</param
 1885    /// <param name="outGrid">The closest grid, if found.</param>
 1886    /// <param name="topologyKind">Optional topology filter. When supplied, only grids using the requested topology are 
 1887    /// <returns>True if a closest active grid was resolved; otherwise false.</returns>
 1888    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1889    public bool TryGetClosestGrid(
 1890        Vector2d position,
 1891        out VoxelGrid? outGrid,
 1892        GridTopologyKind? topologyKind = null) =>
 11893        TryGetClosestGrid(position, default, out outGrid, topologyKind);
 1894
 1895    /// <summary>
 1896    /// Retrieves the active grid whose bounds are nearest to a 2D XZ-plane world position on the supplied world Y layer
 1897    /// </summary>
 1898    /// <param name="position">The 2D position whose X component maps to world X and Y component maps to world Z.</param
 1899    /// <param name="layerY">The world Y layer to resolve. Defaults to zero when omitted by paired overloads.</param>
 1900    /// <param name="outGrid">The closest grid, if found.</param>
 1901    /// <param name="topologyKind">Optional topology filter. When supplied, only grids using the requested topology are 
 1902    /// <returns>True if a closest active grid was resolved; otherwise false.</returns>
 1903    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1904    public bool TryGetClosestGrid(
 1905        Vector2d position,
 1906        Fixed64 layerY,
 1907        out VoxelGrid? outGrid,
 1908        GridTopologyKind? topologyKind = null) =>
 31909        TryGetClosestGrid(GridPlane2d.ToWorld(position, layerY), out outGrid, topologyKind);
 1910
 1911    /// <summary>
 1912    /// Retrieves a grid by a world-scoped voxel identity.
 1913    /// </summary>
 1914    /// <param name="worldVoxelIndex">The voxel identity whose grid should be resolved.</param>
 1915    /// <param name="result">The resolved grid, if found.</param>
 1916    /// <returns>True if the grid was resolved; otherwise false.</returns>
 1917    public bool TryGetGrid(WorldVoxelIndex worldVoxelIndex, out VoxelGrid? result)
 1918    {
 12761919        result = null;
 12761920        if (worldVoxelIndex.WorldSpawnToken != SpawnToken
 12761921            || !TryGetGrid(worldVoxelIndex.GridIndex, out VoxelGrid? resolvedGrid)
 12761922            || worldVoxelIndex.GridSpawnToken != resolvedGrid!.SpawnToken)
 1923        {
 231924            return false;
 1925        }
 1926
 12531927        result = resolvedGrid;
 12531928        return true;
 1929    }
 1930
 1931    /// <summary>
 1932    /// Retrieves the grid and voxel containing a given world position.
 1933    /// </summary>
 1934    /// <param name="position">The world position to resolve.</param>
 1935    /// <param name="outGrid">The resolved grid, if found.</param>
 1936    /// <param name="outVoxel">The resolved voxel, if found.</param>
 1937    /// <returns>True if both the grid and voxel were resolved; otherwise false.</returns>
 1938    public bool TryGetGridAndVoxel(
 1939        Vector3d position,
 1940        out VoxelGrid? outGrid,
 1941        out Voxel? outVoxel)
 1942    {
 281943        outVoxel = null;
 281944        return TryGetGrid(position, out outGrid)
 281945            && outGrid!.TryGetVoxel(position, out outVoxel);
 1946    }
 1947
 1948    /// <summary>
 1949    /// Retrieves the grid and voxel containing a 2D XZ-plane world position on the default world Y layer.
 1950    /// </summary>
 1951    /// <param name="position">The 2D position whose X component maps to world X and Y component maps to world Z.</param
 1952    /// <param name="outGrid">The resolved grid, if found.</param>
 1953    /// <param name="outVoxel">The resolved voxel, if found.</param>
 1954    /// <returns>True if both the grid and voxel were resolved; otherwise false.</returns>
 1955    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1956    public bool TryGetGridAndVoxel(
 1957        Vector2d position,
 1958        out VoxelGrid? outGrid,
 1959        out Voxel? outVoxel) =>
 11960         TryGetGridAndVoxel(position, default, out outGrid, out outVoxel);
 1961
 1962    /// <summary>
 1963    /// Retrieves the grid and voxel containing a 2D XZ-plane world position on the supplied world Y layer.
 1964    /// </summary>
 1965    /// <param name="position">The 2D position whose X component maps to world X and Y component maps to world Z.</param
 1966    /// <param name="layerY">The world Y layer to resolve. Defaults to zero when omitted by paired overloads.</param>
 1967    /// <param name="outGrid">The resolved grid, if found.</param>
 1968    /// <param name="outVoxel">The resolved voxel, if found.</param>
 1969    /// <returns>True if both the grid and voxel were resolved; otherwise false.</returns>
 1970    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1971    public bool TryGetGridAndVoxel(
 1972        Vector2d position,
 1973        Fixed64 layerY,
 1974        out VoxelGrid? outGrid,
 1975        out Voxel? outVoxel) =>
 31976         TryGetGridAndVoxel(GridPlane2d.ToWorld(position, layerY), out outGrid, out outVoxel);
 1977
 1978    /// <summary>
 1979    /// Retrieves the physical voxel whose center is nearest to the supplied world position and the grid that owns it.
 1980    /// Sparse grids only consider configured physical voxels.
 1981    /// </summary>
 1982    /// <param name="position">The world position to resolve.</param>
 1983    /// <param name="outGrid">The grid that owns the closest physical voxel, if found.</param>
 1984    /// <param name="outVoxel">The closest physical voxel, if found.</param>
 1985    /// <param name="topologyKind">Optional topology filter. When supplied, only grids using the requested topology are 
 1986    /// <returns>True if a physical voxel was resolved; otherwise false.</returns>
 1987    public bool TryGetClosestGridAndVoxel(
 1988        Vector3d position,
 1989        out VoxelGrid? outGrid,
 1990        out Voxel? outVoxel,
 1991        GridTopologyKind? topologyKind = null)
 1992    {
 161993        outGrid = null;
 161994        outVoxel = null;
 161995        if (!CanResolveActiveGrid())
 21996            return false;
 1997
 141998        Fixed64 closestDistanceSquared = Fixed64.MaxValue;
 141999        if (TryGetClosestGrid(position, out VoxelGrid? closestBoundsGrid, topologyKind)
 142000            && closestBoundsGrid!.ConfiguredVoxelCount != 0)
 2001        {
 102002            bool resolved = closestBoundsGrid.TryGetClosestVoxel(
 102003                position,
 102004                out outVoxel,
 102005                out closestDistanceSquared);
 2006            Debug.Assert(resolved);
 102007            outGrid = closestBoundsGrid;
 2008        }
 2009
 662010        foreach (VoxelGrid candidateGrid in ActiveGrids)
 2011        {
 192012            if (candidateGrid == null
 192013                || !candidateGrid.IsActive
 192014                || candidateGrid.ConfiguredVoxelCount == 0
 192015                || !MatchesTopologyKind(candidateGrid, topologyKind))
 2016            {
 2017                continue;
 2018            }
 142019            if (ReferenceEquals(candidateGrid, outGrid))
 2020                continue;
 2021
 52022            Fixed64 boundsDistanceSquared = GetDistanceSquaredToBounds(position, candidateGrid.BoundsMin, candidateGrid.
 52023            if (outVoxel != null && boundsDistanceSquared > closestDistanceSquared)
 2024                continue;
 2025
 42026            candidateGrid.TryGetClosestVoxel(
 42027                position,
 42028                out Voxel? candidateVoxel,
 42029                out Fixed64 candidateDistanceSquared);
 2030
 42031            if (IsBetterClosestVoxel(
 42032                candidateDistanceSquared,
 42033                candidateGrid,
 42034                candidateVoxel!,
 42035                closestDistanceSquared,
 42036                outGrid,
 42037                outVoxel))
 2038            {
 32039                outGrid = candidateGrid;
 32040                outVoxel = candidateVoxel;
 32041                closestDistanceSquared = candidateDistanceSquared;
 2042            }
 2043        }
 2044
 142045        return outVoxel != null;
 2046    }
 2047
 2048    /// <summary>
 2049    /// Retrieves the physical voxel whose center is nearest to a 2D XZ-plane world position on the default world Y laye
 2050    /// Sparse grids only consider configured physical voxels.
 2051    /// </summary>
 2052    /// <param name="position">The 2D position whose X component maps to world X and Y component maps to world Z.</param
 2053    /// <param name="outGrid">The grid that owns the closest physical voxel, if found.</param>
 2054    /// <param name="outVoxel">The closest physical voxel, if found.</param>
 2055    /// <param name="topologyKind">Optional topology filter. When supplied, only grids using the requested topology are 
 2056    /// <returns>True if a physical voxel was resolved; otherwise false.</returns>
 2057    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 2058    public bool TryGetClosestGridAndVoxel(
 2059        Vector2d position,
 2060        out VoxelGrid? outGrid,
 2061        out Voxel? outVoxel,
 2062        GridTopologyKind? topologyKind = null) =>
 12063        TryGetClosestGridAndVoxel(position, default, out outGrid, out outVoxel, topologyKind);
 2064
 2065    /// <summary>
 2066    /// Retrieves the physical voxel whose center is nearest to a 2D XZ-plane world position on the supplied world Y lay
 2067    /// Sparse grids only consider configured physical voxels.
 2068    /// </summary>
 2069    /// <param name="position">The 2D position whose X component maps to world X and Y component maps to world Z.</param
 2070    /// <param name="layerY">The world Y layer to resolve. Defaults to zero when omitted by paired overloads.</param>
 2071    /// <param name="outGrid">The grid that owns the closest physical voxel, if found.</param>
 2072    /// <param name="outVoxel">The closest physical voxel, if found.</param>
 2073    /// <param name="topologyKind">Optional topology filter. When supplied, only grids using the requested topology are 
 2074    /// <returns>True if a physical voxel was resolved; otherwise false.</returns>
 2075    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 2076    public bool TryGetClosestGridAndVoxel(
 2077        Vector2d position,
 2078        Fixed64 layerY,
 2079        out VoxelGrid? outGrid,
 2080        out Voxel? outVoxel,
 2081        GridTopologyKind? topologyKind = null) =>
 32082        TryGetClosestGridAndVoxel(GridPlane2d.ToWorld(position, layerY), out outGrid, out outVoxel, topologyKind);
 2083
 2084    /// <summary>
 2085    /// Retrieves the grid and voxel for a given voxel identity.
 2086    /// </summary>
 2087    /// <param name="worldVoxelIndex">The voxel identity to resolve.</param>
 2088    /// <param name="outGrid">The resolved grid, if found.</param>
 2089    /// <param name="result">The resolved voxel, if found.</param>
 2090    /// <returns>True if both the grid and voxel were resolved; otherwise false.</returns>
 2091    public bool TryGetGridAndVoxel(
 2092        WorldVoxelIndex worldVoxelIndex,
 2093        out VoxelGrid? outGrid,
 2094        out Voxel? result)
 2095    {
 702096        result = null;
 702097        return TryGetGrid(worldVoxelIndex, out outGrid)
 702098            && outGrid!.TryGetVoxel(worldVoxelIndex.VoxelIndex, out result);
 2099    }
 2100
 2101    /// <summary>
 2102    /// Retrieves a voxel from a world position.
 2103    /// </summary>
 2104    /// <param name="position">The world position to resolve.</param>
 2105    /// <param name="result">The resolved voxel, if found.</param>
 2106    /// <returns>True if the voxel was resolved; otherwise false.</returns>
 2107    public bool TryGetVoxel(
 2108        Vector3d position,
 2109        out Voxel? result)
 2110    {
 1012111        result = null;
 1012112        return TryGetGrid(position, out VoxelGrid? grid)
 1012113            && grid!.TryGetVoxel(position, out result);
 2114    }
 2115
 2116    /// <summary>
 2117    /// Retrieves a voxel from a 2D XZ-plane world position on the default world Y layer.
 2118    /// </summary>
 2119    /// <param name="position">The 2D position whose X component maps to world X and Y component maps to world Z.</param
 2120    /// <param name="result">The resolved voxel, if found.</param>
 2121    /// <returns>True if the voxel was resolved; otherwise false.</returns>
 2122    public bool TryGetVoxel(
 2123        Vector2d position,
 2124        out Voxel? result)
 2125    {
 12126        return TryGetVoxel(position, default, out result);
 2127    }
 2128
 2129    /// <summary>
 2130    /// Retrieves a voxel from a 2D XZ-plane world position on the supplied world Y layer.
 2131    /// </summary>
 2132    /// <param name="position">The 2D position whose X component maps to world X and Y component maps to world Z.</param
 2133    /// <param name="layerY">The world Y layer to resolve. Defaults to zero when omitted by paired overloads.</param>
 2134    /// <param name="result">The resolved voxel, if found.</param>
 2135    /// <returns>True if the voxel was resolved; otherwise false.</returns>
 2136    public bool TryGetVoxel(
 2137        Vector2d position,
 2138        Fixed64 layerY,
 2139        out Voxel? result)
 2140    {
 32141        return TryGetVoxel(GridPlane2d.ToWorld(position, layerY), out result);
 2142    }
 2143
 2144    /// <summary>
 2145    /// Retrieves the physical voxel whose center is nearest to the supplied world position.
 2146    /// Sparse grids only consider configured physical voxels.
 2147    /// </summary>
 2148    /// <param name="position">The world position to resolve.</param>
 2149    /// <param name="result">The closest physical voxel, if found.</param>
 2150    /// <param name="topologyKind">Optional topology filter. When supplied, only grids using the requested topology are 
 2151    /// <returns>True if a physical voxel was resolved; otherwise false.</returns>
 2152    public bool TryGetClosestVoxel(
 2153        Vector3d position,
 2154        out Voxel? result,
 2155        GridTopologyKind? topologyKind = null)
 2156    {
 62157        result = null;
 62158        if (!TryGetClosestGridAndVoxel(position, out _, out Voxel? closestVoxel, topologyKind))
 22159            return false;
 2160
 42161        result = closestVoxel;
 42162        return true;
 2163    }
 2164
 2165    /// <summary>
 2166    /// Retrieves the physical voxel whose center is nearest to a 2D XZ-plane world position on the default world Y laye
 2167    /// Sparse grids only consider configured physical voxels.
 2168    /// </summary>
 2169    /// <param name="position">The 2D position whose X component maps to world X and Y component maps to world Z.</param
 2170    /// <param name="result">The closest physical voxel, if found.</param>
 2171    /// <param name="topologyKind">Optional topology filter. When supplied, only grids using the requested topology are 
 2172    /// <returns>True if a physical voxel was resolved; otherwise false.</returns>
 2173    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 2174    public bool TryGetClosestVoxel(
 2175        Vector2d position,
 2176        out Voxel? result,
 2177        GridTopologyKind? topologyKind = null) =>
 12178        TryGetClosestVoxel(position, default, out result, topologyKind);
 2179
 2180    /// <summary>
 2181    /// Retrieves the physical voxel whose center is nearest to a 2D XZ-plane world position on the supplied world Y lay
 2182    /// Sparse grids only consider configured physical voxels.
 2183    /// </summary>
 2184    /// <param name="position">The 2D position whose X component maps to world X and Y component maps to world Z.</param
 2185    /// <param name="layerY">The world Y layer to resolve. Defaults to zero when omitted by paired overloads.</param>
 2186    /// <param name="result">The closest physical voxel, if found.</param>
 2187    /// <param name="topologyKind">Optional topology filter. When supplied, only grids using the requested topology are 
 2188    /// <returns>True if a physical voxel was resolved; otherwise false.</returns>
 2189    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 2190    public bool TryGetClosestVoxel(
 2191        Vector2d position,
 2192        Fixed64 layerY,
 2193        out Voxel? result,
 2194        GridTopologyKind? topologyKind = null) =>
 32195        TryGetClosestVoxel(GridPlane2d.ToWorld(position, layerY), out result, topologyKind);
 2196
 2197    /// <summary>
 2198    /// Retrieves a voxel from a world-scoped voxel identity.
 2199    /// </summary>
 2200    /// <param name="worldVoxelIndex">The voxel identity to resolve.</param>
 2201    /// <param name="result">The resolved voxel, if found.</param>
 2202    /// <returns>True if the voxel was resolved; otherwise false.</returns>
 2203    public bool TryGetVoxel(
 2204        WorldVoxelIndex worldVoxelIndex,
 2205        out Voxel? result)
 2206    {
 32207        result = null;
 32208        return TryGetGrid(worldVoxelIndex, out VoxelGrid? grid)
 32209            && grid!.TryGetVoxel(worldVoxelIndex.VoxelIndex, out result);
 2210    }
 2211
 2212    #endregion
 2213
 2214    #region Internal Helpers
 2215
 2216    /// <summary>
 2217    /// Increments the version of the specified grid and optionally the world version.
 2218    /// </summary>
 2219    public void IncrementGridVersion(int index, bool significant = false)
 2220    {
 52221        if (!IsActive)
 2222        {
 32223            GridForgeLogger.Channel.Warn($"Grid world not active. Cannot increment grid versions.");
 32224            return;
 2225        }
 2226
 22227        _gridLock.EnterWriteLock();
 2228        try
 2229        {
 22230            if (significant)
 12231                Version++;
 2232
 22233            if (ActiveGrids.IsAllocated(index))
 12234                ActiveGrids[index].IncrementVersion();
 22235        }
 2236        finally
 2237        {
 22238            _gridLock.ExitWriteLock();
 22239        }
 22240    }
 2241
 2242    /// <summary>
 2243    /// Finds active grids in this world that overlap the supplied target grid.
 2244    /// </summary>
 2245    public IEnumerable<VoxelGrid> FindOverlappingGrids(VoxelGrid targetGrid)
 2246    {
 52247        SwiftList<VoxelGrid> overlappingGrids = new();
 52248        FindOverlappingGridsInto(targetGrid, overlappingGrids);
 52249        return overlappingGrids;
 2250    }
 2251
 2252    /// <summary>
 2253    /// Clears and fills caller-owned storage with active grids that overlap the supplied target grid.
 2254    /// </summary>
 2255    /// <param name="targetGrid">The grid whose expanded topology bounds define the overlap query.</param>
 2256    /// <param name="results">Caller-owned storage cleared and filled in ascending grid-slot order.</param>
 2257    public void FindOverlappingGridsInto(VoxelGrid targetGrid, SwiftList<VoxelGrid> results)
 2258    {
 72259        SwiftThrowHelper.ThrowIfNull(targetGrid, nameof(targetGrid));
 72260        SwiftThrowHelper.ThrowIfNull(results, nameof(results));
 2261
 72262        results.Clear();
 2263
 72264        if (!IsActive)
 2265        {
 32266            GridForgeLogger.Channel.Warn($"Grid world not active. Cannot resolve overlaps.");
 32267            return;
 2268        }
 2269
 42270        _spatialIndex.CollectCandidates(
 42271            CreateExpandedBounds(
 42272                targetGrid.BoundsMin,
 42273                targetGrid.BoundsMax,
 42274                targetGrid.Topology.OverlapTolerance),
 42275            ActiveGrids,
 42276            _gridCandidates);
 262277        for (int candidateIndex = 0; candidateIndex < _gridCandidates.Count; candidateIndex++)
 92278            TryAddOverlappingGrid(targetGrid, _gridCandidates[candidateIndex], results);
 42279    }
 2280
 2281    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 2282    private static bool MatchesTopologyKind(VoxelGrid grid, GridTopologyKind? topologyKind) =>
 532283        !topologyKind.HasValue || grid.TopologyKind == topologyKind.Value;
 2284
 2285    private static bool IsBetterClosestVoxel(
 2286        Fixed64 candidateDistanceSquared,
 2287        VoxelGrid candidateGrid,
 2288        Voxel candidateVoxel,
 2289        Fixed64 closestDistanceSquared,
 2290        VoxelGrid? closestGrid,
 2291        Voxel? closestVoxel)
 2292    {
 42293        if (closestVoxel == null || closestGrid == null)
 12294            return true;
 2295
 32296        if (candidateDistanceSquared != closestDistanceSquared)
 12297            return candidateDistanceSquared < closestDistanceSquared;
 2298
 22299        return candidateGrid.GridIndex < closestGrid.GridIndex;
 2300    }
 2301
 2302    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 2303    private static Fixed64 GetDistanceSquaredToBounds(Vector3d position, Vector3d boundsMin, Vector3d boundsMax)
 2304    {
 352305        Fixed64 x = GetAxisDistanceToBounds(position.X, boundsMin.X, boundsMax.X);
 352306        Fixed64 y = GetAxisDistanceToBounds(position.Y, boundsMin.Y, boundsMax.Y);
 352307        Fixed64 z = GetAxisDistanceToBounds(position.Z, boundsMin.Z, boundsMax.Z);
 352308        return x * x + y * y + z * z;
 2309    }
 2310
 2311    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 2312    private static Fixed64 GetAxisDistanceToBounds(Fixed64 coordinate, Fixed64 min, Fixed64 max)
 2313    {
 1052314        if (coordinate < min)
 172315            return min - coordinate;
 2316
 882317        return coordinate > max ? coordinate - max : Fixed64.Zero;
 2318    }
 2319
 2320    private bool CanResolveGrid(int index)
 2321    {
 15702322        if (!CanResolveActiveGrid())
 22323            return false;
 2324
 15682325        if (!IsGridIndexInActiveRange(index))
 2326        {
 72327            GridForgeLogger.Channel.Error($"GridIndex '{index}' is out-of-bounds for ActiveGrids.");
 72328            return false;
 2329        }
 2330
 15612331        return IsGridIndexAllocated(index);
 2332    }
 2333
 2334    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 2335    private bool CanResolveActiveGrid()
 2336    {
 16132337        if (IsActive)
 16082338            return true;
 2339
 52340        GridForgeLogger.Channel.Warn($"Grid world not active. Cannot resolve grids.");
 52341        return false;
 2342    }
 2343
 2344    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 2345    private bool IsGridIndexInActiveRange(int index) =>
 15682346         (uint)index < MaxGrids && (uint)index <= ActiveGrids.Count;
 2347
 2348    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 2349    private bool IsGridIndexAllocated(int index)
 2350    {
 15612351        if (ActiveGrids.IsAllocated(index))
 15522352            return true;
 2353
 92354        GridForgeLogger.Channel.Error($"GridIndex '{index}' has not been allocated to ActiveGrids.");
 92355        return false;
 2356    }
 2357
 2358    private bool CanResolvePosition()
 2359    {
 1542360        if (IsActive)
 1522361            return true;
 2362
 22363        GridForgeLogger.Channel.Warn($"Grid world not active. Cannot resolve positions.");
 22364        return false;
 2365    }
 2366
 2367    private bool TryGetContainingGrid(
 2368        Vector3d position,
 2369        SwiftList<ushort> gridList,
 2370        out VoxelGrid? outGrid)
 2371    {
 1522372        outGrid = null;
 2373
 3522374        for (int index = 0; index < gridList.Count; index++)
 2375        {
 882376            ushort candidateIndex = gridList[index];
 882377            VoxelGrid candidateGrid = ActiveGrids[candidateIndex];
 882378            if (candidateGrid.IsInBounds(position))
 2379            {
 642380                outGrid = candidateGrid;
 642381                return true;
 2382            }
 2383        }
 2384
 882385        return false;
 2386    }
 2387
 2388    private void TryAddOverlappingGrid(
 2389        VoxelGrid targetGrid,
 2390        ushort neighborIndex,
 2391        SwiftList<VoxelGrid> overlappingGrids)
 2392    {
 92393        if (neighborIndex == targetGrid.GridIndex)
 42394            return;
 2395
 52396        overlappingGrids.Add(ActiveGrids[neighborIndex]);
 52397    }
 2398
 2399    #endregion
 2400
 2401    #region Private Helpers
 2402
 2403    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 2404    private static int ResolveSpatialGridCellSize(int spatialGridCellSize)
 2405    {
 7852406        if (spatialGridCellSize <= 0)
 2407        {
 32408            GridForgeLogger.Channel.Warn($"Spatial grid cell size must be greater than zero. Falling back to default siz
 32409            return DefaultSpatialGridCellSize;
 2410        }
 2411
 7822412        return spatialGridCellSize;
 2413    }
 2414
 2415    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 2416    private GridEventInfo CreateGridEventInfo(
 2417        VoxelGrid grid,
 2418        GridEventKind changeKind,
 2419        GridChangeStamp changeStamp)
 2420    {
 9712421        grid.LastChangeSequence = changeStamp.Sequence;
 9712422        return new GridEventInfo(
 9712423            SpawnToken,
 9712424            grid.GridIndex,
 9712425            grid.SpawnToken,
 9712426            grid.Configuration,
 9712427            grid.Version,
 9712428            changeKind,
 9712429            default,
 9712430            grid.BoundsMin,
 9712431            grid.BoundsMax,
 9712432            changeStamp);
 2433    }
 2434
 2435    internal GridEventInfo CreateGridEventInfo(
 2436        VoxelGrid grid,
 2437        GridEventKind changeKind,
 2438        VoxelIndex voxelIndex,
 2439        Vector3d affectedBoundsMin,
 2440        Vector3d affectedBoundsMax,
 2441        GridChangeStamp changeStamp,
 2442        bool hasVoxelState,
 2443        bool isVoxelPresent,
 2444        byte obstacleCount)
 2445    {
 17302446        grid.LastChangeSequence = changeStamp.Sequence;
 17302447        return new GridEventInfo(
 17302448            SpawnToken,
 17302449            grid.GridIndex,
 17302450            grid.SpawnToken,
 17302451            grid.Configuration,
 17302452            grid.Version,
 17302453            changeKind,
 17302454            voxelIndex,
 17302455            affectedBoundsMin,
 17302456            affectedBoundsMax,
 17302457            changeStamp,
 17302458            hasVoxelState,
 17302459            isVoxelPresent,
 17302460            obstacleCount);
 2461    }
 2462
 2463    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 2464    internal GridChangeStamp AllocateChangeStamp()
 2465    {
 2466        Debug.Assert(Monitor.IsEntered(ChangeSyncRoot));
 34932467        _changeSequence = checked(_changeSequence + 1);
 34932468        return new GridChangeStamp(_changeSequence, _changeSequence);
 2469    }
 2470
 2471    internal bool EnqueueCommittedChange(GridCommittedChange change)
 2472    {
 2473        Debug.Assert(Monitor.IsEntered(ChangeSyncRoot));
 34932474        _committedChanges.Enqueue(change);
 34932475        if (_isPublishingCommittedChanges)
 9422476            return false;
 2477
 25512478        _isPublishingCommittedChanges = true;
 25512479        return true;
 2480    }
 2481
 2482    internal void DrainCommittedChanges()
 2483    {
 25512484        Volatile.Write(ref _committedPublicationOwnerThreadId, Environment.CurrentManagedThreadId);
 2485        try
 2486        {
 2487            while (true)
 2488            {
 2489                GridCommittedChange change;
 60442490                lock (ChangeSyncRoot)
 2491                {
 60442492                    if (!_committedChanges.TryDequeue(out change))
 2493                    {
 25512494                        _isPublishingCommittedChanges = false;
 25512495                        return;
 2496                    }
 34932497                }
 2498
 34932499                GridObstacleManager.NotifyCommittedExact(change);
 34932500                switch (change.GridEvent.ChangeKind)
 2501                {
 2502                    case GridEventKind.GridAdded:
 9152503                        NotifyActiveGridAdded(change.GridEvent);
 9152504                        break;
 2505                    case GridEventKind.GridRemoved:
 562506                        NotifyActiveGridRemoved(change.GridEvent);
 562507                        break;
 2508                    case GridEventKind.WorldReset:
 2509                        break;
 2510                    default:
 17302511                        NotifyActiveGridChange(change.GridEvent);
 2512                        break;
 2513                }
 2514
 34932515                NotifyChangeCommitted(change.GridEvent);
 34932516                lock (ChangeSyncRoot)
 2517                {
 34932518                    _publishedChangeSequence = change.GridEvent.ChangeSequence;
 34932519                    Monitor.PulseAll(ChangeSyncRoot);
 34932520                }
 2521            }
 2522        }
 2523        finally
 2524        {
 25512525            Volatile.Write(ref _committedPublicationOwnerThreadId, 0);
 25512526        }
 25512527    }
 2528
 2529    private void NotifyActiveGridAdded(GridEventInfo eventInfo)
 2530    {
 9152531        Action<GridEventInfo>? handlers = _onActiveGridAdded;
 9152532        if (handlers == null)
 9042533            return;
 2534
 112535        var handlerDelegates = handlers.GetInvocationList();
 482536        for (int i = 0; i < handlerDelegates.Length; i++)
 2537        {
 2538            try
 2539            {
 132540                ((Action<GridEventInfo>)handlerDelegates[i])(eventInfo);
 122541            }
 12542            catch (Exception ex)
 2543            {
 12544                GridForgeLogger.Channel.Error($"[Grid {eventInfo.GridIndex}] added notification error: {ex.Message}");
 12545            }
 2546        }
 112547    }
 2548
 2549    private void NotifyActiveGridRemoved(GridEventInfo eventInfo)
 2550    {
 562551        Action<GridEventInfo>? handlers = _onActiveGridRemoved;
 562552        if (handlers == null)
 482553            return;
 2554
 82555        var handlerDelegates = handlers.GetInvocationList();
 362556        for (int i = 0; i < handlerDelegates.Length; i++)
 2557        {
 2558            try
 2559            {
 102560                ((Action<GridEventInfo>)handlerDelegates[i])(eventInfo);
 92561            }
 12562            catch (Exception ex)
 2563            {
 12564                GridForgeLogger.Channel.Error($"[Grid {eventInfo.GridIndex}] removed notification error: {ex.Message}");
 12565            }
 2566        }
 82567    }
 2568
 2569    internal void NotifyActiveGridChange(GridEventInfo eventInfo)
 2570    {
 17302571        Action<GridEventInfo>? handlers = _onActiveGridChange;
 17302572        if (handlers == null)
 9072573            return;
 2574
 8232575        var handlerDelegates = handlers.GetInvocationList();
 613802576        for (int i = 0; i < handlerDelegates.Length; i++)
 2577        {
 2578            try
 2579            {
 298672580                ((Action<GridEventInfo>)handlerDelegates[i])(eventInfo);
 298652581            }
 22582            catch (Exception ex)
 2583            {
 22584                GridForgeLogger.Channel.Error($"[Grid {eventInfo.GridIndex}] change notification error: {ex.Message}");
 22585            }
 2586        }
 8232587    }
 2588
 2589    private void NotifyChangeCommitted(GridEventInfo eventInfo)
 2590    {
 34932591        Action<GridEventInfo>? handlers = _onChangeCommitted;
 34932592        if (handlers == null)
 34662593            return;
 2594
 272595        var handlerDelegates = handlers.GetInvocationList();
 1102596        for (int i = 0; i < handlerDelegates.Length; i++)
 2597        {
 2598            try
 2599            {
 282600                ((Action<GridEventInfo>)handlerDelegates[i])(eventInfo);
 272601            }
 12602            catch (Exception ex)
 2603            {
 12604                GridForgeLogger.Channel.Error($"[Change {eventInfo.ChangeSequence}] committed notification error: {ex.Me
 12605            }
 2606        }
 272607    }
 2608
 2609    #endregion
 2610}

Methods/Properties

TryBeginCoveredAddresses(GridForge.Grids.Topology.GridCoveredAddressCursor,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,System.Int32)
TryBeginCoveredAddresses(GridForge.Grids.Topology.GridCoveredAddressCursor,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,System.Int32,GridForge.Configuration.GridConfigurationKey)
AdvanceCoveredAddresses(GridForge.Grids.Topology.GridCoveredAddressCursor,System.ReadOnlySpan`1<GridForge.Grids.Topology.GridCoveredAddressGeneration>,System.Span`1<GridForge.Grids.Topology.GridCoveredAddress>,System.Int32,System.Int32,System.Int32,System.Int32&,System.Int32&,System.Int32&,System.Int32&)
TryBeginCoveredAddressesCore(GridForge.Grids.Topology.GridCoveredAddressCursor,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,System.Int32,System.Boolean,GridForge.Configuration.GridConfigurationKey)
AdvanceCoveredAddressesCore(GridForge.Grids.Topology.GridCoveredAddressCursor,System.ReadOnlySpan`1<GridForge.Grids.Topology.GridCoveredAddressGeneration>,System.Span`1<GridForge.Grids.Topology.GridCoveredAddress>,System.Int32,System.Int32,System.Int32,System.Int32&,System.Int32&,System.Int32&,System.Int32&)
TryBindCoveredAddressGeneration(GridForge.Grids.Topology.GridCoveredAddressCursor,GridForge.Grids.Topology.GridCoveredAddressGeneration,GridForge.Grids.Topology.GridCoveredAddressCursor/BoundGeneration&)
IsCoveredAddressCursorCurrent(GridForge.Grids.Topology.GridCoveredAddressCursor)
IsCoveredAddress(GridForge.Grids.Topology.GridCoveredAddressCursor,GridForge.Grids.Topology.GridCoveredAddressGeneration,GridForge.Spatial.VoxelIndex)
AdvanceCoveredAddress(GridForge.Spatial.VoxelIndex&,GridForge.Spatial.VoxelIndex,GridForge.Spatial.VoxelIndex)
ConsumeLookupProbe(GridForge.Grids.Topology.GridCoveredAddressCursor,System.Int32&)
ConsumeAddressProbe(GridForge.Grids.Topology.GridCoveredAddressCursor,System.Int32&)
.cctor()
get_ChangeSequence()
EnterReadLock()
ExitReadLock()
get_IsWriteLockHeld()
.ctor(System.Int32)
add_OnActiveGridAdded(System.Action`1<GridForge.Grids.GridEventInfo>)
remove_OnActiveGridAdded(System.Action`1<GridForge.Grids.GridEventInfo>)
add_OnActiveGridRemoved(System.Action`1<GridForge.Grids.GridEventInfo>)
remove_OnActiveGridRemoved(System.Action`1<GridForge.Grids.GridEventInfo>)
add_OnActiveGridChange(System.Action`1<GridForge.Grids.GridEventInfo>)
remove_OnActiveGridChange(System.Action`1<GridForge.Grids.GridEventInfo>)
add_OnChangeCommitted(System.Action`1<GridForge.Grids.GridEventInfo>)
remove_OnChangeCommitted(System.Action`1<GridForge.Grids.GridEventInfo>)
add_OnReset(System.Action)
remove_OnReset(System.Action)
Reset(System.Boolean)
NotifyResetHandlers()
ReleaseActiveGrids()
Dispose()
AllocateObstacleToken()
TryCaptureNavigationBaseline(GridForge.Configuration.GridConfigurationKey,System.ReadOnlySpan`1<GridForge.Spatial.VoxelIndex>,GridForge.Grids.GridNavigationBaseline&)
ExecuteNavigationMaintenanceSnapshot(System.Action)
BeginBoundaryContacts(GridForge.Grids.Topology.GridBoundaryContactCursor)
TryBeginBoundaryContacts(GridForge.Configuration.GridConfigurationKey,GridForge.Grids.Topology.GridBoundaryContactCursor)
AdvanceBoundaryContacts(GridForge.Grids.Topology.GridBoundaryContactCursor,System.Span`1<GridForge.Grids.Topology.VoxelContactManifold>,System.Int32,System.Int32,System.Int32&,System.Int32&)
AdvanceBoundaryContacts(GridForge.Grids.Topology.GridBoundaryContactCursor,System.Span`1<GridForge.Grids.Topology.GridBoundaryContact>,System.Int32,System.Int32,System.Int32&,System.Int32&)
AdvanceBoundaryContactsUnderGate(GridForge.Grids.Topology.GridBoundaryContactCursor,System.Span`1<GridForge.Grids.Topology.VoxelContactManifold>,System.Span`1<GridForge.Grids.Topology.GridBoundaryContact>,System.Boolean,System.Int32,System.Int32,System.Int32&,System.Int32&)
AdvanceBoundaryContactsCore(GridForge.Grids.Topology.GridBoundaryContactCursor,System.Span`1<GridForge.Grids.Topology.VoxelContactManifold>,System.Span`1<GridForge.Grids.Topology.GridBoundaryContact>,System.Boolean,System.Int32,System.Int32,System.Int32&,System.Int32&)
IsBoundaryContactCursorCurrent(GridForge.Grids.Topology.GridBoundaryContactCursor)
TryAdvanceFilteredBoundaryContactPair(GridForge.Grids.Topology.GridBoundaryContactCursor,System.Int32,System.Int32&,System.UInt16&,System.UInt16&)
BindBoundaryContactPair(GridForge.Grids.Topology.GridBoundaryContactCursor,System.UInt16,System.UInt16)
ProbeBoundaryContactTarget(GridForge.Grids.Topology.GridBoundaryContactCursor,System.Int32&)
AdvanceBoundaryContactAddress(GridForge.Spatial.VoxelIndex&,GridForge.Spatial.VoxelIndex,GridForge.Spatial.VoxelIndex)
TryCreateTopologyPrism(GridForge.Grids.VoxelGrid,GridForge.Spatial.VoxelIndex,GridForge.Grids.Topology.GridCellPrism&)
GetTrailingZeroCount(System.UInt64)
ConsumeBoundaryContactProbe(GridForge.Grids.Topology.GridBoundaryContactCursor,System.Int32&)
ThrowIfNavigationMaintenanceUnavailable()
TryEnterNavigationMaintenanceSnapshot()
ExitNavigationMaintenanceSnapshot()
WaitForPublishedChangePrefix()
TryCaptureNavigationBaselineCore(GridForge.Configuration.GridConfigurationKey,System.ReadOnlySpan`1<GridForge.Spatial.VoxelIndex>,GridForge.Grids.GridNavigationBaseline&)
AreNavigationBaselineAddressesValid(GridForge.Grids.VoxelGrid,System.ReadOnlySpan`1<GridForge.Spatial.VoxelIndex>)
TryAddGrid(GridForge.Configuration.GridConfiguration,System.UInt16&)
TryAddGrid(GridForge.Configuration.GridConfiguration,System.Collections.Generic.IEnumerable`1<GridForge.Spatial.VoxelIndex>,System.UInt16&)
TryAddGrid(GridForge.Configuration.GridConfiguration,System.Boolean[0...,0...,0...],System.UInt16&)
TryAddGridCore(GridForge.Configuration.GridConfiguration,System.Collections.Generic.IEnumerable`1<GridForge.Spatial.VoxelIndex>,System.Boolean[0...,0...,0...],System.UInt16&)
TryRemoveGrid(System.UInt16)
CanAddGrid()
TryPrepareConfiguredVoxels(GridForge.Configuration.GridConfiguration,GridForge.Grids.Topology.GridDimensions,System.Collections.Generic.IEnumerable`1<GridForge.Spatial.VoxelIndex>,System.Boolean[0...,0...,0...],GridForge.Spatial.VoxelIndex[]&)
TryPrepareConfiguredVoxelMask(System.Boolean[0...,0...,0...],GridForge.Grids.Topology.GridDimensions,GridForge.Spatial.VoxelIndex[]&)
TryPrepareConfiguredVoxelIndices(System.Collections.Generic.IEnumerable`1<GridForge.Spatial.VoxelIndex>,GridForge.Grids.Topology.GridDimensions,GridForge.Spatial.VoxelIndex[]&)
IsConfiguredVoxelInBounds(GridForge.Spatial.VoxelIndex,GridForge.Grids.Topology.GridDimensions)
CompactPreparedVoxels(GridForge.Spatial.VoxelIndex[]&)
UpdateMaxTopologyCellEdge(FixedMathSharp.Fixed64)
RecalculateMaxTopologyCellEdgeIfNeeded(FixedMathSharp.Fixed64)
TryFindExistingGridUnsafe(GridForge.Configuration.GridConfigurationKey,System.UInt16&)
RegisterGrid(GridForge.Grids.VoxelGrid,System.UInt16)
UnregisterGrid(GridForge.Grids.VoxelGrid,System.UInt16)
TryCreateBoundaryContactEnvelope(GridForge.Grids.VoxelGrid,SwiftCollections.Query.FixedBoundVolume&)
InsertBoundaryContactPair(System.UInt16,System.UInt16)
RemoveBoundaryContactPairs(System.UInt16)
RemoveBoundaryContactIncident(SwiftCollections.SwiftDictionary`2<System.UInt16,SwiftCollections.SwiftList`1<System.UInt16>>,System.UInt16,System.UInt16,System.Boolean)
GetOrCreateBoundaryContactRow(SwiftCollections.SwiftDictionary`2<System.UInt16,SwiftCollections.SwiftList`1<System.UInt16>>,System.UInt16,System.Boolean&)
InsertSorted(SwiftCollections.SwiftList`1<System.UInt16>,System.UInt16)
RemoveSorted(SwiftCollections.SwiftList`1<System.UInt16>,System.UInt16)
FindSortedIndex(SwiftCollections.SwiftList`1<System.UInt16>,System.UInt16)
SetBoundaryContactSource(System.UInt16)
ClearBoundaryContactSource(System.UInt16)
ReleaseBoundaryContactPairs()
UnlinkGridNeighbors(GridForge.Grids.VoxelGrid)
CollectGridCandidates(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,SwiftCollections.SwiftList`1<System.UInt16>,System.Int32)
CreateExpandedBounds(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Fixed64)
TryGetGrid(System.Int32,GridForge.Grids.VoxelGrid&)
TryGetGrid(FixedMathSharp.Vector3d,GridForge.Grids.VoxelGrid&)
TryGetGrid(FixedMathSharp.Vector2d,GridForge.Grids.VoxelGrid&)
TryGetGrid(FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,GridForge.Grids.VoxelGrid&)
TryGetClosestGrid(FixedMathSharp.Vector3d,GridForge.Grids.VoxelGrid&,System.Nullable`1<GridForge.Grids.Topology.GridTopologyKind>)
TryGetClosestGrid(FixedMathSharp.Vector2d,GridForge.Grids.VoxelGrid&,System.Nullable`1<GridForge.Grids.Topology.GridTopologyKind>)
TryGetClosestGrid(FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,GridForge.Grids.VoxelGrid&,System.Nullable`1<GridForge.Grids.Topology.GridTopologyKind>)
TryGetGrid(GridForge.Spatial.WorldVoxelIndex,GridForge.Grids.VoxelGrid&)
TryGetGridAndVoxel(FixedMathSharp.Vector3d,GridForge.Grids.VoxelGrid&,GridForge.Grids.Voxel&)
TryGetGridAndVoxel(FixedMathSharp.Vector2d,GridForge.Grids.VoxelGrid&,GridForge.Grids.Voxel&)
TryGetGridAndVoxel(FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,GridForge.Grids.VoxelGrid&,GridForge.Grids.Voxel&)
TryGetClosestGridAndVoxel(FixedMathSharp.Vector3d,GridForge.Grids.VoxelGrid&,GridForge.Grids.Voxel&,System.Nullable`1<GridForge.Grids.Topology.GridTopologyKind>)
TryGetClosestGridAndVoxel(FixedMathSharp.Vector2d,GridForge.Grids.VoxelGrid&,GridForge.Grids.Voxel&,System.Nullable`1<GridForge.Grids.Topology.GridTopologyKind>)
TryGetClosestGridAndVoxel(FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,GridForge.Grids.VoxelGrid&,GridForge.Grids.Voxel&,System.Nullable`1<GridForge.Grids.Topology.GridTopologyKind>)
TryGetGridAndVoxel(GridForge.Spatial.WorldVoxelIndex,GridForge.Grids.VoxelGrid&,GridForge.Grids.Voxel&)
TryGetVoxel(FixedMathSharp.Vector3d,GridForge.Grids.Voxel&)
TryGetVoxel(FixedMathSharp.Vector2d,GridForge.Grids.Voxel&)
TryGetVoxel(FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,GridForge.Grids.Voxel&)
TryGetClosestVoxel(FixedMathSharp.Vector3d,GridForge.Grids.Voxel&,System.Nullable`1<GridForge.Grids.Topology.GridTopologyKind>)
TryGetClosestVoxel(FixedMathSharp.Vector2d,GridForge.Grids.Voxel&,System.Nullable`1<GridForge.Grids.Topology.GridTopologyKind>)
TryGetClosestVoxel(FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,GridForge.Grids.Voxel&,System.Nullable`1<GridForge.Grids.Topology.GridTopologyKind>)
TryGetVoxel(GridForge.Spatial.WorldVoxelIndex,GridForge.Grids.Voxel&)
IncrementGridVersion(System.Int32,System.Boolean)
FindOverlappingGrids(GridForge.Grids.VoxelGrid)
FindOverlappingGridsInto(GridForge.Grids.VoxelGrid,SwiftCollections.SwiftList`1<GridForge.Grids.VoxelGrid>)
MatchesTopologyKind(GridForge.Grids.VoxelGrid,System.Nullable`1<GridForge.Grids.Topology.GridTopologyKind>)
IsBetterClosestVoxel(FixedMathSharp.Fixed64,GridForge.Grids.VoxelGrid,GridForge.Grids.Voxel,FixedMathSharp.Fixed64,GridForge.Grids.VoxelGrid,GridForge.Grids.Voxel)
GetDistanceSquaredToBounds(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d)
GetAxisDistanceToBounds(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64)
CanResolveGrid(System.Int32)
CanResolveActiveGrid()
IsGridIndexInActiveRange(System.Int32)
IsGridIndexAllocated(System.Int32)
CanResolvePosition()
TryGetContainingGrid(FixedMathSharp.Vector3d,SwiftCollections.SwiftList`1<System.UInt16>,GridForge.Grids.VoxelGrid&)
TryAddOverlappingGrid(GridForge.Grids.VoxelGrid,System.UInt16,SwiftCollections.SwiftList`1<GridForge.Grids.VoxelGrid>)
ResolveSpatialGridCellSize(System.Int32)
CreateGridEventInfo(GridForge.Grids.VoxelGrid,GridForge.Grids.GridEventKind,GridForge.Grids.GridChangeStamp)
CreateGridEventInfo(GridForge.Grids.VoxelGrid,GridForge.Grids.GridEventKind,GridForge.Spatial.VoxelIndex,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,GridForge.Grids.GridChangeStamp,System.Boolean,System.Boolean,System.Byte)
AllocateChangeStamp()
EnqueueCommittedChange(GridForge.Grids.GridCommittedChange)
DrainCommittedChanges()
NotifyActiveGridAdded(GridForge.Grids.GridEventInfo)
NotifyActiveGridRemoved(GridForge.Grids.GridEventInfo)
NotifyActiveGridChange(GridForge.Grids.GridEventInfo)
NotifyChangeCommitted(GridForge.Grids.GridEventInfo)