< Summary

Information
Class: GridForge.Grids.GridObstacleManager
Assembly: GridForge
File(s): /home/runner/work/GridForge/GridForge/src/GridForge/Grids/Managers/GridObstacleManager.cs
Line coverage
100%
Covered lines: 214
Uncovered lines: 0
Coverable lines: 214
Total lines: 523
Line coverage: 100%
Branch coverage
100%
Covered branches: 84
Total branches: 84
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

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

#LineLine coverage
 1//=======================================================================
 2// GridObstacleManager.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.Spatial;
 11using SwiftCollections.Pool;
 12
 13namespace GridForge.Grids;
 14
 15/// <summary>
 16/// Handles the addition, removal, and tracking of obstacles within a grid.
 17/// Ensures thread safety and proper event notifications when obstacles change.
 18/// </summary>
 19public static class GridObstacleManager
 20{
 21    #region Constants & Events
 22
 23    /// <summary>
 24    /// Maximum number of obstacles that can exist on a single voxel.
 25    /// </summary>
 26    public const byte MaxObstacleCount = byte.MaxValue;
 27
 28    /// <summary>
 29    /// Event triggered when an obstacle is added.
 30    /// </summary>
 31    private static Action<ObstacleEventInfo>? _onObstacleAdded;
 32
 33    /// <inheritdoc cref="_onObstacleAdded"/>
 34    public static event Action<ObstacleEventInfo> OnObstacleAdded
 35    {
 2036        add => _onObstacleAdded += value;
 2037        remove => _onObstacleAdded -= value;
 38    }
 39
 40    /// <summary>
 41    /// Event triggered when an obstacle is removed.
 42    /// </summary>
 43    private static Action<ObstacleEventInfo>? _onObstacleRemoved;
 44
 45    /// <inheritdoc cref="_onObstacleRemoved"/>
 46    public static event Action<ObstacleEventInfo> OnObstacleRemoved
 47    {
 1748        add => _onObstacleRemoved += value;
 1749        remove => _onObstacleRemoved -= value;
 50    }
 51
 52    /// <summary>
 53    /// Event triggered when all obstacles on a voxel are cleared at once.
 54    /// </summary>
 55    private static Action<ObstacleClearEventInfo>? _onObstaclesCleared;
 56
 57    /// <inheritdoc cref="_onObstaclesCleared"/>
 58    public static event Action<ObstacleClearEventInfo> OnObstaclesCleared
 59    {
 1860        add => _onObstaclesCleared += value;
 1861        remove => _onObstaclesCleared -= value;
 62    }
 63
 64    #endregion
 65
 66    #region Public Methods
 67
 68    /// <summary>
 69    /// Attempts to add an obstacle at the given world-scoped voxel identity in the supplied world.
 70    /// </summary>
 71    public static bool TryAddObstacle(
 72        GridWorld world,
 73        WorldVoxelIndex index,
 74        ObstacleToken obstacleToken)
 75    {
 376        return world != null
 377            && world.TryGetGridAndVoxel(index, out VoxelGrid? grid, out Voxel? voxel)
 378            && grid!.TryAddObstacle(voxel!, obstacleToken) == true;
 79    }
 80
 81    /// <summary>
 82    /// Attempts to add an obstacle at the given world position.
 83    /// </summary>
 84    public static bool TryAddObstacle(this VoxelGrid grid, Vector3d position, ObstacleToken obstacleToken)
 85    {
 686        return grid.TryGetVoxel(position, out Voxel? voxel)
 687            && grid.TryAddObstacle(voxel!, obstacleToken);
 88    }
 89
 90    /// <summary>
 91    /// Attempts to add an obstacle at the given XZ-plane world position on the default world Y layer.
 92    /// </summary>
 93    /// <param name="grid">The grid to mutate.</param>
 94    /// <param name="position">The 2D position whose X component maps to world X and Y component maps to world Z.</param
 95    /// <param name="obstacleToken">The obstacle token to attach to the resolved voxel.</param>
 96    /// <returns>True if an obstacle was added to the resolved voxel; otherwise false.</returns>
 97    public static bool TryAddObstacle(this VoxelGrid grid, Vector2d position, ObstacleToken obstacleToken)
 98    {
 299        return grid.TryAddObstacle(position, default, obstacleToken);
 100    }
 101
 102    /// <summary>
 103    /// Attempts to add an obstacle at the given XZ-plane world position on the supplied world Y layer.
 104    /// </summary>
 105    /// <param name="grid">The grid to mutate.</param>
 106    /// <param name="position">The 2D position whose X component maps to world X and Y component maps to world Z.</param
 107    /// <param name="layerY">The world Y layer to resolve.</param>
 108    /// <param name="obstacleToken">The obstacle token to attach to the resolved voxel.</param>
 109    /// <returns>True if an obstacle was added to the resolved voxel; otherwise false.</returns>
 110    public static bool TryAddObstacle(this VoxelGrid grid, Vector2d position, Fixed64 layerY, ObstacleToken obstacleToke
 111    {
 3112        return grid.TryAddObstacle(GridPlane2d.ToWorld(position, layerY), obstacleToken);
 113    }
 114
 115    /// <summary>
 116    /// Adds an obstacle to this voxel.
 117    /// </summary>
 118    /// <param name="grid"></param>
 119    /// <param name="targetVoxel"></param>
 120    /// <param name="obstacleToken">The process-unique obstacle registration token.</param>
 121    /// <exception cref="Exception"></exception>
 122    public static bool TryAddObstacle(this VoxelGrid grid, Voxel targetVoxel, ObstacleToken obstacleToken)
 123    {
 1031124        if (!obstacleToken.IsValid || !targetVoxel.IsBlockable)
 4125            return false;
 126
 127        byte obstacleCount;
 128        uint gridVersion;
 129        bool drainCommittedChanges;
 1027130        GridWorld? world = grid.World;
 1027131        if (world == null)
 1132            return false;
 133
 1026134        world.EnterReadLock();
 135        try
 136        {
 1026137            lock (grid.ObstacleSyncRoot)
 138            {
 1026139                lock (world.ChangeSyncRoot)
 140                {
 1026141                    if (targetVoxel.ObstacleCount >= MaxObstacleCount)
 1142                        return false;
 143
 1025144                    targetVoxel.ObstacleTracker ??= SwiftHashSetPool<ObstacleToken>.Shared.Rent();
 1025145                    if (!targetVoxel.ObstacleTracker.Add(obstacleToken))
 1146                        return false;
 1024147                    targetVoxel.ObstacleCount++;
 148
 1024149                    grid.ObstacleCount++;
 1024150                    gridVersion = grid.IncrementVersion();
 1024151                    obstacleCount = targetVoxel.ObstacleCount;
 152
 1024153                    CreateObstacleCommittedChange(
 1024154                        world,
 1024155                        grid,
 1024156                        targetVoxel,
 1024157                        GridEventKind.ObstacleAdded,
 1024158                        GridExactChangeKind.ObstacleAdded,
 1024159                        obstacleToken,
 1024160                        obstacleCount,
 1024161                        gridVersion,
 1024162                        out _,
 1024163                        out drainCommittedChanges);
 1024164                }
 165            }
 166        }
 167        finally
 168        {
 1026169            world.ExitReadLock();
 1026170        }
 171
 1024172        if (drainCommittedChanges)
 650173            world.DrainCommittedChanges();
 174
 1024175        return true;
 2176    }
 177
 178    /// <summary>
 179    /// Attempts to remove an obstacle at the given world-scoped voxel identity in the supplied world.
 180    /// </summary>
 181    public static bool TryRemoveObstacle(
 182        GridWorld world,
 183        WorldVoxelIndex index,
 184        ObstacleToken obstacleToken)
 185    {
 33186        return world != null
 33187            && world.TryGetGridAndVoxel(index, out VoxelGrid? grid, out Voxel? voxel)
 33188            && grid!.TryRemoveObstacle(voxel!, obstacleToken);
 189    }
 190
 191    /// <summary>
 192    /// Attempts to remove an obstacle from the specified world position.
 193    /// </summary>
 194    public static bool TryRemoveObstacle(this VoxelGrid grid, Vector3d position, ObstacleToken obstacleToken)
 195    {
 6196        return grid.TryGetVoxel(position, out Voxel? voxel)
 6197            && grid.TryRemoveObstacle(voxel!, obstacleToken);
 198    }
 199
 200    /// <summary>
 201    /// Attempts to remove an obstacle from the given XZ-plane world position on the default world Y layer.
 202    /// </summary>
 203    /// <param name="grid">The grid to mutate.</param>
 204    /// <param name="position">The 2D position whose X component maps to world X and Y component maps to world Z.</param
 205    /// <param name="obstacleToken">The obstacle token to remove from the resolved voxel.</param>
 206    /// <returns>True if the obstacle was removed from the resolved voxel; otherwise false.</returns>
 207    public static bool TryRemoveObstacle(this VoxelGrid grid, Vector2d position, ObstacleToken obstacleToken)
 208    {
 1209        return grid.TryRemoveObstacle(position, default, obstacleToken);
 210    }
 211
 212    /// <summary>
 213    /// Attempts to remove an obstacle from the given XZ-plane world position on the supplied world Y layer.
 214    /// </summary>
 215    /// <param name="grid">The grid to mutate.</param>
 216    /// <param name="position">The 2D position whose X component maps to world X and Y component maps to world Z.</param
 217    /// <param name="layerY">The world Y layer to resolve.</param>
 218    /// <param name="obstacleToken">The obstacle token to remove from the resolved voxel.</param>
 219    /// <returns>True if the obstacle was removed from the resolved voxel; otherwise false.</returns>
 220    public static bool TryRemoveObstacle(this VoxelGrid grid, Vector2d position, Fixed64 layerY, ObstacleToken obstacleT
 221    {
 3222        return grid.TryRemoveObstacle(GridPlane2d.ToWorld(position, layerY), obstacleToken);
 223    }
 224
 225    /// <summary>
 226    /// Removes an obstacle from a given voxel.
 227    /// </summary>
 228    public static bool TryRemoveObstacle(this VoxelGrid grid, Voxel targetVoxel, ObstacleToken obstacleToken)
 229    {
 98230        if (!obstacleToken.IsValid)
 1231            return false;
 232
 97233        if (targetVoxel.ObstacleCount == 0)
 234        {
 5235            GridForgeLogger.Channel.Warn($"No obstacle to remove on voxel ({targetVoxel.WorldIndex})!");
 5236            return false;
 237        }
 238
 239        byte obstacleCount;
 240        uint gridVersion;
 241        bool drainCommittedChanges;
 92242        GridWorld? world = grid.World;
 92243        if (world == null)
 1244            return false;
 245
 91246        world.EnterReadLock();
 247        try
 248        {
 91249            lock (grid.ObstacleSyncRoot)
 250            {
 91251                lock (world.ChangeSyncRoot)
 252                {
 91253                    if (!targetVoxel.ObstacleTracker!.Remove(obstacleToken))
 2254                        return false;
 255
 89256                    if (--targetVoxel.ObstacleCount <= 0)
 257                    {
 79258                        SwiftHashSetPool<ObstacleToken>.Shared.Release(targetVoxel.ObstacleTracker);
 79259                        targetVoxel.ObstacleTracker = null;
 79260                        targetVoxel.ObstacleCount = 0;
 261                    }
 262
 89263                    grid.ObstacleCount--;
 89264                    gridVersion = grid.IncrementVersion();
 89265                    obstacleCount = targetVoxel.ObstacleCount;
 266
 89267                    CreateObstacleCommittedChange(
 89268                        world,
 89269                        grid,
 89270                        targetVoxel,
 89271                        GridEventKind.ObstacleRemoved,
 89272                        GridExactChangeKind.ObstacleRemoved,
 89273                        obstacleToken,
 89274                        obstacleCount,
 89275                        gridVersion,
 89276                        out _,
 89277                        out drainCommittedChanges);
 89278                }
 279            }
 280        }
 281        finally
 282        {
 91283            world.ExitReadLock();
 91284        }
 285
 89286        if (drainCommittedChanges)
 87287            world.DrainCommittedChanges();
 288
 89289        return true;
 2290    }
 291
 292    /// <summary>
 293    /// Clears all obstacles from the specified voxel.
 294    /// </summary>
 295    /// <param name="grid"></param>
 296    /// <param name="targetVoxel"></param>
 297    public static void ClearObstacles(this VoxelGrid grid, Voxel targetVoxel)
 298    {
 567299        if (targetVoxel.ObstacleCount == 0)
 1300            return;
 301
 302        byte clearedObstacleCount;
 303        uint gridVersion;
 304        bool drainCommittedChanges;
 566305        GridWorld? world = grid.World;
 566306        if (world == null)
 1307            return;
 308
 565309        bool enteredReadLock = !world.IsWriteLockHeld;
 565310        if (enteredReadLock)
 10311            world.EnterReadLock();
 312        try
 313        {
 565314            lock (grid.ObstacleSyncRoot)
 315            {
 565316                lock (world.ChangeSyncRoot)
 317                {
 565318                    clearedObstacleCount = targetVoxel.ObstacleCount;
 565319                    if (targetVoxel.ObstacleTracker != null)
 320                    {
 565321                        SwiftHashSetPool<ObstacleToken>.Shared.Release(targetVoxel.ObstacleTracker);
 565322                        targetVoxel.ObstacleTracker = null;
 323                    }
 324
 565325                    grid.ObstacleCount -= targetVoxel.ObstacleCount;
 565326                    targetVoxel.ObstacleCount = 0;
 565327                    gridVersion = grid.IncrementVersion();
 328
 565329                    CreateObstacleClearCommittedChange(
 565330                        world,
 565331                        grid,
 565332                        targetVoxel,
 565333                        clearedObstacleCount,
 565334                        gridVersion,
 565335                        out _,
 565336                        out drainCommittedChanges);
 565337                }
 338            }
 339        }
 340        finally
 341        {
 565342            if (enteredReadLock)
 10343                world.ExitReadLock();
 565344        }
 345
 565346        if (drainCommittedChanges && enteredReadLock)
 4347            world.DrainCommittedChanges();
 565348    }
 349
 350    #endregion
 351
 352    #region Private Methods
 353
 354    private static void CreateObstacleCommittedChange(
 355        GridWorld world,
 356        VoxelGrid grid,
 357        Voxel targetVoxel,
 358        GridEventKind gridEventKind,
 359        GridExactChangeKind exactChangeKind,
 360        ObstacleToken obstacleToken,
 361        byte obstacleCount,
 362        uint gridVersion,
 363        out GridCommittedChange committedChange,
 364        out bool drainCommittedChanges)
 365    {
 1113366        GridChangeStamp changeStamp = world.AllocateChangeStamp();
 1113367        ObstacleEventInfo obstacleEvent = new ObstacleEventInfo(
 1113368            targetVoxel.WorldIndex,
 1113369            obstacleToken,
 1113370            obstacleCount,
 1113371            gridVersion,
 1113372            changeStamp);
 1113373        GridEventInfo gridEvent = world.CreateGridEventInfo(
 1113374            grid,
 1113375            gridEventKind,
 1113376            targetVoxel.Index,
 1113377            targetVoxel.WorldPosition,
 1113378            targetVoxel.WorldPosition,
 1113379            changeStamp,
 1113380            hasVoxelState: true,
 1113381            isVoxelPresent: true,
 1113382            obstacleCount);
 1113383        committedChange = new GridCommittedChange(
 1113384            gridEvent,
 1113385            exactChangeKind,
 1113386            obstacleEvent,
 1113387            targetVoxel);
 1113388        targetVoxel.CachedGridVersion = gridVersion;
 1113389        drainCommittedChanges = world.EnqueueCommittedChange(committedChange);
 1113390    }
 391
 392    private static void CreateObstacleClearCommittedChange(
 393        GridWorld world,
 394        VoxelGrid grid,
 395        Voxel targetVoxel,
 396        byte clearedObstacleCount,
 397        uint gridVersion,
 398        out GridCommittedChange committedChange,
 399        out bool drainCommittedChanges)
 400    {
 565401        GridChangeStamp changeStamp = world.AllocateChangeStamp();
 565402        ObstacleClearEventInfo clearEvent = new ObstacleClearEventInfo(
 565403            targetVoxel.WorldIndex,
 565404            clearedObstacleCount,
 565405            gridVersion,
 565406            changeStamp);
 565407        GridEventInfo gridEvent = world.CreateGridEventInfo(
 565408            grid,
 565409            GridEventKind.ObstaclesCleared,
 565410            targetVoxel.Index,
 565411            targetVoxel.WorldPosition,
 565412            targetVoxel.WorldPosition,
 565413            changeStamp,
 565414            hasVoxelState: true,
 565415            isVoxelPresent: true,
 565416            obstacleCount: 0);
 565417        committedChange = new GridCommittedChange(gridEvent, clearEvent, targetVoxel);
 565418        targetVoxel.CachedGridVersion = gridVersion;
 565419        drainCommittedChanges = world.EnqueueCommittedChange(committedChange);
 565420    }
 421
 422    /// <summary>
 423    /// Notifies listeners that an obstacle was added.
 424    /// </summary>
 425    private static void NotifyObstacleAdded(ObstacleEventInfo eventInfo)
 426    {
 1025427        Action<ObstacleEventInfo>? handlers = _onObstacleAdded;
 1025428        if (handlers != null)
 429        {
 11430            var handlerDelegates = handlers.GetInvocationList();
 50431            for (int i = 0; i < handlerDelegates.Length; i++)
 432            {
 433                try
 434                {
 14435                    ((Action<ObstacleEventInfo>)handlerDelegates[i])(eventInfo);
 12436                }
 2437                catch (Exception ex)
 438                {
 2439                    GridForgeLogger.Channel.Error($"[Voxel {eventInfo.VoxelIndex}] Obstacle add error: {ex.Message}");
 2440                }
 441            }
 442        }
 443
 444        // Voxel-local delivery is handled by NotifyCommittedExact after exact identity validation.
 1025445    }
 446
 447    /// <summary>
 448    /// Notifies listeners that an obstacle was removed.
 449    /// </summary>
 450    private static void NotifyObstacleRemoved(ObstacleEventInfo eventInfo)
 451    {
 90452        Action<ObstacleEventInfo>? handlers = _onObstacleRemoved;
 90453        if (handlers != null)
 454        {
 4455            var handlerDelegates = handlers.GetInvocationList();
 18456            for (int i = 0; i < handlerDelegates.Length; i++)
 457            {
 458                try
 459                {
 5460                    ((Action<ObstacleEventInfo>)handlerDelegates[i])(eventInfo);
 4461                }
 1462                catch (Exception ex)
 463                {
 1464                    GridForgeLogger.Channel.Error($"[Voxel {eventInfo.VoxelIndex}] Obstacle remove error: {ex.Message}")
 1465                }
 466            }
 467        }
 468
 469        // Voxel-local delivery is handled by NotifyCommittedExact after exact identity validation.
 90470    }
 471
 472    /// <summary>
 473    /// Notifies listeners that all obstacles on a voxel were cleared.
 474    /// </summary>
 475    private static void NotifyObstaclesCleared(ObstacleClearEventInfo eventInfo)
 476    {
 565477        Action<ObstacleClearEventInfo>? handlers = _onObstaclesCleared;
 565478        if (handlers != null)
 479        {
 5480            var handlerDelegates = handlers.GetInvocationList();
 22481            for (int i = 0; i < handlerDelegates.Length; i++)
 482            {
 483                try
 484                {
 6485                    ((Action<ObstacleClearEventInfo>)handlerDelegates[i])(eventInfo);
 5486                }
 1487                catch (Exception ex)
 488                {
 1489                    GridForgeLogger.Channel.Error($"[Voxel {eventInfo.VoxelIndex}] Obstacle clear error: {ex.Message}");
 1490                }
 491            }
 492        }
 493
 494        // Voxel-local delivery is handled by NotifyCommittedExact after exact identity validation.
 565495    }
 496
 497    internal static void NotifyCommittedExact(GridCommittedChange change)
 498    {
 3495499        switch (change.ExactKind)
 500        {
 501            case GridExactChangeKind.ObstacleAdded:
 1025502                NotifyObstacleAdded(change.ObstacleEvent);
 1025503                if (IsSameVoxel(change.TargetVoxel, change.ObstacleEvent.VoxelIndex))
 1024504                    change.TargetVoxel!.NotifyObstacleAdded(change.ObstacleEvent);
 1024505                break;
 506            case GridExactChangeKind.ObstacleRemoved:
 90507                NotifyObstacleRemoved(change.ObstacleEvent);
 90508                if (IsSameVoxel(change.TargetVoxel, change.ObstacleEvent.VoxelIndex))
 89509                    change.TargetVoxel!.NotifyObstacleRemoved(change.ObstacleEvent);
 89510                break;
 511            case GridExactChangeKind.ObstaclesCleared:
 565512                NotifyObstaclesCleared(change.ObstacleClearEvent);
 565513                if (IsSameVoxel(change.TargetVoxel, change.ObstacleClearEvent.VoxelIndex))
 4514                    change.TargetVoxel!.NotifyObstaclesCleared(change.ObstacleClearEvent);
 515                break;
 516        }
 567517    }
 518
 519    private static bool IsSameVoxel(Voxel? voxel, WorldVoxelIndex index) =>
 1680520        voxel != null && voxel.IsAllocated && voxel.WorldIndex == index;
 521
 522    #endregion
 523}

Methods/Properties

add_OnObstacleAdded(System.Action`1<GridForge.Grids.ObstacleEventInfo>)
remove_OnObstacleAdded(System.Action`1<GridForge.Grids.ObstacleEventInfo>)
add_OnObstacleRemoved(System.Action`1<GridForge.Grids.ObstacleEventInfo>)
remove_OnObstacleRemoved(System.Action`1<GridForge.Grids.ObstacleEventInfo>)
add_OnObstaclesCleared(System.Action`1<GridForge.Grids.ObstacleClearEventInfo>)
remove_OnObstaclesCleared(System.Action`1<GridForge.Grids.ObstacleClearEventInfo>)
TryAddObstacle(GridForge.Grids.GridWorld,GridForge.Spatial.WorldVoxelIndex,GridForge.ObstacleToken)
TryAddObstacle(GridForge.Grids.VoxelGrid,FixedMathSharp.Vector3d,GridForge.ObstacleToken)
TryAddObstacle(GridForge.Grids.VoxelGrid,FixedMathSharp.Vector2d,GridForge.ObstacleToken)
TryAddObstacle(GridForge.Grids.VoxelGrid,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,GridForge.ObstacleToken)
TryAddObstacle(GridForge.Grids.VoxelGrid,GridForge.Grids.Voxel,GridForge.ObstacleToken)
TryRemoveObstacle(GridForge.Grids.GridWorld,GridForge.Spatial.WorldVoxelIndex,GridForge.ObstacleToken)
TryRemoveObstacle(GridForge.Grids.VoxelGrid,FixedMathSharp.Vector3d,GridForge.ObstacleToken)
TryRemoveObstacle(GridForge.Grids.VoxelGrid,FixedMathSharp.Vector2d,GridForge.ObstacleToken)
TryRemoveObstacle(GridForge.Grids.VoxelGrid,FixedMathSharp.Vector2d,FixedMathSharp.Fixed64,GridForge.ObstacleToken)
TryRemoveObstacle(GridForge.Grids.VoxelGrid,GridForge.Grids.Voxel,GridForge.ObstacleToken)
ClearObstacles(GridForge.Grids.VoxelGrid,GridForge.Grids.Voxel)
CreateObstacleCommittedChange(GridForge.Grids.GridWorld,GridForge.Grids.VoxelGrid,GridForge.Grids.Voxel,GridForge.Grids.GridEventKind,GridForge.Grids.GridExactChangeKind,GridForge.ObstacleToken,System.Byte,System.UInt32,GridForge.Grids.GridCommittedChange&,System.Boolean&)
CreateObstacleClearCommittedChange(GridForge.Grids.GridWorld,GridForge.Grids.VoxelGrid,GridForge.Grids.Voxel,System.Byte,System.UInt32,GridForge.Grids.GridCommittedChange&,System.Boolean&)
NotifyObstacleAdded(GridForge.Grids.ObstacleEventInfo)
NotifyObstacleRemoved(GridForge.Grids.ObstacleEventInfo)
NotifyObstaclesCleared(GridForge.Grids.ObstacleClearEventInfo)
NotifyCommittedExact(GridForge.Grids.GridCommittedChange)
IsSameVoxel(GridForge.Grids.Voxel,GridForge.Spatial.WorldVoxelIndex)