< Summary

Information
Class: GridForge.Grids.GridWorld
Assembly: GridForge
File(s): /home/runner/work/GridForge/GridForge/src/GridForge/Grids/Managers/GridWorld.cs
Line coverage
100%
Covered lines: 504
Uncovered lines: 0
Coverable lines: 504
Total lines: 1418
Line coverage: 100%
Branch coverage
100%
Covered branches: 266
Total branches: 266
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
.ctor(...)100%11100%
add_OnActiveGridAdded(...)100%11100%
remove_OnActiveGridAdded(...)100%11100%
add_OnActiveGridRemoved(...)100%11100%
remove_OnActiveGridRemoved(...)100%11100%
add_OnActiveGridChange(...)100%11100%
remove_OnActiveGridChange(...)100%11100%
add_OnReset(...)100%11100%
remove_OnReset(...)100%11100%
Reset(...)100%66100%
NotifyResetHandlers()100%66100%
ReleaseActiveGrids()100%22100%
Dispose()100%11100%
AllocateObstacleToken()100%22100%
TryAddGrid(...)100%11100%
TryAddGrid(...)100%11100%
TryAddGrid(...)100%11100%
TryAddGridCore(...)100%1010100%
TryRemoveGrid(...)100%66100%
CanAddGrid()100%88100%
TryPrepareConfiguredVoxels(...)100%44100%
TryValidateGridDimensions(...)100%66100%
TryPrepareConfiguredVoxelMask(...)100%2626100%
TryPrepareConfiguredVoxelIndices(...)100%1212100%
IsConfiguredVoxelInBounds(...)100%44100%
CompactPreparedVoxels(...)100%88100%
UpdateMaxTopologyCellEdge(...)100%22100%
RecalculateMaxTopologyCellEdgeIfNeeded(...)100%66100%
TryFindExistingGrid(...)100%44100%
RegisterGrid(...)100%44100%
UnregisterGrid(...)100%11100%
UnlinkGridNeighbors(...)100%66100%
CollectGridCandidates(...)100%11100%
CreateExpandedBounds(...)100%11100%
TryGetGrid(...)100%22100%
TryGetGrid(...)100%66100%
TryGetGrid(...)100%11100%
TryGetGrid(...)100%11100%
TryGetClosestGrid(...)100%1212100%
TryGetClosestGrid(...)100%11100%
TryGetClosestGrid(...)100%11100%
TryGetGrid(...)100%66100%
TryGetGridAndVoxel(...)100%22100%
TryGetGridAndVoxel(...)100%11100%
TryGetGridAndVoxel(...)100%11100%
TryGetClosestGridAndVoxel(...)100%2424100%
TryGetClosestGridAndVoxel(...)100%11100%
TryGetClosestGridAndVoxel(...)100%11100%
TryGetGridAndVoxel(...)100%22100%
TryGetVoxel(...)100%22100%
TryGetVoxel(...)100%11100%
TryGetVoxel(...)100%11100%
TryGetClosestVoxel(...)100%22100%
TryGetClosestVoxel(...)100%11100%
TryGetClosestVoxel(...)100%11100%
TryGetVoxel(...)100%22100%
TryNormalizeConfiguration(...)100%22100%
IncrementGridVersion(...)100%88100%
FindOverlappingGrids(...)100%11100%
FindOverlappingGridsInto(...)100%66100%
MatchesTopologyKind(...)100%22100%
IsBetterClosestVoxel(...)100%66100%
GetDistanceSquaredToBounds(...)100%11100%
GetAxisDistanceToBounds(...)100%44100%
CanResolveGrid(...)100%66100%
CanResolveActiveGrid()100%44100%
IsGridIndexInActiveRange(...)100%22100%
IsGridIndexAllocated(...)100%44100%
CanResolvePosition()100%44100%
TryGetContainingGrid(...)100%44100%
TryAddOverlappingGrid(...)100%22100%
NotifyActiveGridChange(...)100%44100%
NotifyActiveGridChange(...)100%44100%
ResolveSpatialGridCellSize(...)100%44100%
CreateGridEventInfo(...)100%11100%
CreateGridEventInfo(...)100%11100%
NotifyActiveGridAdded(...)100%66100%
NotifyActiveGridRemoved(...)100%66100%
NotifyActiveGridChange(...)100%66100%

File(s)

