< Summary

Information
Class: SwiftCollections.Dimensions.SwiftArray3D<T>
Assembly: SwiftCollections
File(s): /home/runner/work/SwiftCollections/SwiftCollections/src/SwiftCollections/Dimension/SwiftArray3D.cs
Line coverage
100%
Covered lines: 107
Uncovered lines: 0
Coverable lines: 107
Total lines: 391
Line coverage: 100%
Branch coverage
100%
Covered branches: 50
Total branches: 50
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%
.ctor(...)100%11100%
.ctor(...)100%11100%
.ctor(...)100%11100%
get_Width()100%11100%
get_Height()100%11100%
get_Depth()100%11100%
get_Size()100%11100%
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(...)100%44100%
WrapForwardIndex(...)100%22100%
GetShiftRange(...)100%44100%
IsValidIndex(...)100%1010100%
GetEnumerator()100%66100%
System.Collections.IEnumerable.GetEnumerator()100%11100%

File(s)

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

#LineLine coverage
 1//=======================================================================
 2// SwiftArray3D.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 Chronicler;
 9using MemoryPack;
 10using SwiftCollections.Diagnostics;
 11using System;
 12using System.Collections;
 13using System.Collections.Generic;
 14using System.Text.Json.Serialization;
 15
 16namespace SwiftCollections.Dimensions;
 17
 18/// <summary>
 19/// Represents a generic, flattened 3D array with efficient indexing and resizing capabilities.
 20/// Optimized for use in performance-critical applications like game grids.
 21/// </summary>
 22/// <typeparam name="T">The type of elements in the 3D array.</typeparam>
 23[Serializable]
 24[JsonConverter(typeof(StateJsonConverterFactory))]
 25[MemoryPackable]
 26public partial class SwiftArray3D<T> : IStateBacked<Array3DState<T>>, IEnumerable<T>, IEnumerable
 27{
 28    #region Fields
 29
 30    private T[] _innerArray;
 31
 32    private int _width;
 33
 34    private int _height;
 35
 36    private int _depth;
 37
 38    #endregion
 39
 40    #region Constructors
 41
 42    /// <summary>
 43    /// Initializes a new instance of the SwiftArray3D class with zero dimensions.
 44    /// </summary>
 45    /// <remarks>
 46    /// This constructor creates an empty three-dimensional array.
 47    /// Use this overload when you intend to set the dimensions later or create an empty array.
 48    /// </remarks>
 449    public SwiftArray3D() : this(0, 0, 0) { }
 50
 51    /// <summary>
 52    /// Initializes a new instance of the SwiftArray3D class with the specified dimensions.
 53    /// </summary>
 54    /// <param name="width">The number of elements in the first dimension. Must be greater than zero.</param>
 55    /// <param name="height">The number of elements in the second dimension. Must be greater than zero.</param>
 56    /// <param name="depth">The number of elements in the third dimension. Must be greater than zero.</param>
 2357    public SwiftArray3D(int width, int height, int depth)
 58    {
 2359        _width = width;
 2360        _height = height;
 2361        _depth = depth;
 2362        _innerArray = new T[width * height * depth];
 2363    }
 64
 65    /// <summary>
 66    /// Initializes a new instance of the SwiftArray3D class with the specified dimensions and fills all elements with
 67    /// the provided default value.
 68    /// </summary>
 69    /// <param name="width">The number of elements in the first dimension. Must be greater than zero.</param>
 70    /// <param name="height">The number of elements in the second dimension. Must be greater than zero.</param>
 71    /// <param name="depth">The number of elements in the third dimension. Must be greater than zero.</param>
 72    /// <param name="defaultValue">The value to assign to each element in the array upon initialization.</param>
 173    public SwiftArray3D(int width, int height, int depth, T defaultValue) : this(width, height, depth)
 74    {
 175        Fill(defaultValue);
 176    }
 77
 78    /// <summary>
 79    /// Initializes a new instance of the SwiftArray3D class with the specified array state.
 80    /// </summary>
 81    /// <param name="state">
 82    /// The state object that encapsulates the underlying data and configuration for the three-dimensional array.
 83    /// Cannot be null.
 84    /// </param>
 85    [MemoryPackConstructor]
 286    public SwiftArray3D(Array3DState<T> state)
 87    {
 288        State = state;
 289        SwiftThrowHelper.ThrowIfNull(_innerArray, nameof(_innerArray));
 290    }
 91
 92    #endregion
 93
 94    #region Properties
 95
 96    /// <summary>
 97    /// Gets the width of the object.
 98    /// </summary>
 99    [JsonIgnore]
 100    [MemoryPackIgnore]
 2252236101    public int Width => _width;
 102
 103    /// <summary>
 104    /// Gets the height value associated with the current instance.
 105    /// </summary>
 106    [JsonIgnore]
 107    [MemoryPackIgnore]
 4505283108    public int Height => _height;
 109
 110    /// <summary>
 111    /// Gets the current depth value for this instance.
 112    /// </summary>
 113    [JsonIgnore]
 114    [MemoryPackIgnore]
 6758829115    public int Depth => _depth;
 116
 117    /// <summary>
 118    /// Total size of the array.
 119    /// </summary>
 120    [JsonIgnore]
 121    [MemoryPackIgnore]
 2122    public int Size => _width * _height * _depth;
 123
 124    /// <inheritdoc cref="Array.Length" />
 125    [JsonIgnore]
 126    [MemoryPackIgnore]
 3127    public int Length => _innerArray.Length;
 128
 129    /// <summary>
 130    /// Gets or sets the element at the specified three-dimensional indices.
 131    /// </summary>
 132    /// <remarks>An exception is thrown if any index is outside the valid range for its dimension.</remarks>
 133    /// <param name="x">The zero-based index along the first dimension.</param>
 134    /// <param name="y">The zero-based index along the second dimension.</param>
 135    /// <param name="z">The zero-based index along the third dimension.</param>
 136    /// <returns>The element located at the specified indices.</returns>
 137    [JsonIgnore]
 138    [MemoryPackIgnore]
 139    public T this[int x, int y, int z]
 140    {
 141        get
 142        {
 1126127143            ValidateIndex(x, y, z);
 1126123144            return _innerArray[GetIndex(x, y, z)];
 145        }
 146        set
 147        {
 1126017148            ValidateIndex(x, y, z);
 1126017149            _innerArray[GetIndex(x, y, z)] = value;
 1126017150        }
 151    }
 152
 153    /// <summary>
 154    /// Gets or sets the complete state of the 3D array, including its dimensions and data contents.
 155    /// </summary>
 156    /// <remarks>
 157    /// Setting this property replaces the current array's dimensions and data with those from the specified state.
 158    /// Getting this property returns a snapshot of the current array state.
 159    /// This property is intended for serialization and deserialization scenarios.
 160    /// </remarks>
 161    [JsonInclude]
 162    [MemoryPackInclude]
 163    public Array3DState<T> State
 164    {
 165        get
 166        {
 2167            var data = new T[_innerArray.Length];
 2168            Array.Copy(_innerArray, data, data.Length);
 169
 2170            return new Array3DState<T>(
 2171                _width,
 2172                _height,
 2173                _depth,
 2174                data
 2175            );
 176        }
 177
 178        internal set
 179        {
 2180            _width = value.Width;
 2181            _height = value.Height;
 2182            _depth = value.Depth;
 183
 2184            _innerArray = new T[value.Data.Length];
 2185            Array.Copy(value.Data, _innerArray, value.Data.Length);
 2186        }
 187    }
 188
 189    #endregion
 190
 191    #region Methods
 192
 193    /// <summary>
 194    /// Resizes the 3D array to the specified dimensions.
 195    /// Retains existing data where possible.
 196    /// </summary>
 197    public void Resize(int newWidth, int newHeight, int newDepth)
 198    {
 5199        var newArray = new T[newWidth * newHeight * newDepth];
 200
 5201        int minWidth = Math.Min(Width, newWidth);
 5202        int minHeight = Math.Min(Height, newHeight);
 5203        int minDepth = Math.Min(Depth, newDepth);
 204
 32205        for (int x = 0; x < minWidth; x++)
 206        {
 88207            for (int y = 0; y < minHeight; y++)
 208            {
 280209                for (int z = 0; z < minDepth; z++)
 210                {
 107211                    int srcIndex = GetIndex(x, y, z);
 107212                    int dstIndex = x * (newHeight * newDepth) + y * newDepth + z;
 107213                    newArray[dstIndex] = _innerArray[srcIndex];
 214                }
 215            }
 216        }
 217
 5218        _innerArray = newArray;
 5219        _width = newWidth;
 5220        _height = newHeight;
 5221        _depth = newDepth;
 5222    }
 223
 224    /// <summary>
 225    /// Shifts the elements in the array by the specified offsets along each axis.
 226    /// </summary>
 227    /// <param name="xOffset">The offset to apply along the X-axis.</param>
 228    /// <param name="yOffset">The offset to apply along the Y-axis.</param>
 229    /// <param name="zOffset">The offset to apply along the Z-axis.</param>
 230    /// <param name="wrap">
 231    /// Specifies whether to wrap elements that exceed the array's boundaries.
 232    /// If <c>true</c>, values wrap around to the other side of the array.
 233    /// If <c>false</c>, values that exceed boundaries are discarded.
 234    /// </param>
 235    /// <remarks>
 236    /// - Wrapping behavior ensures that no data is lost during shifts.
 237    /// - Non-wrapping behavior discards elements that move out of bounds.
 238    /// </remarks>
 239    public void Shift(int xOffset, int yOffset, int zOffset, bool wrap = true)
 240    {
 7241        var newArray = new T[Width * Height * Depth];
 242
 7243        if (wrap)
 6244            ShiftWrapped(newArray, xOffset, yOffset, zOffset);
 245        else
 1246            ShiftClamped(newArray, xOffset, yOffset, zOffset);
 247
 7248        _innerArray = newArray;
 7249    }
 250
 251    private void ShiftWrapped(T[] newArray, int xOffset, int yOffset, int zOffset)
 252    {
 6253        int normalizedXOffset = NormalizeShift(xOffset, Width);
 6254        int normalizedYOffset = NormalizeShift(yOffset, Height);
 6255        int normalizedZOffset = NormalizeShift(zOffset, Depth);
 256
 48257        for (int x = 0; x < Width; x++)
 258        {
 18259            int newX = WrapForwardIndex(x, normalizedXOffset, Width);
 180260            for (int y = 0; y < Height; y++)
 261            {
 72262                int newY = WrapForwardIndex(y, normalizedYOffset, Height);
 792263                for (int z = 0; z < Depth; z++)
 264                {
 324265                    int newZ = WrapForwardIndex(z, normalizedZOffset, Depth);
 324266                    newArray[GetIndex(newX, newY, newZ)] = _innerArray[GetIndex(x, y, z)];
 267                }
 268            }
 269        }
 6270    }
 271
 272    private void ShiftClamped(T[] newArray, int xOffset, int yOffset, int zOffset)
 273    {
 1274        GetShiftRange(Width, xOffset, out int xStart, out int xEnd);
 1275        GetShiftRange(Height, yOffset, out int yStart, out int yEnd);
 1276        GetShiftRange(Depth, zOffset, out int zStart, out int zEnd);
 277
 6278        for (int x = xStart; x < xEnd; x++)
 279        {
 2280            int newX = x + xOffset;
 12281            for (int y = yStart; y < yEnd; y++)
 282            {
 4283                int newY = y + yOffset;
 16284                for (int z = zStart; z < zEnd; z++)
 285                {
 4286                    int newZ = z + zOffset;
 4287                    newArray[GetIndex(newX, newY, newZ)] = _innerArray[GetIndex(x, y, z)];
 288                }
 289            }
 290        }
 1291    }
 292
 293    /// <summary>
 294    /// Clears all elements in the array.
 295    /// </summary>
 1296    public void Clear() => Array.Clear(_innerArray, 0, _innerArray.Length);
 297
 298    /// <summary>
 299    /// Fills the entire array with the specified value.
 300    /// </summary>
 301    public void Fill(T value)
 302    {
 224303        for (int i = 0; i < _innerArray.Length; i++)
 108304            _innerArray[i] = value;
 4305    }
 306
 307    /// <summary>
 308    /// Calculates the one-dimensional array index corresponding to the specified three-dimensional coordinates.
 309    /// </summary>
 310    /// <remarks>
 311    /// Use this method to map three-dimensional coordinates to a linear array index when working with flattened 3D data
 312    /// The valid ranges for x, y, and z depend on the dimensions of the underlying data structure.
 313    /// </remarks>
 314    /// <param name="x">The zero-based X coordinate to convert.</param>
 315    /// <param name="y">The zero-based Y coordinate to convert.</param>
 316    /// <param name="z">The zero-based Z coordinate to convert.</param>
 317    /// <returns>The zero-based index in the underlying one-dimensional array that corresponds to the specified (x, y, z
 318    public virtual int GetIndex(int x, int y, int z)
 319    {
 2252903320        return x * (Height * Depth) + y * Depth + z;
 321    }
 322
 323    /// <summary>
 324    /// Validates the specified indices.
 325    /// Throws an exception if the indices are out of bounds.
 326    /// </summary>
 327    public virtual void ValidateIndex(int x, int y, int z)
 328    {
 2252144329        if (!IsValidIndex(x, y, z))
 4330            throw new IndexOutOfRangeException($"Invalid index ({x}, {y}, {z}) for dimensions ({Width}, {Height}, {Depth
 2252140331    }
 332
 333    private static int NormalizeShift(int shift, int length)
 334    {
 18335        if (length == 0)
 3336            return 0;
 337
 15338        int normalized = shift % length;
 15339        return normalized < 0 ? normalized + length : normalized;
 340    }
 341
 342    private static int WrapForwardIndex(int index, int normalizedShift, int length)
 343    {
 414344        int shifted = index + normalizedShift;
 414345        return shifted >= length ? shifted - length : shifted;
 346    }
 347
 348    private static void GetShiftRange(int length, int shift, out int start, out int end)
 349    {
 3350        long startValue = shift < 0 ? -(long)shift : 0L;
 3351        long endValue = shift > 0 ? (long)length - shift : length;
 352
 3353        start = (int)Math.Min(startValue, length);
 3354        end = (int)Math.Max(0L, Math.Min(endValue, length));
 3355    }
 356
 357    /// <summary>
 358    /// Checks if the specified indices are within bounds.
 359    /// </summary>
 360    public virtual bool IsValidIndex(int x, int y, int z) =>
 2252147361        x >= 0 && x < Width && y >= 0 && y < Height && z >= 0 && z < Depth;
 362
 363    #endregion
 364
 365    #region IEnumerator Implementation
 366
 367    /// <summary>
 368    /// Returns an enumerator that iterates through all elements in the 3D array.
 369    /// </summary>
 370    /// <returns>An enumerator for the 3D array.</returns>
 371    public IEnumerator<T> GetEnumerator()
 372    {
 44373        for (int x = 0; x < Width; x++)
 374        {
 100375            for (int y = 0; y < Height; y++)
 376            {
 270377                for (int z = 0; z < Depth; z++)
 99378                    yield return this[x, y, z];
 379            }
 380        }
 8381    }
 382
 383    /// <summary>
 384    /// Returns an enumerator that iterates through all elements in the 3D array (non-generic).
 385    /// </summary>
 386    /// <returns>An enumerator for the 3D array.</returns>
 1387    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
 388
 389
 390    #endregion
 391}