math: begin rewrite of mach.math
Signed-off-by: Stephen Gutekanst <stephen@hexops.com>
This commit is contained in:
parent
6b5a9990b9
commit
5b25db1025
7 changed files with 1449 additions and 1192 deletions
|
|
@ -13,7 +13,7 @@ pub const gfx = @import("gfx/util.zig");
|
||||||
pub const gfx2d = struct {
|
pub const gfx2d = struct {
|
||||||
pub const Sprite2D = @import("gfx2d/Sprite2D.zig");
|
pub const Sprite2D = @import("gfx2d/Sprite2D.zig");
|
||||||
};
|
};
|
||||||
pub const math = @import("math.zig");
|
pub const math = @import("math/main.zig");
|
||||||
pub const testing = @import("testing.zig");
|
pub const testing = @import("testing.zig");
|
||||||
|
|
||||||
pub const Atlas = @import("atlas/Atlas.zig");
|
pub const Atlas = @import("atlas/Atlas.zig");
|
||||||
|
|
|
||||||
1191
src/math.zig
1191
src/math.zig
File diff suppressed because it is too large
Load diff
126
src/math/main.zig
Normal file
126
src/math/main.zig
Normal file
|
|
@ -0,0 +1,126 @@
|
||||||
|
//! # mach/math is opinionated
|
||||||
|
//!
|
||||||
|
//! Math is hard enough as-is, without you having to question ground truths while problem solving.
|
||||||
|
//! As a result, mach/math takes a more opinionated approach than some other math libraries: we try
|
||||||
|
//! to encourage you through API design to use what we believe to be the best choices. For example,
|
||||||
|
//! other math libraries provide both LH and RH (left-handed and right-handed) variants for each
|
||||||
|
//! operation, and they sit on equal footing for you to choose from; mach/math may also provide both
|
||||||
|
//! variants as needed for conversions, but unlike other libraries will bless one choice with e.g.
|
||||||
|
//! a shorter function name to nudge you in the right direction and towards _consistency_.
|
||||||
|
//!
|
||||||
|
//! ## Matrices
|
||||||
|
//!
|
||||||
|
//! * Column-major matrix storage
|
||||||
|
//! * Column-vectors (i.e. right-associative multiplication, matrix * vector = vector)
|
||||||
|
//!
|
||||||
|
//! The benefit of using this "OpenGL-style" matrix is that it matches the conventions accepted by
|
||||||
|
//! the scientific community, it's what you'll find in linear algebra textbooks. It also matches
|
||||||
|
//! WebGPU, Vulkan, Unity3D, etc. It does NOT match DirectX-style which e.g. Unreal Engine uses.
|
||||||
|
//!
|
||||||
|
//! Note: many people will say "row major" or "column major" and implicitly mean three or more
|
||||||
|
//! different concepts; to avoid confusion we'll go over this in more depth below.
|
||||||
|
//!
|
||||||
|
//! ## Coordinate system (+Y up, left-handed)
|
||||||
|
//!
|
||||||
|
//! * Normalized Device coordinates: +Y up; (-1, -1) is at the bottom-left corner.
|
||||||
|
//! * Framebuffer coordinates: +Y down; (0, 0) is at the top-left corner.
|
||||||
|
//! * Texture coordinates: +Y down; (0, 0) is at the top-left corner.
|
||||||
|
//!
|
||||||
|
//! This coordinate system is consistent with WebGPU, Metal, DirectX, and Unity (NDC only.)
|
||||||
|
//!
|
||||||
|
//! Note that since +Y is up (not +Z), developers can seamlessly transition from 2D applications
|
||||||
|
//! to 3D applications by adding the Z component. This is in contrast to e.g. Z-up coordinate
|
||||||
|
//! systems, where 2D and 3D must differ.
|
||||||
|
//!
|
||||||
|
//! ## Additional reading
|
||||||
|
//!
|
||||||
|
//! * [Coordinate system explainer](https://machengine.org/engine/math/coordinate-system/)
|
||||||
|
//! * [Matrix storage explainer](https://machengine.org/engine/math/matrix-storage/)
|
||||||
|
//!
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const testing = std.testing;
|
||||||
|
|
||||||
|
const vec = @import("vec.zig");
|
||||||
|
const mat = @import("mat.zig");
|
||||||
|
const q = @import("quat.zig");
|
||||||
|
|
||||||
|
/// Standard f32 precision types
|
||||||
|
pub const Vec2 = vec.Vec(2, f32);
|
||||||
|
pub const Vec3 = vec.Vec(3, f32);
|
||||||
|
pub const Vec4 = vec.Vec(4, f32);
|
||||||
|
pub const Quat = q.Quat(f32);
|
||||||
|
pub const Mat3x3 = mat.Mat(3, 3, Vec4);
|
||||||
|
pub const Mat4x4 = mat.Mat(4, 4, Vec4);
|
||||||
|
|
||||||
|
/// Half-precision f16 types
|
||||||
|
pub const Vec2h = vec.Vec(2, f16);
|
||||||
|
pub const Vec3h = vec.Vec(3, f16);
|
||||||
|
pub const Vec4h = vec.Vec(4, f16);
|
||||||
|
pub const Quath = q.Quat(f16);
|
||||||
|
pub const Mat3x3h = mat.Mat(3, 3, Vec4h);
|
||||||
|
pub const Mat4x4h = mat.Mat(4, 4, Vec4h);
|
||||||
|
|
||||||
|
/// Double-precision f64 types
|
||||||
|
pub const Vec2d = vec.Vec(2, f64);
|
||||||
|
pub const Vec3d = vec.Vec(3, f64);
|
||||||
|
pub const Vec4d = vec.Vec(4, f64);
|
||||||
|
pub const Quatd = q.Quat(f64);
|
||||||
|
pub const Mat3x3d = mat.Mat(3, 3, Vec4d);
|
||||||
|
pub const Mat4x4d = mat.Mat(4, 4, Vec4d);
|
||||||
|
|
||||||
|
/// Standard f32 precision initializers
|
||||||
|
pub const vec2 = Vec2.init;
|
||||||
|
pub const vec3 = Vec3.init;
|
||||||
|
pub const vec4 = Vec4.init;
|
||||||
|
pub const quat = Quat.init;
|
||||||
|
pub const mat3x3 = Mat3x3.init;
|
||||||
|
pub const mat4x4 = Mat4x4.init;
|
||||||
|
|
||||||
|
/// Half-precision f16 initializers
|
||||||
|
pub const vec2h = Vec2h.init;
|
||||||
|
pub const vec3h = Vec3h.init;
|
||||||
|
pub const vec4h = Vec4h.init;
|
||||||
|
pub const quath = Quath.init;
|
||||||
|
pub const mat3x3h = Mat3x3h.init;
|
||||||
|
pub const mat4x4h = Mat4x4h.init;
|
||||||
|
|
||||||
|
/// Double-precision f64 initializers
|
||||||
|
pub const vec2d = Vec2d.init;
|
||||||
|
pub const vec3d = Vec3d.init;
|
||||||
|
pub const vec4d = Vec4d.init;
|
||||||
|
pub const quatd = Quatd.init;
|
||||||
|
pub const mat3x3d = Mat3x3d.init;
|
||||||
|
pub const mat4x4d = Mat4x4d.init;
|
||||||
|
|
||||||
|
test {
|
||||||
|
testing.refAllDeclsRecursive(@This());
|
||||||
|
}
|
||||||
|
|
||||||
|
// std.math customizations
|
||||||
|
pub const eql = std.math.approxEqAbs;
|
||||||
|
pub const eps = std.math.floatEps;
|
||||||
|
pub const eps_f16 = std.math.floatEps(f16);
|
||||||
|
pub const eps_f32 = std.math.floatEps(f32);
|
||||||
|
pub const eps_f64 = std.math.floatEps(f64);
|
||||||
|
pub const nan_f16 = std.math.nan(f16);
|
||||||
|
pub const nan_f32 = std.math.nan(f32);
|
||||||
|
pub const nan_f64 = std.math.nan(f64);
|
||||||
|
|
||||||
|
// std.math 1:1 re-exports below here
|
||||||
|
//
|
||||||
|
// Having two 'math' imports in your code is annoying, so we in general expect that people will not
|
||||||
|
// need to do this and instead can just import mach.math - we add to this list of re-exports as
|
||||||
|
// needed.
|
||||||
|
|
||||||
|
pub const sqrt = std.math.sqrt;
|
||||||
|
pub const isNan = std.math.isNan;
|
||||||
|
|
||||||
|
/// 2/sqrt(π)
|
||||||
|
pub const two_sqrtpi = std.math.two_sqrtpi;
|
||||||
|
|
||||||
|
/// sqrt(2)
|
||||||
|
pub const sqrt2 = std.math.sqrt2;
|
||||||
|
|
||||||
|
/// 1/sqrt(2)
|
||||||
|
pub const sqrt1_2 = std.math.sqrt1_2;
|
||||||
773
src/math/mat.zig
Normal file
773
src/math/mat.zig
Normal file
|
|
@ -0,0 +1,773 @@
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const mach = @import("../main.zig");
|
||||||
|
const testing = mach.testing;
|
||||||
|
const math = mach.math;
|
||||||
|
const vec = @import("vec.zig");
|
||||||
|
|
||||||
|
pub fn Mat(
|
||||||
|
comptime n_cols: usize,
|
||||||
|
comptime n_rows: usize,
|
||||||
|
comptime Vector: type,
|
||||||
|
) type {
|
||||||
|
return struct {
|
||||||
|
v: [cols]Vec,
|
||||||
|
|
||||||
|
/// The number of columns, e.g. Mat3x4.cols == 3
|
||||||
|
pub const cols = n_cols;
|
||||||
|
|
||||||
|
/// The number of rows, e.g. Mat3x4.rows == 4
|
||||||
|
pub const rows = n_rows;
|
||||||
|
|
||||||
|
/// The scalar type of this matrix, e.g. Mat3x3.T == f32
|
||||||
|
pub const T = Vector.T;
|
||||||
|
|
||||||
|
/// The underlying Vec type, e.g. Mat3x3.Vec == Vec4
|
||||||
|
pub const Vec = Vector;
|
||||||
|
|
||||||
|
/// The Vec type corresponding to the number of rows, e.g. Mat3x3.RowVec == Vec3
|
||||||
|
pub const RowVec = vec.Vec(rows, T);
|
||||||
|
|
||||||
|
const Matrix = @This();
|
||||||
|
|
||||||
|
/// Identity matrix
|
||||||
|
pub const ident = switch (Matrix) {
|
||||||
|
inline math.Mat3x3, math.Mat3x3h, math.Mat3x3d => Matrix.init(
|
||||||
|
RowVec.init(1, 0, 0),
|
||||||
|
RowVec.init(0, 1, 0),
|
||||||
|
RowVec.init(0, 0, 1),
|
||||||
|
),
|
||||||
|
inline math.Mat4x4, math.Mat4x4h, math.Mat4x4d => Matrix.init(
|
||||||
|
Vec.init(1, 0, 0, 0),
|
||||||
|
Vec.init(0, 1, 0, 0),
|
||||||
|
Vec.init(0, 0, 1, 0),
|
||||||
|
Vec.init(0, 0, 0, 1),
|
||||||
|
),
|
||||||
|
else => @compileError("Expected Mat3x3, Mat4x4 found '" ++ @typeName(Matrix) ++ "'"),
|
||||||
|
};
|
||||||
|
|
||||||
|
pub usingnamespace switch (Matrix) {
|
||||||
|
inline math.Mat3x3, math.Mat3x3h, math.Mat3x3d => struct {
|
||||||
|
pub inline fn init(
|
||||||
|
col0: RowVec,
|
||||||
|
col1: RowVec,
|
||||||
|
col2: RowVec,
|
||||||
|
) Matrix {
|
||||||
|
return .{ .v = [_]Vec{
|
||||||
|
Vec.init(col0.x(), col0.y(), col0.z(), 1),
|
||||||
|
Vec.init(col1.x(), col1.y(), col1.z(), 1),
|
||||||
|
Vec.init(col2.x(), col2.y(), col2.z(), 1),
|
||||||
|
} };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
inline math.Mat4x4, math.Mat4x4h, math.Mat4x4d => struct {
|
||||||
|
pub inline fn init(col0: Vec, col1: Vec, col2: Vec, col3: Vec) Matrix {
|
||||||
|
return .{ .v = [_]Vec{
|
||||||
|
col0,
|
||||||
|
col1,
|
||||||
|
col2,
|
||||||
|
col3,
|
||||||
|
} };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
else => @compileError("Expected Mat3x3, Mat4x4 found '" ++ @typeName(Matrix) ++ "'"),
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: the below code was correct in our old implementation, it just needs to be updated
|
||||||
|
// to work with this new Mat approach, swapping f32 for the generic T float type, moving 3x3
|
||||||
|
// and 4x4 specific functions into the mixin above, writing new tests, etc.
|
||||||
|
|
||||||
|
// /// Constructs an orthographic projection matrix; an orthogonal transformation matrix which
|
||||||
|
// /// transforms from the given left, right, bottom, and top dimensions into -1 +1 in x and y,
|
||||||
|
// /// and 0 to +1 in z.
|
||||||
|
// ///
|
||||||
|
// /// The near/far parameters denotes the depth (z coordinate) of the near/far clipping plane.
|
||||||
|
// ///
|
||||||
|
// /// Returns an orthographic projection matrix.
|
||||||
|
// pub inline fn ortho(
|
||||||
|
// /// The sides of the near clipping plane viewport
|
||||||
|
// left: f32,
|
||||||
|
// right: f32,
|
||||||
|
// bottom: f32,
|
||||||
|
// top: f32,
|
||||||
|
// /// The depth (z coordinate) of the near/far clipping plane.
|
||||||
|
// near: f32,
|
||||||
|
// far: f32,
|
||||||
|
// ) Mat4x4 {
|
||||||
|
// const xx = 2 / (right - left);
|
||||||
|
// const yy = 2 / (top - bottom);
|
||||||
|
// const zz = 1 / (near - far);
|
||||||
|
// const tx = (right + left) / (left - right);
|
||||||
|
// const ty = (top + bottom) / (bottom - top);
|
||||||
|
// const tz = near / (near - far);
|
||||||
|
// return init(Mat4x4, .{
|
||||||
|
// xx, 0, 0, 0,
|
||||||
|
// 0, yy, 0, 0,
|
||||||
|
// 0, 0, zz, 0,
|
||||||
|
// tx, ty, tz, 1,
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /// Constructs a 2D matrix which translates coordinates by v.
|
||||||
|
// pub inline fn translate2d(v: Vec2) Mat3x3 {
|
||||||
|
// return init(Mat3x3, .{
|
||||||
|
// 1, 0, 0, 0,
|
||||||
|
// 0, 1, 0, 0,
|
||||||
|
// v[0], v[1], 1, 0,
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /// Constructs a 3D matrix which translates coordinates by v.
|
||||||
|
// pub inline fn translate3d(v: Vec3) Mat4x4 {
|
||||||
|
// return init(Mat4x4, .{
|
||||||
|
// 1, 0, 0, 0,
|
||||||
|
// 0, 1, 0, 0,
|
||||||
|
// 0, 0, 1, 0,
|
||||||
|
// v[0], v[1], v[2], 1,
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /// Returns the translation component of the 2D matrix.
|
||||||
|
// pub inline fn translation2d(v: Mat3x3) Vec2 {
|
||||||
|
// return .{ mat.index(v, 8), mat.index(v, 9) };
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /// Returns the translation component of the 3D matrix.
|
||||||
|
// pub inline fn translation3d(v: Mat4x4) Vec3 {
|
||||||
|
// return .{ mat.index(v, 12), mat.index(v, 13), mat.index(v, 14) };
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /// Constructs a 3D matrix which scales each dimension by the given vector.
|
||||||
|
// pub inline fn scale3d(v: Vec3) Mat4x4 {
|
||||||
|
// return init(Mat4x4, .{
|
||||||
|
// v[0], 0, 0, 0,
|
||||||
|
// 0, v[1], 0, 0,
|
||||||
|
// 0, 0, v[2], 0,
|
||||||
|
// 0, 0, 0, 1,
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /// Constructs a 3D matrix which scales each dimension by the given vector.
|
||||||
|
// pub inline fn scale2d(v: Vec2) Mat3x3 {
|
||||||
|
// return init(Mat3x3, .{
|
||||||
|
// v[0], 0, 0, 0,
|
||||||
|
// 0, v[1], 0, 0,
|
||||||
|
// 0, 0, 1, 0,
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Multiplies matrices a * b
|
||||||
|
// pub inline fn mul(a: anytype, b: @TypeOf(a)) @TypeOf(a) {
|
||||||
|
// return if (@TypeOf(a) == Mat3x3) {
|
||||||
|
// const a00 = a[0][0];
|
||||||
|
// const a01 = a[0][1];
|
||||||
|
// const a02 = a[0][2];
|
||||||
|
// const a10 = a[1][0];
|
||||||
|
// const a11 = a[1][1];
|
||||||
|
// const a12 = a[1][2];
|
||||||
|
// const a20 = a[2][0];
|
||||||
|
// const a21 = a[2][1];
|
||||||
|
// const a22 = a[2][2];
|
||||||
|
// const b00 = b[0][0];
|
||||||
|
// const b01 = b[0][1];
|
||||||
|
// const b02 = b[0][2];
|
||||||
|
// const b10 = b[1][0];
|
||||||
|
// const b11 = b[1][1];
|
||||||
|
// const b12 = b[1][2];
|
||||||
|
// const b20 = b[2][0];
|
||||||
|
// const b21 = b[2][1];
|
||||||
|
// const b22 = b[2][2];
|
||||||
|
// return init(Mat3x3, .{
|
||||||
|
// a00 * b00 + a10 * b01 + a20 * b02,
|
||||||
|
// a01 * b00 + a11 * b01 + a21 * b02,
|
||||||
|
// a02 * b00 + a12 * b01 + a22 * b02,
|
||||||
|
// a00 * b10 + a10 * b11 + a20 * b12,
|
||||||
|
// a01 * b10 + a11 * b11 + a21 * b12,
|
||||||
|
// a02 * b10 + a12 * b11 + a22 * b12,
|
||||||
|
// a00 * b20 + a10 * b21 + a20 * b22,
|
||||||
|
// a01 * b20 + a11 * b21 + a21 * b22,
|
||||||
|
// a02 * b20 + a12 * b21 + a22 * b22,
|
||||||
|
// });
|
||||||
|
// } else if (@TypeOf(a) == Mat4x4) {
|
||||||
|
// const a00 = a[0][0];
|
||||||
|
// const a01 = a[0][1];
|
||||||
|
// const a02 = a[0][2];
|
||||||
|
// const a03 = a[0][3];
|
||||||
|
// const a10 = a[1][0];
|
||||||
|
// const a11 = a[1][1];
|
||||||
|
// const a12 = a[1][2];
|
||||||
|
// const a13 = a[1][3];
|
||||||
|
// const a20 = a[2][0];
|
||||||
|
// const a21 = a[2][1];
|
||||||
|
// const a22 = a[2][2];
|
||||||
|
// const a23 = a[2][3];
|
||||||
|
// const a30 = a[3][0];
|
||||||
|
// const a31 = a[3][1];
|
||||||
|
// const a32 = a[3][2];
|
||||||
|
// const a33 = a[3][3];
|
||||||
|
// const b00 = b[0][0];
|
||||||
|
// const b01 = b[0][1];
|
||||||
|
// const b02 = b[0][2];
|
||||||
|
// const b03 = b[0][3];
|
||||||
|
// const b10 = b[1][0];
|
||||||
|
// const b11 = b[1][1];
|
||||||
|
// const b12 = b[1][2];
|
||||||
|
// const b13 = b[1][3];
|
||||||
|
// const b20 = b[2][0];
|
||||||
|
// const b21 = b[2][1];
|
||||||
|
// const b22 = b[2][2];
|
||||||
|
// const b23 = b[2][3];
|
||||||
|
// const b30 = b[3][0];
|
||||||
|
// const b31 = b[3][1];
|
||||||
|
// const b32 = b[3][2];
|
||||||
|
// const b33 = b[3][3];
|
||||||
|
// return init(Mat4x4, .{
|
||||||
|
// a00 * b00 + a10 * b01 + a20 * b02 + a30 * b03,
|
||||||
|
// a01 * b00 + a11 * b01 + a21 * b02 + a31 * b03,
|
||||||
|
// a02 * b00 + a12 * b01 + a22 * b02 + a32 * b03,
|
||||||
|
// a03 * b00 + a13 * b01 + a23 * b02 + a33 * b03,
|
||||||
|
// a00 * b10 + a10 * b11 + a20 * b12 + a30 * b13,
|
||||||
|
// a01 * b10 + a11 * b11 + a21 * b12 + a31 * b13,
|
||||||
|
// a02 * b10 + a12 * b11 + a22 * b12 + a32 * b13,
|
||||||
|
// a03 * b10 + a13 * b11 + a23 * b12 + a33 * b13,
|
||||||
|
// a00 * b20 + a10 * b21 + a20 * b22 + a30 * b23,
|
||||||
|
// a01 * b20 + a11 * b21 + a21 * b22 + a31 * b23,
|
||||||
|
// a02 * b20 + a12 * b21 + a22 * b22 + a32 * b23,
|
||||||
|
// a03 * b20 + a13 * b21 + a23 * b22 + a33 * b23,
|
||||||
|
// a00 * b30 + a10 * b31 + a20 * b32 + a30 * b33,
|
||||||
|
// a01 * b30 + a11 * b31 + a21 * b32 + a31 * b33,
|
||||||
|
// a02 * b30 + a12 * b31 + a22 * b32 + a32 * b33,
|
||||||
|
// a03 * b30 + a13 * b31 + a23 * b32 + a33 * b33,
|
||||||
|
// });
|
||||||
|
// } else @compileError("Expected matrix, found '" ++ @typeName(@TypeOf(a)) ++ "'");
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /// Check if two matrices are approximate equal. Returns true if the absolute difference between
|
||||||
|
// /// each element in matrix them is less or equal than the specified tolerance.
|
||||||
|
// pub inline fn equals(a: anytype, b: @TypeOf(a), tolerance: f32) bool {
|
||||||
|
// // TODO: leverage a vec.equals function
|
||||||
|
// return if (@TypeOf(a) == Mat3x3) {
|
||||||
|
// return float.equals(f32, a[0][0], b[0][0], tolerance) and
|
||||||
|
// float.equals(f32, a[0][1], b[0][1], tolerance) and
|
||||||
|
// float.equals(f32, a[0][2], b[0][2], tolerance) and
|
||||||
|
// float.equals(f32, a[0][3], b[0][3], tolerance) and
|
||||||
|
// float.equals(f32, a[1][0], b[1][0], tolerance) and
|
||||||
|
// float.equals(f32, a[1][1], b[1][1], tolerance) and
|
||||||
|
// float.equals(f32, a[1][2], b[1][2], tolerance) and
|
||||||
|
// float.equals(f32, a[1][3], b[1][3], tolerance) and
|
||||||
|
// float.equals(f32, a[2][0], b[2][0], tolerance) and
|
||||||
|
// float.equals(f32, a[2][1], b[2][1], tolerance) and
|
||||||
|
// float.equals(f32, a[2][2], b[2][2], tolerance) and
|
||||||
|
// float.equals(f32, a[2][3], b[2][3], tolerance);
|
||||||
|
// } else if (@TypeOf(a) == Mat4x4) {
|
||||||
|
// return float.equals(f32, a[0][0], b[0][0], tolerance) and
|
||||||
|
// float.equals(f32, a[0][1], b[0][1], tolerance) and
|
||||||
|
// float.equals(f32, a[0][2], b[0][2], tolerance) and
|
||||||
|
// float.equals(f32, a[0][3], b[0][3], tolerance) and
|
||||||
|
// float.equals(f32, a[1][0], b[1][0], tolerance) and
|
||||||
|
// float.equals(f32, a[1][1], b[1][1], tolerance) and
|
||||||
|
// float.equals(f32, a[1][2], b[1][2], tolerance) and
|
||||||
|
// float.equals(f32, a[1][3], b[1][3], tolerance) and
|
||||||
|
// float.equals(f32, a[2][0], b[2][0], tolerance) and
|
||||||
|
// float.equals(f32, a[2][1], b[2][1], tolerance) and
|
||||||
|
// float.equals(f32, a[2][2], b[2][2], tolerance) and
|
||||||
|
// float.equals(f32, a[2][3], b[2][3], tolerance) and
|
||||||
|
// float.equals(f32, a[3][0], b[3][0], tolerance) and
|
||||||
|
// float.equals(f32, a[3][1], b[3][1], tolerance) and
|
||||||
|
// float.equals(f32, a[3][2], b[3][2], tolerance) and
|
||||||
|
// float.equals(f32, a[3][3], b[3][3], tolerance);
|
||||||
|
// } else @compileError("Expected matrix, found '" ++ @typeName(@TypeOf(a)) ++ "'");
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /// Constructs a 3D matrix which rotates around the X axis by `angle_radians`.
|
||||||
|
// pub inline fn rotateX(angle_radians: f32) Mat4x4 {
|
||||||
|
// const c = std.math.cos(angle_radians);
|
||||||
|
// const s = std.math.sin(angle_radians);
|
||||||
|
|
||||||
|
// return init(Mat4x4, .{
|
||||||
|
// 1, 0, 0, 0,
|
||||||
|
// 0, c, s, 0,
|
||||||
|
// 0, -s, c, 0,
|
||||||
|
// 0, 0, 0, 1,
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /// Constructs a 3D matrix which rotates around the X axis by `angle_radians`.
|
||||||
|
// pub inline fn rotateY(angle_radians: f32) Mat4x4 {
|
||||||
|
// const c = std.math.cos(angle_radians);
|
||||||
|
// const s = std.math.sin(angle_radians);
|
||||||
|
|
||||||
|
// return init(Mat4x4, .{
|
||||||
|
// c, 0, -s, 0,
|
||||||
|
// 0, 1, 0, 0,
|
||||||
|
// s, 0, c, 0,
|
||||||
|
// 0, 0, 0, 1,
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /// Constructs a 3D matrix which rotates around the Z axis by `angle_radians`.
|
||||||
|
// pub inline fn rotateZ(angle_radians: f32) Mat4x4 {
|
||||||
|
// const c = std.math.cos(angle_radians);
|
||||||
|
// const s = std.math.sin(angle_radians);
|
||||||
|
|
||||||
|
// return init(Mat4x4, .{
|
||||||
|
// c, s, 0, 0,
|
||||||
|
// -s, c, 0, 0,
|
||||||
|
// 0, 0, 1, 0,
|
||||||
|
// 0, 0, 0, 1,
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
test "gpu_compatibility" {
|
||||||
|
// https://www.w3.org/TR/WGSL/#alignment-and-size
|
||||||
|
try testing.expect(usize, 48).eql(@sizeOf(math.Mat3x3));
|
||||||
|
try testing.expect(usize, 64).eql(@sizeOf(math.Mat4x4));
|
||||||
|
|
||||||
|
try testing.expect(usize, 24).eql(@sizeOf(math.Mat3x3h));
|
||||||
|
try testing.expect(usize, 32).eql(@sizeOf(math.Mat4x4h));
|
||||||
|
|
||||||
|
try testing.expect(usize, 48 * 2).eql(@sizeOf(math.Mat3x3d)); // speculative
|
||||||
|
try testing.expect(usize, 64 * 2).eql(@sizeOf(math.Mat4x4d)); // speculative
|
||||||
|
}
|
||||||
|
|
||||||
|
test "zero_struct_overhead" {
|
||||||
|
// Proof that using e.g. [3]Vec4 is equal to [3]@Vector(4, f32)
|
||||||
|
try testing.expect(usize, @alignOf([3]@Vector(4, f32))).eql(@alignOf(math.Mat3x3));
|
||||||
|
try testing.expect(usize, @alignOf([4]@Vector(4, f32))).eql(@alignOf(math.Mat4x4));
|
||||||
|
try testing.expect(usize, @sizeOf([3]@Vector(4, f32))).eql(@sizeOf(math.Mat3x3));
|
||||||
|
try testing.expect(usize, @sizeOf([4]@Vector(4, f32))).eql(@sizeOf(math.Mat4x4));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "n" {
|
||||||
|
try testing.expect(usize, 3).eql(math.Mat3x3.cols);
|
||||||
|
try testing.expect(usize, 3).eql(math.Mat3x3.rows);
|
||||||
|
try testing.expect(type, math.Vec4).eql(math.Mat3x3.Vec);
|
||||||
|
try testing.expect(usize, 4).eql(math.Mat3x3.Vec.n);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "init" {
|
||||||
|
try testing.expect(math.Mat3x3, math.mat3x3(
|
||||||
|
math.vec3(1, 2, 3),
|
||||||
|
math.vec3(4, 5, 6),
|
||||||
|
math.vec3(7, 8, 9),
|
||||||
|
)).eql(math.Mat3x3{
|
||||||
|
.v = [_]math.Vec4{
|
||||||
|
math.Vec4.init(1, 2, 3, 1),
|
||||||
|
math.Vec4.init(4, 5, 6, 1),
|
||||||
|
math.Vec4.init(7, 8, 9, 1),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
test "mat3x3_ident" {
|
||||||
|
try testing.expect(math.Mat3x3, math.Mat3x3.ident).eql(math.Mat3x3{
|
||||||
|
.v = [_]math.Vec4{
|
||||||
|
math.Vec4.init(1, 0, 0, 1),
|
||||||
|
math.Vec4.init(0, 1, 0, 1),
|
||||||
|
math.Vec4.init(0, 0, 1, 1),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
test "mat4x4_ident" {
|
||||||
|
try testing.expect(math.Mat4x4, math.Mat4x4.ident).eql(math.Mat4x4{
|
||||||
|
.v = [_]math.Vec4{
|
||||||
|
math.Vec4.init(1, 0, 0, 0),
|
||||||
|
math.Vec4.init(0, 1, 0, 0),
|
||||||
|
math.Vec4.init(0, 0, 1, 0),
|
||||||
|
math.Vec4.init(0, 0, 1, 1),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(math): the tests below violate our styleguide (https://machengine.org/about/style/) we
|
||||||
|
// should write new tests loosely based on them:
|
||||||
|
|
||||||
|
// test "mat.ortho" {
|
||||||
|
// const ortho_mat = mat.ortho(-2, 2, -2, 3, 10, 110);
|
||||||
|
|
||||||
|
// // Computed Values
|
||||||
|
// try expectEqual(ortho_mat[0][0], 0.5);
|
||||||
|
// try expectEqual(ortho_mat[1][1], 0.4);
|
||||||
|
// try expectEqual(ortho_mat[2][2], -0.01);
|
||||||
|
// try expectEqual(ortho_mat[3][0], 0);
|
||||||
|
// try expectEqual(ortho_mat[3][1], -0.2);
|
||||||
|
// try expectEqual(ortho_mat[3][2], -0.1);
|
||||||
|
|
||||||
|
// // Constant values, which should not change but we still check for completeness
|
||||||
|
// const zero_value_indexes = [_]u8{
|
||||||
|
// 1, 2, 3,
|
||||||
|
// 4, 4 + 2, 4 + 3,
|
||||||
|
// 4 * 2, 4 * 2 + 1, 4 * 2 + 3,
|
||||||
|
// };
|
||||||
|
// for (zero_value_indexes) |index| {
|
||||||
|
// try expectEqual(mat.index(ortho_mat, index), 0);
|
||||||
|
// }
|
||||||
|
// try expectEqual(ortho_mat[3][3], 1);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// test "mat.translate2d" {
|
||||||
|
// const v = Vec2{ 1.0, -2.5 };
|
||||||
|
// const translation_mat = mat.translate2d(v);
|
||||||
|
|
||||||
|
// // Computed Values
|
||||||
|
// try expectEqual(translation_mat[2][0], v[0]);
|
||||||
|
// try expectEqual(translation_mat[2][1], v[1]);
|
||||||
|
|
||||||
|
// // Constant values, which should not change but we still check for completeness
|
||||||
|
// const zero_value_indexes = [_]u8{
|
||||||
|
// 1, 2, 3,
|
||||||
|
// 4, 4 + 2, 4 + 3,
|
||||||
|
// 4 * 2 + 3,
|
||||||
|
// };
|
||||||
|
// for (zero_value_indexes) |index| {
|
||||||
|
// try expectEqual(mat.index(translation_mat, index), 0);
|
||||||
|
// }
|
||||||
|
// try expectEqual(translation_mat[0][0], 1);
|
||||||
|
// try expectEqual(translation_mat[1][1], 1);
|
||||||
|
// try expectEqual(translation_mat[2][2], 1);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// test "mat.translate3d" {
|
||||||
|
// const v = Vec3{ 1.0, -2.5, 0.001 };
|
||||||
|
// const translation_mat = mat.translate3d(v);
|
||||||
|
|
||||||
|
// // Computed Values
|
||||||
|
// try expectEqual(translation_mat[3][0], v[0]);
|
||||||
|
// try expectEqual(translation_mat[3][1], v[1]);
|
||||||
|
// try expectEqual(translation_mat[3][2], v[2]);
|
||||||
|
|
||||||
|
// // Constant values, which should not change but we still check for completeness
|
||||||
|
// const zero_value_indexes = [_]u8{
|
||||||
|
// 1, 2, 3,
|
||||||
|
// 4, 4 + 2, 4 + 3,
|
||||||
|
// 4 * 2, 4 * 2 + 1, 4 * 2 + 3,
|
||||||
|
// };
|
||||||
|
// for (zero_value_indexes) |index| {
|
||||||
|
// try expectEqual(mat.index(translation_mat, index), 0);
|
||||||
|
// }
|
||||||
|
// try expectEqual(translation_mat[3][3], 1);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// test "mat.translation" {
|
||||||
|
// {
|
||||||
|
// const v = Vec2{ 1.0, -2.5 };
|
||||||
|
// const translation_mat = mat.translate2d(v);
|
||||||
|
// const result = mat.translation2d(translation_mat);
|
||||||
|
// try expectEqual(result[0], v[0]);
|
||||||
|
// try expectEqual(result[1], v[1]);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// {
|
||||||
|
// const v = Vec3{ 1.0, -2.5, 0.001 };
|
||||||
|
// const translation_mat = mat.translate3d(v);
|
||||||
|
// const result = mat.translation3d(translation_mat);
|
||||||
|
// try expectEqual(result[0], v[0]);
|
||||||
|
// try expectEqual(result[1], v[1]);
|
||||||
|
// try expectEqual(result[2], v[2]);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// test "mat.scale2d" {
|
||||||
|
// const v = Vec2{ 1.0, -2.5 };
|
||||||
|
// const scale_mat = mat.scale2d(v);
|
||||||
|
|
||||||
|
// // Computed Values
|
||||||
|
// try expectEqual(scale_mat[0][0], v[0]);
|
||||||
|
// try expectEqual(scale_mat[1][1], v[1]);
|
||||||
|
|
||||||
|
// // Constant values, which should not change but we still check for completeness
|
||||||
|
// const zero_value_indexes = [_]u8{
|
||||||
|
// 1, 2, 3,
|
||||||
|
// 4, 4 + 2, 4 + 3,
|
||||||
|
// 4 * 2, 4 * 2 + 1, 4 * 2 + 3,
|
||||||
|
// };
|
||||||
|
// for (zero_value_indexes) |index| {
|
||||||
|
// try expectEqual(mat.index(scale_mat, index), 0);
|
||||||
|
// }
|
||||||
|
// try expectEqual(scale_mat[2][2], 1);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// test "mat.scale3d" {
|
||||||
|
// const v = Vec3{ 1.0, -2.5, 0.001 };
|
||||||
|
// const scale_mat = mat.scale3d(v);
|
||||||
|
|
||||||
|
// // Computed Values
|
||||||
|
// try expectEqual(scale_mat[0][0], v[0]);
|
||||||
|
// try expectEqual(scale_mat[1][1], v[1]);
|
||||||
|
// try expectEqual(scale_mat[2][2], v[2]);
|
||||||
|
|
||||||
|
// // Constant values, which should not change but we still check for completeness
|
||||||
|
// const zero_value_indexes = [_]u8{
|
||||||
|
// 1, 2, 3,
|
||||||
|
// 4, 4 + 2, 4 + 3,
|
||||||
|
// 4 * 2, 4 * 2 + 1, 4 * 2 + 3,
|
||||||
|
// 4 * 3, 4 * 3 + 1, 4 * 3 + 2,
|
||||||
|
// };
|
||||||
|
// for (zero_value_indexes) |index| {
|
||||||
|
// try expectEqual(mat.index(scale_mat, index), 0);
|
||||||
|
// }
|
||||||
|
// try expectEqual(scale_mat[3][3], 1);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const degreesToRadians = std.math.degreesToRadians;
|
||||||
|
|
||||||
|
// // TODO: Maybe reconsider based on feedback to join all test for rotation into one test as only
|
||||||
|
// // location of values change. And create some kind of struct that will hold this indexes and
|
||||||
|
// // coresponding values
|
||||||
|
// test "mat.rotateX" {
|
||||||
|
// const zero_value_indexes = [_]u8{
|
||||||
|
// 1, 2, 3,
|
||||||
|
// 4, 4 + 3, 4 * 2,
|
||||||
|
// 4 * 2 + 3, 4 * 3, 4 * 3 + 1,
|
||||||
|
// 4 * 3 + 2,
|
||||||
|
// };
|
||||||
|
|
||||||
|
// const one_value_indexes = [_]u8{
|
||||||
|
// 0, 4 * 3 + 3,
|
||||||
|
// };
|
||||||
|
|
||||||
|
// const tolerance = 1e-7;
|
||||||
|
|
||||||
|
// {
|
||||||
|
// const r = 90;
|
||||||
|
// const R_x = mat.rotateX(degreesToRadians(f32, r));
|
||||||
|
// try expectApproxEqAbs(R_x[1][1], 0, tolerance);
|
||||||
|
// try expectApproxEqAbs(R_x[2][2], 0, tolerance);
|
||||||
|
// try expectApproxEqAbs(R_x[1][2], 1, tolerance);
|
||||||
|
// try expectApproxEqAbs(R_x[2][1], -1, tolerance);
|
||||||
|
|
||||||
|
// for (zero_value_indexes) |index| {
|
||||||
|
// try expectEqual(mat.index(R_x, index), 0);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// for (one_value_indexes) |index| {
|
||||||
|
// try expectEqual(mat.index(R_x, index), 1);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// {
|
||||||
|
// const r = 0;
|
||||||
|
// const R_x = mat.rotateX(degreesToRadians(f32, r));
|
||||||
|
// try expectApproxEqAbs(R_x[1][1], 1, tolerance);
|
||||||
|
// try expectApproxEqAbs(R_x[2][2], 1, tolerance);
|
||||||
|
// try expectApproxEqAbs(R_x[1][2], 0, tolerance);
|
||||||
|
// try expectApproxEqAbs(R_x[2][1], 0, tolerance);
|
||||||
|
|
||||||
|
// for (zero_value_indexes) |index| {
|
||||||
|
// try expectEqual(mat.index(R_x, index), 0);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// for (one_value_indexes) |index| {
|
||||||
|
// try expectEqual(mat.index(R_x, index), 1);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// {
|
||||||
|
// const r = 45;
|
||||||
|
// const result: f32 = std.math.sqrt(2.0) / 2.0; // sqrt(2) / 2
|
||||||
|
// const R_x = mat.rotateX(degreesToRadians(f32, r));
|
||||||
|
// try expectApproxEqAbs(R_x[1][1], result, tolerance);
|
||||||
|
// try expectApproxEqAbs(R_x[2][2], result, tolerance);
|
||||||
|
// try expectApproxEqAbs(R_x[1][2], result, tolerance);
|
||||||
|
// try expectApproxEqAbs(R_x[2][1], -result, tolerance);
|
||||||
|
|
||||||
|
// for (zero_value_indexes) |index| {
|
||||||
|
// try expectEqual(mat.index(R_x, index), 0);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// for (one_value_indexes) |index| {
|
||||||
|
// try expectEqual(mat.index(R_x, index), 1);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// test "mat.rotateY" {
|
||||||
|
// const zero_value_indexes = [_]u8{
|
||||||
|
// 1, 3,
|
||||||
|
// 4, 4 + 2,
|
||||||
|
// 4 + 3, 4 * 2 + 1,
|
||||||
|
// 4 * 2 + 3, 4 * 3,
|
||||||
|
// 4 * 3 + 1, 4 * 3 + 2,
|
||||||
|
// };
|
||||||
|
|
||||||
|
// const one_value_indexes = [_]u8{
|
||||||
|
// 4 + 1, 4 * 3 + 3,
|
||||||
|
// };
|
||||||
|
|
||||||
|
// const tolerance = 1e-7;
|
||||||
|
|
||||||
|
// {
|
||||||
|
// const r = 90;
|
||||||
|
// const R_y = mat.rotateY(degreesToRadians(f32, r));
|
||||||
|
// try expectApproxEqAbs(R_y[0][0], 0, tolerance);
|
||||||
|
// try expectApproxEqAbs(R_y[2][2], 0, tolerance);
|
||||||
|
// try expectApproxEqAbs(R_y[0][2], -1, tolerance);
|
||||||
|
// try expectApproxEqAbs(R_y[2][0], 1, tolerance);
|
||||||
|
|
||||||
|
// for (zero_value_indexes) |index| {
|
||||||
|
// try expectEqual(mat.index(R_y, index), 0);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// for (one_value_indexes) |index| {
|
||||||
|
// try expectEqual(mat.index(R_y, index), 1);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// {
|
||||||
|
// const r = 0;
|
||||||
|
// const R_y = mat.rotateY(degreesToRadians(f32, r));
|
||||||
|
// try expectApproxEqAbs(R_y[0][0], 1, tolerance);
|
||||||
|
// try expectApproxEqAbs(R_y[2][2], 1, tolerance);
|
||||||
|
// try expectApproxEqAbs(R_y[0][2], 0, tolerance);
|
||||||
|
// try expectApproxEqAbs(R_y[3][0], 0, tolerance); // TODO: [2][0] ?
|
||||||
|
|
||||||
|
// for (zero_value_indexes) |index| {
|
||||||
|
// try expectEqual(mat.index(R_y, index), 0);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// for (one_value_indexes) |index| {
|
||||||
|
// try expectEqual(mat.index(R_y, index), 1);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// {
|
||||||
|
// const r = 45;
|
||||||
|
// const result: f32 = std.math.sqrt(2.0) / 2.0; // sqrt(2) / 2
|
||||||
|
// const R_y = mat.rotateY(degreesToRadians(f32, r));
|
||||||
|
// try expectApproxEqAbs(R_y[0][0], result, tolerance);
|
||||||
|
// try expectApproxEqAbs(R_y[2][2], result, tolerance);
|
||||||
|
// try expectApproxEqAbs(R_y[0][2], -result, tolerance);
|
||||||
|
// try expectApproxEqAbs(R_y[2][0], result, tolerance);
|
||||||
|
|
||||||
|
// for (zero_value_indexes) |index| {
|
||||||
|
// try expectEqual(mat.index(R_y, index), 0);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// for (one_value_indexes) |index| {
|
||||||
|
// try expectEqual(mat.index(R_y, index), 1);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// test "mat.rotateZ" {
|
||||||
|
// const zero_value_indexes = [_]u8{
|
||||||
|
// 2, 3,
|
||||||
|
// 4 + 2, 4 + 3,
|
||||||
|
// 4 * 2, 4 * 2 + 1,
|
||||||
|
// 4 * 2 + 3, 4 * 3,
|
||||||
|
// 4 * 3 + 1, 4 * 3 + 2,
|
||||||
|
// };
|
||||||
|
|
||||||
|
// const one_value_indexes = [_]u8{
|
||||||
|
// 4 * 2 + 2, 4 * 3 + 3,
|
||||||
|
// };
|
||||||
|
|
||||||
|
// const tolerance = 1e-7;
|
||||||
|
|
||||||
|
// {
|
||||||
|
// const r = 90;
|
||||||
|
// const R_z = mat.rotateZ(degreesToRadians(f32, r));
|
||||||
|
// try expectApproxEqAbs(R_z[0][0], 0, tolerance);
|
||||||
|
// try expectApproxEqAbs(R_z[1][1], 0, tolerance);
|
||||||
|
// try expectApproxEqAbs(R_z[0][1], 1, tolerance);
|
||||||
|
// try expectApproxEqAbs(R_z[1][0], -1, tolerance);
|
||||||
|
|
||||||
|
// for (zero_value_indexes) |index| {
|
||||||
|
// try expectEqual(mat.index(R_z, index), 0);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// for (one_value_indexes) |index| {
|
||||||
|
// try expectEqual(mat.index(R_z, index), 1);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// {
|
||||||
|
// const r = 0;
|
||||||
|
// const R_z = mat.rotateZ(degreesToRadians(f32, r));
|
||||||
|
// try expectApproxEqAbs(R_z[0][0], 1, tolerance);
|
||||||
|
// try expectApproxEqAbs(R_z[1][1], 1, tolerance);
|
||||||
|
// try expectApproxEqAbs(R_z[0][1], 0, tolerance);
|
||||||
|
// try expectApproxEqAbs(R_z[1][0], 0, tolerance);
|
||||||
|
|
||||||
|
// for (zero_value_indexes) |index| {
|
||||||
|
// try expectEqual(mat.index(R_z, index), 0);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// for (one_value_indexes) |index| {
|
||||||
|
// try expectEqual(mat.index(R_z, index), 1);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// {
|
||||||
|
// const r = 45;
|
||||||
|
// const result: f32 = std.math.sqrt(2.0) / 2.0; // sqrt(2) / 2
|
||||||
|
// const R_z = mat.rotateZ(degreesToRadians(f32, r));
|
||||||
|
// try expectApproxEqAbs(R_z[0][0], result, tolerance);
|
||||||
|
// try expectApproxEqAbs(R_z[1][1], result, tolerance);
|
||||||
|
// try expectApproxEqAbs(R_z[0][1], result, tolerance);
|
||||||
|
// try expectApproxEqAbs(R_z[1][0], -result, tolerance);
|
||||||
|
|
||||||
|
// for (zero_value_indexes) |index| {
|
||||||
|
// try expectEqual(mat.index(R_z, index), 0);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// for (one_value_indexes) |index| {
|
||||||
|
// try expectEqual(mat.index(R_z, index), 1);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// test "mat.mul" {
|
||||||
|
// {
|
||||||
|
// const tolerance = 1e-6;
|
||||||
|
// const t = Vec3{ 1, 2, -3 };
|
||||||
|
// const T = mat.translate3d(t);
|
||||||
|
// const s = Vec3{ 3, 1, -5 };
|
||||||
|
// const S = mat.scale3d(s);
|
||||||
|
// const r = Vec3{ 30, -40, 235 };
|
||||||
|
// const R_x = mat.rotateX(degreesToRadians(f32, r[0]));
|
||||||
|
// const R_y = mat.rotateY(degreesToRadians(f32, r[1]));
|
||||||
|
// const R_z = mat.rotateZ(degreesToRadians(f32, r[2]));
|
||||||
|
|
||||||
|
// const R_yz = mat.mul(R_y, R_z);
|
||||||
|
// // This values are calculated by hand with help of matrix calculator: https://matrix.reshish.com/multCalculation.php
|
||||||
|
// const expected_R_yz = mat.init(Mat4x4, .{
|
||||||
|
// -0.43938504177070496278, -0.8191520442889918, -0.36868782649461236545, 0,
|
||||||
|
// 0.62750687159713312638, -0.573576436351046, 0.52654078451836329713, 0,
|
||||||
|
// -0.6427876096865394, 0, 0.766044443118978, 0,
|
||||||
|
// 0, 0, 0, 1,
|
||||||
|
// });
|
||||||
|
// try expect(mat.equals(R_yz, expected_R_yz, tolerance));
|
||||||
|
|
||||||
|
// const R_xyz = mat.mul(R_x, R_yz);
|
||||||
|
// const expected_R_xyz = mat.init(Mat4x4, .{
|
||||||
|
// -0.439385041770705, -0.52506256666891627986, -0.72886904595489960019, 0,
|
||||||
|
// 0.6275068715971331, -0.76000215715133560834, 0.16920947734596765363, 0,
|
||||||
|
// -0.6427876096865394, -0.383022221559489, 0.66341394816893832989, 0,
|
||||||
|
// 0, 0, 0, 1,
|
||||||
|
// });
|
||||||
|
// try expect(mat.equals(R_xyz, expected_R_xyz, tolerance));
|
||||||
|
|
||||||
|
// const SR = mat.mul(S, R_xyz);
|
||||||
|
// const expected_SR = mat.init(Mat4x4, .{
|
||||||
|
// -1.318155125312115, -0.5250625666689163, 3.6443452297744985, 0,
|
||||||
|
// 1.8825206147913993, -0.7600021571513356, -0.8460473867298382, 0,
|
||||||
|
// -1.9283628290596182, -0.383022221559489, -3.3170697408446915, 0,
|
||||||
|
// 0, 0, 0, 1,
|
||||||
|
// });
|
||||||
|
// try expect(mat.equals(SR, expected_SR, tolerance));
|
||||||
|
|
||||||
|
// const TSR = mat.mul(T, SR);
|
||||||
|
// const expected_TSR = mat.init(Mat4x4, .{
|
||||||
|
// -1.318155125312115, -0.5250625666689163, 3.6443452297744985, 0,
|
||||||
|
// 1.8825206147913993, -0.7600021571513356, -0.8460473867298382, 0,
|
||||||
|
// -1.9283628290596182, -0.383022221559489, -3.3170697408446914, 0,
|
||||||
|
// 1, 2, -3, 1,
|
||||||
|
// });
|
||||||
|
|
||||||
|
// try expect(mat.equals(TSR, expected_TSR, tolerance));
|
||||||
|
// }
|
||||||
|
// }
|
||||||
34
src/math/quat.zig
Normal file
34
src/math/quat.zig
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const mach = @import("../main.zig");
|
||||||
|
const testing = mach.testing;
|
||||||
|
const math = mach.math;
|
||||||
|
const vec = @import("vec.zig");
|
||||||
|
|
||||||
|
pub fn Quat(comptime Scalar: type) type {
|
||||||
|
return struct {
|
||||||
|
v: vec.Vec(4, Scalar),
|
||||||
|
|
||||||
|
/// The scalar type of this matrix, e.g. Mat3x3.T == f32
|
||||||
|
pub const T = Vec.T;
|
||||||
|
|
||||||
|
/// The underlying Vec type, e.g. math.Vec4, math.Vec4h, math.Vec4d
|
||||||
|
pub const Vec = vec.Vec(4, Scalar);
|
||||||
|
|
||||||
|
pub inline fn init(x: T, y: T, z: T, w: T) Quat(Scalar) {
|
||||||
|
return .{ .v = math.vec4(x, y, z, w) };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
test "zero_struct_overhead" {
|
||||||
|
// Proof that using Quat is equal to @Vector(4, f32)
|
||||||
|
try testing.expect(usize, @alignOf(@Vector(4, f32))).eql(@alignOf(math.Quat));
|
||||||
|
try testing.expect(usize, @sizeOf(@Vector(4, f32))).eql(@sizeOf(math.Quat));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "init" {
|
||||||
|
try testing.expect(math.Quat, math.quat(1, 2, 3, 4)).eql(math.Quat{
|
||||||
|
.v = math.vec4(1, 2, 3, 4),
|
||||||
|
});
|
||||||
|
}
|
||||||
494
src/math/vec.zig
Normal file
494
src/math/vec.zig
Normal file
|
|
@ -0,0 +1,494 @@
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const mach = @import("../main.zig");
|
||||||
|
const testing = mach.testing;
|
||||||
|
const math = mach.math;
|
||||||
|
|
||||||
|
pub fn Vec(comptime n_value: usize, comptime Scalar: type) type {
|
||||||
|
return struct {
|
||||||
|
v: Vector,
|
||||||
|
|
||||||
|
/// The vector dimension size, e.g. Vec3.n == 3
|
||||||
|
pub const n = n_value;
|
||||||
|
|
||||||
|
/// The scalar type of this vector, e.g. Vec3.T == f32
|
||||||
|
pub const T = Scalar;
|
||||||
|
|
||||||
|
// The underlying @Vector type
|
||||||
|
pub const Vector = @Vector(n_value, Scalar);
|
||||||
|
|
||||||
|
const VecN = @This();
|
||||||
|
|
||||||
|
pub usingnamespace switch (VecN.n) {
|
||||||
|
inline 2 => struct {
|
||||||
|
pub inline fn init(xs: Scalar, ys: Scalar) VecN {
|
||||||
|
return .{ .v = .{ xs, ys } };
|
||||||
|
}
|
||||||
|
pub inline fn x(v: VecN) Scalar {
|
||||||
|
return v.v[0];
|
||||||
|
}
|
||||||
|
pub inline fn y(v: VecN) Scalar {
|
||||||
|
return v.v[1];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
inline 3 => struct {
|
||||||
|
pub inline fn init(xs: Scalar, ys: Scalar, zs: Scalar) VecN {
|
||||||
|
return .{ .v = .{ xs, ys, zs } };
|
||||||
|
}
|
||||||
|
pub inline fn x(v: VecN) Scalar {
|
||||||
|
return v.v[0];
|
||||||
|
}
|
||||||
|
pub inline fn y(v: VecN) Scalar {
|
||||||
|
return v.v[1];
|
||||||
|
}
|
||||||
|
pub inline fn z(v: VecN) Scalar {
|
||||||
|
return v.v[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(math): come up with a better strategy for swizzling?
|
||||||
|
pub inline fn yzw(v: VecN) VecN {
|
||||||
|
return VecN.init(v.y(), v.z(), v.w());
|
||||||
|
}
|
||||||
|
pub inline fn zxy(v: VecN) VecN {
|
||||||
|
return VecN.init(v.z(), v.x(), v.y());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calculates the cross product between vector a and b. This can be done only in 3D
|
||||||
|
/// and required inputs are Vec3.
|
||||||
|
pub inline fn cross(a: VecN, b: VecN) VecN {
|
||||||
|
// https://gamemath.com/book/vectors.html#cross_product
|
||||||
|
const s1 = a.yzx().mul(b.zxy());
|
||||||
|
const s2 = a.zxy().mul(b.yzx());
|
||||||
|
return s1.sub(s2);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
inline 4 => struct {
|
||||||
|
pub inline fn init(xs: Scalar, ys: Scalar, zs: Scalar, ws: Scalar) VecN {
|
||||||
|
return .{ .v = .{ xs, ys, zs, ws } };
|
||||||
|
}
|
||||||
|
pub inline fn x(v: VecN) Scalar {
|
||||||
|
return v.v[0];
|
||||||
|
}
|
||||||
|
pub inline fn y(v: VecN) Scalar {
|
||||||
|
return v.v[1];
|
||||||
|
}
|
||||||
|
pub inline fn z(v: VecN) Scalar {
|
||||||
|
return v.v[2];
|
||||||
|
}
|
||||||
|
pub inline fn w(v: VecN) Scalar {
|
||||||
|
return v.v[3];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
else => @compileError("Expected Vec2, Vec3, Vec4, found '" ++ @typeName(VecN) ++ "'"),
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Element-wise addition
|
||||||
|
pub inline fn add(a: VecN, b: VecN) VecN {
|
||||||
|
return .{ .v = a.v + b.v };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Element-wise subtraction
|
||||||
|
pub inline fn sub(a: VecN, b: VecN) VecN {
|
||||||
|
return .{ .v = a.v - b.v };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Element-wise division
|
||||||
|
pub inline fn div(a: VecN, b: VecN) VecN {
|
||||||
|
return .{ .v = a.v / b.v };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Element-wise multiplication.
|
||||||
|
///
|
||||||
|
/// See also .cross()
|
||||||
|
pub inline fn mul(a: VecN, b: VecN) VecN {
|
||||||
|
return .{ .v = a.v * b.v };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Scalar addition
|
||||||
|
pub inline fn addScalar(a: VecN, s: Scalar) VecN {
|
||||||
|
return .{ .v = a.v + VecN.splat(s) };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Scalar subtraction
|
||||||
|
pub inline fn subScalar(a: VecN, s: Scalar) VecN {
|
||||||
|
return .{ .v = a.v - VecN.splat(s) };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Scalar division
|
||||||
|
pub inline fn divScalar(a: VecN, s: Scalar) VecN {
|
||||||
|
return .{ .v = a.v / VecN.splat(s) };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Scalar multiplication.
|
||||||
|
///
|
||||||
|
/// See .dot() for the dot product
|
||||||
|
pub inline fn mulScalar(a: VecN, s: Scalar) VecN {
|
||||||
|
return .{ .v = a.v * VecN.splat(s) };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a vector with all components set to the `scalar` value:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// var v = Vec3.splat(1337.0).v;
|
||||||
|
/// // v.x == 1337, v.y == 1337, v.z == 1337
|
||||||
|
/// ```
|
||||||
|
pub inline fn splat(scalar: Scalar) VecN {
|
||||||
|
return .{ .v = @splat(scalar) };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Computes the squared length of the vector. Faster than `len()`
|
||||||
|
pub inline fn len2(v: VecN) Scalar {
|
||||||
|
return switch (VecN.n) {
|
||||||
|
inline 2 => (v.x() * v.x()) + (v.y() * v.y()),
|
||||||
|
inline 3 => (v.x() * v.x()) + (v.y() * v.y()) + (v.z() * v.z()),
|
||||||
|
inline 4 => (v.x() * v.x()) + (v.y() * v.y()) + (v.z() * v.z()) + (v.w() * v.w()),
|
||||||
|
else => @compileError("Expected Vec2, Vec3, Vec4, found '" ++ @typeName(VecN) ++ "'"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Computes the length of the vector.
|
||||||
|
pub inline fn len(v: VecN) Scalar {
|
||||||
|
return math.sqrt(len2(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Normalizes a vector, such that all components end up in the range [0.0, 1.0].
|
||||||
|
///
|
||||||
|
/// d0 is added to the divisor, which means that e.g. if you provide a near-zero value, then in
|
||||||
|
/// situations where you would otherwise get NaN back you will instead get a near-zero vector.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// math.vec3(1.0, 2.0, 3.0).normalize(v, 0.00000001);
|
||||||
|
/// ```
|
||||||
|
pub inline fn normalize(v: VecN, d0: Scalar) VecN {
|
||||||
|
return v.div(VecN.splat(v.len() + d0));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the normalized direction vector from points a and b.
|
||||||
|
///
|
||||||
|
/// d0 is added to the divisor, which means that e.g. if you provide a near-zero value, then in
|
||||||
|
/// situations where you would otherwise get NaN back you will instead get a near-zero vector.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// var v = a_point.dir(b_point, 0.0000001);
|
||||||
|
/// ```
|
||||||
|
pub inline fn dir(a: VecN, b: VecN, d0: Scalar) VecN {
|
||||||
|
return b.sub(a).normalize(d0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calculates the squared distance between points a and b. Faster than `dist()`.
|
||||||
|
pub inline fn dist2(a: VecN, b: VecN) Scalar {
|
||||||
|
return b.sub(a).len2();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calculates the distance between points a and b.
|
||||||
|
pub inline fn dist(a: VecN, b: VecN) Scalar {
|
||||||
|
return math.sqrt(a.dist2(b));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Performs linear interpolation between a and b by some amount.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// a.lerp(b, 0.0) == a
|
||||||
|
/// a.lerp(b, 1.0) == b
|
||||||
|
/// ```
|
||||||
|
pub inline fn lerp(a: VecN, b: VecN, amount: Scalar) VecN {
|
||||||
|
return a.mulScalar(1.0 - amount) + b.mulScalar(amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calculates the dot product between vector a and b and returns scalar.
|
||||||
|
pub inline fn dot(a: VecN, b: VecN) Scalar {
|
||||||
|
return .{ .v = @reduce(.Add, a.v * b.v) };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
test "gpu_compatibility" {
|
||||||
|
// https://www.w3.org/TR/WGSL/#alignment-and-size
|
||||||
|
try testing.expect(usize, 8).eql(@sizeOf(math.Vec2)); // WGSL AlignOf 8, SizeOf 8
|
||||||
|
try testing.expect(usize, 16).eql(@sizeOf(math.Vec3)); // WGSL AlignOf 16, SizeOf 12
|
||||||
|
try testing.expect(usize, 16).eql(@sizeOf(math.Vec4)); // WGSL AlignOf 16, SizeOf 16
|
||||||
|
|
||||||
|
try testing.expect(usize, 4).eql(@sizeOf(math.Vec2h)); // WGSL AlignOf 4, SizeOf 4
|
||||||
|
try testing.expect(usize, 8).eql(@sizeOf(math.Vec3h)); // WGSL AlignOf 8, SizeOf 6
|
||||||
|
try testing.expect(usize, 8).eql(@sizeOf(math.Vec4h)); // WGSL AlignOf 8, SizeOf 8
|
||||||
|
|
||||||
|
try testing.expect(usize, 8 * 2).eql(@sizeOf(math.Vec2d)); // speculative
|
||||||
|
try testing.expect(usize, 16 * 2).eql(@sizeOf(math.Vec3d)); // speculative
|
||||||
|
try testing.expect(usize, 16 * 2).eql(@sizeOf(math.Vec4d)); // speculative
|
||||||
|
}
|
||||||
|
|
||||||
|
test "zero_struct_overhead" {
|
||||||
|
// Proof that using Vec4 is equal to @Vector(4, f32)
|
||||||
|
try testing.expect(usize, @alignOf(@Vector(4, f32))).eql(@alignOf(math.Vec4));
|
||||||
|
try testing.expect(usize, @sizeOf(@Vector(4, f32))).eql(@sizeOf(math.Vec4));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "dimensions" {
|
||||||
|
try testing.expect(usize, 3).eql(math.Vec3.n);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "type" {
|
||||||
|
try testing.expect(type, f16).eql(math.Vec3h.T);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "init" {
|
||||||
|
try testing.expect(math.Vec3h, math.vec3h(1, 2, 3)).eql(math.vec3h(1, 2, 3));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "splat" {
|
||||||
|
try testing.expect(math.Vec3h, math.vec3h(1337, 1337, 1337)).eql(math.Vec3h.splat(1337));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "swizzle_singular" {
|
||||||
|
try testing.expect(f32, 1).eql(math.vec3(1, 2, 3).x());
|
||||||
|
try testing.expect(f32, 2).eql(math.vec3(1, 2, 3).y());
|
||||||
|
try testing.expect(f32, 3).eql(math.vec3(1, 2, 3).z());
|
||||||
|
}
|
||||||
|
|
||||||
|
test "len2" {
|
||||||
|
try testing.expect(f32, 2).eql(math.vec2(1, 1).len2());
|
||||||
|
try testing.expect(f32, 29).eql(math.vec3(2, 3, -4).len2());
|
||||||
|
try testing.expect(f32, 38.115).eqlApprox(math.vec4(1.5, 2.25, 3.33, 4.44).len2(), 0.0001);
|
||||||
|
try testing.expect(f32, 0).eql(math.vec4(0, 0, 0, 0).len2());
|
||||||
|
}
|
||||||
|
|
||||||
|
test "len" {
|
||||||
|
try testing.expect(f32, 5).eql(math.vec2(3, 4).len());
|
||||||
|
try testing.expect(f32, 6).eql(math.vec3(4, 4, 2).len());
|
||||||
|
try testing.expect(f32, 6.17373468817700328621).eql(math.vec4(1.5, 2.25, 3.33, 4.44).len());
|
||||||
|
try testing.expect(f32, 0).eql(math.vec4(0, 0, 0, 0).len());
|
||||||
|
}
|
||||||
|
|
||||||
|
test "normalize_example" {
|
||||||
|
const normalized = math.vec4(10, 0.5, -3, -0.2).normalize(math.eps_f32);
|
||||||
|
try testing.expect(math.Vec4, math.vec4(0.95, 0.05, -0.3, -0.02)).eqlApprox(normalized, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "normalize_accuracy" {
|
||||||
|
const normalized = math.vec2(1, 1).normalize(0);
|
||||||
|
const norm_val = std.math.sqrt1_2; // 1 / sqrt(2)
|
||||||
|
try testing.expect(math.Vec2, math.Vec2.splat(norm_val)).eql(normalized);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "normalize_nan" {
|
||||||
|
const near_zero = 0.0;
|
||||||
|
const normalized = math.vec2(0, 0).normalize(near_zero);
|
||||||
|
try testing.expect(bool, true).eql(math.isNan(normalized.x()));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "normalize_no_nan" {
|
||||||
|
const near_zero = math.eps_f32;
|
||||||
|
const normalized = math.vec2(0, 0).normalize(near_zero);
|
||||||
|
try testing.expect(math.Vec2, math.vec2(0, 0)).eqlBinary(normalized);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(math): add basic tests for these:
|
||||||
|
//
|
||||||
|
// pub inline fn add(a: VecN, b: VecN) VecN {
|
||||||
|
// pub inline fn sub(a: VecN, b: VecN) VecN {
|
||||||
|
// pub inline fn div(a: VecN, b: VecN) VecN {
|
||||||
|
// pub inline fn mul(a: VecN, b: VecN) VecN {
|
||||||
|
// pub inline fn addScalar(a: VecN, s: Scalar) VecN {
|
||||||
|
// pub inline fn subScalar(a: VecN, s: Scalar) VecN {
|
||||||
|
// pub inline fn divScalar(a: VecN, s: Scalar) VecN {
|
||||||
|
// pub inline fn mulScalar(a: VecN, s: Scalar) VecN {
|
||||||
|
|
||||||
|
// TODO(math): the tests below violate our styleguide (https://machengine.org/about/style/) we
|
||||||
|
// should write new tests loosely based on them:
|
||||||
|
|
||||||
|
// test "vec.dir" {
|
||||||
|
// const near_zero_value = 1e-8;
|
||||||
|
|
||||||
|
// {
|
||||||
|
// const a = Vec2{ 0, 0 };
|
||||||
|
// const b = Vec2{ 0, 0 };
|
||||||
|
// const d = vec.dir(a, b, near_zero_value);
|
||||||
|
// try expect(d[0] == 0 and d[1] == 0);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// {
|
||||||
|
// const a = Vec2{ 1, 2 };
|
||||||
|
// const b = Vec2{ 1, 2 };
|
||||||
|
// const d = vec.dir(a, b, near_zero_value);
|
||||||
|
// try expect(d[0] == 0 and d[1] == 0);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// {
|
||||||
|
// const a = Vec2{ 1, 2 };
|
||||||
|
// const b = Vec2{ 3, 4 };
|
||||||
|
// const d = vec.dir(a, b, 0);
|
||||||
|
// const result = std.math.sqrt1_2; // 1 / sqrt(2)
|
||||||
|
// try expect(d[0] == result and d[1] == result);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// {
|
||||||
|
// const a = Vec2{ 1, 2 };
|
||||||
|
// const b = Vec2{ -1, -2 };
|
||||||
|
// const d = vec.dir(a, b, 0);
|
||||||
|
// const result = -0.44721359549995793928; // 1 / sqrt(5)
|
||||||
|
// try expectApproxEqAbs(d[0], result, near_zero_value);
|
||||||
|
// try expectApproxEqAbs(d[1], 2 * result, near_zero_value);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// {
|
||||||
|
// const a = Vec3{ 1, -1, 0 };
|
||||||
|
// const b = Vec3{ 0, 1, 1 };
|
||||||
|
// const d = vec.dir(a, b, 0);
|
||||||
|
|
||||||
|
// const result_3 = 0.40824829046386301637; // 1 / sqrt(6)
|
||||||
|
// const result_1 = -result_3; // -1 / sqrt(6)
|
||||||
|
// const result_2 = 0.81649658092772603273; // sqrt(2/3)
|
||||||
|
// try expectApproxEqAbs(d[0], result_1, 1e-7);
|
||||||
|
// try expectApproxEqAbs(d[1], result_2, 1e-7);
|
||||||
|
// try expectApproxEqAbs(d[2], result_3, 1e-7);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// test "vec.dist2" {
|
||||||
|
// {
|
||||||
|
// const a = Vec4{ 0, 0, 0, 0 };
|
||||||
|
// const b = Vec4{ 0, 0, 0, 0 };
|
||||||
|
// try expect(vec.dist2(a, b) == 0);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// {
|
||||||
|
// const a = Vec2{ 1, 1 };
|
||||||
|
// try expect(vec.dist2(a, a) == 0);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// {
|
||||||
|
// const a = Vec2{ 1, 2 };
|
||||||
|
// const b = Vec2{ 3, 4 };
|
||||||
|
// try expect(vec.dist2(a, b) == 8);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// {
|
||||||
|
// const a = Vec3{ -1, -2, -3 };
|
||||||
|
// const b = Vec3{ 3, 2, 1 };
|
||||||
|
// try expect(vec.dist2(a, b) == 48);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// {
|
||||||
|
// const a = Vec4{ 1.5, 2.25, 3.33, 4.44 };
|
||||||
|
// const b = Vec4{ 1.44, -9.33, 7.25, -0.5 };
|
||||||
|
// try expectApproxEqAbs(vec.dist2(a, b), 173.87, 1e-8);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// test "vec.dist" {
|
||||||
|
// {
|
||||||
|
// const a = Vec4{ 0, 0, 0, 0 };
|
||||||
|
// const b = Vec4{ 0, 0, 0, 0 };
|
||||||
|
// try expect(vec.dist(a, b) == 0);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// {
|
||||||
|
// const a = Vec2{ 1, 1 };
|
||||||
|
// try expect(vec.dist(a, a) == 0);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// {
|
||||||
|
// const a = Vec2{ 1, 2 };
|
||||||
|
// const b = Vec2{ 4, 6 };
|
||||||
|
// try expectEqual(vec.dist(a, b), 5);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// {
|
||||||
|
// const a = Vec3{ -1, -2, -3 };
|
||||||
|
// const b = Vec3{ 3, 2, -1 };
|
||||||
|
// try expect(vec.dist(a, b) == 6);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// {
|
||||||
|
// const a = Vec4{ 1.5, 2.25, 3.33, 4.44 };
|
||||||
|
// const b = Vec4{ 1.44, -9.33, 7.25, -0.5 };
|
||||||
|
// try expectApproxEqAbs(vec.dist(a, b), 13.18597740025364975978, 1e-8);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// test "vec.lerp" {
|
||||||
|
// {
|
||||||
|
// const a = Vec4{ 1, 1, 1, 1 };
|
||||||
|
// const b = Vec4{ 0, 0, 0, 0 };
|
||||||
|
// const lerp_to_a = vec.lerp(a, b, 0.0);
|
||||||
|
// try expectEqual(lerp_to_a[0], a[0]);
|
||||||
|
// try expectEqual(lerp_to_a[1], a[1]);
|
||||||
|
// try expectEqual(lerp_to_a[2], a[2]);
|
||||||
|
// try expectEqual(lerp_to_a[3], a[3]);
|
||||||
|
|
||||||
|
// const lerp_to_b = vec.lerp(a, b, 1.0);
|
||||||
|
// try expectEqual(lerp_to_b[0], b[0]);
|
||||||
|
// try expectEqual(lerp_to_b[1], b[1]);
|
||||||
|
// try expectEqual(lerp_to_b[2], b[2]);
|
||||||
|
// try expectEqual(lerp_to_b[3], b[3]);
|
||||||
|
|
||||||
|
// const lerp_to_mid = vec.lerp(a, b, 0.5);
|
||||||
|
// try expectEqual(lerp_to_mid[0], 0.5);
|
||||||
|
// try expectEqual(lerp_to_mid[1], 0.5);
|
||||||
|
// try expectEqual(lerp_to_mid[2], 0.5);
|
||||||
|
// try expectEqual(lerp_to_mid[3], 0.5);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// test "vec.cross" {
|
||||||
|
// {
|
||||||
|
// const a = Vec3{ 1, 3, 4 };
|
||||||
|
// const b = Vec3{ 2, -5, 8 };
|
||||||
|
// const cross = vec.cross(a, b);
|
||||||
|
// try expectEqual(cross[0], 44);
|
||||||
|
// try expectEqual(cross[1], 0);
|
||||||
|
// try expectEqual(cross[2], -11);
|
||||||
|
// }
|
||||||
|
// {
|
||||||
|
// const a = Vec3{ 1.0, 0.0, 0.0 };
|
||||||
|
// const b = Vec3{ 0.0, 1.0, 0.0 };
|
||||||
|
// const cross = vec.cross(a, b);
|
||||||
|
// try expectEqual(cross[0], 0.0);
|
||||||
|
// try expectEqual(cross[1], 0.0);
|
||||||
|
// try expectEqual(cross[2], 1.0);
|
||||||
|
// }
|
||||||
|
// {
|
||||||
|
// const a = Vec3{ 1.0, 0.0, 0.0 };
|
||||||
|
// const b = Vec3{ 0.0, -1.0, 0.0 };
|
||||||
|
// const cross = vec.cross(a, b);
|
||||||
|
// try expectEqual(cross[0], 0.0);
|
||||||
|
// try expectEqual(cross[1], 0.0);
|
||||||
|
// try expectEqual(cross[2], -1.0);
|
||||||
|
// }
|
||||||
|
// {
|
||||||
|
// const a = Vec3{ -3.0, 0.0, -2.0 };
|
||||||
|
// const b = Vec3{ 5.0, -1.0, 2.0 };
|
||||||
|
// const cross = vec.cross(a, b);
|
||||||
|
// try expectEqual(cross[0], -2.0);
|
||||||
|
// try expectEqual(cross[1], -4.0);
|
||||||
|
// try expectEqual(cross[2], 3.0);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// test "vec.dot" {
|
||||||
|
// {
|
||||||
|
// const a = Vec2{ -1, 2 };
|
||||||
|
// const b = Vec2{ 4, 5 };
|
||||||
|
// const dot = vec.dot(a, b);
|
||||||
|
// try expectEqual(dot, 6);
|
||||||
|
// }
|
||||||
|
// {
|
||||||
|
// const a = Vec3{ -1.0, 2.0, 3.0 };
|
||||||
|
// const b = Vec3{ 4.0, 5.0, 6.0 };
|
||||||
|
// const dot = vec.dot(a, b);
|
||||||
|
// try expectEqual(dot, 24.0);
|
||||||
|
// }
|
||||||
|
// {
|
||||||
|
// const a = Vec4{ -1.0, 2.0, 3.0, -2.0 };
|
||||||
|
// const b = Vec4{ 4.0, 5.0, 6.0, 2.0 };
|
||||||
|
// const dot = vec.dot(a, b);
|
||||||
|
// try expectEqual(dot, 20.0);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// {
|
||||||
|
// const a = Vec4{ 0, 0, 0, 0 };
|
||||||
|
// const b = Vec4{ 0, 0, 0, 0 };
|
||||||
|
// const dot = vec.dot(a, b);
|
||||||
|
// try expectEqual(dot, 0.0);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
@ -116,6 +116,27 @@ fn Expect(comptime T: type) type {
|
||||||
const is_vec4 = T == math.Vec4 or T == math.Vec4h or T == math.Vec4d;
|
const is_vec4 = T == math.Vec4 or T == math.Vec4h or T == math.Vec4d;
|
||||||
if (is_vec2 or is_vec3 or is_vec4) return ExpectVecMat(T);
|
if (is_vec2 or is_vec3 or is_vec4) return ExpectVecMat(T);
|
||||||
|
|
||||||
|
// TODO(testing): TODO(math): handle Mat, []Vec, []Mat without generic equality below.
|
||||||
|
// We can look at how std.testing handles slices, e.g. we should have equal or better output than
|
||||||
|
// what generic equality below gets us:
|
||||||
|
//
|
||||||
|
// ```
|
||||||
|
// ============ expected this output: ============= len: 4 (0x4)
|
||||||
|
//
|
||||||
|
// [0]: math.vec.Vec(4,f32){ .v = { 1.0e+00, 0.0e+00, 0.0e+00, 0.0e+00 } }
|
||||||
|
// [1]: math.vec.Vec(4,f32){ .v = { 0.0e+00, 1.0e+00, 0.0e+00, 0.0e+00 } }
|
||||||
|
// [2]: math.vec.Vec(4,f32){ .v = { 0.0e+00, 0.0e+00, 1.0e+00, 0.0e+00 } }
|
||||||
|
// [3]: math.vec.Vec(4,f32){ .v = { 0.0e+00, 0.0e+00, 0.0e+00, 1.0e+00 } }
|
||||||
|
//
|
||||||
|
// ============= instead found this: ============== len: 4 (0x4)
|
||||||
|
//
|
||||||
|
// [0]: math.vec.Vec(4,f32){ .v = { 1.0e+00, 0.0e+00, 0.0e+00, 0.0e+00 } }
|
||||||
|
// [1]: math.vec.Vec(4,f32){ .v = { 0.0e+00, 1.0e+00, 0.0e+00, 0.0e+00 } }
|
||||||
|
// [2]: math.vec.Vec(4,f32){ .v = { 0.0e+00, 0.0e+00, 1.0e+00, 0.0e+00 } }
|
||||||
|
// [3]: math.vec.Vec(4,f32){ .v = { 0.0e+00, 0.0e+00, 1.0e+00, 1.0e+00 } }
|
||||||
|
// ```
|
||||||
|
//
|
||||||
|
|
||||||
// Generic equality
|
// Generic equality
|
||||||
return struct {
|
return struct {
|
||||||
expected: T,
|
expected: T,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue