< Summary

Information
Class: SwiftCollections.Query.SwiftOctree<T1, T2>
Assembly: SwiftCollections
File(s): /home/runner/work/SwiftCollections/SwiftCollections/src/SwiftCollections/Query/Octree/SwiftOctree.cs
Line coverage
100%
Covered lines: 216
Uncovered lines: 0
Coverable lines: 216
Total lines: 511
Line coverage: 100%
Branch coverage
97%
Covered branches: 90
Total branches: 92
Branch coverage: 97.8%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
get_Count()100%11100%
get_DebugNodeCount()100%11100%
get_DebugMaxDepth()100%11100%
get_DebugRootHasChildren()100%11100%
Insert(...)100%22100%
Remove(...)75%44100%
TryGetBounds(...)100%22100%
UpdateEntryBounds(...)100%22100%
Contains(...)100%11100%
Query(...)100%44100%
Clear()100%44100%
QueryNode(...)100%22100%
AddIntersectingEntries(...)100%44100%
QueryIntersectingChildren(...)100%44100%
RelocateEntry(...)75%44100%
InsertIntoNode(...)100%66100%
ShouldSubdivide(...)100%66100%
Subdivide(...)100%11100%
CreateChildNodes(...)100%22100%
MoveContainedEntriesToChildren(...)100%44100%
SubdivideOverflowingChildren(...)100%44100%
ChildShouldSubdivide(...)100%44100%
CreateChildNode(...)100%11100%
TryMergeUp(...)100%88100%
CanMerge(...)100%66100%
CollapseChildrenInto(...)100%44100%
RemoveEntryFromNode(...)100%11100%
AllocateEntry(...)100%22100%
EnsureEntryCapacity(...)100%44100%
FindEntryIndex(...)100%11100%
MatchesEntryKey(...)100%11100%
IsAllocatedEntry(...)100%11100%
GetEntryKey(...)100%11100%
EnsureWithinWorldBounds(...)100%22100%
CountNodes(...)100%44100%
GetMaxDepth(...)100%44100%
.ctor(...)100%11100%
get_HasChildren()100%11100%
Reset()100%11100%

File(s)

/home/runner/work/SwiftCollections/SwiftCollections/src/SwiftCollections/Query/Octree/SwiftOctree.cs

