Files
Just.Core/Core/Collections/Map.cs
2023-12-14 21:48:48 +04:00

173 lines
4.6 KiB
C#

using System.Collections;
namespace Just.Core.Collections;
public class Map<T> : IMap<T>
{
internal readonly T[] _values;
internal Map(int width, int height, T[] values)
{
Width = width;
Height = height;
_values = values;
}
public Map(ReadOnlySpan<T> 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<T> AsSpan() => _values;
[Pure] public ReadOnlyMemory<T> 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<T> 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<T> 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<MapPoint<T>> IEnumerable<MapPoint<T>>.GetEnumerator() => new Enumerator(this);
IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this);
public struct Enumerator : IEnumerator<MapPoint<T>>, IEnumerator
{
private readonly Map<T> _map;
private int _index;
private MapPoint<T> _current;
internal Enumerator(Map<T> 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<T> 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;
}
}
}