using System.Collections; namespace Just.Core.Collections; public class Map : IMap { internal readonly T[] _values; internal Map(int width, int height, T[] values) { Width = width; Height = height; _values = values; } public Map(ReadOnlySpan values, int width, int height) { ArgumentOutOfRangeException.ThrowIfLessThan(width, 1); ArgumentOutOfRangeException.ThrowIfLessThan(height, 1); ArgumentOutOfRangeException.ThrowIfNotEqual(width * height, values.Length); Width = width; Height = height; _values = new T[values.Length]; values.CopyTo(_values); } public Map(T[] values, int width, int height) { ArgumentNullException.ThrowIfNull(values); ArgumentOutOfRangeException.ThrowIfLessThan(width, 1); ArgumentOutOfRangeException.ThrowIfLessThan(height, 1); ArgumentOutOfRangeException.ThrowIfNotEqual(width * height, values.Length); Width = width; Height = height; _values = new T[Width * Height]; values.CopyTo(_values.AsSpan()); } public Map(T[,] values) { ArgumentNullException.ThrowIfNull(values); ArgumentOutOfRangeException.ThrowIfZero(values.Length); Width = values.GetLength(0); Height = values.GetLength(1); _values = new T[Width * Height]; var data = _values.AsSpan(); for (int y = 0; y < Height; y++) { int idy = y * Width; for (int x = 0; x < Width; x++) { int id = idy + x; data[id] = values[x, y]; } } } [Pure] public ReadOnlySpan AsSpan() => _values; [Pure] public ReadOnlyMemory AsMemory() => _values; public int Width { get; } public int Height { get; } public int Count => _values.Length; [Pure] public ref readonly T this[int x, int y] { get { x = Math.Clamp(x, 0, Width); y = Math.Clamp(y, 0, Height); return ref _values[(y * Width) + x]; } } [Pure] public ref readonly T this[double x, double y] { get { int xi = Math.Clamp((int)x, 0, Width - 1); int yi = Math.Clamp((int)y, 0, Height - 1); return ref _values[(yi * Width) + xi]; } } [Pure] public MapPoint Get(int x, int y) { x = Math.Clamp(x, 0, Width - 1); y = Math.Clamp(y, 0, Height - 1); return new(_values[(y * Width) + x], x, y, this); } [Pure] public MapPoint Get(double x, double y) { int xi = Math.Clamp((int)x, 0, Width - 1); int yi = Math.Clamp((int)y, 0, Height - 1); return new(_values[(yi * Width) + xi], xi, yi, this); } [Pure] public T[,] ToArray() { T[,] arr = new T[Width, Height]; var data = _values.AsSpan(); for (int y = 0; y < Height; y++) { int idy = y * Width; for (int x = 0; x < Width; x++) { int id = idy + x; arr[x, y] = data[id]; } } return arr; } public Enumerator GetEnumerator() => new(this); IEnumerator> IEnumerable>.GetEnumerator() => new Enumerator(this); IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this); public struct Enumerator : IEnumerator>, IEnumerator { private readonly Map _map; private int _index; private MapPoint _current; internal Enumerator(Map map) { _map = map; _index = 0; _current = default; } public readonly void Dispose() { } public bool MoveNext() { if ((uint)_index < (uint)_map.Count) { _current = new( _map._values[_index], _index % _map.Width, _index / _map.Width, _map); _index++; return true; } _index = _map.Count + 1; _current = default; return false; } public readonly MapPoint Current => _current!; readonly object? IEnumerator.Current { get { if (_index == 0 || _index == _map.Count + 1) { throw new InvalidOperationException(); } return Current; } } void IEnumerator.Reset() { _index = 0; _current = default; } } }