#LineLine coverage
 1//=======================================================================
 2// SwiftOctree.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 SwiftCollections.Diagnostics;
 9using SwiftCollections.Utility;
 10using System;
 11using System.Collections.Generic;
 12using System.Runtime.CompilerServices;
 13
 14namespace SwiftCollections.Query;
 15
 16/// <summary>
 17/// Represents a mutable octree that stores keyed bounding volumes within immutable world bounds.
 18/// </summary>
 19/// <typeparam name="TKey">The key used to identify each stored entry.</typeparam>
 20/// <typeparam name="TVolume">The volume type used for octree registration and queries.</typeparam>
 21public class SwiftOctree<TKey, TVolume>
 22    where TKey : notnull
 23    where TVolume : struct, IBoundVolume<TVolume>
 24{
 25    private const string _diagnosticSource = nameof(SwiftOctree<TKey, TVolume>);
 26
 27    private readonly IOctreeBoundsPartitioner<TVolume> _boundsPartitioner;
 28    private readonly QueryKeyIndexMap<TKey> _keyToEntryIndex;
 29    private readonly SwiftIntStack _freeEntries;
 30
 31    private OctreeEntry[] _entries;
 32    private OctreeNode _root;
 33    private int _peakCount;
 34    private int _count;
 35
 36    /// <summary>
 37    /// Initializes a new instance of the <see cref="SwiftOctree{TKey, TVolume}"/> class.
 38    /// </summary>
 39    /// <param name="worldBounds">The immutable world bounds covered by the octree.</param>
 40    /// <param name="options">Subdivision options for the octree.</param>
 41    /// <param name="boundsPartitioner">The backend-owned partitioner that maps bounds into octants.</param>
 4142    public SwiftOctree(TVolume worldBounds, SwiftOctreeOptions options, IOctreeBoundsPartitioner<TVolume> boundsPartitio
 43    {
 4144        SwiftThrowHelper.ThrowIfNull(boundsPartitioner, nameof(boundsPartitioner));
 45
 4146        WorldBounds = worldBounds;
 4147        Options = options;
 4148        _boundsPartitioner = boundsPartitioner;
 49
 4150        int capacity = SwiftHashTools.NextPowerOfTwo(Math.Max(4, options.NodeCapacity));
 4151        _keyToEntryIndex = new QueryKeyIndexMap<TKey>(capacity);
 4152        _freeEntries = new SwiftIntStack();
 4153        _entries = new OctreeEntry[capacity];
 4154        _root = new OctreeNode(worldBounds, 0, null);
 4155    }
 56
 57    /// <summary>
 58    /// Gets the number of active entries stored in the octree.
 59    /// </summary>
 1960    public int Count => _count;
 61
 62    /// <summary>
 63    /// Gets the immutable world bounds covered by this octree.
 64    /// </summary>
 65    public TVolume WorldBounds { get; }
 66
 67    /// <summary>
 68    /// Gets the subdivision options used by this octree.
 69    /// </summary>
 70    public SwiftOctreeOptions Options { get; }
 71
 472    internal int DebugNodeCount => CountNodes(_root);
 73
 474    internal int DebugMaxDepth => GetMaxDepth(_root);
 75
 1276    internal bool DebugRootHasChildren => _root.HasChildren;
 77
 78    /// <summary>
 79    /// Inserts a new entry or replaces the bounds of an existing key.
 80    /// </summary>
 81    /// <param name="key">The entry key.</param>
 82    /// <param name="bounds">The entry bounds.</param>
 83    /// <returns><c>true</c> when a new key was added; <c>false</c> when an existing key was replaced.</returns>
 84    public bool Insert(TKey key, TVolume bounds)
 85    {
 9986        SwiftThrowHelper.ThrowIfNull(key, nameof(key));
 9987        EnsureWithinWorldBounds(bounds, nameof(bounds));
 88
 8689        int existingIndex = FindEntryIndex(key);
 8690        if (existingIndex >= 0)
 91        {
 192            RelocateEntry(existingIndex, bounds);
 193            return false;
 94        }
 95
 8596        EnsureEntryCapacity(_count + 1);
 97
 8598        int entryIndex = AllocateEntry(key, bounds);
 8599        _keyToEntryIndex.Insert(key, entryIndex);
 85100        InsertIntoNode(_root, entryIndex);
 85101        _count++;
 85102        return true;
 103    }
 104
 105    /// <summary>
 106    /// Removes an entry from the octree.
 107    /// </summary>
 108    /// <param name="key">The entry key.</param>
 109    /// <returns><c>true</c> when the key existed and was removed; otherwise, <c>false</c>.</returns>
 110    public bool Remove(TKey key)
 111    {
 5112        SwiftThrowHelper.ThrowIfNullGeneric(key, nameof(key));
 113
 5114        int entryIndex = FindEntryIndex(key);
 5115        if (entryIndex < 0)
 1116            return false;
 117
 4118        OctreeNode node = _entries[entryIndex].Node!;
 4119        RemoveEntryFromNode(node, entryIndex);
 120
 4121        _keyToEntryIndex.Remove(key, MatchesEntryKey, IsAllocatedEntry, GetEntryKey);
 4122        _entries[entryIndex].Reset();
 4123        _freeEntries.Push(entryIndex);
 4124        _count--;
 125
 4126        if (Options.EnableMergeOnRemove)
 4127            TryMergeUp(node);
 128
 4129        return true;
 130    }
 131
 132    /// <summary>
 133    /// Attempts to retrieve the bounds registered for the supplied key.
 134    /// </summary>
 135    public bool TryGetBounds(TKey key, out TVolume bounds)
 136    {
 2137        SwiftThrowHelper.ThrowIfNullGeneric(key, nameof(key));
 138
 2139        int entryIndex = FindEntryIndex(key);
 2140        if (entryIndex < 0)
 141        {
 1142            bounds = default;
 1143            return false;
 144        }
 145
 1146        bounds = _entries[entryIndex].Bounds;
 1147        return true;
 148    }
 149
 150    /// <summary>
 151    /// Updates the bounds for an existing entry.
 152    /// </summary>
 153    /// <param name="key">The entry key.</param>
 154    /// <param name="newBounds">The replacement bounds.</param>
 155    /// <returns><c>true</c> when the key existed; otherwise, <c>false</c>.</returns>
 156    public bool UpdateEntryBounds(TKey key, TVolume newBounds)
 157    {
 6158        SwiftThrowHelper.ThrowIfNullGeneric(key, nameof(key));
 159
 6160        int entryIndex = FindEntryIndex(key);
 6161        if (entryIndex < 0)
 1162            return false;
 163
 5164        return RelocateEntry(entryIndex, newBounds);
 165    }
 166
 167    /// <summary>
 168    /// Determines whether the octree contains the specified key.
 169    /// </summary>
 170    public bool Contains(TKey key)
 171    {
 3172        SwiftThrowHelper.ThrowIfNullGeneric(key, nameof(key));
 3173        return FindEntryIndex(key) >= 0;
 174    }
 175
 176    /// <summary>
 177    /// Queries the octree and returns only entries whose bounds intersect the supplied query volume.
 178    /// </summary>
 179    /// <param name="queryBounds">The bounds used to test for intersection.</param>
 180    /// <param name="results">The collection that receives intersecting keys.</param>
 181    public void Query(TVolume queryBounds, ICollection<TKey> results)
 182    {
 36183        SwiftThrowHelper.ThrowIfNull(results, nameof(results));
 184
 36185        if (_count == 0 || !_root.Bounds.Intersects(queryBounds))
 4186            return;
 187
 32188        QueryNode(_root, queryBounds, results);
 32189    }
 190
 191    /// <summary>
 192    /// Removes all entries from the octree while preserving the configured world bounds.
 193    /// </summary>
 194    public void Clear()
 195    {
 3196        if (_count == 0)
 1197            return;
 198
 12199        for (int i = 0; i < _peakCount; i++)
 4200            _entries[i].Reset();
 201
 2202        _keyToEntryIndex.Clear();
 2203        _freeEntries.Reset();
 2204        _peakCount = 0;
 2205        _count = 0;
 2206        _root = new OctreeNode(WorldBounds, 0, null);
 2207    }
 208
 209    private void QueryNode(OctreeNode node, TVolume queryBounds, ICollection<TKey> results)
 210    {
 117211        AddIntersectingEntries(node, queryBounds, results);
 212
 117213        if (node.HasChildren)
 28214            QueryIntersectingChildren(node, queryBounds, results);
 117215    }
 216
 217    private void AddIntersectingEntries(OctreeNode node, TVolume queryBounds, ICollection<TKey> results)
 218    {
 344219        for (int i = 0; i < node.EntryIndices.Count; i++)
 220        {
 55221            int entryIndex = node.EntryIndices[i];
 55222            ref OctreeEntry entry = ref _entries[entryIndex];
 55223            if (entry.Bounds.Intersects(queryBounds))
 50224                results.Add(entry.Key);
 225        }
 117226    }
 227
 228    private void QueryIntersectingChildren(OctreeNode node, TVolume queryBounds, ICollection<TKey> results)
 229    {
 28230        OctreeNode[] children = node.Children!;
 504231        for (int i = 0; i < children.Length; i++)
 232        {
 224233            OctreeNode child = children[i];
 224234            if (child.Bounds.Intersects(queryBounds))
 85235                QueryNode(child, queryBounds, results);
 236        }
 28237    }
 238
 239    private bool RelocateEntry(int entryIndex, TVolume newBounds)
 240    {
 6241        EnsureWithinWorldBounds(newBounds, nameof(newBounds));
 242
 5243        TVolume currentBounds = _entries[entryIndex].Bounds;
 5244        if (currentBounds.BoundsEquals(newBounds))
 1245            return true;
 246
 4247        OctreeNode oldNode = _entries[entryIndex].Node!;
 4248        RemoveEntryFromNode(oldNode, entryIndex);
 249
 4250        _entries[entryIndex].Bounds = newBounds;
 4251        InsertIntoNode(_root, entryIndex);
 252
 4253        if (Options.EnableMergeOnRemove)
 4254            TryMergeUp(oldNode);
 255
 4256        return true;
 257    }
 258
 259    private void InsertIntoNode(OctreeNode node, int entryIndex)
 260    {
 153261        if (node.HasChildren && _boundsPartitioner.TryGetContainingChildIndex(node.Bounds, _entries[entryIndex].Bounds, 
 262        {
 64263            InsertIntoNode(node.Children![childIndex], entryIndex);
 64264            return;
 265        }
 266
 89267        node.EntryIndices.Add(entryIndex);
 89268        _entries[entryIndex].Node = node;
 269
 89270        if (ShouldSubdivide(node))
 15271            Subdivide(node);
 89272    }
 273
 274    private bool ShouldSubdivide(OctreeNode node)
 275    {
 89276        return !node.HasChildren &&
 89277               node.Depth < Options.MaxDepth &&
 89278               node.EntryIndices.Count > Options.NodeCapacity &&
 89279               _boundsPartitioner.CanSubdivide(node.Bounds);
 280    }
 281
 282    private void Subdivide(OctreeNode node)
 283    {
 27284        CreateChildNodes(node);
 27285        MoveContainedEntriesToChildren(node);
 27286        SubdivideOverflowingChildren(node);
 27287    }
 288
 289    private void CreateChildNodes(OctreeNode node)
 290    {
 27291        node.Children = new OctreeNode[8];
 486292        for (int i = 0; i < node.Children.Length; i++)
 216293            node.Children[i] = CreateChildNode(node, i);
 27294    }
 295
 296    private void MoveContainedEntriesToChildren(OctreeNode node)
 297    {
 27298        OctreeNode[] children = node.Children!;
 27299        int entryIndex = 0;
 98300        while (entryIndex < node.EntryIndices.Count)
 301        {
 71302            int currentEntryIndex = node.EntryIndices[entryIndex];
 71303            if (!_boundsPartitioner.TryGetContainingChildIndex(node.Bounds, _entries[currentEntryIndex].Bounds, out int 
 304            {
 1305                entryIndex++;
 1306                continue;
 307            }
 308
 70309            OctreeNode child = children[childIndex];
 70310            child.EntryIndices.Add(currentEntryIndex);
 70311            _entries[currentEntryIndex].Node = child;
 70312            node.EntryIndices.RemoveAt(entryIndex);
 313        }
 27314    }
 315
 316    private void SubdivideOverflowingChildren(OctreeNode node)
 317    {
 27318        OctreeNode[] children = node.Children!;
 486319        for (int i = 0; i < children.Length; i++)
 320        {
 216321            OctreeNode child = children[i];
 216322            if (ChildShouldSubdivide(child))
 12323                Subdivide(child);
 324        }
 27325    }
 326
 327    private bool ChildShouldSubdivide(OctreeNode child)
 328    {
 216329        return child.EntryIndices.Count > Options.NodeCapacity &&
 216330               child.Depth < Options.MaxDepth &&
 216331               _boundsPartitioner.CanSubdivide(child.Bounds);
 332    }
 333
 334    private OctreeNode CreateChildNode(OctreeNode parent, int childIndex)
 335    {
 216336        TVolume bounds = _boundsPartitioner.CreateChildBounds(parent.Bounds, childIndex);
 216337        return new OctreeNode(bounds, parent.Depth + 1, parent);
 338    }
 339
 340    private void TryMergeUp(OctreeNode node)
 341    {
 8342        OctreeNode? current = node;
 21343        while (current != null)
 344        {
 13345            if (current.HasChildren && CanMerge(current))
 2346                CollapseChildrenInto(current);
 347
 13348            current = current.Parent ?? null;
 349        }
 8350    }
 351
 352    private bool CanMerge(OctreeNode node)
 353    {
 5354        int totalEntries = node.EntryIndices.Count;
 5355        OctreeNode[] children = node.Children!;
 44356        for (int i = 0; i < children.Length; i++)
 357        {
 20358            OctreeNode child = children[i];
 20359            if (child.HasChildren)
 2360                return false;
 361
 18362            totalEntries += child.EntryIndices.Count;
 18363            if (totalEntries > Options.NodeCapacity)
 1364                return false;
 365        }
 366
 2367        return true;
 368    }
 369
 370    private void CollapseChildrenInto(OctreeNode node)
 371    {
 2372        OctreeNode[] children = node.Children!;
 36373        for (int i = 0; i < children.Length; i++)
 374        {
 16375            OctreeNode child = children[i];
 36376            for (int j = 0; j < child.EntryIndices.Count; j++)
 377            {
 2378                int entryIndex = child.EntryIndices[j];
 2379                node.EntryIndices.Add(entryIndex);
 2380                _entries[entryIndex].Node = node;
 381            }
 382        }
 383
 2384        node.Children = null;
 2385    }
 386
 387    private void RemoveEntryFromNode(OctreeNode node, int entryIndex)
 388    {
 8389        int index = node.EntryIndices.IndexOf(entryIndex);
 8390        node.EntryIndices.RemoveAt(index);
 8391        _entries[entryIndex].Node = null;
 8392    }
 393
 394    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 395    private int AllocateEntry(TKey key, TVolume bounds)
 396    {
 397        int entryIndex;
 85398        if (_freeEntries.Count > 0)
 1399            entryIndex = _freeEntries.Pop();
 400        else
 84401            entryIndex = _peakCount++;
 402
 85403        _entries[entryIndex].Key = key;
 85404        _entries[entryIndex].Bounds = bounds;
 85405        _entries[entryIndex].Node = null;
 85406        _entries[entryIndex].IsAllocated = true;
 85407        return entryIndex;
 408    }
 409
 410    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 411    private void EnsureEntryCapacity(int capacity)
 412    {
 85413        if (capacity <= _entries.Length)
 78414            return;
 415
 7416        int newCapacity = SwiftHashTools.NextPowerOfTwo(capacity);
 7417        Array.Resize(ref _entries, newCapacity);
 7418        _keyToEntryIndex.ResizeAndRehash(newCapacity, _peakCount, IsAllocatedEntry, GetEntryKey);
 7419        SwiftCollectionDiagnostics.Shared.Info($"Resized octree entry storage to {newCapacity} entries.", _diagnosticSou
 7420    }
 421
 422    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 423    private int FindEntryIndex(TKey key)
 424    {
 102425        return _keyToEntryIndex.Find(key, MatchesEntryKey);
 426    }
 427
 428    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 429    private bool MatchesEntryKey(int index, TKey key)
 430    {
 39431        return EqualityComparer<TKey>.Default.Equals(_entries[index].Key, key);
 432    }
 433
 434    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 32435    private bool IsAllocatedEntry(int index) => _entries[index].IsAllocated;
 436
 437    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 32438    private TKey GetEntryKey(int index) => _entries[index].Key;
 439
 440    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 441    private void EnsureWithinWorldBounds(TVolume bounds, string paramName)
 442    {
 105443        if (!_boundsPartitioner.ContainsBounds(WorldBounds, bounds))
 14444            throw new ArgumentOutOfRangeException(paramName, "Bounds must be fully contained within the octree world bou
 91445    }
 446
 447    private static int CountNodes(OctreeNode node)
 448    {
 28449        int count = 1;
 28450        if (!node.HasChildren)
 25451            return count;
 452
 3453        OctreeNode[] children = node.Children!;
 54454        for (int i = 0; i < children.Length; i++)
 24455            count += CountNodes(children[i]);
 456
 3457        return count;
 458    }
 459
 460    private static int GetMaxDepth(OctreeNode node)
 461    {
 76462        int maxDepth = node.Depth;
 76463        if (!node.HasChildren)
 67464            return maxDepth;
 465
 9466        OctreeNode[] children = node.Children!;
 162467        for (int i = 0; i < children.Length; i++)
 72468            maxDepth = Math.Max(maxDepth, GetMaxDepth(children[i]));
 469
 9470        return maxDepth;
 471    }
 472
 473    private sealed class OctreeNode
 474    {
 259475        public OctreeNode(TVolume bounds, int depth, OctreeNode? parent)
 476        {
 259477            Bounds = bounds;
 259478            Depth = depth;
 259479            Parent = parent;
 259480            EntryIndices = new SwiftList<int>();
 259481        }
 482
 483        public TVolume Bounds { get; }
 484
 485        public int Depth { get; }
 486
 487        public OctreeNode? Parent { get; }
 488
 489        public SwiftList<int> EntryIndices { get; }
 490
 491        public OctreeNode[]? Children { get; set; }
 492
 508493        public bool HasChildren => Children != null;
 494    }
 495
 496    private struct OctreeEntry
 497    {
 498        public TKey Key;
 499        public TVolume Bounds;
 500        public OctreeNode? Node;
 501        public bool IsAllocated;
 502
 503        public void Reset()
 504        {
 8505            Key = default!;
 8506            Bounds = default;
 8507            Node = null;
 8508            IsAllocated = false;
 8509        }
 510    }
 511}

Methods/Properties

.ctor(TVolume,SwiftCollections.Query.SwiftOctreeOptions,SwiftCollections.Query.IOctreeBoundsPartitioner`1<TVolume>)
get_Count()
get_DebugNodeCount()
get_DebugMaxDepth()
get_DebugRootHasChildren()
Insert(TKey,TVolume)
Remove(TKey)
TryGetBounds(TKey,TVolume&)
UpdateEntryBounds(TKey,TVolume)
Contains(TKey)
Query(TVolume,System.Collections.Generic.ICollection`1<TKey>)
Clear()
QueryNode(SwiftCollections.Query.SwiftOctree`2/OctreeNode<TKey,TVolume>,TVolume,System.Collections.Generic.ICollection`1<TKey>)
AddIntersectingEntries(SwiftCollections.Query.SwiftOctree`2/OctreeNode<TKey,TVolume>,TVolume,System.Collections.Generic.ICollection`1<TKey>)
QueryIntersectingChildren(SwiftCollections.Query.SwiftOctree`2/OctreeNode<TKey,TVolume>,TVolume,System.Collections.Generic.ICollection`1<TKey>)
RelocateEntry(System.Int32,TVolume)
InsertIntoNode(SwiftCollections.Query.SwiftOctree`2/OctreeNode<TKey,TVolume>,System.Int32)
ShouldSubdivide(SwiftCollections.Query.SwiftOctree`2/OctreeNode<TKey,TVolume>)
Subdivide(SwiftCollections.Query.SwiftOctree`2/OctreeNode<TKey,TVolume>)
CreateChildNodes(SwiftCollections.Query.SwiftOctree`2/OctreeNode<TKey,TVolume>)
MoveContainedEntriesToChildren(SwiftCollections.Query.SwiftOctree`2/OctreeNode<TKey,TVolume>)
SubdivideOverflowingChildren(SwiftCollections.Query.SwiftOctree`2/OctreeNode<TKey,TVolume>)
ChildShouldSubdivide(SwiftCollections.Query.SwiftOctree`2/OctreeNode<TKey,TVolume>)
CreateChildNode(SwiftCollections.Query.SwiftOctree`2/OctreeNode<TKey,TVolume>,System.Int32)
TryMergeUp(SwiftCollections.Query.SwiftOctree`2/OctreeNode<TKey,TVolume>)
CanMerge(SwiftCollections.Query.SwiftOctree`2/OctreeNode<TKey,TVolume>)
CollapseChildrenInto(SwiftCollections.Query.SwiftOctree`2/OctreeNode<TKey,TVolume>)
RemoveEntryFromNode(SwiftCollections.Query.SwiftOctree`2/OctreeNode<TKey,TVolume>,System.Int32)
AllocateEntry(TKey,TVolume)
EnsureEntryCapacity(System.Int32)
FindEntryIndex(TKey)
MatchesEntryKey(System.Int32,TKey)
IsAllocatedEntry(System.Int32)
GetEntryKey(System.Int32)
EnsureWithinWorldBounds(TVolume,System.String)
CountNodes(SwiftCollections.Query.SwiftOctree`2/OctreeNode<TKey,TVolume>)
GetMaxDepth(SwiftCollections.Query.SwiftOctree`2/OctreeNode<TKey,TVolume>)
.ctor(TVolume,System.Int32,SwiftCollections.Query.SwiftOctree`2/OctreeNode<TKey,TVolume>)
get_HasChildren()
Reset()