Common
A library of common classes.
Array2D.cs
2using System;
3using System.Collections.Generic;
4using System.Runtime.CompilerServices;
5using System.Runtime.Serialization;
6using System.Text;
7
9{
13 [DataContract(Name = "Array2D", Namespace = Constants.DataContractNamespace)]
14 public class Array2D<T>
15 {
19 [DataMember(Order = 1)]
20 public int Rows { get; private set; }
21
25 [DataMember(Order = 2)]
26 public int Columns { get; private set; }
27
31 [DataMember(Order = 3)]
32 public T[] Array { get; private set; } = System.Array.Empty<T>();
33
37 public Array2D()
38 {
39
40 }
41
48 public Array2D(int rows, int columns)
49 {
50 if (rows < 0)
51 throw new ArgumentException($"Rows cannot be negative: {rows}.");
52 if (columns < 0)
53 throw new ArgumentException($"Columns cannot be negative: {columns}.");
54
55 if (rows > 0 && columns > 0)
56 {
57 Rows = rows;
58 Columns = columns;
59 Array = new T[rows * columns];
60 }
61 }
62
67 public Array2D(Array2D<T> other)
68 {
69 if (other.Rows > 0 && other.Columns > 0)
70 {
71 Rows = other.Rows;
72 Columns = other.Columns;
73 Array = new T[Rows * Columns];
74 other.Array.CopyTo(Array, 0);
75 }
76 }
77
82 public Array2D(T[,] array)
83 {
84 if (array.Length > 0)
85 {
86 Rows = array.GetLength(0);
87 Columns = array.GetLength(1);
88 Array = FlattenArray(array);
89 }
90 }
91
98 public T this[int row, int column]
99 {
100 get
101 {
102 if (!IndexExists(row, column))
103 throw new IndexOutOfRangeException($"Index out of range: ({row}, {column}).");
104 return Array[Index(row, column)];
105 }
106 set
107 {
108 if (!IndexExists(row, column))
109 throw new IndexOutOfRangeException($"Index out of range: ({row}, {column}).");
110 Array[Index(row, column)] = value;
111 }
112 }
113
118 public T this[Vector2DInt index]
119 {
120 get => this[index.X, index.Y];
121 set => this[index.X, index.Y] = value;
122 }
123
128 public static implicit operator Array2D<T>(T[,] array) => new Array2D<T>(array);
129
131 public override string ToString()
132 {
133 return $"Array2D<{typeof(T)}>(Rows = {Rows}, Columns = {Columns})";
134 }
135
140 {
141 return new Array2D<T>(this);
142 }
143
147 public string ToArrayString()
148 {
149 var size = 2 + 2 * Array.Length + 4 * Rows;
150 var builder = new StringBuilder(size);
151 builder.Append('[');
152
153 for (int i = 0; i < Rows; i++)
154 {
155 builder.Append('[');
156
157 for (int j = 0; j < Columns; j++)
158 {
159 builder.Append(this[i, j]);
160
161 if (j < Columns - 1)
162 builder.Append(", ");
163 }
164
165 builder.Append(']');
166
167 if (i < Rows - 1)
168 builder.Append("\n ");
169 }
170
171 builder.Append(']');
172 return builder.ToString();
173 }
174
178 public void Clear()
179 {
180 System.Array.Clear(Array, 0, Array.Length);
181 }
182
187 public void Fill(T value)
188 {
189 for (int i = 0; i < Array.Length; i++)
190 {
191 Array[i] = value;
192 }
193 }
194
200 public static bool ValuesAreEqual(Array2D<T> x, Array2D<T> y)
201 {
202 return ValuesAreEqual(x, y, EqualityComparer<T>.Default.Equals);
203 }
204
211 public static bool ValuesAreEqual(Array2D<T> x, Array2D<T> y, Func<T, T, bool> comparer)
212 {
213 if (x == y)
214 return true;
215
216 if (x == null || y == null)
217 return false;
218
219 return x.ValuesAreEqual(y, comparer);
220 }
221
226 public bool ValuesAreEqual(Array2D<T> other)
227 {
228 return ValuesAreEqual(other, EqualityComparer<T>.Default.Equals);
229 }
230
236 public bool ValuesAreEqual(Array2D<T> other, Func<T, T, bool> comparer)
237 {
238 if (Array.Length != other.Array.Length || Rows != other.Rows || Columns != other.Columns)
239 return false;
240
241 for (int i = 0; i < Array.Length; i++)
242 {
243 if (!comparer.Invoke(Array[i], other.Array[i]))
244 return false;
245 }
246
247 return true;
248 }
249
255 [MethodImpl(MethodImplOptions.AggressiveInlining)]
256 public bool IndexExists(int row, int column)
257 {
258 return (uint)row < (uint)Rows && (uint)column < (uint)Columns;
259 }
260
266 [MethodImpl(MethodImplOptions.AggressiveInlining)]
267 private int Index(int row, int column)
268 {
269 return row * Columns + column;
270 }
271
277 public Vector2DInt InverseIndex(int index)
278 {
279 if ((uint)index < (uint)Array.Length)
280 {
281 var row = index / Columns;
282 var column = index - row * Columns;
283 return new Vector2DInt(row, column);
284 }
285
286 throw new IndexOutOfRangeException($"Index out of range: {index}.");
287 }
288
294 public Vector2DInt FindIndex(Func<T, bool> predicate)
295 {
296 for (int i = 0; i < Rows; i++)
297 {
298 for (int j = 0; j < Columns; j++)
299 {
300 if (predicate.Invoke(Array[Index(i, j)]))
301 return new Vector2DInt(i, j);
302 }
303 }
304
305 return new Vector2DInt(-1, -1);
306 }
307
312 public List<Vector2DInt> FindIndexes(Func<T, bool> predicate)
313 {
314 var result = new List<Vector2DInt>();
315
316 for (int i = 0; i < Rows; i++)
317 {
318 for (int j = 0; j < Columns; j++)
319 {
320 if (predicate.Invoke(Array[Index(i, j)]))
321 result.Add(new Vector2DInt(i, j));
322 }
323 }
324
325 return result;
326 }
327
332 public static T[] FlattenArray(T[,] array)
333 {
334 var result = new T[array.Length];
335 int k = 0;
336
337 for (int i = 0; i < array.GetLength(0); i++)
338 {
339 for (int j = 0; j < array.GetLength(1); j++)
340 {
341 result[k++] = array[i, j];
342 }
343 }
344
345 return result;
346 }
347
355 public T GetOrDefault(int row, int column, T fallback = default)
356 {
357 if (IndexExists(row, column))
358 return Array[Index(row, column)];
359 return fallback;
360 }
361
368 public T GetOrDefault(Vector2DInt index, T fallback = default)
369 {
370 return GetOrDefault(index.X, index.Y, fallback);
371 }
372
378 {
379 return IndexRotated90(index.X, index.Y);
380 }
381
387 public Vector2DInt IndexRotated90(int row, int column)
388 {
389 return new Vector2DInt(column, Rows - 1 - row);
390 }
391
396 {
397 var rotation = new Array2D<T>(Columns, Rows);
398
399 for (int i = 0; i < Rows; i++)
400 {
401 for (int j = 0; j < Columns; j++)
402 {
403 rotation[IndexRotated90(i, j)] = this[i, j];
404 }
405 }
406
407 return rotation;
408 }
409
415 {
416 return IndexRotated180(index.X, index.Y);
417 }
418
424 public Vector2DInt IndexRotated180(int row, int column)
425 {
426 return new Vector2DInt(Rows - 1 - row, Columns - 1 - column);
427 }
428
433 {
434 var rotation = new Array2D<T>(Rows, Columns);
435
436 for (int i = 0; i < Rows; i++)
437 {
438 for (int j = 0; j < Columns; j++)
439 {
440 rotation[IndexRotated180(i, j)] = this[i, j];
441 }
442 }
443
444 return rotation;
445 }
446
452 {
453 return IndexRotated270(index.X, index.Y);
454 }
455
461 public Vector2DInt IndexRotated270(int row, int column)
462 {
463 return new Vector2DInt(Columns - 1 - column, row);
464 }
465
470 {
471 var rotation = new Array2D<T>(Columns, Rows);
472
473 for (int i = 0; i < Rows; i++)
474 {
475 for (int j = 0; j < Columns; j++)
476 {
477 rotation[IndexRotated270(i, j)] = this[i, j];
478 }
479 }
480
481 return rotation;
482 }
483
489 {
490 return IndexMirroredHorizontally(index.X, index.Y);
491 }
492
498 public Vector2DInt IndexMirroredHorizontally(int row, int column)
499 {
500 return new Vector2DInt(row, Columns - 1 - column);
501 }
502
507 {
508 var mirror = new Array2D<T>(Rows, Columns);
509
510 for (int i = 0; i < Rows; i++)
511 {
512 for (int j = 0; j < Columns; j++)
513 {
514 mirror[IndexMirroredHorizontally(i, j)] = this[i, j];
515 }
516 }
517
518 return mirror;
519 }
520
526 {
527 return IndexMirroredVertically(index.X, index.Y);
528 }
529
535 public Vector2DInt IndexMirroredVertically(int row, int column)
536 {
537 return new Vector2DInt(Rows - 1 - row, column);
538 }
539
544 {
545 var mirror = new Array2D<T>(Rows, Columns);
546
547 for (int i = 0; i < Rows; i++)
548 {
549 for (int j = 0; j < Columns; j++)
550 {
551 mirror[IndexMirroredVertically(i, j)] = this[i, j];
552 }
553 }
554
555 return mirror;
556 }
557
566 public Array2D<int> FindDistances(int row, int column, Func<T, bool> predicate = null)
567 {
568 if (predicate == null)
569 predicate = x => EqualityComparer<T>.Default.Equals(x, default);
570
571 var distances = new Array2D<int>(Rows, Columns);
572 distances.Fill(-1);
573 SearchDistances(row, column, 0, distances, predicate);
574 return distances;
575 }
576
585 private void SearchDistances(int row, int column, int distance, Array2D<int> distances, Func<T, bool> predicate)
586 {
587 if (IndexExists(row, column) && !predicate.Invoke(this[row, column]))
588 {
589 var currentDistance = distances[row, column];
590
591 if (currentDistance > distance || currentDistance < 0)
592 {
593 distances[row, column] = distance++;
594 SearchDistances(row, column - 1, distance, distances, predicate);
595 SearchDistances(row, column + 1, distance, distances, predicate);
596 SearchDistances(row - 1, column, distance, distances, predicate);
597 SearchDistances(row + 1, column, distance, distances, predicate);
598 }
599 }
600 }
601 }
602}
A 2D array that can be serialized.
Definition: Array2D.cs:15
Array2D< T > MirroredHorizontally()
Returns a new array mirrored horizontally, i.e. about the vertical axis.
Definition: Array2D.cs:506
Array2D< T > Rotated180()
Returns a new array rotated 180 degrees.
Definition: Array2D.cs:432
Vector2DInt FindIndex(Func< T, bool > predicate)
Returns the first 2D index where the specified predicate is true. Returns a -1 vector if no index is ...
Definition: Array2D.cs:294
Array2D(Array2D< T > other)
Initializes a copy of an array.
Definition: Array2D.cs:67
bool IndexExists(int row, int column)
Returns true if the index exists.
Definition: Array2D.cs:256
T GetOrDefault(Vector2DInt index, T fallback=default)
Returns the value at the specified index if it exists. If not, returns the fallback value.
Definition: Array2D.cs:368
Vector2DInt IndexRotated270(Vector2DInt index)
Returns the index corresponding to when the array is rotated clockwise 270 degrees.
Definition: Array2D.cs:451
Vector2DInt IndexRotated270(int row, int column)
Returns the index corresponding to when the array is rotated clockwise 270 degrees.
Definition: Array2D.cs:461
static bool ValuesAreEqual(Array2D< T > x, Array2D< T > y)
Returns true if the values in the arrays of equal based on the default comparer.
Definition: Array2D.cs:200
Vector2DInt IndexRotated180(Vector2DInt index)
Returns the index corresponding to when the array is rotated 180 degrees.
Definition: Array2D.cs:414
Vector2DInt InverseIndex(int index)
Returns the 2D index corresponding to the specified flat index.
Definition: Array2D.cs:277
void SearchDistances(int row, int column, int distance, Array2D< int > distances, Func< T, bool > predicate)
Performs a recursive crawl of the array cells to determine the distance to an index.
Definition: Array2D.cs:585
Vector2DInt IndexMirroredHorizontally(Vector2DInt index)
Returns the index corresponding to when the array is mirrored horizontally.
Definition: Array2D.cs:488
int Index(int row, int column)
Returns the flat array index corresponding to the specified 2D index.
Definition: Array2D.cs:267
Vector2DInt IndexRotated90(int row, int column)
Returns the index corresponding to when the array is rotated clockwise 90 degrees.
Definition: Array2D.cs:387
Array2D(int rows, int columns)
Initializes an array by size.
Definition: Array2D.cs:48
string ToArrayString()
Returns a string of all array elements.
Definition: Array2D.cs:147
T[] Array
The underlying flat array.
Definition: Array2D.cs:32
Array2D(T[,] array)
Initializes an array from a built-in 2D array.
Definition: Array2D.cs:82
Vector2DInt IndexRotated90(Vector2DInt index)
Returns the index corresponding to when the array is rotated clockwise 90 degrees.
Definition: Array2D.cs:377
Vector2DInt IndexMirroredHorizontally(int row, int column)
Returns the index corresponding to when the array is mirrored horizontally.
Definition: Array2D.cs:498
Vector2DInt IndexRotated180(int row, int column)
Returns the index corresponding to when the array is rotated 180 degrees.
Definition: Array2D.cs:424
static T[] FlattenArray(T[,] array)
Returns a new flattened array from a built-in 2D array.
Definition: Array2D.cs:332
T GetOrDefault(int row, int column, T fallback=default)
Returns the value at the specified index if it exists. If not, returns the fallback value.
Definition: Array2D.cs:355
Array2D< T > Rotated270()
Returns a new array rotated clockwise 270 degrees.
Definition: Array2D.cs:469
void Clear()
Sets the contents of the array to the default value.
Definition: Array2D.cs:178
override string ToString()
Definition: Array2D.cs:131
int Rows
The number of rows in the array.
Definition: Array2D.cs:20
Array2D< T > Copy()
Returns a shallow copy of the array.
Definition: Array2D.cs:139
bool ValuesAreEqual(Array2D< T > other, Func< T, T, bool > comparer)
Returns true if the values in the arrays are equal.
Definition: Array2D.cs:236
Array2D< T > MirroredVertically()
Returns a new array mirrored vertically, i.e. about the horizontal axis.
Definition: Array2D.cs:543
int Columns
The number of columns in the array.
Definition: Array2D.cs:26
Vector2DInt IndexMirroredVertically(int row, int column)
Returns the index corresponding to when the array is mirrored vertically.
Definition: Array2D.cs:535
Vector2DInt IndexMirroredVertically(Vector2DInt index)
Returns the index corresponding to when the array is mirrored vertically.
Definition: Array2D.cs:525
Array2D< int > FindDistances(int row, int column, Func< T, bool > predicate=null)
Returns an array of distances from the specified index to each cell. Values of -1 indicate that the i...
Definition: Array2D.cs:566
static bool ValuesAreEqual(Array2D< T > x, Array2D< T > y, Func< T, T, bool > comparer)
Returns true if the values in the arrays are equal.
Definition: Array2D.cs:211
void Fill(T value)
Sets all elements of the array to the value.
Definition: Array2D.cs:187
List< Vector2DInt > FindIndexes(Func< T, bool > predicate)
Returns a list of indexes where the specified predicate is true.
Definition: Array2D.cs:312
Array2D()
Initializes an empty array.
Definition: Array2D.cs:37
Array2D< T > Rotated90()
Returns a new array rotated clockwise 90 degrees.
Definition: Array2D.cs:395
bool ValuesAreEqual(Array2D< T > other)
Returns true if the values in the arrays are equal based on the default comparer.
Definition: Array2D.cs:226
A 2D vector with integer values.
Definition: Vector2DInt.cs:11