Gamedev Framework (gf)  0.6.0
A C++11 framework for 2D games
Array2D.h
1 /*
2  * Gamedev Framework (gf)
3  * Copyright (C) 2016-2017 Julien Bernard
4  *
5  * This software is provided 'as-is', without any express or implied
6  * warranty. In no event will the authors be held liable for any damages
7  * arising from the use of this software.
8  *
9  * Permission is granted to anyone to use this software for any purpose,
10  * including commercial applications, and to alter it and redistribute it
11  * freely, subject to the following restrictions:
12  *
13  * 1. The origin of this software must not be misrepresented; you must not
14  * claim that you wrote the original software. If you use this software
15  * in a product, an acknowledgment in the product documentation would be
16  * appreciated but is not required.
17  * 2. Altered source versions must be plainly marked as such, and must not be
18  * misrepresented as being the original software.
19  * 3. This notice may not be removed or altered from any source distribution.
20  */
21 #ifndef ARRAY2D_H
22 #define ARRAY2D_H
23 
24 #include <cassert>
25 #include <type_traits>
26 #include <utility>
27 #include <vector>
28 
29 #include "Log.h"
30 
31 #include "Math.h"
32 #include "Portability.h"
33 #include "Range.h"
34 #include "Vector.h"
35 
36 namespace gf {
37 #ifndef DOXYGEN_SHOULD_SKIP_THIS
38 inline namespace v1 {
39 #endif
40 
41  /**
42  * @ingroup core
43  * @brief A two-dimensional array
44  *
45  * gf::Array represents a two-dimensional array, organized in row-major order.
46  *
47  * The array is templated with the type of the data and the type of the
48  * indices (defaults to `unsigned`).
49  *
50  * Contrary to the usual way of accessing 2D arrays, the first coordinate is
51  * the column and the second coordinate is the row. So that, if `size` is the
52  * size of the array and `pos` is the position in the array:
53  *
54  * - @f$ 0 \leq \mathtt{pos.x} = \mathtt{pos.col} < \mathtt{size.width} = \mathtt{size.col} @f$
55  * - @f$ 0 \leq \mathtt{pos.y} = \mathtt{pos.row} < \mathtt{size.height} = \mathtt{size.row} @f$
56  *
57  * Some convinient visitors are provided to visit the four neighbors (up,
58  * down, left and right), or the eight neighbors.
59  *
60  * @sa gf::Matrix
61  */
62  template<typename T, typename I = unsigned>
63  class Array2D {
64  public:
65  /**
66  * @brief Default constructor
67  *
68  * Creates an empty array.
69  */
71  : m_size(0, 0)
72  {
73 
74  }
75 
76  /**
77  * @brief Constructor with a size
78  *
79  * @param size The size of the array
80  */
81  Array2D(Vector<I, 2> size)
82  : m_size(size)
84  {
85 
86  }
87 
88  /**
89  * @brief Constructor with a size and a value
90  *
91  * @param size The size of the array
92  * @param value The initial value in the array
93  */
94  Array2D(Vector<I, 2> size, const T& value)
95  : m_size(size)
97  {
98 
99  }
100 
101  /**
102  * @brief Default copy constructor
103  */
104  Array2D(const Array2D&) = default;
105 
106  /**
107  * @brief Default copy assignment
108  */
109  Array2D& operator=(const Array2D&) = default;
110 
111  /**
112  * @brief Default move constructor
113  */
114  Array2D(Array2D&&) = default;
115 
116  /**
117  * @brief Default move assignement
118  */
119  Array2D& operator=(Array2D&&) = default;
120 
121  /**
122  * @brief Swap with another array
123  *
124  * @param other An other array
125  */
126  void swap(Array2D& other) {
127  std::swap(m_data, other.m_data);
128  std::swap(m_size, other.m_size);
129  }
130 
131  /**
132  * @name Raw data access
133  * @{
134  */
135 
136  /**
137  * @brief Get the pointer to raw data
138  *
139  * The returned pointer is `const` so you can not modify the array with
140  * this function.
141  *
142  * @return The pointer to raw data
143  */
144  const T *getDataPtr() const noexcept {
145  return m_data.data();
146  }
147 
148  /**
149  * @brief Get the raw data size
150  *
151  * @return The total number of elements in the array
152  */
153  std::size_t getDataSize() const noexcept {
154  return m_data.size();
155  }
156 
157  /**
158  * @brief Get the size of the array
159  *
160  * @return The size of the array
161  */
162  constexpr Vector<I, 2> getSize() const noexcept {
163  return m_size;
164  }
165 
166  /**
167  * @brief Get the number of columns
168  *
169  * @return The number of columns
170  */
171  constexpr I getCols() const noexcept {
172  return m_size.col;
173  }
174 
175  /**
176  * @brief Get the number of rows
177  *
178  * @return The number of rows
179  */
180  constexpr I getRows() const noexcept {
181  return m_size.row;
182  }
183 
184  /**
185  * @brief Check if the array is empty
186  *
187  * An empty array is an array with @f$ 0 @f$ elements, i.e. either the
188  * number of columns is @f$ 0 @f$ or the number of rows is @f$ 0 @f$.
189  *
190  * @return True if the array is empty
191  */
192  constexpr bool isEmpty() const noexcept {
193  return m_data.empty();
194  }
195 
196  /**
197  * @brief Check if a position is valid
198  *
199  * A valid position is a position inside the array
200  *
201  * @return True if the position is valid
202  */
203  constexpr bool isValid(Vector<I, 2> pos) const noexcept {
204  return pos.col < m_size.col && pos.row < m_size.row && (std::is_unsigned<I>::value || (0 <= pos.col && 0 <= pos.row));
205  }
206 
207  /** @} */
208 
209  /**
210  * @name Elements access
211  * @{
212  */
213 
214  /**
215  * @brief Get the element at a given 2D position
216  *
217  * @param pos The 2D position of the element
218  */
219  T& operator()(Vector<I, 2> pos) {
220  return get(pos);
221  }
222 
223  /**
224  * @brief Get the element at a given 1D index
225  *
226  * @param index The 1D index of the element
227  * @sa getPositionRange()
228  */
229  T& operator()(std::size_t index) {
230  return m_data[index];
231  }
232 
233  /**
234  * @brief Get the element at a given 2D position
235  *
236  * @param pos The 2D position of the element
237  */
238  const T& operator()(Vector<I, 2> pos) const {
239  return get(pos);
240  }
241 
242  /**
243  * @brief Get the element at a given 1D index
244  *
245  * @param index The 1D index of the element
246  * @sa getPositionRange()
247  */
248  const T& operator()(std::size_t index) const {
249  return m_data[index];
250  }
251 
252  /**
253  * @brief Transform a 1D position into a 2D position
254  *
255  * @param pos A 1D position
256  * @return The corresponding 2D position
257  */
258  constexpr Vector<I, 2> toPosition(std::size_t pos) const noexcept {
259  return { pos % m_size.col, pos / m_size.col };
260  }
261 
262  /** @} */
263 
264  /**
265  * @name Visitors
266  * @{
267  */
268 
269  /**
270  * @brief Visit the 4 neighbors of a given position
271  *
272  * This function calls a callback function for every neighbor in the
273  * vertical and horizontal direction. The function checks if the neighbor
274  * actually exists.
275  *
276  * The callback function has the following prototype:
277  *
278  * ~~~{.cc}
279  * void callback(Vector<I, 2> pos, T value);
280  * // pos is the position of the neighbor
281  * // value is the value of the neighbor
282  * ~~~
283  *
284  * The callback function can be a simple function but also a
285  * [lambda expression](http://en.cppreference.com/w/cpp/language/lambda).
286  *
287  * @param pos The position
288  * @param func A callback function
289  */
290  template<typename Func>
291  void visit4Neighbors(Vector<I, 2> pos, Func func) {
292  visitNeighborsDiamond(pos, func, 1);
293  }
294 
295  /**
296  * @brief Visit the 4 neighbors of a given position
297  *
298  * This function calls a callback function for every neighbor in the
299  * vertical and horizontal direction. The function checks if the neighbor
300  * actually exists.
301  *
302  * The callback function has the following prototype:
303  *
304  * ~~~{.cc}
305  * void callback(Vector<I, 2> pos, T value);
306  * // pos is the position of the neighbor
307  * // value is the value of the neighbor
308  * ~~~
309  *
310  * The callback function can be a simple function but also a
311  * [lambda expression](http://en.cppreference.com/w/cpp/language/lambda).
312  *
313  * @param pos The position
314  * @param func A callback function
315  */
316  template<typename Func>
317  void visit4Neighbors(Vector<I, 2> pos, Func func) const {
318  visitNeighborsDiamond(pos, func, 1);
319  }
320 
321  /**
322  * @brief Visit the 12 neighbors of a given position
323  *
324  * This function calls a callback function for every neighbor in the
325  * vertical and horizontal direction. The function checks if the neighbor
326  * actually exists.
327  *
328  * The callback function has the following prototype:
329  *
330  * ~~~{.cc}
331  * void callback(Vector<I, 2> pos, T value);
332  * // pos is the position of the neighbor
333  * // value is the value of the neighbor
334  * ~~~
335  *
336  * The callback function can be a simple function but also a
337  * [lambda expression](http://en.cppreference.com/w/cpp/language/lambda).
338  *
339  * @param pos The position
340  * @param func A callback function
341  */
342  template<typename Func>
343  void visit12Neighbors(Vector<I, 2> pos, Func func) {
344  visitNeighborsDiamond(pos, func, 2);
345  }
346 
347  /**
348  * @brief Visit the 12 neighbors of a given position
349  *
350  * This function calls a callback function for every neighbor in the
351  * vertical and horizontal direction. The function checks if the neighbor
352  * actually exists.
353  *
354  * The callback function has the following prototype:
355  *
356  * ~~~{.cc}
357  * void callback(Vector<I, 2> pos, T value);
358  * // pos is the position of the neighbor
359  * // value is the value of the neighbor
360  * ~~~
361  *
362  * The callback function can be a simple function but also a
363  * [lambda expression](http://en.cppreference.com/w/cpp/language/lambda).
364  *
365  * @param pos The position
366  * @param func A callback function
367  */
368  template<typename Func>
369  void visit12Neighbors(Vector<I, 2> pos, Func func) const {
370  visitNeighborsDiamond(pos, func, 2);
371  }
372 
373  /**
374  * @brief Visit the 8 neighbors of a given position
375  *
376  * This function calls a callback function for every neighbor in the
377  * vertical, horizontal and diagonal direction. The function checks if the
378  * neighbor actually exists.
379  *
380  * The callback function has the following prototype:
381  *
382  * ~~~{.cc}
383  * void callback(Vector<I, 2> pos, T value);
384  * // pos is the position of the neighbor
385  * // value is the value of the neighbor
386  * ~~~
387  *
388  * The callback function can be a simple function but also a
389  * [lambda expression](http://en.cppreference.com/w/cpp/language/lambda).
390  *
391  * @param pos The position
392  * @param func A callback function
393  */
394  template<typename Func>
395  void visit8Neighbors(Vector<I, 2> pos, Func func) {
396  visitNeighborsSquare(pos, func, 1);
397  }
398 
399  /**
400  * @brief Visit the 8 neighbors of a given position
401  *
402  * This function calls a callback function for every neighbor in the
403  * vertical, horizontal and diagonal direction. The function checks if the
404  * neighbor actually exists.
405  *
406  * The callback function has the following prototype:
407  *
408  * ~~~{.cc}
409  * void callback(Vector<I, 2> pos, T value);
410  * // pos is the position of the neighbor
411  * // value is the value of the neighbor
412  * ~~~
413  *
414  * The callback function can be a simple function but also a
415  * [lambda expression](http://en.cppreference.com/w/cpp/language/lambda).
416  *
417  * @param pos The position
418  * @param func A callback function
419  */
420 
421 
422  template<typename Func>
423  void visit8Neighbors(Vector<I, 2> pos, Func func) const {
424  visitNeighborsSquare(pos, func, 1);
425  }
426 
427  /**
428  * @brief Visit the 24 neighbors of a given position
429  *
430  * This function calls a callback function for every neighbor in the
431  * vertical, horizontal and diagonal direction. The function checks if the
432  * neighbor actually exists.
433  *
434  * The callback function has the following prototype:
435  *
436  * ~~~{.cc}
437  * void callback(Vector<I, 2> pos, T value);
438  * // pos is the position of the neighbor
439  * // value is the value of the neighbor
440  * ~~~
441  *
442  * The callback function can be a simple function but also a
443  * [lambda expression](http://en.cppreference.com/w/cpp/language/lambda).
444  *
445  * @param pos The position
446  * @param func A callback function
447  */
448  template<typename Func>
449  void visit24Neighbors(Vector<I, 2> pos, Func func) {
450  visitNeighborsSquare(pos, func, 2);
451  }
452 
453  /**
454  * @brief Visit the 24 neighbors of a given position
455  *
456  * This function calls a callback function for every neighbor in the
457  * vertical, horizontal and diagonal direction. The function checks if the
458  * neighbor actually exists.
459  *
460  * The callback function has the following prototype:
461  *
462  * ~~~{.cc}
463  * void callback(Vector<I, 2> pos, T value);
464  * // pos is the position of the neighbor
465  * // value is the value of the neighbor
466  * ~~~
467  *
468  * The callback function can be a simple function but also a
469  * [lambda expression](http://en.cppreference.com/w/cpp/language/lambda).
470  *
471  * @param pos The position
472  * @param func A callback function
473  */
474  template<typename Func>
475  void visit24Neighbors(Vector<I, 2> pos, Func func) const {
476  visitNeighborsSquare(pos, func, 2);
477  }
478 
479  /** @} */
480 
481  /**
482  * @name Iterators and ranges
483  * @{
484  */
485 
486  /**
487  * @brief Get an iterator to the first element of the array
488  *
489  * @return A `begin` iterator to the array
490  * @sa end()
491  */
492  const T *begin() const noexcept {
493  return m_data.data();
494  }
495 
496  /**
497  * @brief Get an iterator to the element following the last element of the array
498  *
499  * @return An `end` iterator to the array
500  * @sa begin()
501  */
502  const T *end() const noexcept {
503  return m_data.data() + m_data.size();
504  }
505 
506  /**
507  * @brief Get an iterator to the first element of the array
508  *
509  * @return A `begin` iterator to the array
510  * @sa end()
511  */
512  T *begin() noexcept {
513  return m_data.data();
514  }
515 
516  /**
517  * @brief Get an iterator to the element following the last element of the array
518  *
519  * @return An `end` iterator to the array
520  * @sa begin()
521  */
522  T *end() noexcept {
523  return m_data.data() + m_data.size();
524  }
525 
526  /**
527  * @brief Get the 1D index range of the array
528  *
529  * @return A range with all the 1D index in the array
530  */
531  constexpr RangeZ getIndexRange() const noexcept {
532  return { 0, m_size.col * m_size.row };
533  }
534 
535  /**
536  * @brief Get the row range
537  *
538  * @return A range with all the rows
539  */
540  constexpr Range<I> getRowRange() const noexcept {
541  return { 0, m_size.row };
542  }
543 
544  /**
545  * @brief Get the column range
546  *
547  * @return A range with all the columns
548  */
549  constexpr Range<I> getColRange() const noexcept {
550  return { 0, m_size.col };
551  }
552 
553  /**
554  * @brief Get the position range
555  *
556  * @return A range for iterating among the positions
557  */
558  constexpr PositionRange<I> getPositionRange() const noexcept {
559  return { getColRange(), getRowRange() };
560  }
561 
562  /** @} */
563 
564  private:
565  T& get(Vector<I, 2> pos) {
566  return m_data[pos.row * m_size.col + pos.col];
567  }
568 
569  const T& get(Vector<I, 2> pos) const {
570  return m_data[pos.row * m_size.col + pos.col];
571  }
572 
573  template<typename Func>
574  void visitNeighborsSquare(Vector<I, 2> pos, Func func, I n) const {
575  assert(isValid(pos));
576 
577  auto colMin = pos.col - std::min(pos.col, n);
578  auto colMax = pos.col + std::min(m_size.col - pos.col - 1, n);
579  auto rowMin = pos.row - std::min(pos.row, n);
580  auto rowMax = pos.row + std::min(m_size.row - pos.row - 1, n);
581 
582  for (auto row = rowMin; row <= rowMax; ++row) {
583  for (auto col = colMin; col <= colMax; ++col) {
584  if (col == pos.col && row == pos.row) { // avoid to include VectorOps.h
585  continue;
586  }
587 
588  func({ col, row }, get({ col, row }));
589  }
590  }
591  }
592 
593  template<typename Func>
594  void visitNeighborsSquare(Vector<I, 2> pos, Func func, I n) {
595  assert(isValid(pos));
596 
597  auto colMin = pos.col - std::min(pos.col, n);
598  auto colMax = pos.col + std::min(m_size.col - pos.col - 1, n);
599  auto rowMin = pos.row - std::min(pos.row, n);
600  auto rowMax = pos.row + std::min(m_size.row - pos.row - 1, n);
601 
602  for (auto row = rowMin; row <= rowMax; ++row) {
603  for (auto col = colMin; col <= colMax; ++col) {
604  if (col == pos.col && row == pos.row) { // avoid to include VectorOps.h
605  continue;
606  }
607 
608  func({ col, row }, get({ col, row }));
609  }
610  }
611  }
612 
613 
614  template<typename Func>
615  void visitNeighborsDiamond(Vector<I, 2> pos, Func func, I n) const {
616  assert(isValid(pos));
617 
618  auto colMin = pos.col - std::min(pos.col, n);
619  auto colMax = pos.col + std::min(m_size.col - pos.col - 1, n);
620  auto rowMin = pos.row - std::min(pos.row, n);
621  auto rowMax = pos.row + std::min(m_size.row - pos.row - 1, n);
622 
623  for (auto row = rowMin; row <= rowMax; ++row) {
624  for (auto col = colMin; col <= colMax; ++col) {
625  if (col == pos.col && row == pos.row) { // avoid to include VectorOps.h
626  continue;
627  }
628 
629  if (gf::absdiff(col, pos.col) + gf::absdiff(row, pos.row) > n) {
630  continue;
631  }
632 
633  func({ col, row }, get({ col, row }));
634  }
635  }
636  }
637 
638  template<typename Func>
639  void visitNeighborsDiamond(Vector<I, 2> pos, Func func, I n) {
640  assert(isValid(pos));
641 
642  auto colMin = pos.col - std::min(pos.col, n);
643  auto colMax = pos.col + std::min(m_size.col - pos.col - 1, n);
644  auto rowMin = pos.row - std::min(pos.row, n);
645  auto rowMax = pos.row + std::min(m_size.row - pos.row - 1, n);
646 
647  for (auto row = rowMin; row <= rowMax; ++row) {
648  for (auto col = colMin; col <= colMax; ++col) {
649  if (col == pos.col && row == pos.row) { // avoid to include VectorOps.h
650  continue;
651  }
652 
653  if (gf::absdiff(col, pos.col) + gf::absdiff(row, pos.row) > n) {
654  continue;
655  }
656 
657  func({ col, row }, get({ col, row }));
658  }
659  }
660  }
661 
662  private:
663  Vector<I, 2> m_size;
664  std::vector<T> m_data;
665  };
666 
667 #ifndef DOXYGEN_SHOULD_SKIP_THIS
668 }
669 #endif
670 }
671 
672 #endif // ARRAY2D_H
A two-dimensional array.
Definition: Array2D.h:63
A half-open range of values.
Definition: Range.h:42
Array2D & operator=(const Array2D &)=default
Default copy assignment.
T * end() noexcept
Get an iterator to the element following the last element of the array.
Definition: Array2D.h:522
const T * begin() const noexcept
Get an iterator to the first element of the array.
Definition: Array2D.h:492
const T * getDataPtr() const noexcept
Get the pointer to raw data.
Definition: Array2D.h:144
std::size_t getDataSize() const noexcept
Get the raw data size.
Definition: Array2D.h:153
void swap(Array2D &other)
Swap with another array.
Definition: Array2D.h:126
Array2D(const Array2D &)=default
Default copy constructor.
Array2D(Array2D &&)=default
Default move constructor.
void visit4Neighbors(Vector< I, 2 > pos, Func func) const
Visit the 4 neighbors of a given position.
Definition: Array2D.h:317
constexpr Range< I > getRowRange() const noexcept
Get the row range.
Definition: Array2D.h:540
constexpr PositionRange< I > getPositionRange() const noexcept
Get the position range.
Definition: Array2D.h:558
void visit12Neighbors(Vector< I, 2 > pos, Func func) const
Visit the 12 neighbors of a given position.
Definition: Array2D.h:369
constexpr I getCols() const noexcept
Get the number of columns.
Definition: Array2D.h:171
constexpr Vector< I, 2 > toPosition(std::size_t pos) const noexcept
Transform a 1D position into a 2D position.
Definition: Array2D.h:258
void visit24Neighbors(Vector< I, 2 > pos, Func func) const
Visit the 24 neighbors of a given position.
Definition: Array2D.h:475
void visit8Neighbors(Vector< I, 2 > pos, Func func) const
Visit the 8 neighbors of a given position.
Definition: Array2D.h:423
The namespace for gf classes.
Definition: Action.h:34
Array2D()
Default constructor.
Definition: Array2D.h:70
Array2D(Vector< I, 2 > size)
Constructor with a size.
Definition: Array2D.h:81
constexpr bool isValid(Vector< I, 2 > pos) const noexcept
Check if a position is valid.
Definition: Array2D.h:203
void visit24Neighbors(Vector< I, 2 > pos, Func func)
Visit the 24 neighbors of a given position.
Definition: Array2D.h:449
T & operator()(Vector< I, 2 > pos)
Get the element at a given 2D position.
Definition: Array2D.h:219
Array2D & operator=(Array2D &&)=default
Default move assignement.
const T & operator()(std::size_t index) const
Get the element at a given 1D index.
Definition: Array2D.h:248
constexpr bool isEmpty() const noexcept
Check if the array is empty.
Definition: Array2D.h:192
constexpr I getRows() const noexcept
Get the number of rows.
Definition: Array2D.h:180
constexpr Vector< I, 2 > getSize() const noexcept
Get the size of the array.
Definition: Array2D.h:162
A 2D range.
Definition: Range.h:225
constexpr Range< I > getColRange() const noexcept
Get the column range.
Definition: Array2D.h:549
T * begin() noexcept
Get an iterator to the first element of the array.
Definition: Array2D.h:512
Array2D(Vector< I, 2 > size, const T &value)
Constructor with a size and a value.
Definition: Array2D.h:94
constexpr RangeZ getIndexRange() const noexcept
Get the 1D index range of the array.
Definition: Array2D.h:531
General purpose math vector.
Definition: Vector.h:60
T & operator()(std::size_t index)
Get the element at a given 1D index.
Definition: Array2D.h:229
const T & operator()(Vector< I, 2 > pos) const
Get the element at a given 2D position.
Definition: Array2D.h:238
void visit12Neighbors(Vector< I, 2 > pos, Func func)
Visit the 12 neighbors of a given position.
Definition: Array2D.h:343
void visit8Neighbors(Vector< I, 2 > pos, Func func)
Visit the 8 neighbors of a given position.
Definition: Array2D.h:395
void visit4Neighbors(Vector< I, 2 > pos, Func func)
Visit the 4 neighbors of a given position.
Definition: Array2D.h:291
const T * end() const noexcept
Get an iterator to the element following the last element of the array.
Definition: Array2D.h:502