< 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: 392
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 System;
 9using System.Collections;
 10using System.Collections.Generic;
 11using System.Text.Json.Serialization;
 12using Chronicler;
 13using Chronicler.Diagnostics;
 14using Chronicler.Serialization;
 15using MemoryPack;
 16
 17namespace SwiftCollections.Dimensions;
 18
 19/// <summary>
 20/// Represents a generic, flattened 3D array with efficient indexing and resizing capabilities.
 21/// Optimized for use in performance-critical applications like game grids.
 22/// </summary>
 23/// <typeparam name="T">The type of elements in the 3D array.</typeparam>
 24[Serializable]
 25[JsonConverter(typeof(StateJsonConverterFactory))]
 26[MemoryPackable]
 27public partial class SwiftArray3D<T> : IStateBacked<Array3DState<T>>, IEnumerable<T>, IEnumerable
 28{
 29    #region Fields
 30
 31    private T[] _innerArray;
 32
 33    private int _width;
 34
 35    private int _height;
 36
 37    private int _depth;
 38
 39    #endregion
 40
 41    #region Constructors
 42
 43    /// <summary>
 44    /// Initializes a new instance of the SwiftArray3D class with zero dimensions.
 45    /// </summary>
 46    /// <remarks>
 47    /// This constructor creates an empty three-dimensional array.
 48    /// Use this overload when you intend to set the dimensions later or create an empty array.
 49    /// </remarks>
 450    public SwiftArray3D() : this(0, 0, 0) { }
 51
 52    /// <summary>
 53    /// Initializes a new instance of the SwiftArray3D class with the specified dimensions.
 54    /// </summary>
 55    /// <param name="width">The number of elements in the first dimension. Must be greater than zero.</param>
 56    /// <param name="height">The number of elements in the second dimension. Must be greater than zero.</param>
 57    /// <param name="depth">The number of elements in the third dimension. Must be greater than zero.</param>
 2658    public SwiftArray3D(int width, int height, int depth)
 59    {
 2660        _width = width;
 2661        _height = height;
 2662        _depth = depth;
 2663        _innerArray = new T[width * height * depth];
 2664    }
 65
 66    /// <summary>
 67    /// Initializes a new instance of the SwiftArray3D class with the specified dimensions and fills all elements with
 68    /// the provided default value.
 69    /// </summary>
 70    /// <param name="width">The number of elements in the first dimension. Must be greater than zero.</param>
 71    /// <param name="height">The number of elements in the second dimension. Must be greater than zero.</param>
 72    /// <param name="depth">The number of elements in the third dimension. Must be greater than zero.</param>
 73    /// <param name="defaultValue">The value to assign to each element in the array upon initialization.</param>
 174    public SwiftArray3D(int width, int height, int depth, T defaultValue) : this(width, height, depth)
 75    {
 176        Fill(defaultValue);
 177    }
 78
 79    /// <summary>
 80    /// Initializes a new instance of the SwiftArray3D class with the specified array state.
 81    /// </summary>
 82    /// <param name="state">
 83    /// The state object that encapsulates the underlying data and configuration for the three-dimensional array.
 84    /// Cannot be null.
 85    /// </param>
 86    [MemoryPackConstructor]
 287    public SwiftArray3D(Array3DState<T> state)
 88    {
 289        State = state;
 290        ChronicleGuard.ThrowIfNull(_innerArray, nameof(_innerArray));
 291    }
 92
 93    #endregion
 94
 95    #region Properties
 96
 97    /// <summary>
 98    /// Gets the width of the object.
 99    /// </summary>
 100    [JsonIgnore]
 101    [MemoryPackIgnore]
 2252251102    public int Width => _width;
 103
 104    /// <summary>
 105    /// Gets the height value associated with the current instance.
 106    /// </summary>
 107    [JsonIgnore]
 108    [MemoryPackIgnore]
 4505307109    public int Height => _height;
 110
 111    /// <summary>
 112    /// Gets the current depth value for this instance.
 113    /// </summary>
 114    [JsonIgnore]
 115    [MemoryPackIgnore]
 6758861116    public int Depth => _depth;
 117
 118    /// <summary>
 119    /// Total size of the array.
 120    /// </summary>
 121    [JsonIgnore]
 122    [MemoryPackIgnore]
 2123    public int Size => _width * _height * _depth;
 124
 125    /// <inheritdoc cref="Array.Length" />
 126    [JsonIgnore]
 127    [MemoryPackIgnore]
 3128    public int Length => _innerArray.Length;
 129
 130    /// <summary>
 131    /// Gets or sets the element at the specified three-dimensional indices.
 132    /// </summary>
 133    /// <remarks>An exception is thrown if any index is outside the valid range for its dimension.</remarks>
 134    /// <param name="x">The zero-based index along the first dimension.</param>
 135    /// <param name="y">The zero-based index along the second dimension.</param>
 136    /// <param name="z">The zero-based index along the third dimension.</param>
 137    /// <returns>The element located at the specified indices.</returns>
 138    [JsonIgnore]
 139    [MemoryPackIgnore]
 140    public T this[int x, int y, int z]
 141    {
 142        get
 143        {
 1126131144            ValidateIndex(x, y, z);
 1126127145            return _innerArray[GetIndex(x, y, z)];
 146        }
 147        set
 148        {
 1126021149            ValidateIndex(x, y, z);
 1126021150            _innerArray[GetIndex(x, y, z)] = value;
 1126021151        }
 152    }
 153
 154    /// <summary>
 155    /// Gets or sets the complete state of the 3D array, including its dimensions and data contents.
 156    /// </summary>
 157    /// <remarks>
 158    /// Setting this property replaces the current array's dimensions and data with those from the specified state.
 159    /// Getting this property returns a snapshot of the current array state.
 160    /// This property is intended for serialization and deserialization scenarios.
 161    /// </remarks>
 162    [JsonInclude]
 163    [MemoryPackInclude]
 164    public Array3DState<T> State
 165    {
 166        get
 167        {
 2168            var data = new T[_innerArray.Length];
 2169            Array.Copy(_innerArray, data, data.Length);
 170
 2171            return new Array3DState<T>(
 2172                _width,
 2173                _height,
 2174                _depth,
 2175                data
 2176            );
 177        }
 178
 179        internal set
 180        {
 2181            _width = value.Width;
 2182            _height = value.Height;
 2183            _depth = value.Depth;
 184
 2185            _innerArray = new T[value.Data.Length];
 2186            Array.Copy(value.Data, _innerArray, value.Data.Length);
 2187        }
 188    }
 189
 190    #endregion
 191
 192    #region Methods
 193
 194    /// <summary>
 195    /// Resizes the 3D array to the specified dimensions.
 196    /// Retains existing data where possible.
 197    /// </summary>
 198    public void Resize(int newWidth, int newHeight, int newDepth)
 199    {
 5200        var newArray = new T[newWidth * newHeight * newDepth];
 201
 5202        int minWidth = Math.Min(Width, newWidth);
 5203        int minHeight = Math.Min(Height, newHeight);
 5204        int minDepth = Math.Min(Depth, newDepth);
 205
 32206        for (int x = 0; x < minWidth; x++)
 207        {
 88208            for (int y = 0; y < minHeight; y++)
 209            {
 280210                for (int z = 0; z < minDepth; z++)
 211                {
 107212                    int srcIndex = GetIndex(x, y, z);
 107213                    int dstIndex = x * (newHeight * newDepth) + y * newDepth + z;
 107214                    newArray[dstIndex] = _innerArray[srcIndex];
 215                }
 216            }
 217        }
 218
 5219        _innerArray = newArray;
 5220        _width = newWidth;
 5221        _height = newHeight;
 5222        _depth = newDepth;
 5223    }
 224
 225    /// <summary>
 226    /// Shifts the elements in the array by the specified offsets along each axis.
 227    /// </summary>
 228    /// <param name="xOffset">The offset to apply along the X-axis.</param>
 229    /// <param name="yOffset">The offset to apply along the Y-axis.</param>
 230    /// <param name="zOffset">The offset to apply along the Z-axis.</param>
 231    /// <param name="wrap">
 232    /// Specifies whether to wrap elements that exceed the array's boundaries.
 233    /// If <c>true</c>, values wrap around to the other side of the array.
 234    /// If <c>false</c>, values that exceed boundaries are discarded.
 235    /// </param>
 236    /// <remarks>
 237    /// - Wrapping behavior ensures that no data is lost during shifts.
 238    /// - Non-wrapping behavior discards elements that move out of bounds.
 239    /// </remarks>
 240    public void Shift(int xOffset, int yOffset, int zOffset, bool wrap = true)
 241    {
 7242        var newArray = new T[Width * Height * Depth];
 243
 7244        if (wrap)
 6245            ShiftWrapped(newArray, xOffset, yOffset, zOffset);
 246        else
 1247            ShiftClamped(newArray, xOffset, yOffset, zOffset);
 248
 7249        _innerArray = newArray;
 7250    }
 251
 252    private void ShiftWrapped(T[] newArray, int xOffset, int yOffset, int zOffset)
 253    {
 6254        int normalizedXOffset = NormalizeShift(xOffset, Width);
 6255        int normalizedYOffset = NormalizeShift(yOffset, Height);
 6256        int normalizedZOffset = NormalizeShift(zOffset, Depth);
 257
 48258        for (int x = 0; x < Width; x++)
 259        {
 18260            int newX = WrapForwardIndex(x, normalizedXOffset, Width);
 180261            for (int y = 0; y < Height; y++)
 262            {
 72263                int newY = WrapForwardIndex(y, normalizedYOffset, Height);
 792264                for (int z = 0; z < Depth; z++)
 265                {
 324266                    int newZ = WrapForwardIndex(z, normalizedZOffset, Depth);
 324267                    newArray[GetIndex(newX, newY, newZ)] = _innerArray[GetIndex(x, y, z)];
 268                }
 269            }
 270        }
 6271    }
 272
 273    private void ShiftClamped(T[] newArray, int xOffset, int yOffset, int zOffset)
 274    {
 1275        GetShiftRange(Width, xOffset, out int xStart, out int xEnd);
 1276        GetShiftRange(Height, yOffset, out int yStart, out int yEnd);
 1277        GetShiftRange(Depth, zOffset, out int zStart, out int zEnd);
 278
 6279        for (int x = xStart; x < xEnd; x++)
 280        {
 2281            int newX = x + xOffset;
 12282            for (int y = yStart; y < yEnd; y++)
 283            {
 4284                int newY = y + yOffset;
 16285                for (int z = zStart; z < zEnd; z++)
 286                {
 4287                    int newZ = z + zOffset;
 4288                    newArray[GetIndex(newX, newY, newZ)] = _innerArray[GetIndex(x, y, z)];
 289                }
 290            }
 291        }
 1292    }
 293
 294    /// <summary>
 295    /// Clears all elements in the array.
 296    /// </summary>
 1297    public void Clear() => Array.Clear(_innerArray, 0, _innerArray.Length);
 298
 299    /// <summary>
 300    /// Fills the entire array with the specified value.
 301    /// </summary>
 302    public void Fill(T value)
 303    {
 224304        for (int i = 0; i < _innerArray.Length; i++)
 108305            _innerArray[i] = value;
 4306    }
 307
 308    /// <summary>
 309    /// Calculates the one-dimensional array index corresponding to the specified three-dimensional coordinates.
 310    /// </summary>
 311    /// <remarks>
 312    /// Use this method to map three-dimensional coordinates to a linear array index when working with flattened 3D data
 313    /// The valid ranges for x, y, and z depend on the dimensions of the underlying data structure.
 314    /// </remarks>
 315    /// <param name="x">The zero-based X coordinate to convert.</param>
 316    /// <param name="y">The zero-based Y coordinate to convert.</param>
 317    /// <param name="z">The zero-based Z coordinate to convert.</param>
 318    /// <returns>The zero-based index in the underlying one-dimensional array that corresponds to the specified (x, y, z
 319    public virtual int GetIndex(int x, int y, int z)
 320    {
 2252911321        return x * (Height * Depth) + y * Depth + z;
 322    }
 323
 324    /// <summary>
 325    /// Validates the specified indices.
 326    /// Throws an exception if the indices are out of bounds.
 327    /// </summary>
 328    public virtual void ValidateIndex(int x, int y, int z)
 329    {
 2252152330        if (!IsValidIndex(x, y, z))
 4331            throw new IndexOutOfRangeException($"Invalid index ({x}, {y}, {z}) for dimensions ({Width}, {Height}, {Depth
 2252148332    }
 333
 334    private static int NormalizeShift(int shift, int length)
 335    {
 18336        if (length == 0)
 3337            return 0;
 338
 15339        int normalized = shift % length;
 15340        return normalized < 0 ? normalized + length : normalized;
 341    }
 342
 343    private static int WrapForwardIndex(int index, int normalizedShift, int length)
 344    {
 414345        int shifted = index + normalizedShift;
 414346        return shifted >= length ? shifted - length : shifted;
 347    }
 348
 349    private static void GetShiftRange(int length, int shift, out int start, out int end)
 350    {
 3351        long startValue = shift < 0 ? -(long)shift : 0L;
 3352        long endValue = shift > 0 ? (long)length - shift : length;
 353
 3354        start = (int)Math.Min(startValue, length);
 3355        end = (int)Math.Max(0L, Math.Min(endValue, length));
 3356    }
 357
 358    /// <summary>
 359    /// Checks if the specified indices are within bounds.
 360    /// </summary>
 361    public virtual bool IsValidIndex(int x, int y, int z) =>
 2252155362        x >= 0 && x < Width && y >= 0 && y < Height && z >= 0 && z < Depth;
 363
 364    #endregion
 365
 366    #region IEnumerator Implementation
 367
 368    /// <summary>
 369    /// Returns an enumerator that iterates through all elements in the 3D array.
 370    /// </summary>
 371    /// <returns>An enumerator for the 3D array.</returns>
 372    public IEnumerator<T> GetEnumerator()
 373    {
 58374        for (int x = 0; x < Width; x++)
 375        {
 116376            for (int y = 0; y < Height; y++)
 377            {
 286378                for (int z = 0; z < Depth; z++)
 103379                    yield return this[x, y, z];
 380            }
 381        }
 11382    }
 383
 384    /// <summary>
 385    /// Returns an enumerator that iterates through all elements in the 3D array (non-generic).
 386    /// </summary>
 387    /// <returns>An enumerator for the 3D array.</returns>
 4388    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
 389
 390
 391    #endregion
 392}