< Summary

Information
Class: SwiftCollections.Dimensions.SwiftArray3D<T>
Assembly: SwiftCollections
File(s): /home/runner/work/SwiftCollections/SwiftCollections/src/SwiftCollections/Dimension/SwiftArray3D.cs
Line coverage
95%
Covered lines: 104
Uncovered lines: 5
Coverable lines: 109
Total lines: 385
Line coverage: 95.4%
Branch coverage
94%
Covered branches: 51
Total branches: 54
Branch coverage: 94.4%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor()100%210%
.ctor(...)100%11100%
.ctor(...)100%11100%
.ctor(...)50%22100%
get_Width()100%11100%
get_Height()100%11100%
get_Depth()100%11100%
get_Size()100%210%
get_Length()100%11100%
get_Item(...)100%11100%
set_Item(...)100%11100%
get_State()100%11100%
set_State(...)100%11100%
Resize(...)100%66100%
Shift(...)100%22100%
ShiftWrapped(...)100%66100%
ShiftClamped(...)100%66100%
Clear()100%11100%
Fill(...)100%22100%
GetIndex(...)100%11100%
ValidateIndex(...)100%22100%
NormalizeShift(...)75%4475%
WrapForwardIndex(...)100%22100%
GetShiftRange(...)83.33%6685.71%
IsValidIndex(...)100%1010100%
GetEnumerator()100%66100%
System.Collections.IEnumerable.GetEnumerator()100%210%

File(s)

/home/runner/work/SwiftCollections/SwiftCollections/src/SwiftCollections/Dimension/SwiftArray3D.cs

