< 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
97%
Covered lines: 218
Uncovered lines: 6
Coverable lines: 224
Total lines: 510
Line coverage: 97.3%
Branch coverage
89%
Covered branches: 107
Total branches: 120
Branch coverage: 89.1%
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_WorldBounds()100%11100%
get_Options()100%11100%
get_DebugNodeCount()100%11100%
get_DebugMaxDepth()100%11100%
get_DebugRootHasChildren()100%11100%
Insert(...)100%22100%
Remove(...)100%88100%
TryGetBounds(...)100%22100%
UpdateEntryBounds(...)100%22100%
Contains(...)100%11100%
Query(...)100%44100%
Clear()75%4490%
QueryNode(...)100%22100%
AddIntersectingEntries(...)100%66100%
QueryIntersectingChildren(...)87.5%88100%
RelocateEntry(...)90%101092.85%
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(...)62.5%9877.77%
CollapseChildrenInto(...)83.33%66100%
RemoveEntryFromNode(...)50%4483.33%
AllocateEntry(...)50%2287.5%
EnsureEntryCapacity(...)100%22100%
FindEntryIndex(...)100%11100%
MatchesEntryKey(...)50%22100%
IsAllocatedEntry(...)100%11100%
GetEntryKey(...)100%11100%
EnsureWithinWorldBounds(...)100%22100%
CountNodes(...)83.33%66100%
GetMaxDepth(...)83.33%66100%
.ctor(...)100%11100%
get_Bounds()100%11100%
get_Depth()100%11100%
get_Parent()100%11100%
get_EntryIndices()100%11100%
get_Children()100%11100%
get_HasChildren()100%11100%
Reset()100%11100%

