# Struct Matrix

Square (NxN) matrix.

``` struct Matrix(T, ulong N) ; ```

Implementation notes:

- The storage order is column-major.

- Affine vector of 4x4 matrix is in the 4th column (as in OpenGL).

- Elements are stored in a fixed manner, so it is impossible to change matrix size once it's created.

- Actual data is allocated as a static array, so no references, no GC touching. When you pass a Matrix by value, it will be safely copied.

- This implementation is not perfect (as for now) for dealing with really big matrices, but ideal for smaller ones, e.g. those which are meant to be manipulated in real-time (in game engines, rendering pipelines etc).

## Constructors

NameDescription
`this` (arr) Create matrix from array. This is a convenient way to deal with arrays of "classic" layout: the storage order in an array should be row-major

## Fields

NameTypeDescription
`arrayof` `T[N*N]`Linear array representing elements column by column

## Properties

NameTypeDescription
`adjugate`[get] `Matrix!(T,N)`Adjugate and cofactor matrices
`cofactor`[get] `Matrix!(T,N)`Adjugate and cofactor matrices
`elements`[set] `string`Symbolic element access
`inverse`[get] `Matrix!(T,N)`Inverse of a matrix
`isAffine`[get] `bool`Check if matrix represents affine transformation
`isSingular`[get] `bool`Return true if matrix is singular
`negative`[get] `Matrix!(T,N)`Negative matrix
`toString`[get] `string`Convert to string
`transposed`[get] `Matrix!(T,N)`Return the transposed matrix

## Methods

NameDescription
`determinant` () Determinant (of upper-left 3x3 portion for 4x4 matrices)
`getRow` (i) Row/column manipulations
`identity` () Return identity matrix
`invert` () Invert
`invRotate` (v) Rotate a vector by the inverse 3x3 upper-left portion of the matrix
`opBinary` (mat) Matrix + Matrix
`opBinary` (mat) Matrix - Matrix
`opBinary` (mat) Matrix * Matrix
`opBinary` (k) Matrix * T
`opBinaryRight` (v) Multiply column vector by the matrix
`opBinaryRight` (v) Multiply column 3D vector by the affine 4x4 matrix
`opEquals` (that) Compare two matrices.
`opIndex` (i, j) T = Matrix[i, j]
`opIndex` (index) T = Matrix[index] Indices start with 0
`opIndexAssign` (t, i, j) Matrix[i, j] = T
`opIndexAssign` (t, index) Matrix[index] = T Indices start with 0
`opOpAssign` (mat) Matrix += Matrix
`opOpAssign` (mat) Matrix -= Matrix
`opOpAssign` (mat) Matrix *= Matrix
`opOpAssign` (k) Matrix *= T
`opSliceAssign` (t, index1, index2) Matrix4x4!(T)[index1..index2] = T
`opSliceAssign` (t) Matrix[] = T
`rotate` (v) Rotate a vector by the 3x3 upper-left portion of the matrix
`setIdentity` () Set to identity
`transpose` () Transpose
`zero` () Return zero matrix

## Aliases

NameDescription
`affine` Check if matrix represents affine transformation

## Example

``````auto m1 = matrixf(
1, 2, 0, 6,
4, 6, 3, 1,
2, 7, 8, 2,
0, 5, 2, 1
);
auto m2 = matrixf(
0, 3, 7, 1,
1, 0, 2, 5,
1, 9, 2, 6,
5, 2, 0, 0
);
assert(m1 * m2 == matrixf(
32, 15, 11, 11,
14, 41, 46, 52,
25, 82, 44, 85,
12, 20, 14, 37)
);

auto m3 = Matrix4f.identity;
assert(m3 == matrixf(
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1)
);

m3[12] = 1;
m3.a24 = 2;
m3.a34 = 3;
m3[1..4] = 0;

assert(m3[12] == 1);

assert(m1.determinant3x3 == -25);
assert(m1.determinant == 567);

assert(m1.singular == false);

assert(m1.affine == false);

assert(m1.transposed == matrixf(
1, 4, 2, 0,
2, 6, 7, 5,
0, 3, 8, 2,
6, 1, 2, 1)
);

auto m4 = matrixf(
0, 3, 2,
1, 0, 8,
0, 1, 0
);

assert(m4.inverse == matrixf(
-4,   1, 12,
-0,   0,  1,
0.5, 0, -1.5)
);

7, 148, -16, -158,
-14,  28, -49,  154,
-14, -53, 113,  -89,
98, -34,  19,  -25)
);

