An array is a function from a rectangular subset of onto an arbitrary set.
Pastel implements the Array
class template for storing and manipulating
homogeneous-type multi-dimensional arrays with dynamic extents.
Array
is a class template defined as:
template <typename Type, integer N = 2>
class Array;
The first template parameter Type
is the type of the elements that are stored
in the array. The second template parameter N
is the dimensionality
of the array. For example, N == 1
corresponds roughly to an std::vector
, while
N == 2
corresponds to a matrix of values. N == Dynamic
can be used for an
array whose dimensionality can be set at run-time.
The elements of an Array
are accessed using n-dimensional Vectors with
integer elements. For example:
// Create a 2d-array of extents (3, 3) and fill with zeros.
Array<float> a(Vector2i(3, 3), 0);
// Set the element (0, 2) to 2.
a(Vector2i(0, 2)) = 2;
// Alternatively for 2d-arrays the same can be achieved by:
b(0, 2) = 2;
It is possible to consider a sub-array of an array and read from and write to it. This is done by specifying an integer rectangle on the element coordinates by its minimum and maximum coordinates. For example:
// Create a 2d-array of size 100 x 100, filled with zeros.
Array<float> b(Vector2i(100, 100), 0);
// Consider the lower-left corner of 'b'.
SubArray<float> bSub = b(Vector2i(0, 0), Vector2i(50, 50));
// Fill the lower-left corner with ones.
std::fill(bSub.begin(), bSub.end(), 1);
In the following we select from the lower-left corner of ‘b’ every 2nd in the horizontal direction, and every 3rd in the vertical direction, and fill that sub-array with twos.
// Consider the lower-left corner of 'b' with sparseness.
SubArray<float> sbSub = b(Vector2i(0, 0), Vector2i(50, 50), Vector2i(2, 3));
// Fill the lower-left corner sparsily with twos.
std::fill(sbSub.begin(), sbSub.end(), 2);
The ‘Array’ supports some special member functions which might be more convenient to use when the dimension is 3 or lower and genericy is not needed.
There are additional member functions width(), height(), and depth(),
available from dimensionalities up from 1, 2, and 3, respectively.
These refer to the first, second, and third extents of the Array
.
An element can be accessed by a list of n coordinates, rather than by a Vector of dimension n. Similarly for cursors.
While Array
is a multi-dimensional data structure, it is still sometimes
useful to traverse its elements sequentially. The elements in this sequence
can be accessed through an iterator interface, or alternatively by specifying
an index. Specifically, this allows using the Standard Library algorithms on
the elements. The ordering of the sequence depends on the storage order.
The elements of an Array
are stored sequentially in memory by a
lexicographical ordering of their coordinates. There are two ways to
order the elements: either row-major, or column-major.
In row-major ordering the elements are listed in lexicographical order of their coordinates, where the first coordinate is dominant, and for k > 1, the (k - 1):th coordinate is more dominant than the k:th coordinate (in 2d, column-by-column, row-by-row).
In column-major ordering the elements are listed in lexicographical order of their coordinates, where the last coordinate is dominant, and for k < n, the (k + 1):th coordinate is more dominant than the k:th coordinate (in 2d, row-by-row, column-by-column).
An Array
can reuse an existing memory region for its elements.
This is called aliasing. In this case Array
does not release the aliased
memory region when it is no longer needed. Aliasing is especially
useful when interfacing with another software: then the data need not
be copied just to be able manipulate it through an Array
class.
When aliasing, it is important to keep in mind the storage ordering
of Pastel and the storage ordering of the aliased memory region.
For example, Matlab uses column-major storage order.
Resizing changes the extents of an Array
. In this process, those coordinates
which are inside the new extents preserve their value, and the possible
newly created elements are assigned a given value. For example,
a 2-dimensional integer array with extents (3, 4)
[1, 2, 3]
[4, 5, 6]
[7, 8, 9]
[10, 11, 12]
can be resized into a 2-dimensional array with extents (6, 2) and assigning new elements zero value:
[1, 2, 3, 0, 0, 0]
[4, 5, 6, 0, 0, 0]
Reshaping is the reinterpretation of the elements currently in the array as an array with different extents. Reshaping requires that the old extents and the new extents cover the same amount of elements. No memory is allocated or deallocated in this process. For example, a 2-dimensional integer array with extents (3, 4)
[1, 2, 3]
[4, 5, 6]
[7, 8, 9]
[10, 11, 12]
can be reshaped into a 2-dimensional array with extents (6, 2):
[1, 2, 3, 4, 5, 6]
[7, 8, 9, 10, 11, 12]
The result of the reshaping depends on the storage order. In the example above we assumed row-major storage order. If the storage order were column-major instead, then the result would be
[1, 7, 2, 8, 3, 9]
[4, 10, 5, 11, 6, 12]
An n-dimensional array