/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.Query;
 20
 21namespace GridForge.Grids;
 22
 23/// <summary>
 24/// Owns the mutable runtime state for one GridForge world.
 25/// </summary>
 26public sealed class GridWorld : IDisposable
 27{
 28    #region Constants
 29
 30    /// <summary>
 31    /// Maximum number of grids that can be managed within a world.
 32    /// </summary>
 33    public const ushort MaxGrids = ushort.MaxValue - 1;
 34
 35    /// <summary>
 36    /// The default rectangular cell edge in world units.
 37    /// </summary>
 138    public static readonly Fixed64 DefaultRectangularCellSize = Fixed64.One;
 39
 40    /// <summary>
 41    /// The default cell size used to tune ordinary-grid lookup.
 42    /// Oversized grids are indexed automatically outside this tier.
 43    /// </summary>
 44    public const int DefaultSpatialGridCellSize = 50;
 45
 46    #endregion
 47
 48    #region Properties
 49
 150    private static readonly Comparison<VoxelIndex> CompareVoxelIndices =
 151        static (left, right) => left.CompareTo(right);
 52
 53    /// <summary>
 54    /// The cell size used to tune ordinary-grid lookup in this world.
 55    /// Oversized grids are indexed automatically outside this tier.
 56    /// </summary>
 57    public int SpatialGridCellSize { get; }
 58
 59    /// <summary>
 60    /// Collection of all active grids owned by this world.
 61    /// </summary>
 62    public SwiftBucket<VoxelGrid> ActiveGrids { get; }
 63
 64    /// <summary>
 65    /// Dictionary mapping exact grid configuration keys to grid indices to prevent duplicate grids.
 66    /// </summary>
 67    public SwiftDictionary<GridConfigurationKey, ushort> BoundsTracker { get; }
 68
 69    /// <summary>
 70    /// Nonzero process-unique 64-bit runtime allocation token for this active world.
 71    /// Zero indicates an inactive world.
 72    /// </summary>
 73    public long SpawnToken { get; private set; }
 74
 75    /// <summary>
 76    /// The current version of the world, incremented on major changes.
 77    /// </summary>
 78    public uint Version { get; private set; }
 79
 80    /// <summary>
 81    /// Indicates whether this world is currently active.
 82    /// </summary>
 83    public bool IsActive { get; private set; }
 84
 85    internal Fixed64 MaxTopologyCellEdge { get; private set; }
 86
 87    private static long s_worldAllocationCounter;
 88    private static long s_obstacleRegistrationCounter;
 89
 53790    private readonly ReaderWriterLockSlim _gridLock = new();
 53791    private readonly SwiftList<ushort> _gridCandidates = new();
 92    private readonly GridSpatialIndex _spatialIndex;
 93    private long _gridGenerationCounter;
 94
 95    #endregion
 96
 97    #region Events
 98
 99    private Action<GridEventInfo>? _onActiveGridAdded;
 100    private Action<GridEventInfo>? _onActiveGridRemoved;
 101    private Action<GridEventInfo>? _onActiveGridChange;
 102    private Action? _onReset;
 103
 104    /// <summary>
 105    /// Event triggered when a new grid is added to this world.
 106    /// </summary>
 107    public event Action<GridEventInfo> OnActiveGridAdded
 108    {
 170109        add => _onActiveGridAdded += value;
 167110        remove => _onActiveGridAdded -= value;
 111    }
 112
 113    /// <summary>
 114    /// Event triggered when a grid is removed from this world.
 115    /// </summary>
 116    public event Action<GridEventInfo> OnActiveGridRemoved
 117    {
 170118        add => _onActiveGridRemoved += value;
 167119        remove => _onActiveGridRemoved -= value;
 120    }
 121
 122    /// <summary>
 123    /// Event triggered when a grid in this world undergoes a significant change.
 124    /// </summary>
 125    public event Action<GridEventInfo> OnActiveGridChange
 126    {
 176127        add => _onActiveGridChange += value;
 168128        remove => _onActiveGridChange -= value;
 129    }
 130
 131    /// <summary>
 132    /// Event triggered when this world is reset.
 133    /// </summary>
 134    public event Action OnReset
 135    {
 170136        add => _onReset += value;
 167137        remove => _onReset -= value;
 138    }
 139
 140    #endregion
 141
 142    /// <summary>
 143    /// Initializes a new world with optional ordinary-grid lookup tuning.
 144    /// </summary>
 145    /// <param name="spatialGridCellSize">Optional ordinary-grid lookup cell size for this world.</param>
 537146    public GridWorld(int spatialGridCellSize = DefaultSpatialGridCellSize)
 147    {
 537148        ActiveGrids = new SwiftBucket<VoxelGrid>();
 537149        BoundsTracker = new SwiftDictionary<GridConfigurationKey, ushort>();
 150
 537151        SpatialGridCellSize = ResolveSpatialGridCellSize(spatialGridCellSize);
 537152        _spatialIndex = new GridSpatialIndex(SpatialGridCellSize);
 537153        SpawnToken = RuntimeIdentityAllocator.Allocate(ref s_worldAllocationCounter);
 537154        Version = 1;
 537155        IsActive = true;
 537156    }
 157
 158    #region Lifecycle
 159
 160    /// <summary>
 161    /// Clears all grids and spatial data owned by this world.
 162    /// </summary>
 163    /// <param name="deactivate">If true, marks the world inactive and releases its event handlers.</param>
 164    public void Reset(bool deactivate = false)
 165    {
 548166        if (!IsActive)
 167        {
 6168            GridForgeLogger.Channel.Warn($"Grid world not active. Cannot reset an inactive world.");
 6169            return;
 170        }
 171
 542172        NotifyResetHandlers();
 542173        ReleaseActiveGrids();
 542174        GridOccupantManager.ClearTrackedOccupancies(this);
 175
 542176        if (!deactivate)
 9177            return;
 178
 533179        GridOccupantManager.ReleaseTrackedOccupancies(this);
 533180        IsActive = false;
 533181        SpawnToken = 0;
 533182        _onActiveGridAdded = null;
 533183        _onActiveGridRemoved = null;
 533184        _onActiveGridChange = null;
 533185        _onReset = null;
 533186    }
 187
 188    private void NotifyResetHandlers()
 189    {
 542190        Action? resetHandlers = _onReset;
 542191        if (resetHandlers == null)
 520192            return;
 193
 22194        var handlerDelegates = resetHandlers.GetInvocationList();
 312195        for (int i = 0; i < handlerDelegates.Length; i++)
 196        {
 197            try
 198            {
 134199                ((Action)handlerDelegates[i])();
 132200            }
 2201            catch (Exception ex)
 202            {
 2203                GridForgeLogger.Channel.Error($"World reset notification error: {ex.Message}");
 2204            }
 205        }
 22206    }
 207
 208    private void ReleaseActiveGrids()
 209    {
 542210        _spatialIndex.Clear();
 211
 2110212        foreach (VoxelGrid grid in ActiveGrids)
 513213            Pools.GridPool.Release(grid);
 214
 542215        ActiveGrids.Clear();
 542216        BoundsTracker.Clear();
 542217        MaxTopologyCellEdge = Fixed64.Zero;
 542218    }
 219
 220    /// <inheritdoc />
 221    public void Dispose()
 222    {
 536223        Reset(deactivate: true);
 536224        _gridLock.Dispose();
 536225        GC.SuppressFinalize(this);
 536226    }
 227
 228    #endregion
 229
 230    #region Grid Management
 231
 232    /// <summary>
 233    /// Allocates a nonzero process-unique identity for one obstacle registration lifetime.
 234    /// </summary>
 235    /// <returns>A fresh opaque obstacle token.</returns>
 236    /// <exception cref="InvalidOperationException">The world is inactive or its token space is exhausted.</exception>
 237    public ObstacleToken AllocateObstacleToken()
 238    {
 509239        if (!IsActive)
 1240            throw new InvalidOperationException("Cannot allocate an obstacle token from an inactive world.");
 241
 508242        return new ObstacleToken(RuntimeIdentityAllocator.Allocate(ref s_obstacleRegistrationCounter));
 243    }
 244
 245    /// <summary>
 246    /// Adds a new grid to this world and registers it in the spatial index.
 247    /// </summary>
 248    /// <param name="configuration">The grid configuration to normalize and register.</param>
 249    /// <param name="allocatedIndex">The allocated world-local grid slot on success.</param>
 250    /// <returns>True if the grid was added; otherwise false.</returns>
 251    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 252    public bool TryAddGrid(GridConfiguration configuration, out ushort allocatedIndex) =>
 493253        TryAddGridCore(configuration, null, null, out allocatedIndex);
 254
 255    /// <summary>
 256    /// Adds a new grid to this world and materializes the supplied sparse voxel indices when sparse storage is configur
 257    /// Dense grids ignore the configured voxel input and materialize every in-bounds voxel.
 258    /// </summary>
 259    /// <param name="configuration">The grid configuration to normalize and register.</param>
 260    /// <param name="configuredVoxels">Grid-local voxel indices to materialize for sparse storage.</param>
 261    /// <param name="allocatedIndex">The allocated world-local grid slot on success.</param>
 262    /// <returns>True if the grid was added; otherwise false.</returns>
 263    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 264    public bool TryAddGrid(
 265        GridConfiguration configuration,
 266        IEnumerable<VoxelIndex>? configuredVoxels,
 267        out ushort allocatedIndex) =>
 81268        TryAddGridCore(configuration, configuredVoxels, null, out allocatedIndex);
 269
 270    /// <summary>
 271    /// Adds a new grid to this world and materializes true cells from the supplied sparse voxel mask when sparse storag
 272    /// Dense grids ignore the configured voxel input and materialize every in-bounds voxel.
 273    /// </summary>
 274    /// <param name="configuration">The grid configuration to normalize and register.</param>
 275    /// <param name="configuredVoxels">A [x, y, z] mask whose true values identify sparse voxels to materialize. Sparse 
 276    /// <param name="allocatedIndex">The allocated world-local grid slot on success.</param>
 277    /// <returns>True if the grid was added; otherwise false.</returns>
 278    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 279    public bool TryAddGrid(
 280        GridConfiguration configuration,
 281        bool[,,]? configuredVoxels,
 282        out ushort allocatedIndex) =>
 7283        TryAddGridCore(configuration, null, configuredVoxels, out allocatedIndex);
 284
 285    private bool TryAddGridCore(
 286        GridConfiguration configuration,
 287        IEnumerable<VoxelIndex>? configuredVoxels,
 288        bool[,,]? configuredVoxelMask,
 289        out ushort allocatedIndex)
 290    {
 581291        allocatedIndex = ushort.MaxValue;
 292
 581293        if (!CanAddGrid())
 5294            return false;
 295
 576296        if (!TryNormalizeConfiguration(
 576297                configuration,
 576298                out GridConfiguration normalizedConfiguration,
 576299                out IGridTopology topology,
 576300                out GridDimensions dimensions)
 576301            || !TryValidateGridDimensions(dimensions))
 302        {
 6303            return false;
 304        }
 305
 570306        if (!TryPrepareConfiguredVoxels(
 570307            normalizedConfiguration,
 570308            dimensions,
 570309            configuredVoxels,
 570310            configuredVoxelMask,
 570311            out VoxelIndex[] preparedVoxels))
 312        {
 12313            return false;
 314        }
 315
 558316        GridConfigurationKey boundsKey = normalizedConfiguration.ToGridKey();
 317
 558318        if (TryFindExistingGrid(boundsKey, out allocatedIndex))
 4319            return false;
 320
 554321        long gridGeneration = RuntimeIdentityAllocator.Allocate(ref _gridGenerationCounter);
 554322        VoxelGrid newGrid = Pools.GridPool.Rent();
 554323        GridEventInfo addedGridInfo = default;
 324
 554325        _gridLock.EnterWriteLock();
 326        try
 327        {
 554328            allocatedIndex = (ushort)ActiveGrids.Add(newGrid);
 554329            BoundsTracker.Add(boundsKey, allocatedIndex);
 330
 554331            newGrid.Initialize(this, allocatedIndex, gridGeneration, normalizedConfiguration, topology, preparedVoxels);
 554332            UpdateMaxTopologyCellEdge(newGrid.Topology.MaxCellEdge);
 554333            RegisterGrid(newGrid, allocatedIndex);
 334
 554335            Version++;
 554336            addedGridInfo = CreateGridEventInfo(newGrid, GridEventKind.GridAdded);
 554337        }
 338        finally
 339        {
 554340            _gridLock.ExitWriteLock();
 554341        }
 342
 554343        NotifyActiveGridAdded(addedGridInfo);
 554344        return true;
 345    }
 346
 347    /// <summary>
 348    /// Removes a grid from this world and updates all references to ensure integrity.
 349    /// </summary>
 350    /// <param name="removeIndex">The world-local grid slot to remove.</param>
 351    /// <returns>True if the grid was removed; otherwise false.</returns>
 352    public bool TryRemoveGrid(ushort removeIndex)
 353    {
 43354        if (!IsActive || !ActiveGrids.IsAllocated(removeIndex))
 2355            return false;
 356
 357        VoxelGrid gridToRemove;
 41358        GridEventInfo removedGridInfo = default;
 359
 41360        _gridLock.EnterWriteLock();
 361        try
 362        {
 41363            gridToRemove = ActiveGrids[removeIndex];
 41364            Fixed64 removedMaxCellEdge = gridToRemove.Topology.MaxCellEdge;
 41365            UnregisterGrid(gridToRemove, removeIndex);
 41366            BoundsTracker.Remove(gridToRemove.Configuration.ToGridKey());
 41367            ActiveGrids.RemoveAt(removeIndex);
 41368            RecalculateMaxTopologyCellEdgeIfNeeded(removedMaxCellEdge);
 369
 41370            Version++;
 41371            removedGridInfo = CreateGridEventInfo(gridToRemove, GridEventKind.GridRemoved);
 41372        }
 373        finally
 374        {
 41375            _gridLock.ExitWriteLock();
 41376        }
 377
 41378        Pools.GridPool.Release(gridToRemove);
 41379        NotifyActiveGridRemoved(removedGridInfo);
 380
 41381        if (ActiveGrids.Count == 0)
 25382            ActiveGrids.TrimExcessCapacity();
 383
 41384        return true;
 385    }
 386
 387    #endregion
 388
 389    private bool CanAddGrid()
 390    {
 581391        if (!IsActive)
 392        {
 3393            GridForgeLogger.Channel.Error($"Grid world not active. Cannot add grids to an inactive world.");
 3394            return false;
 395        }
 396
 578397        if ((uint)ActiveGrids.Count >= MaxGrids)
 398        {
 2399            GridForgeLogger.Channel.Warn($"No more grids can be added at this time.");
 2400            return false;
 401        }
 402
 576403        return true;
 404    }
 405
 406    private static bool TryPrepareConfiguredVoxels(
 407        GridConfiguration configuration,
 408        GridDimensions dimensions,
 409        IEnumerable<VoxelIndex>? configuredVoxels,
 410        bool[,,]? configuredVoxelMask,
 411        out VoxelIndex[] preparedVoxels)
 412    {
 570413        preparedVoxels = Array.Empty<VoxelIndex>();
 570414        if (configuration.StorageKind != GridStorageKind.Sparse)
 456415            return true;
 416
 114417        if (configuredVoxelMask != null)
 7418            return TryPrepareConfiguredVoxelMask(configuredVoxelMask, dimensions, out preparedVoxels);
 419
 107420        return TryPrepareConfiguredVoxelIndices(configuredVoxels, dimensions, out preparedVoxels);
 421    }
 422
 423    private static bool TryValidateGridDimensions(GridDimensions dimensions)
 424    {
 575425        long layerSize = (long)dimensions.Width * dimensions.Height;
 575426        if (layerSize > int.MaxValue || layerSize * dimensions.Length > int.MaxValue)
 427        {
 4428            GridForgeLogger.Channel.Warn($"Grid dimensions exceed the supported int voxel address space.");
 4429            return false;
 430        }
 431
 571432        return true;
 433    }
 434
 435    private static bool TryPrepareConfiguredVoxelMask(
 436        bool[,,] configuredVoxelMask,
 437        GridDimensions dimensions,
 438        out VoxelIndex[] preparedVoxels)
 439    {
 7440        preparedVoxels = Array.Empty<VoxelIndex>();
 441
 7442        if (configuredVoxelMask.GetLength(0) != dimensions.Width
 7443            || configuredVoxelMask.GetLength(1) != dimensions.Height
 7444            || configuredVoxelMask.GetLength(2) != dimensions.Length)
 445        {
 5446            GridForgeLogger.Channel.Warn($"Sparse voxel mask dimensions must match normalized grid dimensions.");
 5447            return false;
 448        }
 449
 2450        int configuredCount = 0;
 12451        for (int x = 0; x < dimensions.Width; x++)
 452        {
 16453            for (int y = 0; y < dimensions.Height; y++)
 454            {
 24455                for (int z = 0; z < dimensions.Length; z++)
 456                {
 8457                    if (configuredVoxelMask[x, y, z])
 2458                        configuredCount++;
 459                }
 460            }
 461        }
 462
 2463        if (configuredCount == 0)
 1464            return true;
 465
 1466        preparedVoxels = new VoxelIndex[configuredCount];
 1467        int index = 0;
 6468        for (int x = 0; x < dimensions.Width; x++)
 469        {
 8470            for (int y = 0; y < dimensions.Height; y++)
 471            {
 12472                for (int z = 0; z < dimensions.Length; z++)
 473                {
 4474                    if (configuredVoxelMask[x, y, z])
 2475                        preparedVoxels[index++] = new VoxelIndex(x, y, z);
 476                }
 477            }
 478        }
 479
 1480        return true;
 481    }
 482
 483    private static bool TryPrepareConfiguredVoxelIndices(
 484        IEnumerable<VoxelIndex>? configuredVoxels,
 485        GridDimensions dimensions,
 486        out VoxelIndex[] preparedVoxels)
 487    {
 107488        preparedVoxels = Array.Empty<VoxelIndex>();
 107489        if (configuredVoxels == null)
 27490            return true;
 491
 80492        SwiftList<VoxelIndex> indices = configuredVoxels is ICollection<VoxelIndex> collection
 80493            ? new SwiftList<VoxelIndex>(collection.Count)
 80494            : new SwiftList<VoxelIndex>();
 495
 371496        foreach (VoxelIndex configuredVoxel in configuredVoxels)
 497        {
 109498            if (!IsConfiguredVoxelInBounds(configuredVoxel, dimensions))
 499            {
 7500                GridForgeLogger.Channel.Warn($"Sparse voxel index {configuredVoxel} is outside normalized grid dimension
 7501                return false;
 502            }
 503
 102504            indices.Add(configuredVoxel);
 505        }
 506
 73507        if (indices.Count == 0)
 3508            return true;
 509
 70510        preparedVoxels = indices.ToArray();
 70511        Array.Sort(preparedVoxels, CompareVoxelIndices);
 70512        CompactPreparedVoxels(ref preparedVoxels);
 70513        return true;
 7514    }
 515
 516    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 517    private static bool IsConfiguredVoxelInBounds(VoxelIndex voxelIndex, GridDimensions dimensions) =>
 109518        (uint)voxelIndex.x < (uint)dimensions.Width
 109519        && (uint)voxelIndex.y < (uint)dimensions.Height
 109520        && (uint)voxelIndex.z < (uint)dimensions.Length;
 521
 522    private static void CompactPreparedVoxels(ref VoxelIndex[] preparedVoxels)
 523    {
 70524        if (preparedVoxels.Length < 2)
 49525            return;
 526
 21527        int writeIndex = 1;
 21528        VoxelIndex previous = preparedVoxels[0];
 106529        for (int readIndex = 1; readIndex < preparedVoxels.Length; readIndex++)
 530        {
 32531            VoxelIndex current = preparedVoxels[readIndex];
 32532            if (current == previous)
 533                continue;
 534
 30535            preparedVoxels[writeIndex++] = current;
 30536            previous = current;
 537        }
 538
 21539        if (writeIndex != preparedVoxels.Length)
 2540            Array.Resize(ref preparedVoxels, writeIndex);
 21541    }
 542
 543    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 544    private void UpdateMaxTopologyCellEdge(Fixed64 candidate)
 545    {
 554546        if (candidate > MaxTopologyCellEdge)
 445547            MaxTopologyCellEdge = candidate;
 554548    }
 549
 550    private void RecalculateMaxTopologyCellEdgeIfNeeded(Fixed64 removedMaxCellEdge)
 551    {
 41552        if (removedMaxCellEdge < MaxTopologyCellEdge)
 3553            return;
 554
 38555        Fixed64 maxCellEdge = Fixed64.Zero;
 108556        foreach (VoxelGrid grid in ActiveGrids)
 557        {
 16558            if (grid.Topology.MaxCellEdge > maxCellEdge)
 13559                maxCellEdge = grid.Topology.MaxCellEdge;
 560        }
 561
 38562        MaxTopologyCellEdge = maxCellEdge;
 38563    }
 564
 565    private bool TryFindExistingGrid(GridConfigurationKey boundsKey, out ushort allocatedIndex)
 566    {
 558567        _gridLock.EnterReadLock();
 568        try
 569        {
 558570            if (BoundsTracker.TryGetValue(boundsKey, out allocatedIndex))
 571            {
 4572                GridForgeLogger.Channel.Warn($"A grid with these bounds has already been allocated.");
 4573                return true;
 574            }
 554575        }
 576        finally
 577        {
 558578            _gridLock.ExitReadLock();
 558579        }
 580
 554581        allocatedIndex = ushort.MaxValue;
 554582        return false;
 4583    }
 584
 585    private void RegisterGrid(VoxelGrid newGrid, ushort allocatedIndex)
 586    {
 554587        _spatialIndex.Insert(
 554588            allocatedIndex,
 554589            new FixedBoundVolume(newGrid.BoundsMin, newGrid.BoundsMax));
 554590        _spatialIndex.CollectCandidates(
 554591            CreateExpandedBounds(
 554592                newGrid.BoundsMin,
 554593                newGrid.BoundsMax,
 554594                newGrid.Topology.OverlapTolerance),
 554595            ActiveGrids,
 554596            _gridCandidates);
 597
 2398598        for (int candidateIndex = 0; candidateIndex < _gridCandidates.Count; candidateIndex++)
 599        {
 645600            ushort neighborIndex = _gridCandidates[candidateIndex];
 645601            if (neighborIndex == allocatedIndex)
 602                continue;
 603
 91604            VoxelGrid neighborGrid = ActiveGrids[neighborIndex];
 91605            newGrid.TryAddGridNeighbor(neighborGrid);
 91606            neighborGrid.TryAddGridNeighbor(newGrid);
 607        }
 554608    }
 609
 610    private void UnregisterGrid(VoxelGrid gridToRemove, ushort removeIndex)
 611    {
 41612        _spatialIndex.Remove(removeIndex);
 41613        UnlinkGridNeighbors(gridToRemove);
 41614    }
 615
 616    private void UnlinkGridNeighbors(VoxelGrid gridToRemove)
 617    {
 41618        if (!gridToRemove.IsConjoined)
 29619            return;
 620
 12621        var neighborSets = gridToRemove.Neighbors!.DenseValues;
 12622        int neighborSetCount = gridToRemove.Neighbors.Count;
 54623        for (int neighborSetIndex = 0; neighborSetIndex < neighborSetCount; neighborSetIndex++)
 624        {
 70625            foreach (int neighborIndex in neighborSets[neighborSetIndex])
 626            {
 20627                VoxelGrid neighborGrid = ActiveGrids[neighborIndex];
 20628                neighborGrid.TryRemoveGridNeighbor(gridToRemove);
 629            }
 630        }
 12631    }
 632
 633    internal void CollectGridCandidates(
 634        Vector3d boundsMin,
 635        Vector3d boundsMax,
 636        SwiftList<ushort> candidates) =>
 869637        _spatialIndex.CollectCandidates(
 869638            new FixedBoundVolume(boundsMin, boundsMax),
 869639            ActiveGrids,
 869640            candidates);
 641
 642    private static FixedBoundVolume CreateExpandedBounds(
 643        Vector3d boundsMin,
 644        Vector3d boundsMax,
 645        Fixed64 padding)
 646    {
 558647        Vector3d expansion = new(padding, padding, padding);
 558648        return new FixedBoundVolume(boundsMin - expansion, boundsMax + expansion);
 649    }
 650
 651    #region Lookup
 652
 653    /// <summary>
 654    /// Retrieves a grid by its world-local index.
 655    /// </summary>
 656    /// <param name="index">The world-local grid slot to resolve.</param>
 657    /// <param name="outGrid">The resolved grid, if found.</param>
 658    /// <returns>True if the grid was resolved; otherwise false.</returns>
 659    public bool TryGetGrid(int index, out VoxelGrid? outGrid)
 660    {
 1426661        outGrid = null;
 1426662        if (!CanResolveGrid(index))
 17663            return false;
 664
 1409665        outGrid = ActiveGrids[index];
 1409666        return true;
 667    }
 668
 669    /// <summary>
 670    /// Retrieves the grid containing a given world position.
 671    /// </summary>
 672    /// <param name="position">The world position to resolve.</param>
 673    /// <param name="outGrid">The resolved grid, if found.</param>
 674    /// <returns>True if a containing grid was found; otherwise false.</returns>
 675    public bool TryGetGrid(Vector3d position, out VoxelGrid? outGrid)
 676    {
 154677        outGrid = null;
 154678        if (!CanResolvePosition())
 2679            return false;
 680
 152681        _spatialIndex.CollectPointCandidates(position, _gridCandidates);
 152682        if (TryGetContainingGrid(position, _gridCandidates, out outGrid))
 64683            return true;
 684
 88685        GridForgeLogger.Channel.Info($"No grid contains position {position}.");
 88686        return false;
 687    }
 688
 689    /// <summary>
 690    /// Retrieves the grid containing a 2D XZ-plane world position on the default world Y layer.
 691    /// </summary>
 692    /// <param name="position">The 2D position whose X component maps to world X and Y component maps to world Z.</param
 693    /// <param name="outGrid">The resolved grid, if found.</param>
 694    /// <returns>True if a containing grid was found; otherwise false.</returns>
 695    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 696    public bool TryGetGrid(Vector2d position, out VoxelGrid? outGrid) =>
 1697         TryGetGrid(position, default, out outGrid);
 698
 699    /// <summary>
 700    /// Retrieves the grid containing a 2D XZ-plane world position on the supplied world Y layer.
 701    /// </summary>
 702    /// <param name="position">The 2D position whose X component maps to world X and Y component maps to world Z.</param
 703    /// <param name="layerY">The world Y layer to resolve. Defaults to zero when omitted by paired overloads.</param>
 704    /// <param name="outGrid">The resolved grid, if found.</param>
 705    /// <returns>True if a containing grid was found; otherwise false.</returns>
 706    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 707    public bool TryGetGrid(Vector2d position, Fixed64 layerY, out VoxelGrid? outGrid) =>
 3708        TryGetGrid(GridPlane2d.ToWorld(position, layerY), out outGrid);
 709
 710    /// <summary>
 711    /// Retrieves the active grid whose bounds are nearest to the supplied world position.
 712    /// </summary>
 713    /// <param name="position">The world position to resolve.</param>
 714    /// <param name="outGrid">The closest grid, if found.</param>
 715    /// <param name="topologyKind">Optional topology filter. When supplied, only grids using the requested topology are 
 716    /// <returns>True if a closest active grid was resolved; otherwise false.</returns>
 717    public bool TryGetClosestGrid(
 718        Vector3d position,
 719        out VoxelGrid? outGrid,
 720        GridTopologyKind? topologyKind = null)
 721    {
 27722        outGrid = null;
 27723        if (!CanResolveActiveGrid())
 1724            return false;
 725
 26726        Fixed64 closestDistanceSquared = Fixed64.MaxValue;
 124727        foreach (VoxelGrid candidateGrid in ActiveGrids)
 728        {
 36729            if (!candidateGrid.IsActive
 36730                || !MatchesTopologyKind(candidateGrid, topologyKind))
 731            {
 732                continue;
 733            }
 734
 30735            Fixed64 distanceSquared = GetDistanceSquaredToBounds(position, candidateGrid.BoundsMin, candidateGrid.Bounds
 30736            if (outGrid == null || distanceSquared < closestDistanceSquared)
 737            {
 23738                outGrid = candidateGrid;
 23739                closestDistanceSquared = distanceSquared;
 740            }
 741        }
 742
 26743        return outGrid != null;
 744    }
 745
 746    /// <summary>
 747    /// Retrieves the active grid whose bounds are nearest to a 2D XZ-plane world position on the default world Y layer.
 748    /// </summary>
 749    /// <param name="position">The 2D position whose X component maps to world X and Y component maps to world Z.</param
 750    /// <param name="outGrid">The closest grid, if found.</param>
 751    /// <param name="topologyKind">Optional topology filter. When supplied, only grids using the requested topology are 
 752    /// <returns>True if a closest active grid was resolved; otherwise false.</returns>
 753    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 754    public bool TryGetClosestGrid(
 755        Vector2d position,
 756        out VoxelGrid? outGrid,
 757        GridTopologyKind? topologyKind = null) =>
 1758        TryGetClosestGrid(position, default, out outGrid, topologyKind);
 759
 760    /// <summary>
 761    /// Retrieves the active grid whose bounds are nearest to a 2D XZ-plane world position on the supplied world Y layer
 762    /// </summary>
 763    /// <param name="position">The 2D position whose X component maps to world X and Y component maps to world Z.</param
 764    /// <param name="layerY">The world Y layer to resolve. Defaults to zero when omitted by paired overloads.</param>
 765    /// <param name="outGrid">The closest grid, if found.</param>
 766    /// <param name="topologyKind">Optional topology filter. When supplied, only grids using the requested topology are 
 767    /// <returns>True if a closest active grid was resolved; otherwise false.</returns>
 768    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 769    public bool TryGetClosestGrid(
 770        Vector2d position,
 771        Fixed64 layerY,
 772        out VoxelGrid? outGrid,
 773        GridTopologyKind? topologyKind = null) =>
 3774        TryGetClosestGrid(GridPlane2d.ToWorld(position, layerY), out outGrid, topologyKind);
 775
 776    /// <summary>
 777    /// Retrieves a grid by a world-scoped voxel identity.
 778    /// </summary>
 779    /// <param name="worldVoxelIndex">The voxel identity whose grid should be resolved.</param>
 780    /// <param name="result">The resolved grid, if found.</param>
 781    /// <returns>True if the grid was resolved; otherwise false.</returns>
 782    public bool TryGetGrid(WorldVoxelIndex worldVoxelIndex, out VoxelGrid? result)
 783    {
 1131784        result = null;
 1131785        if (worldVoxelIndex.WorldSpawnToken != SpawnToken
 1131786            || !TryGetGrid(worldVoxelIndex.GridIndex, out VoxelGrid? resolvedGrid)
 1131787            || worldVoxelIndex.GridSpawnToken != resolvedGrid!.SpawnToken)
 788        {
 22789            return false;
 790        }
 791
 1109792        result = resolvedGrid;
 1109793        return true;
 794    }
 795
 796    /// <summary>
 797    /// Retrieves the grid and voxel containing a given world position.
 798    /// </summary>
 799    /// <param name="position">The world position to resolve.</param>
 800    /// <param name="outGrid">The resolved grid, if found.</param>
 801    /// <param name="outVoxel">The resolved voxel, if found.</param>
 802    /// <returns>True if both the grid and voxel were resolved; otherwise false.</returns>
 803    public bool TryGetGridAndVoxel(
 804        Vector3d position,
 805        out VoxelGrid? outGrid,
 806        out Voxel? outVoxel)
 807    {
 28808        outVoxel = null;
 28809        return TryGetGrid(position, out outGrid)
 28810            && outGrid!.TryGetVoxel(position, out outVoxel);
 811    }
 812
 813    /// <summary>
 814    /// Retrieves the grid and voxel containing a 2D XZ-plane world position on the default world Y layer.
 815    /// </summary>
 816    /// <param name="position">The 2D position whose X component maps to world X and Y component maps to world Z.</param
 817    /// <param name="outGrid">The resolved grid, if found.</param>
 818    /// <param name="outVoxel">The resolved voxel, if found.</param>
 819    /// <returns>True if both the grid and voxel were resolved; otherwise false.</returns>
 820    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 821    public bool TryGetGridAndVoxel(
 822        Vector2d position,
 823        out VoxelGrid? outGrid,
 824        out Voxel? outVoxel) =>
 1825         TryGetGridAndVoxel(position, default, out outGrid, out outVoxel);
 826
 827    /// <summary>
 828    /// Retrieves the grid and voxel containing a 2D XZ-plane world position on the supplied world Y layer.
 829    /// </summary>
 830    /// <param name="position">The 2D position whose X component maps to world X and Y component maps to world Z.</param
 831    /// <param name="layerY">The world Y layer to resolve. Defaults to zero when omitted by paired overloads.</param>
 832    /// <param name="outGrid">The resolved grid, if found.</param>
 833    /// <param name="outVoxel">The resolved voxel, if found.</param>
 834    /// <returns>True if both the grid and voxel were resolved; otherwise false.</returns>
 835    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 836    public bool TryGetGridAndVoxel(
 837        Vector2d position,
 838        Fixed64 layerY,
 839        out VoxelGrid? outGrid,
 840        out Voxel? outVoxel) =>
 3841         TryGetGridAndVoxel(GridPlane2d.ToWorld(position, layerY), out outGrid, out outVoxel);
 842
 843    /// <summary>
 844    /// Retrieves the physical voxel whose center is nearest to the supplied world position and the grid that owns it.
 845    /// Sparse grids only consider configured physical voxels.
 846    /// </summary>
 847    /// <param name="position">The world position to resolve.</param>
 848    /// <param name="outGrid">The grid that owns the closest physical voxel, if found.</param>
 849    /// <param name="outVoxel">The closest physical voxel, if found.</param>
 850    /// <param name="topologyKind">Optional topology filter. When supplied, only grids using the requested topology are 
 851    /// <returns>True if a physical voxel was resolved; otherwise false.</returns>
 852    public bool TryGetClosestGridAndVoxel(
 853        Vector3d position,
 854        out VoxelGrid? outGrid,
 855        out Voxel? outVoxel,
 856        GridTopologyKind? topologyKind = null)
 857    {
 16858        outGrid = null;
 16859        outVoxel = null;
 16860        if (!CanResolveActiveGrid())
 2861            return false;
 862
 14863        Fixed64 closestDistanceSquared = Fixed64.MaxValue;
 14864        if (TryGetClosestGrid(position, out VoxelGrid? closestBoundsGrid, topologyKind)
 14865            && closestBoundsGrid!.ConfiguredVoxelCount != 0)
 866        {
 10867            bool resolved = closestBoundsGrid.TryGetClosestVoxel(
 10868                position,
 10869                out outVoxel,
 10870                out closestDistanceSquared);
 871            Debug.Assert(resolved);
 10872            outGrid = closestBoundsGrid;
 873        }
 874
 66875        foreach (VoxelGrid candidateGrid in ActiveGrids)
 876        {
 19877            if (candidateGrid == null
 19878                || !candidateGrid.IsActive
 19879                || candidateGrid.ConfiguredVoxelCount == 0
 19880                || !MatchesTopologyKind(candidateGrid, topologyKind))
 881            {
 882                continue;
 883            }
 14884            if (ReferenceEquals(candidateGrid, outGrid))
 885                continue;
 886
 5887            Fixed64 boundsDistanceSquared = GetDistanceSquaredToBounds(position, candidateGrid.BoundsMin, candidateGrid.
 5888            if (outVoxel != null && boundsDistanceSquared > closestDistanceSquared)
 889                continue;
 890
 4891            candidateGrid.TryGetClosestVoxel(
 4892                position,
 4893                out Voxel? candidateVoxel,
 4894                out Fixed64 candidateDistanceSquared);
 895
 4896            if (IsBetterClosestVoxel(
 4897                candidateDistanceSquared,
 4898                candidateGrid,
 4899                candidateVoxel!,
 4900                closestDistanceSquared,
 4901                outGrid,
 4902                outVoxel))
 903            {
 3904                outGrid = candidateGrid;
 3905                outVoxel = candidateVoxel;
 3906                closestDistanceSquared = candidateDistanceSquared;
 907            }
 908        }
 909
 14910        return outVoxel != null;
 911    }
 912
 913    /// <summary>
 914    /// Retrieves the physical voxel whose center is nearest to a 2D XZ-plane world position on the default world Y laye
 915    /// Sparse grids only consider configured physical voxels.
 916    /// </summary>
 917    /// <param name="position">The 2D position whose X component maps to world X and Y component maps to world Z.</param
 918    /// <param name="outGrid">The grid that owns the closest physical voxel, if found.</param>
 919    /// <param name="outVoxel">The closest physical voxel, if found.</param>
 920    /// <param name="topologyKind">Optional topology filter. When supplied, only grids using the requested topology are 
 921    /// <returns>True if a physical voxel was resolved; otherwise false.</returns>
 922    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 923    public bool TryGetClosestGridAndVoxel(
 924        Vector2d position,
 925        out VoxelGrid? outGrid,
 926        out Voxel? outVoxel,
 927        GridTopologyKind? topologyKind = null) =>
 1928        TryGetClosestGridAndVoxel(position, default, out outGrid, out outVoxel, topologyKind);
 929
 930    /// <summary>
 931    /// Retrieves the physical voxel whose center is nearest to a 2D XZ-plane world position on the supplied world Y lay
 932    /// Sparse grids only consider configured physical voxels.
 933    /// </summary>
 934    /// <param name="position">The 2D position whose X component maps to world X and Y component maps to world Z.</param
 935    /// <param name="layerY">The world Y layer to resolve. Defaults to zero when omitted by paired overloads.</param>
 936    /// <param name="outGrid">The grid that owns the closest physical voxel, if found.</param>
 937    /// <param name="outVoxel">The closest physical voxel, if found.</param>
 938    /// <param name="topologyKind">Optional topology filter. When supplied, only grids using the requested topology are 
 939    /// <returns>True if a physical voxel was resolved; otherwise false.</returns>
 940    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 941    public bool TryGetClosestGridAndVoxel(
 942        Vector2d position,
 943        Fixed64 layerY,
 944        out VoxelGrid? outGrid,
 945        out Voxel? outVoxel,
 946        GridTopologyKind? topologyKind = null) =>
 3947        TryGetClosestGridAndVoxel(GridPlane2d.ToWorld(position, layerY), out outGrid, out outVoxel, topologyKind);
 948
 949    /// <summary>
 950    /// Retrieves the grid and voxel for a given voxel identity.
 951    /// </summary>
 952    /// <param name="worldVoxelIndex">The voxel identity to resolve.</param>
 953    /// <param name="outGrid">The resolved grid, if found.</param>
 954    /// <param name="result">The resolved voxel, if found.</param>
 955    /// <returns>True if both the grid and voxel were resolved; otherwise false.</returns>
 956    public bool TryGetGridAndVoxel(
 957        WorldVoxelIndex worldVoxelIndex,
 958        out VoxelGrid? outGrid,
 959        out Voxel? result)
 960    {
 70961        result = null;
 70962        return TryGetGrid(worldVoxelIndex, out outGrid)
 70963            && outGrid!.TryGetVoxel(worldVoxelIndex.VoxelIndex, out result);
 964    }
 965
 966    /// <summary>
 967    /// Retrieves a voxel from a world position.
 968    /// </summary>
 969    /// <param name="position">The world position to resolve.</param>
 970    /// <param name="result">The resolved voxel, if found.</param>
 971    /// <returns>True if the voxel was resolved; otherwise false.</returns>
 972    public bool TryGetVoxel(
 973        Vector3d position,
 974        out Voxel? result)
 975    {
 101976        result = null;
 101977        return TryGetGrid(position, out VoxelGrid? grid)
 101978            && grid!.TryGetVoxel(position, out result);
 979    }
 980
 981    /// <summary>
 982    /// Retrieves a voxel from a 2D XZ-plane world position on the default world Y layer.
 983    /// </summary>
 984    /// <param name="position">The 2D position whose X component maps to world X and Y component maps to world Z.</param
 985    /// <param name="result">The resolved voxel, if found.</param>
 986    /// <returns>True if the voxel was resolved; otherwise false.</returns>
 987    public bool TryGetVoxel(
 988        Vector2d position,
 989        out Voxel? result)
 990    {
 1991        return TryGetVoxel(position, default, out result);
 992    }
 993
 994    /// <summary>
 995    /// Retrieves a voxel from a 2D XZ-plane world position on the supplied world Y layer.
 996    /// </summary>
 997    /// <param name="position">The 2D position whose X component maps to world X and Y component maps to world Z.</param
 998    /// <param name="layerY">The world Y layer to resolve. Defaults to zero when omitted by paired overloads.</param>
 999    /// <param name="result">The resolved voxel, if found.</param>
 1000    /// <returns>True if the voxel was resolved; otherwise false.</returns>
 1001    public bool TryGetVoxel(
 1002        Vector2d position,
 1003        Fixed64 layerY,
 1004        out Voxel? result)
 1005    {
 31006        return TryGetVoxel(GridPlane2d.ToWorld(position, layerY), out result);
 1007    }
 1008
 1009    /// <summary>
 1010    /// Retrieves the physical voxel whose center is nearest to the supplied world position.
 1011    /// Sparse grids only consider configured physical voxels.
 1012    /// </summary>
 1013    /// <param name="position">The world position to resolve.</param>
 1014    /// <param name="result">The closest physical voxel, if found.</param>
 1015    /// <param name="topologyKind">Optional topology filter. When supplied, only grids using the requested topology are 
 1016    /// <returns>True if a physical voxel was resolved; otherwise false.</returns>
 1017    public bool TryGetClosestVoxel(
 1018        Vector3d position,
 1019        out Voxel? result,
 1020        GridTopologyKind? topologyKind = null)
 1021    {
 61022        result = null;
 61023        if (!TryGetClosestGridAndVoxel(position, out _, out Voxel? closestVoxel, topologyKind))
 21024            return false;
 1025
 41026        result = closestVoxel;
 41027        return true;
 1028    }
 1029
 1030    /// <summary>
 1031    /// Retrieves the physical voxel whose center is nearest to a 2D XZ-plane world position on the default world Y laye
 1032    /// Sparse grids only consider configured physical voxels.
 1033    /// </summary>
 1034    /// <param name="position">The 2D position whose X component maps to world X and Y component maps to world Z.</param
 1035    /// <param name="result">The closest physical voxel, if found.</param>
 1036    /// <param name="topologyKind">Optional topology filter. When supplied, only grids using the requested topology are 
 1037    /// <returns>True if a physical voxel was resolved; otherwise false.</returns>
 1038    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1039    public bool TryGetClosestVoxel(
 1040        Vector2d position,
 1041        out Voxel? result,
 1042        GridTopologyKind? topologyKind = null) =>
 11043        TryGetClosestVoxel(position, default, out result, topologyKind);
 1044
 1045    /// <summary>
 1046    /// Retrieves the physical voxel whose center is nearest to a 2D XZ-plane world position on the supplied world Y lay
 1047    /// Sparse grids only consider configured physical voxels.
 1048    /// </summary>
 1049    /// <param name="position">The 2D position whose X component maps to world X and Y component maps to world Z.</param
 1050    /// <param name="layerY">The world Y layer to resolve. Defaults to zero when omitted by paired overloads.</param>
 1051    /// <param name="result">The closest physical voxel, if found.</param>
 1052    /// <param name="topologyKind">Optional topology filter. When supplied, only grids using the requested topology are 
 1053    /// <returns>True if a physical voxel was resolved; otherwise false.</returns>
 1054    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1055    public bool TryGetClosestVoxel(
 1056        Vector2d position,
 1057        Fixed64 layerY,
 1058        out Voxel? result,
 1059        GridTopologyKind? topologyKind = null) =>
 31060        TryGetClosestVoxel(GridPlane2d.ToWorld(position, layerY), out result, topologyKind);
 1061
 1062    /// <summary>
 1063    /// Retrieves a voxel from a world-scoped voxel identity.
 1064    /// </summary>
 1065    /// <param name="worldVoxelIndex">The voxel identity to resolve.</param>
 1066    /// <param name="result">The resolved voxel, if found.</param>
 1067    /// <returns>True if the voxel was resolved; otherwise false.</returns>
 1068    public bool TryGetVoxel(
 1069        WorldVoxelIndex worldVoxelIndex,
 1070        out Voxel? result)
 1071    {
 31072        result = null;
 31073        return TryGetGrid(worldVoxelIndex, out VoxelGrid? grid)
 31074            && grid!.TryGetVoxel(worldVoxelIndex.VoxelIndex, out result);
 1075    }
 1076
 1077    #endregion
 1078
 1079    #region Internal Helpers
 1080
 1081    internal static bool TryNormalizeConfiguration(
 1082        GridConfiguration configuration,
 1083        out GridConfiguration normalizedConfiguration,
 1084        out IGridTopology topology,
 1085        out GridDimensions dimensions)
 1086    {
 5861087        normalizedConfiguration = default;
 5861088        topology = null!;
 5861089        dimensions = default;
 5861090        if (!GridTopologyFactory.TryCreate(configuration, out IGridTopology? createdTopology))
 111091            return false;
 1092
 5751093        (Vector3d boundsMin, Vector3d boundsMax) =
 5751094            createdTopology!.NormalizeBounds(configuration.BoundsMin, configuration.BoundsMax);
 1095
 5751096        normalizedConfiguration = new GridConfiguration(
 5751097            boundsMin,
 5751098            boundsMax,
 5751099            configuration.ScanCellSize,
 5751100            configuration.TopologyKind,
 5751101            configuration.TopologyMetrics,
 5751102            configuration.StorageKind);
 5751103        topology = createdTopology;
 5751104        dimensions = topology.CalculateDimensions(boundsMin, boundsMax);
 5751105        return true;
 1106    }
 1107
 1108    /// <summary>
 1109    /// Increments the version of the specified grid and optionally the world version.
 1110    /// </summary>
 1111    public void IncrementGridVersion(int index, bool significant = false)
 1112    {
 51113        if (!IsActive)
 1114        {
 31115            GridForgeLogger.Channel.Warn($"Grid world not active. Cannot increment grid versions.");
 31116            return;
 1117        }
 1118
 21119        _gridLock.EnterWriteLock();
 1120        try
 1121        {
 21122            if (significant)
 11123                Version++;
 1124
 21125            if (ActiveGrids.IsAllocated(index))
 11126                ActiveGrids[index].IncrementVersion();
 21127        }
 1128        finally
 1129        {
 21130            _gridLock.ExitWriteLock();
 21131        }
 21132    }
 1133
 1134    /// <summary>
 1135    /// Finds active grids in this world that overlap the supplied target grid.
 1136    /// </summary>
 1137    public IEnumerable<VoxelGrid> FindOverlappingGrids(VoxelGrid targetGrid)
 1138    {
 51139        SwiftList<VoxelGrid> overlappingGrids = new();
 51140        FindOverlappingGridsInto(targetGrid, overlappingGrids);
 51141        return overlappingGrids;
 1142    }
 1143
 1144    /// <summary>
 1145    /// Clears and fills caller-owned storage with active grids that overlap the supplied target grid.
 1146    /// </summary>
 1147    /// <param name="targetGrid">The grid whose expanded topology bounds define the overlap query.</param>
 1148    /// <param name="results">Caller-owned storage cleared and filled in ascending grid-slot order.</param>
 1149    public void FindOverlappingGridsInto(VoxelGrid targetGrid, SwiftList<VoxelGrid> results)
 1150    {
 71151        SwiftThrowHelper.ThrowIfNull(targetGrid, nameof(targetGrid));
 71152        SwiftThrowHelper.ThrowIfNull(results, nameof(results));
 1153
 71154        results.Clear();
 1155
 71156        if (!IsActive)
 1157        {
 31158            GridForgeLogger.Channel.Warn($"Grid world not active. Cannot resolve overlaps.");
 31159            return;
 1160        }
 1161
 41162        _spatialIndex.CollectCandidates(
 41163            CreateExpandedBounds(
 41164                targetGrid.BoundsMin,
 41165                targetGrid.BoundsMax,
 41166                targetGrid.Topology.OverlapTolerance),
 41167            ActiveGrids,
 41168            _gridCandidates);
 261169        for (int candidateIndex = 0; candidateIndex < _gridCandidates.Count; candidateIndex++)
 91170            TryAddOverlappingGrid(targetGrid, _gridCandidates[candidateIndex], results);
 41171    }
 1172
 1173    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1174    private static bool MatchesTopologyKind(VoxelGrid grid, GridTopologyKind? topologyKind) =>
 531175        !topologyKind.HasValue || grid.TopologyKind == topologyKind.Value;
 1176
 1177    private static bool IsBetterClosestVoxel(
 1178        Fixed64 candidateDistanceSquared,
 1179        VoxelGrid candidateGrid,
 1180        Voxel candidateVoxel,
 1181        Fixed64 closestDistanceSquared,
 1182        VoxelGrid? closestGrid,
 1183        Voxel? closestVoxel)
 1184    {
 41185        if (closestVoxel == null || closestGrid == null)
 11186            return true;
 1187
 31188        if (candidateDistanceSquared != closestDistanceSquared)
 11189            return candidateDistanceSquared < closestDistanceSquared;
 1190
 21191        return candidateGrid.GridIndex < closestGrid.GridIndex;
 1192    }
 1193
 1194    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1195    private static Fixed64 GetDistanceSquaredToBounds(Vector3d position, Vector3d boundsMin, Vector3d boundsMax)
 1196    {
 351197        Fixed64 x = GetAxisDistanceToBounds(position.X, boundsMin.X, boundsMax.X);
 351198        Fixed64 y = GetAxisDistanceToBounds(position.Y, boundsMin.Y, boundsMax.Y);
 351199        Fixed64 z = GetAxisDistanceToBounds(position.Z, boundsMin.Z, boundsMax.Z);
 351200        return x * x + y * y + z * z;
 1201    }
 1202
 1203    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1204    private static Fixed64 GetAxisDistanceToBounds(Fixed64 coordinate, Fixed64 min, Fixed64 max)
 1205    {
 1051206        if (coordinate < min)
 171207            return min - coordinate;
 1208
 881209        return coordinate > max ? coordinate - max : Fixed64.Zero;
 1210    }
 1211
 1212    private bool CanResolveGrid(int index)
 1213    {
 14261214        if (!CanResolveActiveGrid())
 21215            return false;
 1216
 14241217        if (!IsGridIndexInActiveRange(index))
 1218        {
 71219            GridForgeLogger.Channel.Error($"GridIndex '{index}' is out-of-bounds for ActiveGrids.");
 71220            return false;
 1221        }
 1222
 14171223        return IsGridIndexAllocated(index);
 1224    }
 1225
 1226    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1227    private bool CanResolveActiveGrid()
 1228    {
 14691229        if (IsActive)
 14641230            return true;
 1231
 51232        GridForgeLogger.Channel.Warn($"Grid world not active. Cannot resolve grids.");
 51233        return false;
 1234    }
 1235
 1236    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1237    private bool IsGridIndexInActiveRange(int index) =>
 14241238         (uint)index < MaxGrids && (uint)index <= ActiveGrids.Count;
 1239
 1240    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1241    private bool IsGridIndexAllocated(int index)
 1242    {
 14171243        if (ActiveGrids.IsAllocated(index))
 14091244            return true;
 1245
 81246        GridForgeLogger.Channel.Error($"GridIndex '{index}' has not been allocated to ActiveGrids.");
 81247        return false;
 1248    }
 1249
 1250    private bool CanResolvePosition()
 1251    {
 1541252        if (IsActive)
 1521253            return true;
 1254
 21255        GridForgeLogger.Channel.Warn($"Grid world not active. Cannot resolve positions.");
 21256        return false;
 1257    }
 1258
 1259    private bool TryGetContainingGrid(
 1260        Vector3d position,
 1261        SwiftList<ushort> gridList,
 1262        out VoxelGrid? outGrid)
 1263    {
 1521264        outGrid = null;
 1265
 3521266        for (int index = 0; index < gridList.Count; index++)
 1267        {
 881268            ushort candidateIndex = gridList[index];
 881269            VoxelGrid candidateGrid = ActiveGrids[candidateIndex];
 881270            if (candidateGrid.IsInBounds(position))
 1271            {
 641272                outGrid = candidateGrid;
 641273                return true;
 1274            }
 1275        }
 1276
 881277        return false;
 1278    }
 1279
 1280    private void TryAddOverlappingGrid(
 1281        VoxelGrid targetGrid,
 1282        ushort neighborIndex,
 1283        SwiftList<VoxelGrid> overlappingGrids)
 1284    {
 91285        if (neighborIndex == targetGrid.GridIndex)
 41286            return;
 1287
 51288        overlappingGrids.Add(ActiveGrids[neighborIndex]);
 51289    }
 1290
 1291    internal void NotifyActiveGridChange(VoxelGrid? grid)
 1292    {
 16591293        if (grid == null || !grid.IsActive)
 41294            return;
 1295
 16551296        NotifyActiveGridChange(CreateGridEventInfo(grid, GridEventKind.GridChanged));
 16551297    }
 1298
 1299    internal void NotifyActiveGridChange(
 1300        VoxelGrid? grid,
 1301        GridEventKind changeKind,
 1302        VoxelIndex voxelIndex,
 1303        Vector3d affectedPosition)
 1304    {
 481305        if (grid == null || !grid.IsActive)
 21306            return;
 1307
 461308        NotifyActiveGridChange(CreateGridEventInfo(grid, changeKind, voxelIndex, affectedPosition, affectedPosition));
 461309    }
 1310
 1311    #endregion
 1312
 1313    #region Private Helpers
 1314
 1315    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1316    private static int ResolveSpatialGridCellSize(int spatialGridCellSize)
 1317    {
 5371318        if (spatialGridCellSize <= 0)
 1319        {
 31320            GridForgeLogger.Channel.Warn($"Spatial grid cell size must be greater than zero. Falling back to default siz
 31321            return DefaultSpatialGridCellSize;
 1322        }
 1323
 5341324        return spatialGridCellSize;
 1325    }
 1326
 1327    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1328    private GridEventInfo CreateGridEventInfo(VoxelGrid grid, GridEventKind changeKind) =>
 22501329        new(
 22501330            SpawnToken,
 22501331            grid.GridIndex,
 22501332            grid.SpawnToken,
 22501333            grid.Configuration,
 22501334            grid.Version,
 22501335            changeKind,
 22501336            default,
 22501337            grid.BoundsMin,
 22501338            grid.BoundsMax);
 1339
 1340    private GridEventInfo CreateGridEventInfo(
 1341        VoxelGrid grid,
 1342        GridEventKind changeKind,
 1343        VoxelIndex voxelIndex,
 1344        Vector3d affectedBoundsMin,
 1345        Vector3d affectedBoundsMax) =>
 461346        new(
 461347            SpawnToken,
 461348            grid.GridIndex,
 461349            grid.SpawnToken,
 461350            grid.Configuration,
 461351            grid.Version,
 461352            changeKind,
 461353            voxelIndex,
 461354            affectedBoundsMin,
 461355            affectedBoundsMax);
 1356
 1357    private void NotifyActiveGridAdded(GridEventInfo eventInfo)
 1358    {
 5541359        Action<GridEventInfo>? handlers = _onActiveGridAdded;
 5541360        if (handlers == null)
 5431361            return;
 1362
 111363        var handlerDelegates = handlers.GetInvocationList();
 481364        for (int i = 0; i < handlerDelegates.Length; i++)
 1365        {
 1366            try
 1367            {
 131368                ((Action<GridEventInfo>)handlerDelegates[i])(eventInfo);
 121369            }
 11370            catch (Exception ex)
 1371            {
 11372                GridForgeLogger.Channel.Error($"[Grid {eventInfo.GridIndex}] added notification error: {ex.Message}");
 11373            }
 1374        }
 111375    }
 1376
 1377    private void NotifyActiveGridRemoved(GridEventInfo eventInfo)
 1378    {
 411379        Action<GridEventInfo>? handlers = _onActiveGridRemoved;
 411380        if (handlers == null)
 331381            return;
 1382
 81383        var handlerDelegates = handlers.GetInvocationList();
 361384        for (int i = 0; i < handlerDelegates.Length; i++)
 1385        {
 1386            try
 1387            {
 101388                ((Action<GridEventInfo>)handlerDelegates[i])(eventInfo);
 91389            }
 11390            catch (Exception ex)
 1391            {
 11392                GridForgeLogger.Channel.Error($"[Grid {eventInfo.GridIndex}] removed notification error: {ex.Message}");
 11393            }
 1394        }
 81395    }
 1396
 1397    private void NotifyActiveGridChange(GridEventInfo eventInfo)
 1398    {
 17011399        Action<GridEventInfo>? handlers = _onActiveGridChange;
 17011400        if (handlers == null)
 8781401            return;
 1402
 8231403        var handlerDelegates = handlers.GetInvocationList();
 437041404        for (int i = 0; i < handlerDelegates.Length; i++)
 1405        {
 1406            try
 1407            {
 210291408                ((Action<GridEventInfo>)handlerDelegates[i])(eventInfo);
 210271409            }
 21410            catch (Exception ex)
 1411            {
 21412                GridForgeLogger.Channel.Error($"[Grid {eventInfo.GridIndex}] change notification error: {ex.Message}");
 21413            }
 1414        }
 8231415    }
 1416
 1417    #endregion
 1418}

Methods/Properties

.cctor()
.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_OnReset(System.Action)
remove_OnReset(System.Action)
Reset(System.Boolean)
NotifyResetHandlers()
ReleaseActiveGrids()
Dispose()
AllocateObstacleToken()
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[]&)
TryValidateGridDimensions(GridForge.Grids.Topology.GridDimensions)
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)
TryFindExistingGrid(GridForge.Configuration.GridConfigurationKey,System.UInt16&)
RegisterGrid(GridForge.Grids.VoxelGrid,System.UInt16)
UnregisterGrid(GridForge.Grids.VoxelGrid,System.UInt16)
UnlinkGridNeighbors(GridForge.Grids.VoxelGrid)
CollectGridCandidates(FixedMathSharp.Vector3d,FixedMathSharp.Vector3d,SwiftCollections.SwiftList`1<System.UInt16>)
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&)
TryNormalizeConfiguration(GridForge.Configuration.GridConfiguration,GridForge.Configuration.GridConfiguration&,GridForge.Grids.Topology.IGridTopology&,GridForge.Grids.Topology.GridDimensions&)
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>)
NotifyActiveGridChange(GridForge.Grids.VoxelGrid)
NotifyActiveGridChange(GridForge.Grids.VoxelGrid,GridForge.Grids.GridEventKind,GridForge.Spatial.VoxelIndex,FixedMathSharp.Vector3d)
ResolveSpatialGridCellSize(System.Int32)
CreateGridEventInfo(GridForge.Grids.VoxelGrid,GridForge.Grids.GridEventKind)
CreateGridEventInfo(GridForge.Grids.VoxelGrid,GridForge.Grids.GridEventKind,GridForge.Spatial.VoxelIndex,FixedMathSharp.Vector3d,FixedMathSharp.Vector3d)
NotifyActiveGridAdded(GridForge.Grids.GridEventInfo)
NotifyActiveGridRemoved(GridForge.Grids.GridEventInfo)
NotifyActiveGridChange(GridForge.Grids.GridEventInfo)