using System; using System.Collections.ObjectModel; using System.Linq; using UnityEngine; namespace DTT.Utils.Workflow { /// /// A grid representation of a data array. /// public class GridBase { /// /// The minimum amount of rows or columns used for the grid. /// public const int MIN_ROW_OR_COLUMN_SIZE = 2; /// /// The amount of columns used in the grid. /// public int Columns { get => _columns; set { if (value < MIN_ROW_OR_COLUMN_SIZE) throw new ArgumentOutOfRangeException(nameof(value)); _columns = value; Resize(); } } /// /// The amount of used in the grid. /// public int Rows { get => _rows; set { if (value < MIN_ROW_OR_COLUMN_SIZE) throw new ArgumentOutOfRangeException(nameof(value)); _rows = value; Resize(); } } /// /// Returns a value from the grid. /// /// The x coordinate of the value. /// The y coordinate of the value. public T this[int x, int y] => GetValue(x, y); /// /// Returns a value from the grid. /// /// The coordinates of the value. public T this[Vector2Int coordinates] => GetValue(coordinates.x, coordinates.y); /// /// The collection of values used by the grid. /// public ReadOnlyCollection Values => Array.AsReadOnly(_values); /// /// The array values used by the grid. /// private T[] _values; /// /// The amount of columns used in the grid. /// private int _columns; /// /// The amount of rows used in the grid. /// private int _rows; /// /// Creates a new default grid with a minimum size. /// public GridBase() { _columns = MIN_ROW_OR_COLUMN_SIZE; _rows = MIN_ROW_OR_COLUMN_SIZE; Resize(); } /// /// Creates a new grid with a given amount of columns and rows. /// /// The amount of columns used in the grid. /// The amount of rows used in the grid. public GridBase(int columns, int rows) { if (columns < MIN_ROW_OR_COLUMN_SIZE) throw new ArgumentOutOfRangeException(nameof(columns)); if (rows < MIN_ROW_OR_COLUMN_SIZE) throw new ArgumentOutOfRangeException(nameof(rows)); _rows = rows; _columns = columns; Resize(); } /// /// Creates a new grid with a given amount of columns and rows. /// /// The amount of columns used in the grid. /// The amount of rows used in the grid. /// The values used by the grid. public GridBase(int columns, int rows, T[] values) : this(columns, rows) { _columns = columns; _rows = rows; Resize(); Populate(values); } /// /// Creates a new grid with a given amount of values. /// /// The values to initialize the grid with. public GridBase(T[,] values) { _columns = values.GetLength(0); _rows = values.GetLength(1); Populate(values); } /// /// Populates the grid with values to use. /// /// The values to be used by the grid. public void Populate(T[] values) => values.CopyTo(_values, 0); /// /// Populates the grid with values to use. /// /// /// Whether to force a resize of the grid if the given size is bigger. public void Populate(T[,] values, bool forceResize = true) { T[] flattened = values.Cast().ToArray(); int columns = values.GetLength(0); int rows = values.GetLength(1); if(forceResize) Resize(columns, rows); Populate(flattened); } /// /// Returns a value from the grid. /// /// The coordinates of the value. /// The value from the grid. public T GetValue(Vector2Int coordinates) => GetValue(coordinates.x, coordinates.y); /// /// Returns a value from the grid. /// /// The x coordinate of the value. /// The y coordinate of the value. /// The value from the grid. public T GetValue(int x, int y) => IsInvalid(x, y) ? default : _values[GetIndex(x, y)]; /// /// Swaps values of two different grid coordinates. Uses the 'GetValue' method to /// swap the values which means default values will be used if coordinates /// are out of bounds. /// /// The first grid coordinate. /// The second grid coordinate. public void SwapValues(Vector2Int coordinates1, Vector2Int coordinates2) => SwapValues(coordinates1.x, coordinates1.y, coordinates2.x, coordinates2.y); /// /// Swaps values of two different grid coordinates. Uses the 'GetValue' method to /// swap the values which means default values will be used if coordinates /// are out of bounds. /// /// The x coordinate of the first value /// The y coordinate of the first value /// The x coordinate of the second value /// The y coordinate of the second value public void SwapValues(int x1, int y1, int x2, int y2) { T temp = GetValue(x1, y1); SetValue(x1, y1, GetValue(x2, y2)); SetValue(x2, y2, temp); } /// /// Sets the value at a given grid coordinate. /// /// The coordinates at which to set the value. /// The new value. public void SetValue(Vector2Int coordinates, T newValue) => SetValue(coordinates.x, coordinates.y, newValue); /// /// Sets the value at a given grid coordinate. /// /// The x coordinate. /// The y coordinate. /// The new value to assign. public void SetValue(int x, int y, T newValue) { if (IsInvalid(x, y)) return; _values[GetIndex(x, y)] = newValue; } /// /// Returns the array index based on given coordinates. /// /// The x coordinate. /// The y coordinate. /// The array index. public int GetIndex(int x, int y) => (Columns * y) + x; /// /// Returns whether given coordinates lie within the grid. /// /// The x coordinate. /// The y coordinate. /// Whether given coordinates lie within the grid. public bool IsInvalid(int x, int y) => (x < 0 || x >= Columns) || (y < 0 || y >= Rows); /// /// Resizes the grid using given column and row values. /// /// The amount of columns used in the grid. /// The amount of rows used in the grid. public void Resize(int columns, int rows) { _columns = columns; _rows = rows; Resize(); } /// /// Resizes the grid using stored column and row values. /// private void Resize() { int size = _columns * _rows; if (_values == null) { _values = new T[_columns * _rows]; } else { Array.Resize(ref _values, size); } } } }