assert(m1.cofactor == matrixf(
7, -14, -14,  98,
148,  28, -53, -34,
-16, -49, 113,  19,
-158, 154, -89, -25)
);

m1.transpose();
assert(m1 == matrixf(
1, 4, 2, 0,
2, 6, 7, 5,
0, 3, 8, 2,
6, 1, 2, 1)
);

Matrix2f m5;
m5[] = 1.0f;
m5 += matrixf(
2, 2,
2, 2
);
m5 *= m5;
assert(m5 == matrixf(
18, 18,
18, 18)
);

m5 = m5 - m5;
assert(m5 == Matrix2f.zero);

Matrix2f m6 = matrixf(
2, 2,
2, 2
);
m6 = m6 * m6;
m6 = m6 * 2;
m6 *= 2;
assert(m6 == matrixf(
32, 32,
32, 32)
);
assert(m6.determinant == 0);

Matrix3f m7 = matrixf(
3, 3, 3,
3, 3, 3,
3, 3, 3
);
m7 = m7 * m7;
assert(m7 == matrixf(
27, 27, 27,
27, 27, 27,
27, 27, 27)
);

Matrix2f m8 = matrixf(
1, 0,
0, 1
);
m8.invert();
assert(m8 == matrixf(
1, 0,
0, 1)
);
assert(m8.negative == matrixf(
-1,  0,
0, -1)
);

auto m9 = matrixf(
1, 0, 0, 2,
0, 1, 0, 3,
0, 0, 1, 4,
0, 0, 0, 1
);
assert(m9.affine == true);
assert(m9.inverse == matrixf(
1, 0, 0, -2,
0, 1, 0, -3,
0, 0, 1, -4,
0, 0, 0,  1)
);

bool isAlmostZero3(Vector3f v)
{
float e = 0.002f;
return abs(v.x) < e &&
abs(v.y) < e &&
abs(v.z) < e;
}

Vector3f v1 = Vector3f(1, 0, 0);
v1 = v1 * matrixf(
1, 0, 0, 2,
0, 1, 0, 3,
0, 0, 1, 4,
0, 0, 0, 1
);
assert(v1 == Vector3f(3, 3, 4));

Vector3f v2 = Vector3f(0, 1, 0);
const float a1 = PI * 0.5f;
v2 = matrixf(
1, 0,        0,       0,
0, cos(a1), -sin(a1), 0,
0, sin(a1),  cos(a1), 0,
0, 0,        0,       1
).rotate(v2);
assert(isAlmostZero3(v2 - Vector3f(0, 0, 1)));

Vector3f v3 = Vector3f(0, 1, 0);
v3 = matrixf(
1, 0,        0,       0,
0, cos(a1), -sin(a1), 0,
0, sin(a1),  cos(a1), 0,
0, 0,        0,       1
).invRotate(v3);
assert(isAlmostZero3(v3 - Vector3f(0, 0, -1)));

auto m10 = matrixf(
1, 2, 3,
3, 2, 1,
2, 3, 1
);

Vector3f r0 = m10.getRow(0);
assert(isAlmostZero3(r0 - Vector3f(1, 2, 3)));

Vector3f c0 = m10.getColumn(0);
assert(isAlmostZero3(c0 - Vector3f(1, 3, 2)));

m10.setRow(2, Vector3f(1, 1, 1));
Vector3f r2 = m10.getRow(2);
assert(isAlmostZero3(r2 - Vector3f(1, 1, 1)));

m10.setColumn(2, Vector3f(1, 1, 1));
Vector3f c2 = m10.getColumn(2);
assert(isAlmostZero3(c2 - Vector3f(1, 1, 1)));

m10.swapRows(0, 1);
Vector3f r1 = m10.getRow(1);
assert(isAlmostZero3(r1 - Vector3f(1, 2, 1)));

m10.swapColumns(1, 2);
Vector3f c1 = m10.getColumn(1);
assert(isAlmostZero3(c1 - Vector3f(1, 1, 1)));

Matrix2f m11 = matrixf(
2, 1,
2, 1
);
1, -1,
-2,  2)
);
assert(m11.cofactor == matrixf(
1, -2,
-1,  2)
);
assert(m11.flatten == [2, 1, 2, 1]);

assert(m11.elements("a") == "T a11;T a21;T a12;T a22;");

Matrix!(float, 1) m12 = matrixf(1);
assert(m12.determinant == 1);
assert(m12.inverse == matrixf(1));