math: make matrix init visually match scientific notation

Signed-off-by: Stephen Gutekanst <stephen@hexops.com>
This commit is contained in:
Stephen Gutekanst 2023-09-08 20:18:39 -07:00
parent 8fd84a6bda
commit de90bb6c12

View file

@ -11,6 +11,22 @@ pub fn Mat(
comptime Vector: type, comptime Vector: type,
) type { ) type {
return extern struct { return extern struct {
/// The column vectors of the matrix.
///
/// Mach matrices use [column-major storage and column-vectors](https://machengine.org/engine/math/matrix-storage/).
/// The translation vector is stored in contiguous memory elements 12, 13, 14:
///
/// ```
/// [4]Vec4{
/// vec4( 1, 0, 0, 0),
/// vec4( 0, 1, 0, 0),
/// vec4( 0, 0, 1, 0),
/// vec4(tx, ty, tz, tw),
/// }
/// ```
///
/// Use the init() constructor to write code which visually matches the same layout as you'd
/// see used in scientific / maths communities.
v: [cols]Vec, v: [cols]Vec,
/// The number of columns, e.g. Mat3x4.cols == 3 /// The number of columns, e.g. Mat3x4.cols == 3
@ -48,104 +64,142 @@ pub fn Mat(
pub usingnamespace switch (Matrix) { pub usingnamespace switch (Matrix) {
inline math.Mat3x3, math.Mat3x3h, math.Mat3x3d => struct { inline math.Mat3x3, math.Mat3x3h, math.Mat3x3d => struct {
pub inline fn init( /// Constructs a 3x3 matrix with the given rows. For example to write a translation
col0: RowVec, /// matrix like in the left part of this equation:
col1: RowVec, ///
col2: RowVec, /// ```
) Matrix { /// |1 0 tx| |x | |x+z*tx|
/// |0 1 ty| |y | = |y+z*ty|
/// |0 0 tz| |z=1| |tz |
/// ```
///
/// You would write it with the same visual layout:
///
/// ```
/// const m = Mat3x3.init(
/// vec4(1, 0, tx),
/// vec4(0, 1, ty),
/// vec4(0, 0, tz),
/// );
/// ```
///
/// Note that Mach matrices use [column-major storage and column-vectors](https://machengine.org/engine/math/matrix-storage/).
pub inline fn init(r0: RowVec, r1: RowVec, r2: RowVec) Matrix {
return .{ .v = [_]Vec{ return .{ .v = [_]Vec{
Vec.init(col0.x(), col0.y(), col0.z(), 1), Vec.init(r0.x(), r1.x(), r2.x(), 1),
Vec.init(col1.x(), col1.y(), col1.z(), 1), Vec.init(r0.y(), r1.y(), r2.y(), 1),
Vec.init(col2.x(), col2.y(), col2.z(), 1), Vec.init(r0.z(), r1.z(), r2.z(), 1),
} }; } };
} }
/// Constructs a 2D matrix which scales each dimension by the given vector. /// Constructs a 2D matrix which scales each dimension by the given vector.
// TODO: needs tests // TODO: needs tests
pub inline fn scale(v: math.Vec2) Matrix { pub inline fn scale(s: math.Vec2) Matrix {
return init( return init(
RowVec.init(v.x(), 0, 0, 1), RowVec.init(s.x(), 0, 0),
RowVec.init(0, v.y(), 0, 1), RowVec.init(0, s.y(), 0),
RowVec.init(0, 0, 1, 1), RowVec.init(0, 0, 1),
); );
} }
/// Constructs a 2D matrix which scales each dimension by the given scalar. /// Constructs a 2D matrix which scales each dimension by the given scalar.
// TODO: needs tests // TODO: needs tests
pub inline fn scaleScalar(scalar: Vec.T) Matrix { pub inline fn scaleScalar(t: Vec.T) Matrix {
return scale(Vec.splat(scalar)); return scale(Vec.splat(t));
} }
/// Constructs a 2D matrix which translates coordinates by the given vector. /// Constructs a 2D matrix which translates coordinates by the given vector.
// TODO: needs tests // TODO: needs tests
pub inline fn translate(v: math.Vec2) Matrix { pub inline fn translate(t: math.Vec2) Matrix {
return init( return init(
RowVec.init(1, 0, 0), RowVec.init(1, 0, t.x()),
RowVec.init(0, 1, 0), RowVec.init(0, 1, t.y()),
RowVec.init(v.x(), v.y(), 1), RowVec.init(0, 0, 1),
); );
} }
/// Constructs a 2D matrix which translates coordinates by the given scalar. /// Constructs a 2D matrix which translates coordinates by the given scalar.
// TODO: needs tests // TODO: needs tests
pub inline fn translateScalar(scalar: Vec.T) Matrix { pub inline fn translateScalar(t: Vec.T) Matrix {
return translate(Vec.splat(scalar)); return translate(Vec.splat(t));
} }
/// Returns the translation component of the matrix. /// Returns the translation component of the matrix.
// TODO: needs tests // TODO: needs tests
pub inline fn translation(v: Matrix) math.Vec2 { pub inline fn translation(t: Matrix) math.Vec2 {
return math.Vec2.init(v.v[2].x(), v.v[2].y()); return math.Vec2.init(t.v[2].x(), t.v[2].y());
} }
}, },
inline math.Mat4x4, math.Mat4x4h, math.Mat4x4d => struct { inline math.Mat4x4, math.Mat4x4h, math.Mat4x4d => struct {
pub inline fn init(col0: Vec, col1: Vec, col2: Vec, col3: Vec) Matrix { /// Constructs a 4x4 matrix with the given rows. For example to write a translation
return .{ .v = [_]RowVec{ /// matrix like in the left part of this equation:
col0, ///
col1, /// ```
col2, /// |1 0 0 tx| |x | |x+w*tx|
col3, /// |0 1 0 ty| |y | = |y+w*ty|
/// |0 0 1 tz| |z | |z+w*tz|
/// |0 0 0 tw| |w=1| |tw |
/// ```
///
/// You would write it with the same visual layout:
///
/// ```
/// const m = Mat4x4.init(
/// vec4(1, 0, 0, tx),
/// vec4(0, 1, 0, ty),
/// vec4(0, 0, 1, tz),
/// vec4(0, 0, 0, tw),
/// );
/// ```
///
/// Note that Mach matrices use [column-major storage and column-vectors](https://machengine.org/engine/math/matrix-storage/).
pub inline fn init(r0: Vec, r1: RowVec, r2: RowVec, r3: RowVec) Matrix {
return .{ .v = [_]Vec{
Vec.init(r0.x(), r1.x(), r2.x(), r3.x()),
Vec.init(r0.y(), r1.y(), r2.y(), r3.y()),
Vec.init(r0.z(), r1.z(), r2.z(), r3.z()),
Vec.init(r0.w(), r1.w(), r2.w(), r3.w()),
} }; } };
} }
/// Constructs a 3D matrix which scales each dimension by the given vector. /// Constructs a 3D matrix which scales each dimension by the given vector.
// TODO: needs tests // TODO: needs tests
pub inline fn scale(v: math.Vec3) Matrix { pub inline fn scale(s: math.Vec3) Matrix {
return init( return init(
RowVec.init(v.x(), 0, 0, 0), Vec.init(s.x(), 0, 0, 0),
RowVec.init(0, v.y(), 0, 0), Vec.init(0, s.y(), 0, 0),
RowVec.init(0, 0, v.z(), 0), Vec.init(0, 0, s.z(), 0),
RowVec.init(0, 0, 0, 1), Vec.init(0, 0, 0, 1),
); );
} }
/// Constructs a 3D matrix which scales each dimension by the given scalar. /// Constructs a 3D matrix which scales each dimension by the given scalar.
// TODO: needs tests // TODO: needs tests
pub inline fn scaleScalar(scalar: Vec.T) Matrix { pub inline fn scaleScalar(s: Vec.T) Matrix {
return scale(Vec.splat(scalar)); return scale(Vec.splat(s));
} }
/// Constructs a 3D matrix which translates coordinates by the given vector. /// Constructs a 3D matrix which translates coordinates by the given vector.
// TODO: needs tests // TODO: needs tests
pub inline fn translate(v: math.Vec3) Matrix { pub inline fn translate(t: math.Vec3) Matrix {
return init( return init(
RowVec.init(1, 0, 0, 0), RowVec.init(1, 0, 0, t.x()),
RowVec.init(0, 1, 0, 0), RowVec.init(0, 1, 0, t.y()),
RowVec.init(0, 0, 1, 0), RowVec.init(0, 0, 1, t.z()),
RowVec.init(v.x(), v.y(), v.z(), 1), RowVec.init(0, 0, 0, 1),
); );
} }
/// Constructs a 3D matrix which translates coordinates by the given scalar. /// Constructs a 3D matrix which translates coordinates by the given scalar.
// TODO: needs tests // TODO: needs tests
pub inline fn translateScalar(scalar: Vec.T) Matrix { pub inline fn translateScalar(t: Vec.T) Matrix {
return translate(Vec.splat(scalar)); return translate(Vec.splat(t));
} }
/// Returns the translation component of the matrix. /// Returns the translation component of the matrix.
// TODO: needs tests // TODO: needs tests
pub inline fn translation(v: Matrix) math.Vec3 { pub inline fn translation(t: Matrix) math.Vec3 {
return math.Vec3.init(v.v[3].x(), v.v[3].y(), v.v[3].z()); return math.Vec3.init(t.v[3].x(), t.v[3].y(), t.v[3].z());
} }
/// Constructs an orthographic projection matrix; an orthogonal transformation matrix /// Constructs an orthographic projection matrix; an orthogonal transformation matrix
@ -174,10 +228,10 @@ pub fn Mat(
const ty = (top + bottom) / (bottom - top); const ty = (top + bottom) / (bottom - top);
const tz = near / (near - far); const tz = near / (near - far);
return init( return init(
RowVec.init(xx, 0, 0, 0), RowVec.init(xx, 0, 0, tx),
RowVec.init(0, yy, 0, 0), RowVec.init(0, yy, 0, ty),
RowVec.init(0, 0, zz, 0), RowVec.init(0, 0, zz, tz),
RowVec.init(tx, ty, tz, 1), RowVec.init(0, 0, 0, 1),
); );
} }
}, },
@ -381,14 +435,14 @@ test "n" {
test "init" { test "init" {
try testing.expect(math.Mat3x3, math.mat3x3( try testing.expect(math.Mat3x3, math.mat3x3(
math.vec3(1, 2, 3), math.vec3(1, 0, 1337),
math.vec3(4, 5, 6), math.vec3(0, 1, 7331),
math.vec3(7, 8, 9), math.vec3(0, 0, 1),
)).eql(math.Mat3x3{ )).eql(math.Mat3x3{
.v = [_]math.Vec4{ .v = [_]math.Vec4{
math.Vec4.init(1, 2, 3, 1), math.Vec4.init(1, 0, 0, 1),
math.Vec4.init(4, 5, 6, 1), math.Vec4.init(0, 1, 0, 1),
math.Vec4.init(7, 8, 9, 1), math.Vec4.init(1337, 7331, 1, 1),
}, },
}); });
} }