< 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: 512
Line coverage: 100%
Branch coverage
100%
Covered branches: 92
Total branches: 92
Branch coverage: 100%
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(...)100%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(...)100%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 System;
 9using System.Collections.Generic;
 10using System.Runtime.CompilerServices;
 11using Chronicler.Diagnostics;
 12using SwiftCollections.Diagnostics;
 13using SwiftCollections.Utility;
 14
 15namespace SwiftCollections.Query;
 16
 17/// <summary>
 18/// Represents a mutable octree that stores keyed bounding volumes within immutable world bounds.
 19/// </summary>
 20/// <typeparam name="TKey">The key used to identify each stored entry.</typeparam>
 21/// <typeparam name="TVolume">The volume type used for octree registration and queries.</typeparam>
 22public class SwiftOctree<TKey, TVolume>
 23    where TKey : notnull
 24    where TVolume : struct, IBoundVolume<TVolume>
 25{
 26    private const string _diagnosticSource = nameof(SwiftOctree<TKey, TVolume>);
 27
 28    private readonly IOctreeBoundsPartitioner<TVolume> _boundsPartitioner;
 29    private readonly QueryKeyIndexMap<TKey> _keyToEntryIndex;
 30    private readonly SwiftIntStack _freeEntries;
 31
 32    private OctreeEntry[] _entries;
 33    private OctreeNode _root;
 34    private int _peakCount;
 35    private int _count;
 36
 37    /// <summary>
 38    /// Initializes a new instance of the <see cref="SwiftOctree{TKey, TVolume}"/> class.
 39    /// </summary>
 40    /// <param name="worldBounds">The immutable world bounds covered by the octree.</param>
 41    /// <param name="options">Subdivision options for the octree.</param>
 42    /// <param name="boundsPartitioner">The backend-owned partitioner that maps bounds into octants.</param>
 3043    public SwiftOctree(TVolume worldBounds, SwiftOctreeOptions options, IOctreeBoundsPartitioner<TVolume> boundsPartitio
 44    {
 3045        ChronicleGuard.ThrowIfNull(boundsPartitioner, nameof(boundsPartitioner));
 46
 3047        WorldBounds = worldBounds;
 3048        Options = options;
 3049        _boundsPartitioner = boundsPartitioner;
 50
 3051        int capacity = SwiftHashTools.NextPowerOfTwo(Math.Max(4, options.NodeCapacity));
 3052        _keyToEntryIndex = new QueryKeyIndexMap<TKey>(capacity, MatchesEntryKey, IsAllocatedEntry, GetEntryKey);
 3053        _freeEntries = new SwiftIntStack();
 3054        _entries = new OctreeEntry[capacity];
 3055        _root = new OctreeNode(worldBounds, 0, null);
 3056    }
 57
 58    /// <summary>
 59    /// Gets the number of active entries stored in the octree.
 60    /// </summary>
 1161    public int Count => _count;
 62
 63    /// <summary>
 64    /// Gets the immutable world bounds covered by this octree.
 65    /// </summary>
 66    public TVolume WorldBounds { get; }
 67
 68    /// <summary>
 69    /// Gets the subdivision options used by this octree.
 70    /// </summary>
 71    public SwiftOctreeOptions Options { get; }
 72
 473    internal int DebugNodeCount => CountNodes(_root);
 74
 475    internal int DebugMaxDepth => GetMaxDepth(_root);
 76
 1277    internal bool DebugRootHasChildren => _root.HasChildren;
 78
 79    /// <summary>
 80    /// Inserts a new entry or replaces the bounds of an existing key.
 81    /// </summary>
 82    /// <param name="key">The entry key.</param>
 83    /// <param name="bounds">The entry bounds.</param>
 84    /// <returns><c>true</c> when a new key was added; <c>false</c> when an existing key was replaced.</returns>
 85    public bool Insert(TKey key, TVolume bounds)
 86    {
 7587        ChronicleGuard.ThrowIfNull(key, nameof(key));
 7588        EnsureWithinWorldBounds(bounds, nameof(bounds));
 89
 6890        int existingIndex = FindEntryIndex(key);
 6891        if (existingIndex >= 0)
 92        {
 193            RelocateEntry(existingIndex, bounds);
 194            return false;
 95        }
 96
 6797        EnsureEntryCapacity(_count + 1);
 98
 6799        int entryIndex = AllocateEntry(key, bounds);
 67100        _keyToEntryIndex.Insert(key, entryIndex);
 67101        InsertIntoNode(_root, entryIndex);
 67102        _count++;
 67103        return true;
 104    }
 105
 106    /// <summary>
 107    /// Removes an entry from the octree.
 108    /// </summary>
 109    /// <param name="key">The entry key.</param>
 110    /// <returns><c>true</c> when the key existed and was removed; otherwise, <c>false</c>.</returns>
 111    public bool Remove(TKey key)
 112    {
 6113        ChronicleGuard.ThrowIfNullGeneric(key, nameof(key));
 114
 6115        int entryIndex = FindEntryIndex(key);
 6116        if (entryIndex < 0)
 1117            return false;
 118
 5119        OctreeNode node = _entries[entryIndex].Node!;
 5120        RemoveEntryFromNode(node, entryIndex);
 121
 5122        _keyToEntryIndex.Remove(key);
 5123        _entries[entryIndex].Reset();
 5124        _freeEntries.Push(entryIndex);
 5125        _count--;
 126
 5127        if (Options.EnableMergeOnRemove)
 4128            TryMergeUp(node);
 129
 5130        return true;
 131    }
 132
 133    /// <summary>
 134    /// Attempts to retrieve the bounds registered for the supplied key.
 135    /// </summary>
 136    public bool TryGetBounds(TKey key, out TVolume bounds)
 137    {
 2138        ChronicleGuard.ThrowIfNullGeneric(key, nameof(key));
 139
 2140        int entryIndex = FindEntryIndex(key);
 2141        if (entryIndex < 0)
 142        {
 1143            bounds = default;
 1144            return false;
 145        }
 146
 1147        bounds = _entries[entryIndex].Bounds;
 1148        return true;
 149    }
 150
 151    /// <summary>
 152    /// Updates the bounds for an existing entry.
 153    /// </summary>
 154    /// <param name="key">The entry key.</param>
 155    /// <param name="newBounds">The replacement bounds.</param>
 156    /// <returns><c>true</c> when the key existed; otherwise, <c>false</c>.</returns>
 157    public bool UpdateEntryBounds(TKey key, TVolume newBounds)
 158    {
 6159        ChronicleGuard.ThrowIfNullGeneric(key, nameof(key));
 160
 6161        int entryIndex = FindEntryIndex(key);
 6162        if (entryIndex < 0)
 1163            return false;
 164
 5165        return RelocateEntry(entryIndex, newBounds);
 166    }
 167
 168    /// <summary>
 169    /// Determines whether the octree contains the specified key.
 170    /// </summary>
 171    public bool Contains(TKey key)
 172    {
 2173        ChronicleGuard.ThrowIfNullGeneric(key, nameof(key));
 2174        return FindEntryIndex(key) >= 0;
 175    }
 176
 177    /// <summary>
 178    /// Queries the octree and returns only entries whose bounds intersect the supplied query volume.
 179    /// </summary>
 180    /// <param name="queryBounds">The bounds used to test for intersection.</param>
 181    /// <param name="results">The collection that receives intersecting keys.</param>
 182    public void Query(TVolume queryBounds, ICollection<TKey> results)
 183    {
 24184        ChronicleGuard.ThrowIfNull(results, nameof(results));
 185
 24186        if (_count == 0 || !_root.Bounds.Intersects(queryBounds))
 3187            return;
 188
 21189        QueryNode(_root, queryBounds, results);
 21190    }
 191
 192    /// <summary>
 193    /// Removes all entries from the octree while preserving the configured world bounds.
 194    /// </summary>
 195    public void Clear()
 196    {
 2197        if (_count == 0)
 1198            return;
 199
 6200        for (int i = 0; i < _peakCount; i++)
 2201            _entries[i].Reset();
 202
 1203        _keyToEntryIndex.Clear();
 1204        _freeEntries.Reset();
 1205        _peakCount = 0;
 1206        _count = 0;
 1207        _root = new OctreeNode(WorldBounds, 0, null);
 1208    }
 209
 210    private void QueryNode(OctreeNode node, TVolume queryBounds, ICollection<TKey> results)
 211    {
 91212        AddIntersectingEntries(node, queryBounds, results);
 213
 91214        if (node.HasChildren)
 20215            QueryIntersectingChildren(node, queryBounds, results);
 91216    }
 217
 218    private void AddIntersectingEntries(OctreeNode node, TVolume queryBounds, ICollection<TKey> results)
 219    {
 260220        for (int i = 0; i < node.EntryIndices.Count; i++)
 221        {
 39222            int entryIndex = node.EntryIndices[i];
 39223            ref OctreeEntry entry = ref _entries[entryIndex];
 39224            if (entry.Bounds.Intersects(queryBounds))
 35225                results.Add(entry.Key);
 226        }
 91227    }
 228
 229    private void QueryIntersectingChildren(OctreeNode node, TVolume queryBounds, ICollection<TKey> results)
 230    {
 20231        OctreeNode[] children = node.Children!;
 360232        for (int i = 0; i < children.Length; i++)
 233        {
 160234            OctreeNode child = children[i];
 160235            if (child.Bounds.Intersects(queryBounds))
 70236                QueryNode(child, queryBounds, results);
 237        }
 20238    }
 239
 240    private bool RelocateEntry(int entryIndex, TVolume newBounds)
 241    {
 6242        EnsureWithinWorldBounds(newBounds, nameof(newBounds));
 243
 5244        TVolume currentBounds = _entries[entryIndex].Bounds;
 5245        if (currentBounds.BoundsEquals(newBounds))
 1246            return true;
 247
 4248        OctreeNode oldNode = _entries[entryIndex].Node!;
 4249        RemoveEntryFromNode(oldNode, entryIndex);
 250
 4251        _entries[entryIndex].Bounds = newBounds;
 4252        InsertIntoNode(_root, entryIndex);
 253
 4254        if (Options.EnableMergeOnRemove)
 3255            TryMergeUp(oldNode);
 256
 4257        return true;
 258    }
 259
 260    private void InsertIntoNode(OctreeNode node, int entryIndex)
 261    {
 130262        if (node.HasChildren && _boundsPartitioner.TryGetContainingChildIndex(node.Bounds, _entries[entryIndex].Bounds, 
 263        {
 59264            InsertIntoNode(node.Children![childIndex], entryIndex);
 59265            return;
 266        }
 267
 71268        node.EntryIndices.Add(entryIndex);
 71269        _entries[entryIndex].Node = node;
 270
 71271        if (ShouldSubdivide(node))
 13272            Subdivide(node);
 71273    }
 274
 275    private bool ShouldSubdivide(OctreeNode node)
 276    {
 71277        return !node.HasChildren &&
 71278               node.Depth < Options.MaxDepth &&
 71279               node.EntryIndices.Count > Options.NodeCapacity &&
 71280               _boundsPartitioner.CanSubdivide(node.Bounds);
 281    }
 282
 283    private void Subdivide(OctreeNode node)
 284    {
 26285        CreateChildNodes(node);
 26286        MoveContainedEntriesToChildren(node);
 26287        SubdivideOverflowingChildren(node);
 26288    }
 289
 290    private void CreateChildNodes(OctreeNode node)
 291    {
 26292        node.Children = new OctreeNode[8];
 468293        for (int i = 0; i < node.Children.Length; i++)
 208294            node.Children[i] = CreateChildNode(node, i);
 26295    }
 296
 297    private void MoveContainedEntriesToChildren(OctreeNode node)
 298    {
 26299        OctreeNode[] children = node.Children!;
 26300        int entryIndex = 0;
 95301        while (entryIndex < node.EntryIndices.Count)
 302        {
 69303            int currentEntryIndex = node.EntryIndices[entryIndex];
 69304            if (!_boundsPartitioner.TryGetContainingChildIndex(node.Bounds, _entries[currentEntryIndex].Bounds, out int 
 305            {
 1306                entryIndex++;
 1307                continue;
 308            }
 309
 68310            OctreeNode child = children[childIndex];
 68311            child.EntryIndices.Add(currentEntryIndex);
 68312            _entries[currentEntryIndex].Node = child;
 68313            node.EntryIndices.RemoveAt(entryIndex);
 314        }
 26315    }
 316
 317    private void SubdivideOverflowingChildren(OctreeNode node)
 318    {
 26319        OctreeNode[] children = node.Children!;
 468320        for (int i = 0; i < children.Length; i++)
 321        {
 208322            OctreeNode child = children[i];
 208323            if (ChildShouldSubdivide(child))
 13324                Subdivide(child);
 325        }
 26326    }
 327
 328    private bool ChildShouldSubdivide(OctreeNode child)
 329    {
 208330        return child.EntryIndices.Count > Options.NodeCapacity &&
 208331               child.Depth < Options.MaxDepth &&
 208332               _boundsPartitioner.CanSubdivide(child.Bounds);
 333    }
 334
 335    private OctreeNode CreateChildNode(OctreeNode parent, int childIndex)
 336    {
 208337        TVolume bounds = _boundsPartitioner.CreateChildBounds(parent.Bounds, childIndex);
 208338        return new OctreeNode(bounds, parent.Depth + 1, parent);
 339    }
 340
 341    private void TryMergeUp(OctreeNode node)
 342    {
 7343        OctreeNode? current = node;
 19344        while (current != null)
 345        {
 12346            if (current.HasChildren && CanMerge(current))
 2347                CollapseChildrenInto(current);
 348
 12349            current = current.Parent ?? null;
 350        }
 7351    }
 352
 353    private bool CanMerge(OctreeNode node)
 354    {
 5355        int totalEntries = node.EntryIndices.Count;
 5356        OctreeNode[] children = node.Children!;
 44357        for (int i = 0; i < children.Length; i++)
 358        {
 20359            OctreeNode child = children[i];
 20360            if (child.HasChildren)
 2361                return false;
 362
 18363            totalEntries += child.EntryIndices.Count;
 18364            if (totalEntries > Options.NodeCapacity)
 1365                return false;
 366        }
 367
 2368        return true;
 369    }
 370
 371    private void CollapseChildrenInto(OctreeNode node)
 372    {
 2373        OctreeNode[] children = node.Children!;
 36374        for (int i = 0; i < children.Length; i++)
 375        {
 16376            OctreeNode child = children[i];
 36377            for (int j = 0; j < child.EntryIndices.Count; j++)
 378            {
 2379                int entryIndex = child.EntryIndices[j];
 2380                node.EntryIndices.Add(entryIndex);
 2381                _entries[entryIndex].Node = node;
 382            }
 383        }
 384
 2385        node.Children = null;
 2386    }
 387
 388    private void RemoveEntryFromNode(OctreeNode node, int entryIndex)
 389    {
 9390        int index = node.EntryIndices.IndexOf(entryIndex);
 9391        node.EntryIndices.RemoveAt(index);
 9392        _entries[entryIndex].Node = null;
 9393    }
 394
 395    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 396    private int AllocateEntry(TKey key, TVolume bounds)
 397    {
 398        int entryIndex;
 67399        if (_freeEntries.Count > 0)
 1400            entryIndex = _freeEntries.Pop();
 401        else
 66402            entryIndex = _peakCount++;
 403
 67404        _entries[entryIndex].Key = key;
 67405        _entries[entryIndex].Bounds = bounds;
 67406        _entries[entryIndex].Node = null;
 67407        _entries[entryIndex].IsAllocated = true;
 67408        return entryIndex;
 409    }
 410
 411    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 412    private void EnsureEntryCapacity(int capacity)
 413    {
 67414        if (capacity <= _entries.Length)
 62415            return;
 416
 5417        int newCapacity = SwiftHashTools.NextPowerOfTwo(capacity);
 5418        Array.Resize(ref _entries, newCapacity);
 5419        _keyToEntryIndex.ResizeAndRehash(newCapacity, _peakCount);
 5420        SwiftCollectionDiagnostics.Shared.Info($"Resized octree entry storage to {newCapacity} entries.", _diagnosticSou
 5421    }
 422
 423    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 424    private int FindEntryIndex(TKey key)
 425    {
 84426        return _keyToEntryIndex.Find(key);
 427    }
 428
 429    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 430    private bool MatchesEntryKey(int index, TKey key)
 431    {
 37432        return EqualityComparer<TKey>.Default.Equals(_entries[index].Key, key);
 433    }
 434
 435    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 24436    private bool IsAllocatedEntry(int index) => _entries[index].IsAllocated;
 437
 438    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 24439    private TKey GetEntryKey(int index) => _entries[index].Key;
 440
 441    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 442    private void EnsureWithinWorldBounds(TVolume bounds, string paramName)
 443    {
 81444        if (!_boundsPartitioner.ContainsBounds(WorldBounds, bounds))
 8445            throw new ArgumentOutOfRangeException(paramName, "Bounds must be fully contained within the octree world bou
 73446    }
 447
 448    private static int CountNodes(OctreeNode node)
 449    {
 28450        int count = 1;
 28451        if (!node.HasChildren)
 25452            return count;
 453
 3454        OctreeNode[] children = node.Children!;
 54455        for (int i = 0; i < children.Length; i++)
 24456            count += CountNodes(children[i]);
 457
 3458        return count;
 459    }
 460
 461    private static int GetMaxDepth(OctreeNode node)
 462    {
 76463        int maxDepth = node.Depth;
 76464        if (!node.HasChildren)
 67465            return maxDepth;
 466
 9467        OctreeNode[] children = node.Children!;
 162468        for (int i = 0; i < children.Length; i++)
 72469            maxDepth = Math.Max(maxDepth, GetMaxDepth(children[i]));
 470
 9471        return maxDepth;
 472    }
 473
 474    private sealed class OctreeNode
 475    {
 239476        public OctreeNode(TVolume bounds, int depth, OctreeNode? parent)
 477        {
 239478            Bounds = bounds;
 239479            Depth = depth;
 239480            Parent = parent;
 239481            EntryIndices = new SwiftList<int>();
 239482        }
 483
 484        public TVolume Bounds { get; }
 485
 486        public int Depth { get; }
 487
 488        public OctreeNode? Parent { get; }
 489
 490        public SwiftList<int> EntryIndices { get; }
 491
 492        public OctreeNode[]? Children { get; set; }
 493
 440494        public bool HasChildren => Children != null;
 495    }
 496
 497    private struct OctreeEntry
 498    {
 499        public TKey Key;
 500        public TVolume Bounds;
 501        public OctreeNode? Node;
 502        public bool IsAllocated;
 503
 504        public void Reset()
 505        {
 7506            Key = default!;
 7507            Bounds = default;
 7508            Node = null;
 7509            IsAllocated = false;
 7510        }
 511    }
 512}

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()