#LineLine coverage
 1using Chronicler;
 2using MemoryPack;
 3using System;
 4using System.Collections;
 5using System.Collections.Generic;
 6using System.Text.Json.Serialization;
 7
 8namespace SwiftCollections.Dimensions;
 9
 10/// <summary>
 11/// Represents a generic, flattened 3D array with efficient indexing and resizing capabilities.
 12/// Optimized for use in performance-critical applications like game grids.
 13/// </summary>
 14/// <typeparam name="T">The type of elements in the 3D array.</typeparam>
 15[Serializable]
 16[JsonConverter(typeof(StateJsonConverterFactory))]
 17[MemoryPackable]
 18public partial class SwiftArray3D<T> : IStateBacked<Array3DState<T>>, IEnumerable<T>, IEnumerable
 19{
 20    #region Fields
 21
 22    private T[] _innerArray;
 23
 24    private int _width;
 25
 26    private int _height;
 27
 28    private int _depth;
 29
 30    #endregion
 31
 32    #region Constructors
 33
 34    /// <summary>
 35    /// Initializes a new instance of the SwiftArray3D class with zero dimensions.
 36    /// </summary>
 37    /// <remarks>
 38    /// This constructor creates an empty three-dimensional array.
 39    /// Use this overload when you intend to set the dimensions later or create an empty array.
 40    /// </remarks>
 041    public SwiftArray3D() : this(0, 0, 0) { }
 42
 43    /// <summary>
 44    /// Initializes a new instance of the SwiftArray3D class with the specified dimensions.
 45    /// </summary>
 46    /// <param name="width">The number of elements in the first dimension. Must be greater than zero.</param>
 47    /// <param name="height">The number of elements in the second dimension. Must be greater than zero.</param>
 48    /// <param name="depth">The number of elements in the third dimension. Must be greater than zero.</param>
 2049    public SwiftArray3D(int width, int height, int depth)
 50    {
 2051        _width = width;
 2052        _height = height;
 2053        _depth = depth;
 2054        _innerArray = new T[width * height * depth];
 2055    }
 56
 57    /// <summary>
 58    /// Initializes a new instance of the SwiftArray3D class with the specified dimensions and fills all elements with
 59    /// the provided default value.
 60    /// </summary>
 61    /// <param name="width">The number of elements in the first dimension. Must be greater than zero.</param>
 62    /// <param name="height">The number of elements in the second dimension. Must be greater than zero.</param>
 63    /// <param name="depth">The number of elements in the third dimension. Must be greater than zero.</param>
 64    /// <param name="defaultValue">The value to assign to each element in the array upon initialization.</param>
 165    public SwiftArray3D(int width, int height, int depth, T defaultValue) : this(width, height, depth)
 66    {
 167        Fill(defaultValue);
 168    }
 69
 70    /// <summary>
 71    /// Initializes a new instance of the SwiftArray3D class with the specified array state.
 72    /// </summary>
 73    /// <param name="state">
 74    /// The state object that encapsulates the underlying data and configuration for the three-dimensional array.
 75    /// Cannot be null.
 76    /// </param>
 77    [MemoryPackConstructor]
 278    public SwiftArray3D(Array3DState<T> state)
 79    {
 280        State = state;
 281        _innerArray ??= Array.Empty<T>(); // Ensure _innerArray is initialized to avoid null reference issues.
 282    }
 83
 84    #endregion
 85
 86    #region Properties
 87
 88    /// <summary>
 89    /// Gets the width of the object.
 90    /// </summary>
 91    [JsonIgnore]
 92    [MemoryPackIgnore]
 237722493    public int Width => _width;
 94
 95    /// <summary>
 96    /// Gets the height value associated with the current instance.
 97    /// </summary>
 98    [JsonIgnore]
 99    [MemoryPackIgnore]
 4755270100    public int Height => _height;
 101
 102    /// <summary>
 103    /// Gets the current depth value for this instance.
 104    /// </summary>
 105    [JsonIgnore]
 106    [MemoryPackIgnore]
 7133811107    public int Depth => _depth;
 108
 109    /// <summary>
 110    /// Total size of the array.
 111    /// </summary>
 112    [JsonIgnore]
 113    [MemoryPackIgnore]
 0114    public int Size => _width * _height * _depth;
 115
 116    /// <inheritdoc cref="Array.Length" />
 117    [JsonIgnore]
 118    [MemoryPackIgnore]
 2119    public int Length => _innerArray.Length;
 120
 121    /// <summary>
 122    /// Gets or sets the element at the specified three-dimensional indices.
 123    /// </summary>
 124    /// <remarks>An exception is thrown if any index is outside the valid range for its dimension.</remarks>
 125    /// <param name="x">The zero-based index along the first dimension.</param>
 126    /// <param name="y">The zero-based index along the second dimension.</param>
 127    /// <param name="z">The zero-based index along the third dimension.</param>
 128    /// <returns>The element located at the specified indices.</returns>
 129    [JsonIgnore]
 130    [MemoryPackIgnore]
 131    public T this[int x, int y, int z]
 132    {
 133        get
 134        {
 1251125135            ValidateIndex(x, y, z);
 1251121136            return _innerArray[GetIndex(x, y, z)];
 137        }
 138        set
 139        {
 1126015140            ValidateIndex(x, y, z);
 1126015141            _innerArray[GetIndex(x, y, z)] = value;
 1126015142        }
 143    }
 144
 145    /// <summary>
 146    /// Gets or sets the complete state of the 3D array, including its dimensions and data contents.
 147    /// </summary>
 148    /// <remarks>
 149    /// Setting this property replaces the current array's dimensions and data with those from the specified state.
 150    /// Getting this property returns a snapshot of the current array state.
 151    /// This property is intended for serialization and deserialization scenarios.
 152    /// </remarks>
 153    [JsonInclude]
 154    [MemoryPackInclude]
 155    public Array3DState<T> State
 156    {
 157        get
 158        {
 2159            var data = new T[_innerArray.Length];
 2160            Array.Copy(_innerArray, data, data.Length);
 161
 2162            return new Array3DState<T>(
 2163                _width,
 2164                _height,
 2165                _depth,
 2166                data
 2167            );
 168        }
 169
 170        internal set
 171        {
 2172            _width = value.Width;
 2173            _height = value.Height;
 2174            _depth = value.Depth;
 175
 2176            _innerArray = new T[value.Data.Length];
 2177            Array.Copy(value.Data, _innerArray, value.Data.Length);
 2178        }
 179    }
 180
 181    #endregion
 182
 183    #region Methods
 184
 185    /// <summary>
 186    /// Resizes the 3D array to the specified dimensions.
 187    /// Retains existing data where possible.
 188    /// </summary>
 189    public void Resize(int newWidth, int newHeight, int newDepth)
 190    {
 5191        var newArray = new T[newWidth * newHeight * newDepth];
 192
 5193        int minWidth = Math.Min(Width, newWidth);
 5194        int minHeight = Math.Min(Height, newHeight);
 5195        int minDepth = Math.Min(Depth, newDepth);
 196
 32197        for (int x = 0; x < minWidth; x++)
 198        {
 88199            for (int y = 0; y < minHeight; y++)
 200            {
 280201                for (int z = 0; z < minDepth; z++)
 202                {
 107203                    int srcIndex = GetIndex(x, y, z);
 107204                    int dstIndex = x * (newHeight * newDepth) + y * newDepth + z;
 107205                    newArray[dstIndex] = _innerArray[srcIndex];
 206                }
 207            }
 208        }
 209
 5210        _innerArray = newArray;
 5211        _width = newWidth;
 5212        _height = newHeight;
 5213        _depth = newDepth;
 5214    }
 215
 216    /// <summary>
 217    /// Shifts the elements in the array by the specified offsets along each axis.
 218    /// </summary>
 219    /// <param name="xOffset">The offset to apply along the X-axis.</param>
 220    /// <param name="yOffset">The offset to apply along the Y-axis.</param>
 221    /// <param name="zOffset">The offset to apply along the Z-axis.</param>
 222    /// <param name="wrap">
 223    /// Specifies whether to wrap elements that exceed the array's boundaries.
 224    /// If <c>true</c>, values wrap around to the other side of the array.
 225    /// If <c>false</c>, values that exceed boundaries are discarded.
 226    /// </param>
 227    /// <remarks>
 228    /// - Wrapping behavior ensures that no data is lost during shifts.
 229    /// - Non-wrapping behavior discards elements that move out of bounds.
 230    /// </remarks>
 231    public void Shift(int xOffset, int yOffset, int zOffset, bool wrap = true)
 232    {
 6233        var newArray = new T[Width * Height * Depth];
 234
 6235        if (wrap)
 5236            ShiftWrapped(newArray, xOffset, yOffset, zOffset);
 237        else
 1238            ShiftClamped(newArray, xOffset, yOffset, zOffset);
 239
 6240        _innerArray = newArray;
 6241    }
 242
 243    private void ShiftWrapped(T[] newArray, int xOffset, int yOffset, int zOffset)
 244    {
 5245        int normalizedXOffset = NormalizeShift(xOffset, Width);
 5246        int normalizedYOffset = NormalizeShift(yOffset, Height);
 5247        int normalizedZOffset = NormalizeShift(zOffset, Depth);
 248
 46249        for (int x = 0; x < Width; x++)
 250        {
 18251            int newX = WrapForwardIndex(x, normalizedXOffset, Width);
 180252            for (int y = 0; y < Height; y++)
 253            {
 72254                int newY = WrapForwardIndex(y, normalizedYOffset, Height);
 792255                for (int z = 0; z < Depth; z++)
 256                {
 324257                    int newZ = WrapForwardIndex(z, normalizedZOffset, Depth);
 324258                    newArray[GetIndex(newX, newY, newZ)] = _innerArray[GetIndex(x, y, z)];
 259                }
 260            }
 261        }
 5262    }
 263
 264    private void ShiftClamped(T[] newArray, int xOffset, int yOffset, int zOffset)
 265    {
 1266        GetShiftRange(Width, xOffset, out int xStart, out int xEnd);
 1267        GetShiftRange(Height, yOffset, out int yStart, out int yEnd);
 1268        GetShiftRange(Depth, zOffset, out int zStart, out int zEnd);
 269
 6270        for (int x = xStart; x < xEnd; x++)
 271        {
 2272            int newX = x + xOffset;
 12273            for (int y = yStart; y < yEnd; y++)
 274            {
 4275                int newY = y + yOffset;
 16276                for (int z = zStart; z < zEnd; z++)
 277                {
 4278                    int newZ = z + zOffset;
 4279                    newArray[GetIndex(newX, newY, newZ)] = _innerArray[GetIndex(x, y, z)];
 280                }
 281            }
 282        }
 1283    }
 284
 285    /// <summary>
 286    /// Clears all elements in the array.
 287    /// </summary>
 1288    public void Clear() => Array.Clear(_innerArray, 0, _innerArray.Length);
 289
 290    /// <summary>
 291    /// Fills the entire array with the specified value.
 292    /// </summary>
 293    public void Fill(T value)
 294    {
 224295        for (int i = 0; i < _innerArray.Length; i++)
 108296            _innerArray[i] = value;
 4297    }
 298
 299    /// <summary>
 300    /// Calculates the one-dimensional array index corresponding to the specified three-dimensional coordinates.
 301    /// </summary>
 302    /// <remarks>
 303    /// Use this method to map three-dimensional coordinates to a linear array index when working with flattened 3D data
 304    /// The valid ranges for x, y, and z depend on the dimensions of the underlying data structure.
 305    /// </remarks>
 306    /// <param name="x">The zero-based X coordinate to convert.</param>
 307    /// <param name="y">The zero-based Y coordinate to convert.</param>
 308    /// <param name="z">The zero-based Z coordinate to convert.</param>
 309    /// <returns>The zero-based index in the underlying one-dimensional array that corresponds to the specified (x, y, z
 310    public virtual int GetIndex(int x, int y, int z)
 311    {
 2377899312        return x * (Height * Depth) + y * Depth + z;
 313    }
 314
 315    /// <summary>
 316    /// Validates the specified indices.
 317    /// Throws an exception if the indices are out of bounds.
 318    /// </summary>
 319    public virtual void ValidateIndex(int x, int y, int z)
 320    {
 2377140321        if (!IsValidIndex(x, y, z))
 4322            throw new IndexOutOfRangeException($"Invalid index ({x}, {y}, {z}) for dimensions ({Width}, {Height}, {Depth
 2377136323    }
 324
 325    private static int NormalizeShift(int shift, int length)
 326    {
 15327        if (length == 0)
 0328            return 0;
 329
 15330        int normalized = shift % length;
 15331        return normalized < 0 ? normalized + length : normalized;
 332    }
 333
 334    private static int WrapForwardIndex(int index, int normalizedShift, int length)
 335    {
 414336        int shifted = index + normalizedShift;
 414337        return shifted >= length ? shifted - length : shifted;
 338    }
 339
 340    private static void GetShiftRange(int length, int shift, out int start, out int end)
 341    {
 3342        long startValue = shift < 0 ? -(long)shift : 0L;
 3343        long endValue = shift > 0 ? (long)length - shift : length;
 344
 3345        start = (int)Math.Min(startValue, length);
 3346        end = (int)Math.Max(0L, Math.Min(endValue, length));
 3347        if (end < start)
 0348            end = start;
 3349    }
 350
 351    /// <summary>
 352    /// Checks if the specified indices are within bounds.
 353    /// </summary>
 354    public virtual bool IsValidIndex(int x, int y, int z) =>
 2377143355        x >= 0 && x < Width && y >= 0 && y < Height && z >= 0 && z < Depth;
 356
 357    #endregion
 358
 359    #region IEnumerator Implementation
 360
 361    /// <summary>
 362    /// Returns an enumerator that iterates through all elements in the 3D array.
 363    /// </summary>
 364    /// <returns>An enumerator for the 3D array.</returns>
 365    public IEnumerator<T> GetEnumerator()
 366    {
 36367        for (int x = 0; x < Width; x++)
 368        {
 96369            for (int y = 0; y < Height; y++)
 370            {
 264371                for (int z = 0; z < Depth; z++)
 97372                    yield return this[x, y, z];
 373            }
 374        }
 5375    }
 376
 377    /// <summary>
 378    /// Returns an enumerator that iterates through all elements in the 3D array (non-generic).
 379    /// </summary>
 380    /// <returns>An enumerator for the 3D array.</returns>
 0381    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
 382
 383
 384    #endregion
 385}