File(s)

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

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.Runtime.CompilerServices;
 4
 5namespace SwiftCollections.Query;
 6
 7/// <summary>
 8/// Represents a mutable octree that stores keyed bounding volumes within immutable world bounds.
 9/// </summary>
 10/// <typeparam name="TKey">The key used to identify each stored entry.</typeparam>
 11/// <typeparam name="TVolume">The volume type used for octree registration and queries.</typeparam>
 12public class SwiftOctree<TKey, TVolume>
 13    where TKey : notnull
 14    where TVolume : struct, IBoundVolume<TVolume>
 15{
 16    private const string _diagnosticSource = nameof(SwiftOctree<TKey, TVolume>);
 17
 18    private readonly IOctreeBoundsPartitioner<TVolume> _boundsPartitioner;
 19    private readonly QueryKeyIndexMap<TKey> _keyToEntryIndex;
 20    private readonly SwiftIntStack _freeEntries;
 21
 22    private OctreeEntry[] _entries;
 23    private OctreeNode _root;
 24    private int _peakCount;
 25    private int _count;
 26
 27    /// <summary>
 28    /// Initializes a new instance of the <see cref="SwiftOctree{TKey, TVolume}"/> class.
 29    /// </summary>
 30    /// <param name="worldBounds">The immutable world bounds covered by the octree.</param>
 31    /// <param name="options">Subdivision options for the octree.</param>
 32    /// <param name="boundsPartitioner">The backend-owned partitioner that maps bounds into octants.</param>
 1833    public SwiftOctree(TVolume worldBounds, SwiftOctreeOptions options, IOctreeBoundsPartitioner<TVolume> boundsPartitio
 34    {
 1835        SwiftThrowHelper.ThrowIfNull(boundsPartitioner, nameof(boundsPartitioner));
 36
 1837        WorldBounds = worldBounds;
 1838        Options = options;
 1839        _boundsPartitioner = boundsPartitioner;
 40
 1841        int capacity = SwiftHashTools.NextPowerOfTwo(Math.Max(4, options.NodeCapacity));
 1842        _keyToEntryIndex = new QueryKeyIndexMap<TKey>(capacity);
 1843        _freeEntries = new SwiftIntStack();
 1844        _entries = new OctreeEntry[capacity];
 1845        _root = new OctreeNode(worldBounds, 0, null);
 1846    }
 47
 48    /// <summary>
 49    /// Gets the number of active entries stored in the octree.
 50    /// </summary>
 251    public int Count => _count;
 52
 53    /// <summary>
 54    /// Gets the immutable world bounds covered by this octree.
 55    /// </summary>
 5956    public TVolume WorldBounds { get; }
 57
 58    /// <summary>
 59    /// Gets the subdivision options used by this octree.
 60    /// </summary>
 26561    public SwiftOctreeOptions Options { get; }
 62
 463    internal int DebugNodeCount => CountNodes(_root);
 64
 265    internal int DebugMaxDepth => GetMaxDepth(_root);
 66
 767    internal bool DebugRootHasChildren => _root.HasChildren;
 68
 69    /// <summary>
 70    /// Inserts a new entry or replaces the bounds of an existing key.
 71    /// </summary>
 72    /// <param name="key">The entry key.</param>
 73    /// <param name="bounds">The entry bounds.</param>
 74    /// <returns><c>true</c> when a new key was added; <c>false</c> when an existing key was replaced.</returns>
 75    public bool Insert(TKey key, TVolume bounds)
 76    {
 5377        SwiftThrowHelper.ThrowIfNull(key, nameof(key));
 5378        EnsureWithinWorldBounds(bounds, nameof(bounds));
 79
 5280        int existingIndex = FindEntryIndex(key);
 5281        if (existingIndex >= 0)
 82        {
 183            RelocateEntry(existingIndex, bounds);
 184            return false;
 85        }
 86
 5187        EnsureEntryCapacity(_count + 1);
 88
 5189        int entryIndex = AllocateEntry(key, bounds);
 5190        _keyToEntryIndex.Insert(key, entryIndex);
 5191        InsertIntoNode(_root, entryIndex);
 5192        _count++;
 5193        return true;
 94    }
 95
 96    /// <summary>
 97    /// Removes an entry from the octree.
 98    /// </summary>
 99    /// <param name="key">The entry key.</param>
 100    /// <returns><c>true</c> when the key existed and was removed; otherwise, <c>false</c>.</returns>
 101    public bool Remove(TKey key)
 102    {
 2103        SwiftThrowHelper.ThrowIfNullGeneric(key, nameof(key));
 104
 2105        int entryIndex = FindEntryIndex(key);
 2106        if (entryIndex < 0)
 1107            return false;
 108
 1109        OctreeNode? node = _entries[entryIndex].Node;
 1110        if (node != null)
 1111            RemoveEntryFromNode(node, entryIndex);
 112
 1113        _keyToEntryIndex.Remove(key, MatchesEntryKey, IsAllocatedEntry, GetEntryKey);
 1114        _entries[entryIndex].Reset();
 1115        _freeEntries.Push(entryIndex);
 1116        _count--;
 117
 1118        if (Options.EnableMergeOnRemove && node != null)
 1119            TryMergeUp(node);
 120
 1121        return true;
 122    }
 123
 124    /// <summary>
 125    /// Attempts to retrieve the bounds registered for the supplied key.
 126    /// </summary>
 127    public bool TryGetBounds(TKey key, out TVolume bounds)
 128    {
 2129        SwiftThrowHelper.ThrowIfNullGeneric(key, nameof(key));
 130
 2131        int entryIndex = FindEntryIndex(key);
 2132        if (entryIndex < 0)
 133        {
 1134            bounds = default;
 1135            return false;
 136        }
 137
 1138        bounds = _entries[entryIndex].Bounds;
 1139        return true;
 140    }
 141
 142    /// <summary>
 143    /// Updates the bounds for an existing entry.
 144    /// </summary>
 145    /// <param name="key">The entry key.</param>
 146    /// <param name="newBounds">The replacement bounds.</param>
 147    /// <returns><c>true</c> when the key existed; otherwise, <c>false</c>.</returns>
 148    public bool UpdateEntryBounds(TKey key, TVolume newBounds)
 149    {
 5150        SwiftThrowHelper.ThrowIfNullGeneric(key, nameof(key));
 151
 5152        int entryIndex = FindEntryIndex(key);
 5153        if (entryIndex < 0)
 1154            return false;
 155
 4156        return RelocateEntry(entryIndex, newBounds);
 157    }
 158
 159    /// <summary>
 160    /// Determines whether the octree contains the specified key.
 161    /// </summary>
 162    public bool Contains(TKey key)
 163    {
 2164        SwiftThrowHelper.ThrowIfNullGeneric(key, nameof(key));
 2165        return FindEntryIndex(key) >= 0;
 166    }
 167
 168    /// <summary>
 169    /// Queries the octree and returns only entries whose bounds intersect the supplied query volume.
 170    /// </summary>
 171    /// <param name="queryBounds">The bounds used to test for intersection.</param>
 172    /// <param name="results">The collection that receives intersecting keys.</param>
 173    public void Query(TVolume queryBounds, ICollection<TKey> results)
 174    {
 22175        SwiftThrowHelper.ThrowIfNull(results, nameof(results));
 176
 22177        if (_count == 0 || !_root.Bounds.Intersects(queryBounds))
 3178            return;
 179
 19180        QueryNode(_root, queryBounds, results);
 19181    }
 182
 183    /// <summary>
 184    /// Removes all entries from the octree while preserving the configured world bounds.
 185    /// </summary>
 186    public void Clear()
 187    {
 1188        if (_count == 0)
 0189            return;
 190
 6191        for (int i = 0; i < _peakCount; i++)
 2192            _entries[i].Reset();
 193
 1194        _keyToEntryIndex.Clear();
 1195        _freeEntries.Reset();
 1196        _peakCount = 0;
 1197        _count = 0;
 1198        _root = new OctreeNode(WorldBounds, 0, null);
 1199    }
 200
 201    private void QueryNode(OctreeNode node, TVolume queryBounds, ICollection<TKey> results)
 202    {
 88203        AddIntersectingEntries(node, queryBounds, results);
 204
 88205        if (node.HasChildren)
 19206            QueryIntersectingChildren(node, queryBounds, results);
 88207    }
 208
 209    private void AddIntersectingEntries(OctreeNode node, TVolume queryBounds, ICollection<TKey> results)
 210    {
 248211        for (int i = 0; i < node.EntryIndices.Count; i++)
 212        {
 36213            int entryIndex = node.EntryIndices[i];
 36214            ref OctreeEntry entry = ref _entries[entryIndex];
 36215            if (entry.IsAllocated && entry.Bounds.Intersects(queryBounds))
 32216                results.Add(entry.Key);
 217        }
 88218    }
 219
 220    private void QueryIntersectingChildren(OctreeNode node, TVolume queryBounds, ICollection<TKey> results)
 221    {
 342222        for (int i = 0; i < node.Children?.Length; i++)
 223        {
 152224            OctreeNode? child = node.Children[i];
 152225            if (child != null && child.Bounds.Intersects(queryBounds))
 69226                QueryNode(child, queryBounds, results);
 227        }
 19228    }
 229
 230    private bool RelocateEntry(int entryIndex, TVolume newBounds)
 231    {
 5232        EnsureWithinWorldBounds(newBounds, nameof(newBounds));
 233
 4234        if (!_entries[entryIndex].IsAllocated)
 0235            return false;
 236
 4237        TVolume currentBounds = _entries[entryIndex].Bounds;
 4238        if (currentBounds.BoundsEquals(newBounds))
 1239            return true;
 240
 3241        OctreeNode? oldNode = _entries[entryIndex].Node;
 3242        if (oldNode != null)
 3243            RemoveEntryFromNode(oldNode, entryIndex);
 244
 3245        _entries[entryIndex].Bounds = newBounds;
 3246        InsertIntoNode(_root, entryIndex);
 247
 3248        if (Options.EnableMergeOnRemove && oldNode != null)
 3249            TryMergeUp(oldNode);
 250
 3251        return true;
 252    }
 253
 254    private void InsertIntoNode(OctreeNode node, int entryIndex)
 255    {
 109256        if (node.HasChildren && _boundsPartitioner.TryGetContainingChildIndex(node.Bounds, _entries[entryIndex].Bounds, 
 257        {
 55258            InsertIntoNode(node.Children![childIndex], entryIndex);
 55259            return;
 260        }
 261
 54262        node.EntryIndices.Add(entryIndex);
 54263        _entries[entryIndex].Node = node;
 264
 54265        if (ShouldSubdivide(node))
 9266            Subdivide(node);
 54267    }
 268
 269    private bool ShouldSubdivide(OctreeNode node)
 270    {
 54271        return !node.HasChildren &&
 54272               node.Depth < Options.MaxDepth &&
 54273               node.EntryIndices.Count > Options.NodeCapacity &&
 54274               _boundsPartitioner.CanSubdivide(node.Bounds);
 275    }
 276
 277    private void Subdivide(OctreeNode node)
 278    {
 17279        CreateChildNodes(node);
 17280        MoveContainedEntriesToChildren(node);
 17281        SubdivideOverflowingChildren(node);
 17282    }
 283
 284    private void CreateChildNodes(OctreeNode node)
 285    {
 17286        node.Children = new OctreeNode[8];
 306287        for (int i = 0; i < node.Children.Length; i++)
 136288            node.Children[i] = CreateChildNode(node, i);
 17289    }
 290
 291    private void MoveContainedEntriesToChildren(OctreeNode node)
 292    {
 17293        OctreeNode[] children = node.Children!;
 17294        int entryIndex = 0;
 59295        while (entryIndex < node.EntryIndices.Count)
 296        {
 42297            int currentEntryIndex = node.EntryIndices[entryIndex];
 42298            if (!_boundsPartitioner.TryGetContainingChildIndex(node.Bounds, _entries[currentEntryIndex].Bounds, out int 
 299            {
 1300                entryIndex++;
 1301                continue;
 302            }
 303
 41304            OctreeNode child = children[childIndex];
 41305            child.EntryIndices.Add(currentEntryIndex);
 41306            _entries[currentEntryIndex].Node = child;
 41307            node.EntryIndices.RemoveAt(entryIndex);
 308        }
 17309    }
 310
 311    private void SubdivideOverflowingChildren(OctreeNode node)
 312    {
 17313        OctreeNode[] children = node.Children!;
 306314        for (int i = 0; i < children.Length; i++)
 315        {
 136316            OctreeNode child = children[i];
 136317            if (ChildShouldSubdivide(child))
 8318                Subdivide(child);
 319        }
 17320    }
 321
 322    private bool ChildShouldSubdivide(OctreeNode child)
 323    {
 136324        return child.EntryIndices.Count > Options.NodeCapacity &&
 136325               child.Depth < Options.MaxDepth &&
 136326               _boundsPartitioner.CanSubdivide(child.Bounds);
 327    }
 328
 329    private OctreeNode CreateChildNode(OctreeNode parent, int childIndex)
 330    {
 136331        TVolume bounds = _boundsPartitioner.CreateChildBounds(parent.Bounds, childIndex);
 136332        return new OctreeNode(bounds, parent.Depth + 1, parent);
 333    }
 334
 335    private void TryMergeUp(OctreeNode node)
 336    {
 4337        OctreeNode? current = node;
 10338        while (current != null)
 339        {
 6340            if (current.HasChildren && CanMerge(current))
 2341                CollapseChildrenInto(current);
 342
 6343            current = current.Parent ?? null;
 344        }
 4345    }
 346
 347    private bool CanMerge(OctreeNode node)
 348    {
 2349        int totalEntries = node.EntryIndices.Count;
 36350        for (int i = 0; i < node.Children?.Length; i++)
 351        {
 16352            OctreeNode child = node.Children[i];
 16353            if (child.HasChildren)
 0354                return false;
 355
 16356            totalEntries += child.EntryIndices.Count;
 16357            if (totalEntries > Options.NodeCapacity)
 0358                return false;
 359        }
 360
 2361        return true;
 362    }
 363
 364    private void CollapseChildrenInto(OctreeNode node)
 365    {
 36366        for (int i = 0; i < node.Children?.Length; i++)
 367        {
 16368            OctreeNode child = node.Children[i];
 36369            for (int j = 0; j < child.EntryIndices.Count; j++)
 370            {
 2371                int entryIndex = child.EntryIndices[j];
 2372                node.EntryIndices.Add(entryIndex);
 2373                _entries[entryIndex].Node = node;
 374            }
 375        }
 376
 2377        node.Children = null;
 2378    }
 379
 380    private void RemoveEntryFromNode(OctreeNode node, int entryIndex)
 381    {
 8382        for (int i = 0; i < node.EntryIndices.Count; i++)
 383        {
 4384            if (node.EntryIndices[i] != entryIndex)
 385                continue;
 386
 4387            node.EntryIndices.RemoveAt(i);
 4388            _entries[entryIndex].Node = null;
 4389            return;
 390        }
 391
 0392        throw new InvalidOperationException("Octree entry was not found in its owning node.");
 393    }
 394
 395    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 396    private int AllocateEntry(TKey key, TVolume bounds)
 397    {
 398        int entryIndex;
 51399        if (_freeEntries.Count > 0)
 0400            entryIndex = _freeEntries.Pop();
 401        else
 51402            entryIndex = _peakCount++;
 403
 51404        _entries[entryIndex].Key = key;
 51405        _entries[entryIndex].Bounds = bounds;
 51406        _entries[entryIndex].Node = null;
 51407        _entries[entryIndex].IsAllocated = true;
 51408        return entryIndex;
 409    }
 410
 411    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 412    private void EnsureEntryCapacity(int capacity)
 413    {
 51414        if (capacity <= _entries.Length)
 47415            return;
 416
 4417        int newCapacity = SwiftHashTools.NextPowerOfTwo(capacity);
 4418        Array.Resize(ref _entries, newCapacity);
 4419        _keyToEntryIndex.ResizeAndRehash(newCapacity, _peakCount, IsAllocatedEntry, GetEntryKey);
 4420        QueryCollectionDiagnostics.WriteInfo(_diagnosticSource, $"Resized octree entry storage to {newCapacity} entries.
 4421    }
 422
 423    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 424    private int FindEntryIndex(TKey key)
 425    {
 63426        return _keyToEntryIndex.Find(key, MatchesEntryKey);
 427    }
 428
 429    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 430    private bool MatchesEntryKey(int index, TKey key)
 431    {
 28432        return _entries[index].IsAllocated && EqualityComparer<TKey>.Default.Equals(_entries[index].Key, key);
 433    }
 434
 435    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 20436    private bool IsAllocatedEntry(int index) => _entries[index].IsAllocated;
 437
 438    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 20439    private TKey GetEntryKey(int index) => _entries[index].Key;
 440
 441    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 442    private void EnsureWithinWorldBounds(TVolume bounds, string paramName)
 443    {
 58444        if (!_boundsPartitioner.ContainsBounds(WorldBounds, bounds))
 2445            throw new ArgumentOutOfRangeException(paramName, "Bounds must be fully contained within the octree world bou
 56446    }
 447
 448    private static int CountNodes(OctreeNode node)
 449    {
 28450        int count = 1;
 28451        if (!node.HasChildren)
 25452            return count;
 453
 54454        for (int i = 0; i < node.Children?.Length; i++)
 24455            count += CountNodes(node.Children[i]);
 456
 3457        return count;
 458    }
 459
 460    private static int GetMaxDepth(OctreeNode node)
 461    {
 26462        int maxDepth = node.Depth;
 26463        if (!node.HasChildren)
 23464            return maxDepth;
 465
 54466        for (int i = 0; i < node.Children?.Length; i++)
 24467            maxDepth = Math.Max(maxDepth, GetMaxDepth(node.Children[i]));
 468
 3469        return maxDepth;
 470    }
 471
 472    private sealed class OctreeNode
 473    {
 155474        public OctreeNode(TVolume bounds, int depth, OctreeNode? parent)
 475        {
 155476            Bounds = bounds;
 155477            Depth = depth;
 155478            Parent = parent;
 155479            EntryIndices = new SwiftList<int>();
 155480        }
 481
 428482        public TVolume Bounds { get; }
 483
 224484        public int Depth { get; }
 485
 6486        public OctreeNode? Parent { get; }
 487
 632488        public SwiftList<int> EntryIndices { get; }
 489
 1224490        public OctreeNode[]? Children { get; set; }
 491
 334492        public bool HasChildren => Children != null;
 493    }
 494
 495    private struct OctreeEntry
 496    {
 497        public TKey Key;
 498        public TVolume Bounds;
 499        public OctreeNode? Node;
 500        public bool IsAllocated;
 501
 502        public void Reset()
 503        {
 3504            Key = default!;
 3505            Bounds = default;
 3506            Node = null;
 3507            IsAllocated = false;
 3508        }
 509    }
 510}

Methods/Properties

.ctor(TVolume,SwiftCollections.Query.SwiftOctreeOptions,SwiftCollections.Query.IOctreeBoundsPartitioner`1<TVolume>)
get_Count()
get_WorldBounds()
get_Options()
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_Bounds()
get_Depth()
get_Parent()
get_EntryIndices()
get_Children()
get_HasChildren()
Reset()