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] stringSymbolic element access
inverse[get] Matrix!(T,N)Inverse of a matrix
isAffine[get] boolCheck if matrix represents affine transformation
isSingular[get] boolReturn true if matrix is singular
negative[get] Matrix!(T,N)Negative matrix
toString[get] stringConvert 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)
);

assert(m1.adjugate == matrixf(
    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
);
assert(m11.adjugate == matrixf(
    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));
assert(m12.adjugate == matrixf(1));
assert(m12.cofactor == matrixf(1));