Reference

std/linear/matrix4

std/linear/src/matrix4.trb

Matrix4, the 4x4 matrix: the affine transformation of space - rotation, scale, shear and a translation - that a scene graph is built out of.

The fields are the columns, and a column is where a basis vector lands; the fourth column is the translation. What is deliberately absent is a projection: a projection matrix depends on the clip space of the API that consumes it - how deep it is and which way it points - and this package knows nothing about an API. Those belong where that convention is known.

type Matrix4

type Matrix4<Scalar: Numeric = Float> with Add, Subtract, Multiply

An affine transformation of space, held as the four vectors its homogeneous basis lands on.

Vectors are columns and a transformation is applied on the left, so composing a after b is a * b and a chain reads right to left, the way it does in mathematics.

Examples

const move = Matrix4.translation of: Vector3(1.0, 2.0, 3.0)
print move.transformedPoint(Vector3(0.0, 0.0, 0.0))

Related

  • Matrix3 - the linear part on its own, and the affine form for the plane.
  • Quaternion - a rotation that interpolates without shearing.

field xAxis

xAxis: Vector4<Scalar>

Where the first basis vector lands.

field yAxis

yAxis: Vector4<Scalar>

Where the second basis vector lands.

field zAxis

zAxis: Vector4<Scalar>

Where the third basis vector lands.

field wAxis

wAxis: Vector4<Scalar>

Where the origin lands: the translation, with a one in its fourth component.

const identity

static identity: Matrix4<Scalar>

The transformation that changes nothing, over whichever scalar is asked for: Matrix4<Int>.identity.

fn linearPart

fn linearPart(): Matrix3<Scalar>

The three rows and columns that are the linear part: the rotation, the scale and the shear, without the move.

fn translationPart

fn translationPart(): Vector3<Scalar>

The translation the transformation carries.

fn transposed

fn transposed(): Matrix4<Scalar>

The rows read as columns.

fn column

fn column(index: Int): Vector4<Scalar>

The column at that index. Panics outside 0..4.

fn at

fn at(row: Int, index: Int): Scalar

The cell in that row and that column. Panics outside 0..4.

fn row

fn row(index: Int): Vector4<Scalar>

The row at that index. Panics outside 0..4.

fn add

fn add(other: Matrix4<Scalar>): Matrix4<Scalar>

Cell by cell. Adding two transformations is not composing them; multiply is.

fn subtract

fn subtract(other: Matrix4<Scalar>): Matrix4<Scalar>

Cell by cell.

fn multiply

fn multiply(other: Matrix4<Scalar>): Matrix4<Scalar>

The composition: self after other.

fn applied

fn applied(to: Vector4<Scalar>): Vector4<Scalar>

The homogeneous vector transformed: the combination of the four columns.

It is a method and not matrix * vector, because a type has one namespace of members and multiply is already the composition of two matrices.

fn transformedDirection

fn transformedDirection(direction: Vector3<Scalar>): Vector3<Scalar>

A direction, turned by the transformation: the translation is not applied.

fn determinant

fn determinant(): Scalar

The factor by which the transformation scales a volume of the homogeneous space, negative where it turns space inside out and zero exactly where it flattens one - which is where Matrix4.inverse answers None.

print Matrix4.scaling(by: Vector3(2.0, 3.0, 4.0)).determinant()

fn scaling

static fn scaling(by: Vector3<Scalar>): Matrix4<Scalar>

A diagonal matrix: each axis scaled on its own, and the translation left at the origin.

fn translation

static fn translation(of: Vector3<Scalar>): Matrix4<Scalar>

The transformation that moves everything by that much and changes nothing else.

const move = Matrix4.translation of: Vector3(1.0, 0.0, 0.0)
print move.translationPart()

fn affine

static fn affine(linear: Matrix3<Scalar>, by: Vector3<Scalar>): Matrix4<Scalar>

The affine transformation that applies the linear part and then moves by by.

fn transformedPoint

fn transformedPoint(point: Vector3<Scalar>): Vector3<Scalar>

A point, moved by the transformation: the translation is applied.

extend Matrix4<Scalar> with Negate

extend<Scalar: Signed> Matrix4<Scalar> with Negate

Every cell turned around.

fn negate

fn negate(): Matrix4<Scalar>

Every cell with its sign flipped.

extend Matrix4<Scalar>

extend<Scalar: Real> Matrix4<Scalar>

What a division buys: the inverse of an affine transformation, and the general one.

fn inverseAffine

fn inverseAffine(): Matrix4<Scalar>?

The transformation that undoes this one, read as an affine transformation: the linear part is inverted and the translation is carried back through it. None where the linear part has no inverse.

It answers nothing about the bottom row, which an affine transformation has as (0, 0, 0, 1) by construction.

fn inverse

fn inverse(): Matrix4<Scalar>?

The transformation that undoes this one, for a matrix of any shape: the adjugate - the transpose of the sixteen cofactors - divided by the determinant. None where the determinant is zero, which is exactly where the transformation flattens space and nothing can undo it.

Matrix4.inverseAffine is the short way for the matrices a scene graph is made of, and the two answer the same transformation for one whose bottom row is (0, 0, 0, 1). Reach for this one where the bottom row is not that - which in practice means a projection.

const move = Matrix4.translation of: Vector3(1.0, 2.0, 3.0)
print move.inverse()?.translationPart()

fn isCloseTo

fn isCloseTo(other: Matrix4<Scalar>, tolerance: Scalar): Bool

Whether every cell is within tolerance of the other matrix's.

extend Matrix4<Scalar> with Power<Int64>

extend<Scalar: Numeric> Matrix4<Scalar> with Power<Int64>

A whole power: the transformation applied that many times over.

fn power

fn power(exponent: Int64): Matrix4<Scalar>

turn ** 3 is turn * turn * turn, by squaring, and matrix ** 0 is the identity.

Panics

On a negative exponent, which is a power of the inverse: a matrix has one only over a Real scalar and only where its determinant is not zero, so the caller asks for inverse() and raises that.