diff --git a/modules/mat4.lua b/modules/mat4.lua index e3c9138..d75c6bb 100644 --- a/modules/mat4.lua +++ b/modules/mat4.lua @@ -46,8 +46,8 @@ end -- Statically allocate a temporary variable used in some of our functions. local tmp = new() -local t44 = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } -local t41 = { 0, 0, 0, 0 } +local tm4 = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } +local tv4 = { 0, 0, 0, 0 } function mat4.new(a) local out = new() @@ -268,63 +268,63 @@ function mat4.clone(a) end function mat4.mul(out, a, b) - t44[1] = a[1] * b[1] + a[2] * b[5] + a[3] * b[9] + a[4] * b[13] - t44[2] = a[1] * b[2] + a[2] * b[6] + a[3] * b[10] + a[4] * b[14] - t44[3] = a[1] * b[3] + a[2] * b[7] + a[3] * b[11] + a[4] * b[15] - t44[4] = a[1] * b[4] + a[2] * b[8] + a[3] * b[12] + a[4] * b[16] - t44[5] = a[5] * b[1] + a[6] * b[5] + a[7] * b[9] + a[8] * b[13] - t44[6] = a[5] * b[2] + a[6] * b[6] + a[7] * b[10] + a[8] * b[14] - t44[7] = a[5] * b[3] + a[6] * b[7] + a[7] * b[11] + a[8] * b[15] - t44[8] = a[5] * b[4] + a[6] * b[8] + a[7] * b[12] + a[8] * b[16] - t44[9] = a[9] * b[1] + a[10] * b[5] + a[11] * b[9] + a[12] * b[13] - t44[10] = a[9] * b[2] + a[10] * b[6] + a[11] * b[10] + a[12] * b[14] - t44[11] = a[9] * b[3] + a[10] * b[7] + a[11] * b[11] + a[12] * b[15] - t44[12] = a[9] * b[4] + a[10] * b[8] + a[11] * b[12] + a[12] * b[16] - t44[13] = a[13] * b[1] + a[14] * b[5] + a[15] * b[9] + a[16] * b[13] - t44[14] = a[13] * b[2] + a[14] * b[6] + a[15] * b[10] + a[16] * b[14] - t44[15] = a[13] * b[3] + a[14] * b[7] + a[15] * b[11] + a[16] * b[15] - t44[16] = a[13] * b[4] + a[14] * b[8] + a[15] * b[12] + a[16] * b[16] + tm4[1] = a[1] * b[1] + a[2] * b[5] + a[3] * b[9] + a[4] * b[13] + tm4[2] = a[1] * b[2] + a[2] * b[6] + a[3] * b[10] + a[4] * b[14] + tm4[3] = a[1] * b[3] + a[2] * b[7] + a[3] * b[11] + a[4] * b[15] + tm4[4] = a[1] * b[4] + a[2] * b[8] + a[3] * b[12] + a[4] * b[16] + tm4[5] = a[5] * b[1] + a[6] * b[5] + a[7] * b[9] + a[8] * b[13] + tm4[6] = a[5] * b[2] + a[6] * b[6] + a[7] * b[10] + a[8] * b[14] + tm4[7] = a[5] * b[3] + a[6] * b[7] + a[7] * b[11] + a[8] * b[15] + tm4[8] = a[5] * b[4] + a[6] * b[8] + a[7] * b[12] + a[8] * b[16] + tm4[9] = a[9] * b[1] + a[10] * b[5] + a[11] * b[9] + a[12] * b[13] + tm4[10] = a[9] * b[2] + a[10] * b[6] + a[11] * b[10] + a[12] * b[14] + tm4[11] = a[9] * b[3] + a[10] * b[7] + a[11] * b[11] + a[12] * b[15] + tm4[12] = a[9] * b[4] + a[10] * b[8] + a[11] * b[12] + a[12] * b[16] + tm4[13] = a[13] * b[1] + a[14] * b[5] + a[15] * b[9] + a[16] * b[13] + tm4[14] = a[13] * b[2] + a[14] * b[6] + a[15] * b[10] + a[16] * b[14] + tm4[15] = a[13] * b[3] + a[14] * b[7] + a[15] * b[11] + a[16] * b[15] + tm4[16] = a[13] * b[4] + a[14] * b[8] + a[15] * b[12] + a[16] * b[16] for i=1, 16 do - out[i] = t44[i] + out[i] = tm4[i] end return out end -function mat4.mul_mat4x1(out, a, b) - t41[1] = b[1] * a[1] + b[2] * a[5] + b [3] * a[9] + b[4] * a[13] - t41[2] = b[1] * a[2] + b[2] * a[6] + b [3] * a[10] + b[4] * a[14] - t41[3] = b[1] * a[3] + b[2] * a[7] + b [3] * a[11] + b[4] * a[15] - t41[4] = b[1] * a[4] + b[2] * a[8] + b [3] * a[12] + b[4] * a[16] +function mat4.mul_vec4(out, a, b) + tv4[1] = b[1] * a[1] + b[2] * a[5] + b [3] * a[9] + b[4] * a[13] + tv4[2] = b[1] * a[2] + b[2] * a[6] + b [3] * a[10] + b[4] * a[14] + tv4[3] = b[1] * a[3] + b[2] * a[7] + b [3] * a[11] + b[4] * a[15] + tv4[4] = b[1] * a[4] + b[2] * a[8] + b [3] * a[12] + b[4] * a[16] for i=1, 4 do - out[i] = t41[i] + out[i] = tv4[i] end return out end function mat4.invert(out, a) - t44[1] = a[6] * a[11] * a[16] - a[6] * a[12] * a[15] - a[10] * a[7] * a[16] + a[10] * a[8] * a[15] + a[14] * a[7] * a[12] - a[14] * a[8] * a[11] - t44[2] = -a[2] * a[11] * a[16] + a[2] * a[12] * a[15] + a[10] * a[3] * a[16] - a[10] * a[4] * a[15] - a[14] * a[3] * a[12] + a[14] * a[4] * a[11] - t44[3] = a[2] * a[7] * a[16] - a[2] * a[8] * a[15] - a[6] * a[3] * a[16] + a[6] * a[4] * a[15] + a[14] * a[3] * a[8] - a[14] * a[4] * a[7] - t44[4] = -a[2] * a[7] * a[12] + a[2] * a[8] * a[11] + a[6] * a[3] * a[12] - a[6] * a[4] * a[11] - a[10] * a[3] * a[8] + a[10] * a[4] * a[7] - t44[5] = -a[5] * a[11] * a[16] + a[5] * a[12] * a[15] + a[9] * a[7] * a[16] - a[9] * a[8] * a[15] - a[13] * a[7] * a[12] + a[13] * a[8] * a[11] - t44[6] = a[1] * a[11] * a[16] - a[1] * a[12] * a[15] - a[9] * a[3] * a[16] + a[9] * a[4] * a[15] + a[13] * a[3] * a[12] - a[13] * a[4] * a[11] - t44[7] = -a[1] * a[7] * a[16] + a[1] * a[8] * a[15] + a[5] * a[3] * a[16] - a[5] * a[4] * a[15] - a[13] * a[3] * a[8] + a[13] * a[4] * a[7] - t44[8] = a[1] * a[7] * a[12] - a[1] * a[8] * a[11] - a[5] * a[3] * a[12] + a[5] * a[4] * a[11] + a[9] * a[3] * a[8] - a[9] * a[4] * a[7] - t44[9] = a[5] * a[10] * a[16] - a[5] * a[12] * a[14] - a[9] * a[6] * a[16] + a[9] * a[8] * a[14] + a[13] * a[6] * a[12] - a[13] * a[8] * a[10] - t44[10] = -a[1] * a[10] * a[16] + a[1] * a[12] * a[14] + a[9] * a[2] * a[16] - a[9] * a[4] * a[14] - a[13] * a[2] * a[12] + a[13] * a[4] * a[10] - t44[11] = a[1] * a[6] * a[16] - a[1] * a[8] * a[14] - a[5] * a[2] * a[16] + a[5] * a[4] * a[14] + a[13] * a[2] * a[8] - a[13] * a[4] * a[6] - t44[12] = -a[1] * a[6] * a[12] + a[1] * a[8] * a[10] + a[5] * a[2] * a[12] - a[5] * a[4] * a[10] - a[9] * a[2] * a[8] + a[9] * a[4] * a[6] - t44[13] = -a[5] * a[10] * a[15] + a[5] * a[11] * a[14] + a[9] * a[6] * a[15] - a[9] * a[7] * a[14] - a[13] * a[6] * a[11] + a[13] * a[7] * a[10] - t44[14] = a[1] * a[10] * a[15] - a[1] * a[11] * a[14] - a[9] * a[2] * a[15] + a[9] * a[3] * a[14] + a[13] * a[2] * a[11] - a[13] * a[3] * a[10] - t44[15] = -a[1] * a[6] * a[15] + a[1] * a[7] * a[14] + a[5] * a[2] * a[15] - a[5] * a[3] * a[14] - a[13] * a[2] * a[7] + a[13] * a[3] * a[6] - t44[16] = a[1] * a[6] * a[11] - a[1] * a[7] * a[10] - a[5] * a[2] * a[11] + a[5] * a[3] * a[10] + a[9] * a[2] * a[7] - a[9] * a[3] * a[6] + tm4[1] = a[6] * a[11] * a[16] - a[6] * a[12] * a[15] - a[10] * a[7] * a[16] + a[10] * a[8] * a[15] + a[14] * a[7] * a[12] - a[14] * a[8] * a[11] + tm4[2] = -a[2] * a[11] * a[16] + a[2] * a[12] * a[15] + a[10] * a[3] * a[16] - a[10] * a[4] * a[15] - a[14] * a[3] * a[12] + a[14] * a[4] * a[11] + tm4[3] = a[2] * a[7] * a[16] - a[2] * a[8] * a[15] - a[6] * a[3] * a[16] + a[6] * a[4] * a[15] + a[14] * a[3] * a[8] - a[14] * a[4] * a[7] + tm4[4] = -a[2] * a[7] * a[12] + a[2] * a[8] * a[11] + a[6] * a[3] * a[12] - a[6] * a[4] * a[11] - a[10] * a[3] * a[8] + a[10] * a[4] * a[7] + tm4[5] = -a[5] * a[11] * a[16] + a[5] * a[12] * a[15] + a[9] * a[7] * a[16] - a[9] * a[8] * a[15] - a[13] * a[7] * a[12] + a[13] * a[8] * a[11] + tm4[6] = a[1] * a[11] * a[16] - a[1] * a[12] * a[15] - a[9] * a[3] * a[16] + a[9] * a[4] * a[15] + a[13] * a[3] * a[12] - a[13] * a[4] * a[11] + tm4[7] = -a[1] * a[7] * a[16] + a[1] * a[8] * a[15] + a[5] * a[3] * a[16] - a[5] * a[4] * a[15] - a[13] * a[3] * a[8] + a[13] * a[4] * a[7] + tm4[8] = a[1] * a[7] * a[12] - a[1] * a[8] * a[11] - a[5] * a[3] * a[12] + a[5] * a[4] * a[11] + a[9] * a[3] * a[8] - a[9] * a[4] * a[7] + tm4[9] = a[5] * a[10] * a[16] - a[5] * a[12] * a[14] - a[9] * a[6] * a[16] + a[9] * a[8] * a[14] + a[13] * a[6] * a[12] - a[13] * a[8] * a[10] + tm4[10] = -a[1] * a[10] * a[16] + a[1] * a[12] * a[14] + a[9] * a[2] * a[16] - a[9] * a[4] * a[14] - a[13] * a[2] * a[12] + a[13] * a[4] * a[10] + tm4[11] = a[1] * a[6] * a[16] - a[1] * a[8] * a[14] - a[5] * a[2] * a[16] + a[5] * a[4] * a[14] + a[13] * a[2] * a[8] - a[13] * a[4] * a[6] + tm4[12] = -a[1] * a[6] * a[12] + a[1] * a[8] * a[10] + a[5] * a[2] * a[12] - a[5] * a[4] * a[10] - a[9] * a[2] * a[8] + a[9] * a[4] * a[6] + tm4[13] = -a[5] * a[10] * a[15] + a[5] * a[11] * a[14] + a[9] * a[6] * a[15] - a[9] * a[7] * a[14] - a[13] * a[6] * a[11] + a[13] * a[7] * a[10] + tm4[14] = a[1] * a[10] * a[15] - a[1] * a[11] * a[14] - a[9] * a[2] * a[15] + a[9] * a[3] * a[14] + a[13] * a[2] * a[11] - a[13] * a[3] * a[10] + tm4[15] = -a[1] * a[6] * a[15] + a[1] * a[7] * a[14] + a[5] * a[2] * a[15] - a[5] * a[3] * a[14] - a[13] * a[2] * a[7] + a[13] * a[3] * a[6] + tm4[16] = a[1] * a[6] * a[11] - a[1] * a[7] * a[10] - a[5] * a[2] * a[11] + a[5] * a[3] * a[10] + a[9] * a[2] * a[7] - a[9] * a[3] * a[6] for i=1, 16 do - out[i] = t44[i] + out[i] = tm4[i] end local det = a[1] * out[1] + a[2] * out[5] + a[3] * out[9] + a[4] * out[13] @@ -423,25 +423,25 @@ function mat4.look_at(out, a, eye, center, up) end function mat4.transpose(out, a) - t44[1] = a[1] - t44[2] = a[5] - t44[3] = a[9] - t44[4] = a[13] - t44[5] = a[2] - t44[6] = a[6] - t44[7] = a[10] - t44[8] = a[14] - t44[9] = a[3] - t44[10] = a[7] - t44[11] = a[11] - t44[12] = a[15] - t44[13] = a[4] - t44[14] = a[8] - t44[15] = a[12] - t44[16] = a[16] + tm4[1] = a[1] + tm4[2] = a[5] + tm4[3] = a[9] + tm4[4] = a[13] + tm4[5] = a[2] + tm4[6] = a[6] + tm4[7] = a[10] + tm4[8] = a[14] + tm4[9] = a[3] + tm4[10] = a[7] + tm4[11] = a[11] + tm4[12] = a[15] + tm4[13] = a[4] + tm4[14] = a[8] + tm4[15] = a[12] + tm4[16] = a[16] for i=1, 16 do - out[i] = t44[i] + out[i] = tm4[i] end return out @@ -452,8 +452,8 @@ end function mat4.project(obj, view, projection, viewport) local position = { obj.x, obj.y, obj.z, 1 } - mat4.mul_mat4x1(position, view, position) - mat4.mul_mat4x1(position, projection, position) + mat4.mul_vec4(position, view, position) + mat4.mul_vec4(position, projection, position) position[1] = position[1] / position[4] * 0.5 + 0.5 position[2] = position[2] / position[4] * 0.5 + 0.5 @@ -478,7 +478,7 @@ function mat4.unproject(win, view, projection, viewport) position[3] = position[3] * 2 - 1 tmp:mul(projection, view):invert(tmp) - mat4.mul_mat4x1(position, tmp, position) + mat4.mul_vec4(position, tmp, position) position[1] = position[1] / position[4] position[2] = position[2] / position[4] @@ -692,7 +692,7 @@ function mat4_mt.__mul(a, b) return new():mul(a, b) end - return mat4.mul_mat4x1({}, a, b) + return mat4.mul_vec4({}, a, b) end if status then diff --git a/spec/mat4_spec.lua b/spec/mat4_spec.lua index f11744f..b4afa64 100644 --- a/spec/mat4_spec.lua +++ b/spec/mat4_spec.lua @@ -3,192 +3,190 @@ local vec3 = require "modules.vec3" local utils = require "modules.utils" describe("mat4:", function() - it("tests new matrices", function() + it("creates an identity matrix", function() local a = mat4() - assert.is.equal(a[1], 1) - assert.is.equal(a[2], 0) - assert.is.equal(a[3], 0) - assert.is.equal(a[4], 0) - assert.is.equal(a[5], 0) - assert.is.equal(a[6], 1) - assert.is.equal(a[7], 0) - assert.is.equal(a[8], 0) - assert.is.equal(a[9], 0) - assert.is.equal(a[10], 0) - assert.is.equal(a[11], 1) - assert.is.equal(a[12], 0) - assert.is.equal(a[13], 0) - assert.is.equal(a[14], 0) - assert.is.equal(a[15], 0) - assert.is.equal(a[16], 1) + assert.is.equal(1, a[1]) + assert.is.equal(0, a[2]) + assert.is.equal(0, a[3]) + assert.is.equal(0, a[4]) + assert.is.equal(0, a[5]) + assert.is.equal(1, a[6]) + assert.is.equal(0, a[7]) + assert.is.equal(0, a[8]) + assert.is.equal(0, a[9]) + assert.is.equal(0, a[10]) + assert.is.equal(1, a[11]) + assert.is.equal(0, a[12]) + assert.is.equal(0, a[13]) + assert.is.equal(0, a[14]) + assert.is.equal(0, a[15]) + assert.is.equal(1, a[16]) assert.is_true(a:is_mat4()) + end) - local b = mat4 { + it("creates a filled matrix", function() + local a = mat4 { 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6 } - assert.is.equal(b[1], 3) - assert.is.equal(b[2], 3) - assert.is.equal(b[3], 3) - assert.is.equal(b[4], 3) - assert.is.equal(b[5], 4) - assert.is.equal(b[6], 4) - assert.is.equal(b[7], 4) - assert.is.equal(b[8], 4) - assert.is.equal(b[9], 5) - assert.is.equal(b[10], 5) - assert.is.equal(b[11], 5) - assert.is.equal(b[12], 5) - assert.is.equal(b[13], 6) - assert.is.equal(b[14], 6) - assert.is.equal(b[15], 6) - assert.is.equal(b[16], 6) + assert.is.equal(3, a[1]) + assert.is.equal(3, a[2]) + assert.is.equal(3, a[3]) + assert.is.equal(3, a[4]) + assert.is.equal(4, a[5]) + assert.is.equal(4, a[6]) + assert.is.equal(4, a[7]) + assert.is.equal(4, a[8]) + assert.is.equal(5, a[9]) + assert.is.equal(5, a[10]) + assert.is.equal(5, a[11]) + assert.is.equal(5, a[12]) + assert.is.equal(6, a[13]) + assert.is.equal(6, a[14]) + assert.is.equal(6, a[15]) + assert.is.equal(6, a[16]) + end) - local c = mat4 { + it("creates a filled matrix from vec4s", function() + local a = mat4 { { 3, 3, 3, 3 }, { 4, 4, 4, 4 }, { 5, 5, 5, 5 }, { 6, 6, 6, 6 } } - assert.is.equal(c[1], 3) - assert.is.equal(c[2], 3) - assert.is.equal(c[3], 3) - assert.is.equal(c[4], 3) - assert.is.equal(c[5], 4) - assert.is.equal(c[6], 4) - assert.is.equal(c[7], 4) - assert.is.equal(c[8], 4) - assert.is.equal(c[9], 5) - assert.is.equal(c[10], 5) - assert.is.equal(c[11], 5) - assert.is.equal(c[12], 5) - assert.is.equal(c[13], 6) - assert.is.equal(c[14], 6) - assert.is.equal(c[15], 6) - assert.is.equal(c[16], 6) + assert.is.equal(3, a[1]) + assert.is.equal(3, a[2]) + assert.is.equal(3, a[3]) + assert.is.equal(3, a[4]) + assert.is.equal(4, a[5]) + assert.is.equal(4, a[6]) + assert.is.equal(4, a[7]) + assert.is.equal(4, a[8]) + assert.is.equal(5, a[9]) + assert.is.equal(5, a[10]) + assert.is.equal(5, a[11]) + assert.is.equal(5, a[12]) + assert.is.equal(6, a[13]) + assert.is.equal(6, a[14]) + assert.is.equal(6, a[15]) + assert.is.equal(6, a[16]) + end) - local d = mat4 { + it("creates a filled matrix from a 3x3 matrix", function() + local a = mat4 { 3, 3, 3, 4, 4, 4, 5, 5, 5 } - assert.is.equal(d[1], 3) - assert.is.equal(d[2], 3) - assert.is.equal(d[3], 3) - assert.is.equal(d[4], 0) - assert.is.equal(d[5], 4) - assert.is.equal(d[6], 4) - assert.is.equal(d[7], 4) - assert.is.equal(d[8], 0) - assert.is.equal(d[9], 5) - assert.is.equal(d[10], 5) - assert.is.equal(d[11], 5) - assert.is.equal(d[12], 0) - assert.is.equal(d[13], 0) - assert.is.equal(d[14], 0) - assert.is.equal(d[15], 0) - assert.is.equal(d[16], 1) - - local e = mat4.identity() - assert.is.equal(a, e) - - local f = c:clone() - assert.is.equal(c, f) + assert.is.equal(3, a[1]) + assert.is.equal(3, a[2]) + assert.is.equal(3, a[3]) + assert.is.equal(0, a[4]) + assert.is.equal(4, a[5]) + assert.is.equal(4, a[6]) + assert.is.equal(4, a[7]) + assert.is.equal(0, a[8]) + assert.is.equal(5, a[9]) + assert.is.equal(5, a[10]) + assert.is.equal(5, a[11]) + assert.is.equal(0, a[12]) + assert.is.equal(0, a[13]) + assert.is.equal(0, a[14]) + assert.is.equal(0, a[15]) + assert.is.equal(1, a[16]) end) - it("tests multiplication", function() - -- 4x4 * 4x4 - do - local a = mat4 { - 1, 2, 3, 4, - 5, 6, 7, 8, - 9, 10, 11, 12, - 13, 14, 15, 16 - } - local b = mat4 { - 1, 5, 9, 13, - 2, 6, 10, 14, - 3, 7, 11, 15, - 4, 8, 12, 16 - } - local c = mat4():mul(a, b) - assert.is.equal(c[1], 30) - assert.is.equal(c[2], 70) - assert.is.equal(c[3], 110) - assert.is.equal(c[4], 150) - assert.is.equal(c[5], 70) - assert.is.equal(c[6], 174) - assert.is.equal(c[7], 278) - assert.is.equal(c[8], 382) - assert.is.equal(c[9], 110) - assert.is.equal(c[10], 278) - assert.is.equal(c[11], 446) - assert.is.equal(c[12], 614) - assert.is.equal(c[13], 150) - assert.is.equal(c[14], 382) - assert.is.equal(c[15], 614) - assert.is.equal(c[16], 846) - end - - -- 4x4 * 4x1 - do - local a = mat4 { - 1, 2, 3, 4, - 5, 6, 7, 8, - 9, 10, 11, 12, - 13, 14, 15, 16 - } - local b = { 10, 20, 30, 40 } - local c = mat4.mul_mat4x1({}, a, b) - assert.is.equal(c[1], 900) - assert.is.equal(c[2], 1000) - assert.is.equal(c[3], 1100) - assert.is.equal(c[4], 1200) - end + it("clones a matrix", function() + local a = mat4.identity() + local b = a:clone() + assert.is.equal(a, b) end) - it("tests transforms", function() + it("multiplies two 4x4 matrices", function() + local a = mat4 { + 1, 2, 3, 4, + 5, 6, 7, 8, + 9, 10, 11, 12, + 13, 14, 15, 16 + } + local b = mat4 { + 1, 5, 9, 13, + 2, 6, 10, 14, + 3, 7, 11, 15, + 4, 8, 12, 16 + } + local c = mat4():mul(a, b) + assert.is.equal(30, c[1]) + assert.is.equal(70, c[2]) + assert.is.equal(110, c[3]) + assert.is.equal(150, c[4]) + assert.is.equal(70, c[5]) + assert.is.equal(174, c[6]) + assert.is.equal(278, c[7]) + assert.is.equal(382, c[8]) + assert.is.equal(110, c[9]) + assert.is.equal(278, c[10]) + assert.is.equal(446, c[11]) + assert.is.equal(614, c[12]) + assert.is.equal(150, c[13]) + assert.is.equal(382, c[14]) + assert.is.equal(614, c[15]) + assert.is.equal(846, c[16]) + end) + + it("multiplies a matrix and a vec4", function() + local a = mat4 { + 1, 2, 3, 4, + 5, 6, 7, 8, + 9, 10, 11, 12, + 13, 14, 15, 16 + } + local b = { 10, 20, 30, 40 } + local c = mat4.mul_vec4({}, a, b) + assert.is.equal(900, c[1]) + assert.is.equal(1000, c[2]) + assert.is.equal(1100, c[3]) + assert.is.equal(1200, c[4]) + end) + + it("scales a matrix", function() local a = mat4() - - -- Scale matrix - do - local b = mat4():scale(a, vec3(5, 5, 5)) - assert.is.equal(b[1], 5) - assert.is.equal(b[6], 5) - assert.is.equal(b[11], 5) - end - - -- Rotate matrix - do - local b = mat4():rotate(a, math.rad(45), vec3.unit_z) - assert.is_true(utils.tolerance( 0.7071-b[1], 0.001)) - assert.is_true(utils.tolerance( 0.7071-b[2], 0.001)) - assert.is_true(utils.tolerance(-0.7071-b[5], 0.001)) - assert.is_true(utils.tolerance( 0.7071-b[6], 0.001)) - end - - -- Translate matrix - do - local b = mat4():translate(a, vec3(5, 5, 5)) - assert.is.equal(b[13], 5) - assert.is.equal(b[14], 5) - assert.is.equal(b[15], 5) - end + local b = mat4():scale(a, vec3(5, 5, 5)) + assert.is.equal(5, b[1]) + assert.is.equal(5, b[6]) + assert.is.equal(5, b[11]) end) - it("tests inversion", function() + it("rotates a matrix", function() + local a = mat4() + local b = mat4():rotate(a, math.rad(45), vec3.unit_z) + assert.is_true(utils.tolerance( 0.7071-b[1], 0.001)) + assert.is_true(utils.tolerance( 0.7071-b[2], 0.001)) + assert.is_true(utils.tolerance(-0.7071-b[5], 0.001)) + assert.is_true(utils.tolerance( 0.7071-b[6], 0.001)) + end) + + it("translates a matrix", function() + local a = mat4() + local b = mat4():translate(a, vec3(5, 5, 5)) + assert.is.equal(5, b[13]) + assert.is.equal(5, b[14]) + assert.is.equal(5, b[15]) + end) + + it("inverts a matrix", function() local a = mat4() a:rotate(a, math.pi/4, vec3.unit_y) a:translate(a, vec3(4, 5, 6)) local b = mat4():invert(a) local c = mat4():mul(a, b) - assert.is.equal(c, mat4()) + assert.is.equal(mat4(), c) end) - it("tests transpose", function() + it("transposes a matrix", function() local a = mat4 { 1, 1, 1, 1, 2, 2, 2, 2, @@ -196,144 +194,138 @@ describe("mat4:", function() 4, 4, 4, 4 } local b = mat4():transpose(a) - assert.is.equal(b[1], 1) - assert.is.equal(b[2], 2) - assert.is.equal(b[3], 3) - assert.is.equal(b[4], 4) - assert.is.equal(b[5], 1) - assert.is.equal(b[6], 2) - assert.is.equal(b[7], 3) - assert.is.equal(b[8], 4) - assert.is.equal(b[9], 1) - assert.is.equal(b[10], 2) - assert.is.equal(b[11], 3) - assert.is.equal(b[12], 4) - assert.is.equal(b[13], 1) - assert.is.equal(b[14], 2) - assert.is.equal(b[15], 3) - assert.is.equal(b[16], 4) + assert.is.equal(1, b[1]) + assert.is.equal(2, b[2]) + assert.is.equal(3, b[3]) + assert.is.equal(4, b[4]) + assert.is.equal(1, b[5]) + assert.is.equal(2, b[6]) + assert.is.equal(3, b[7]) + assert.is.equal(4, b[8]) + assert.is.equal(1, b[9]) + assert.is.equal(2, b[10]) + assert.is.equal(3, b[11]) + assert.is.equal(4, b[12]) + assert.is.equal(1, b[13]) + assert.is.equal(2, b[14]) + assert.is.equal(3, b[15]) + assert.is.equal(4, b[16]) end) - it("tests shear", function() + it("shears a matrix", function() local a = mat4() local yx, zx, xy, zy, xz, yz = 1, 1, 1, -1, -1, -1 local b = mat4():shear(a, yx, zx, xy, zy, xz, yz) - assert.is.equal(b[1], 1) - assert.is.equal(b[2], 1) - assert.is.equal(b[3], 1) - assert.is.equal(b[4], 0) - assert.is.equal(b[5], 1) - assert.is.equal(b[6], 1) - assert.is.equal(b[7], -1) - assert.is.equal(b[8], 0) - assert.is.equal(b[9], -1) - assert.is.equal(b[10], -1) - assert.is.equal(b[11], 1) - assert.is.equal(b[12], 0) - assert.is.equal(b[13], 0) - assert.is.equal(b[14], 0) - assert.is.equal(b[15], 0) - assert.is.equal(b[16], 1) + assert.is.equal( 1, b[2]) + assert.is.equal( 1, b[3]) + assert.is.equal( 1, b[5]) + assert.is.equal(-1, b[7]) + assert.is.equal(-1, b[9]) + assert.is.equal(-1, b[10]) end) - it("tests projections", function() + it("projects a matrix into screen space", function() + local v = vec3(0, 0, 10) local a = mat4() local b = mat4.from_perspective(45, 1, 0.1, 1000) - local v = vec3(0, 0, 10) local vp = { 0, 0, 400, 400 } - - -- Project to pixel space local c = mat4.project(v, a, b, vp) - assert.is.equal(c.x, 200) - assert.is.equal(c.y, 200) + assert.is.equal(200, c.x) + assert.is.equal(200, c.y) assert.is_true(utils.tolerance(1.0101-c.z, 0.001)) + end) - -- Unproject to vector space + it("unprojects a matrix into world space", function() + local v = vec3(0, 0, 10) + local a = mat4() + local b = mat4.from_perspective(45, 1, 0.1, 1000) + local vp = { 0, 0, 400, 400 } + local c = mat4.project(v, a, b, vp) local d = mat4.unproject(c, a, b, vp) assert.is_true(utils.tolerance(v.x-d.x, 0.001)) assert.is_true(utils.tolerance(v.y-d.y, 0.001)) assert.is_true(utils.tolerance(v.z-d.z, 0.001)) end) - it("tests convertions", function() + it("converts a matrix to vec4s", function() local a = mat4() + local v = a:to_vec4s() + assert.is_true(type(v) == "table") + assert.is_true(type(v[1]) == "table") + assert.is_true(type(v[2]) == "table") + assert.is_true(type(v[3]) == "table") + assert.is_true(type(v[4]) == "table") - -- Convert to vec4s - do - local v = a:to_vec4s() - assert.is_true(type(v) == "table") - assert.is_true(type(v[1]) == "table") - assert.is_true(type(v[2]) == "table") - assert.is_true(type(v[3]) == "table") - assert.is_true(type(v[4]) == "table") - assert.is.equal(v[1][1], 1) - assert.is.equal(v[1][2], 0) - assert.is.equal(v[1][3], 0) - assert.is.equal(v[1][4], 0) - assert.is.equal(v[2][1], 0) - assert.is.equal(v[2][2], 1) - assert.is.equal(v[2][3], 0) - assert.is.equal(v[2][4], 0) - assert.is.equal(v[3][1], 0) - assert.is.equal(v[3][2], 0) - assert.is.equal(v[3][3], 1) - assert.is.equal(v[3][4], 0) - assert.is.equal(v[4][1], 0) - assert.is.equal(v[4][2], 0) - assert.is.equal(v[4][3], 0) - assert.is.equal(v[4][4], 1) - end + assert.is.equal(1, v[1][1]) + assert.is.equal(0, v[1][2]) + assert.is.equal(0, v[1][3]) + assert.is.equal(0, v[1][4]) - -- Convert to quaternion - do - local b = mat4 { - 0, 0, 1, 0, - 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 0, 0 - } - local q = b:to_quat() - assert.is.equal(q.x, -0.5) - assert.is.equal(q.y, -0.5) - assert.is.equal(q.z, -0.5) - assert.is.equal(q.w, 0.5) - end + assert.is.equal(0, v[2][1]) + assert.is.equal(1, v[2][2]) + assert.is.equal(0, v[2][3]) + assert.is.equal(0, v[2][4]) - -- Convert to frustum - do - local b = mat4.from_perspective(45, 1, 0.1, 1000) - local f = mat4():mul(b, a):to_frustum() + assert.is.equal(0, v[3][1]) + assert.is.equal(0, v[3][2]) + assert.is.equal(1, v[3][3]) + assert.is.equal(0, v[3][4]) - assert.is_true(utils.tolerance( 0.9239-f.left.a, 0.001)) - assert.is_true(utils.tolerance( 0 -f.left.b, 0.001)) - assert.is_true(utils.tolerance(-0.3827-f.left.c, 0.001)) - assert.is_true(utils.tolerance( 0 -f.left.d, 0.001)) + assert.is.equal(0, v[4][1]) + assert.is.equal(0, v[4][2]) + assert.is.equal(0, v[4][3]) + assert.is.equal(1, v[4][4]) + end) - assert.is_true(utils.tolerance(-0.9239-f.right.a, 0.001)) - assert.is_true(utils.tolerance( 0 -f.right.b, 0.001)) - assert.is_true(utils.tolerance(-0.3827-f.right.c, 0.001)) - assert.is_true(utils.tolerance( 0 -f.right.d, 0.001)) + it("converts a matrix to a quaternion", function() + local a = mat4() + local b = mat4 { + 0, 0, 1, 0, + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 0, 0 + } + local q = b:to_quat() + assert.is.equal(-0.5, q.x) + assert.is.equal(-0.5, q.y) + assert.is.equal(-0.5, q.z) + assert.is.equal( 0.5, q.w) + end) - assert.is_true(utils.tolerance( 0 -f.bottom.a, 0.001)) - assert.is_true(utils.tolerance( 0.9239-f.bottom.b, 0.001)) - assert.is_true(utils.tolerance(-0.3827-f.bottom.c, 0.001)) - assert.is_true(utils.tolerance( 0 -f.bottom.d, 0.001)) + it("converts a matrix to a frustum", function() + local a = mat4() + local b = mat4.from_perspective(45, 1, 0.1, 1000) + local f = mat4():mul(b, a):to_frustum() - assert.is_true(utils.tolerance( 0 -f.top.a, 0.001)) - assert.is_true(utils.tolerance(-0.9239-f.top.b, 0.001)) - assert.is_true(utils.tolerance(-0.3827-f.top.c, 0.001)) - assert.is_true(utils.tolerance( 0 -f.top.d, 0.001)) + assert.is_true(utils.tolerance( 0.9239-f.left.a, 0.001)) + assert.is_true(utils.tolerance( 0 -f.left.b, 0.001)) + assert.is_true(utils.tolerance(-0.3827-f.left.c, 0.001)) + assert.is_true(utils.tolerance( 0 -f.left.d, 0.001)) - assert.is_true(utils.tolerance( 0 -f.near.a, 0.001)) - assert.is_true(utils.tolerance( 0 -f.near.b, 0.001)) - assert.is_true(utils.tolerance(-1 -f.near.c, 0.001)) - assert.is_true(utils.tolerance(-0.1-f.near.d, 0.001)) + assert.is_true(utils.tolerance(-0.9239-f.right.a, 0.001)) + assert.is_true(utils.tolerance( 0 -f.right.b, 0.001)) + assert.is_true(utils.tolerance(-0.3827-f.right.c, 0.001)) + assert.is_true(utils.tolerance( 0 -f.right.d, 0.001)) - assert.is_true(utils.tolerance( 0 -f.far.a, 0.001)) - assert.is_true(utils.tolerance( 0 -f.far.b, 0.001)) - assert.is_true(utils.tolerance( 1 -f.far.c, 0.001)) - assert.is_true(utils.tolerance( 1000-f.far.d, 0.001)) - end + assert.is_true(utils.tolerance( 0 -f.bottom.a, 0.001)) + assert.is_true(utils.tolerance( 0.9239-f.bottom.b, 0.001)) + assert.is_true(utils.tolerance(-0.3827-f.bottom.c, 0.001)) + assert.is_true(utils.tolerance( 0 -f.bottom.d, 0.001)) + + assert.is_true(utils.tolerance( 0 -f.top.a, 0.001)) + assert.is_true(utils.tolerance(-0.9239-f.top.b, 0.001)) + assert.is_true(utils.tolerance(-0.3827-f.top.c, 0.001)) + assert.is_true(utils.tolerance( 0 -f.top.d, 0.001)) + + assert.is_true(utils.tolerance( 0 -f.near.a, 0.001)) + assert.is_true(utils.tolerance( 0 -f.near.b, 0.001)) + assert.is_true(utils.tolerance(-1 -f.near.c, 0.001)) + assert.is_true(utils.tolerance(-0.1-f.near.d, 0.001)) + + assert.is_true(utils.tolerance( 0 -f.far.a, 0.001)) + assert.is_true(utils.tolerance( 0 -f.far.b, 0.001)) + assert.is_true(utils.tolerance( 1 -f.far.c, 0.001)) + assert.is_true(utils.tolerance( 1000-f.far.d, 0.001)) end) end) diff --git a/spec/quat_spec.lua b/spec/quat_spec.lua index 556e6f2..514bf56 100644 --- a/spec/quat_spec.lua +++ b/spec/quat_spec.lua @@ -1,233 +1,247 @@ -local quat = require "modules.quat" -local vec3 = require "modules.vec3" +local quat = require "modules.quat" +local vec3 = require "modules.vec3" +local utils = require "modules.utils" describe("quat:", function() - it("tests creating new quaternions", function() + it("creates an identity quaternion", function() local a = quat() - assert.is.equal(a.x, 0) - assert.is.equal(a.y, 0) - assert.is.equal(a.z, 0) - assert.is.equal(a.w, 1) + assert.is.equal(0, a.x) + assert.is.equal(0, a.y) + assert.is.equal(0, a.z) + assert.is.equal(1, a.w) assert.is_true(a:is_quat()) assert.is_true(a:is_real()) - - local b = quat(0, 0, 0, 0) - assert.is.equal(b.x, 0) - assert.is.equal(b.y, 0) - assert.is.equal(b.z, 0) - assert.is.equal(b.w, 0) - assert.is_true(b:is_zero()) - assert.is_true(b:is_imaginary()) - - local c = quat { 2, 3, 4, 1 } - assert.is.equal(c.x, 2) - assert.is.equal(c.y, 3) - assert.is.equal(c.z, 4) - assert.is.equal(c.w, 1) - - local d = quat { x=2, y=3, z=4, w=1 } - assert.is.equal(d.x, 2) - assert.is.equal(d.y, 3) - assert.is.equal(d.z, 4) - assert.is.equal(d.w, 1) - - local e = quat.from_angle_axis(math.pi, vec3.unit_z) - local angle, axis = e:to_angle_axis() - assert.is.equal(angle, math.pi) - assert.is.equal(axis, vec3.unit_z) - - local f = quat.from_direction(vec3():normalize(vec3(5, 10, 15)), vec3.unit_z) - --assert.is.equal(f.x, 0) - --assert.is.equal(f.y, 0) - --assert.is.equal(f.z, 0) - --assert.is.equal(f.w, 0) - - local g = a:clone() - assert.is.equal(g.x, a.x) - assert.is.equal(g.y, a.y) - assert.is.equal(g.z, a.z) - assert.is.equal(g.w, a.w) end) - it("tests standard operators", function() + it("creates a quaternion from numbers", function() + local a = quat(0, 0, 0, 0) + assert.is.equal(0, a.x) + assert.is.equal(0, a.y) + assert.is.equal(0, a.z) + assert.is.equal(0, a.w) + assert.is_true(a:is_zero()) + assert.is_true(a:is_imaginary()) + end) + + it("creates a quaternion from a list", function() + local a = quat { 2, 3, 4, 1 } + assert.is.equal(2, a.x) + assert.is.equal(3, a.y) + assert.is.equal(4, a.z) + assert.is.equal(1, a.w) + end) + + it("creates a quaternion from a record", function() + local a = quat { x=2, y=3, z=4, w=1 } + assert.is.equal(2, a.x) + assert.is.equal(3, a.y) + assert.is.equal(4, a.z) + assert.is.equal(1, a.w) + end) + + it("creates a quaternion from a direction", function() + local v = vec3():normalize(vec3(-80, 80, -80)) + local a = quat.from_direction(v, vec3.unit_z) + assert.is_true(utils.tolerance(-0.577-a.x, 0.001)) + assert.is_true(utils.tolerance(-0.577-a.y, 0.001)) + assert.is_true(utils.tolerance( 0 -a.z, 0.001)) + assert.is_true(utils.tolerance( 0.423-a.w, 0.001)) + end) + + it("clones a quaternion", function() + local a = quat() + local b = a:clone() + assert.is.equal(a.x, b.x) + assert.is.equal(a.y, b.y) + assert.is.equal(a.z, b.z) + assert.is.equal(a.w, b.w) + end) + + it("adds a quaternion to another", function() local a = quat(2, 3, 4, 1) local b = quat(3, 6, 9, 1) - - do - local c = quat():add(a, b) - assert.is.equal(c.x, 5) - assert.is.equal(c.y, 9) - assert.is.equal(c.z, 13) - assert.is.equal(c.w, 2) - - local d = a + b - assert.is.equal(c.x, d.x) - assert.is.equal(c.y, d.y) - assert.is.equal(c.z, d.z) - assert.is.equal(c.w, d.w) - end - - do - local c = quat():sub(a, b) - assert.is.equal(c.x, -1) - assert.is.equal(c.y, -3) - assert.is.equal(c.z, -5) - assert.is.equal(c.w, 0) - - local d = a - b - assert.is.equal(c.x, d.x) - assert.is.equal(c.y, d.y) - assert.is.equal(c.z, d.z) - assert.is.equal(c.w, d.w) - end - - do - local c = quat():mul(a, b) - assert.is.equal(c.x, 8) - assert.is.equal(c.y, 3) - assert.is.equal(c.z, 16) - assert.is.equal(c.w, -59) - - local d = a * b - assert.is.equal(c.x, d.x) - assert.is.equal(c.y, d.y) - assert.is.equal(c.z, d.z) - assert.is.equal(c.w, d.w) - end - - do - local c = quat():scale(a, 3) - assert.is.equal(c.x, 6) - assert.is.equal(c.y, 9) - assert.is.equal(c.z, 12) - assert.is.equal(c.w, 3) - - local d = a * 3 - assert.is.equal(c.x, d.x) - assert.is.equal(c.y, d.y) - assert.is.equal(c.z, d.z) - assert.is.equal(c.w, d.w) - - local e = -a - assert.is.equal(e.x, -a.x) - assert.is.equal(e.y, -a.y) - assert.is.equal(e.z, -a.z) - assert.is.equal(e.w, -a.w) - end - - do - local v = vec3(3, 4, 5) - local c = quat.mul_vec3(vec3(), a, v) - --assert.is.equal(c.x, 0) - --assert.is.equal(c.y, 0) - --assert.is.equal(c.z, 0) - --assert.is.equal(c.w, 0) - - local d = a * v - --assert.is.equal(c.x, d.x) - --assert.is.equal(c.y, d.y) - --assert.is.equal(c.z, d.z) - --assert.is.equal(c.w, d.w) - end - - do - local c = quat():pow(a, 2) - --assert.is.equal(c.x, 0) - --assert.is.equal(c.y, 0) - --assert.is.equal(c.z, 0) - --assert.is.equal(c.w, 0) - - local d = a^2 - --assert.is.equal(c.x, d.x) - --assert.is.equal(c.y, d.y) - --assert.is.equal(c.z, d.z) - --assert.is.equal(c.w, d.w) - end + local c = quat():add(a, b) + local d = a + b + assert.is.equal(5, c.x) + assert.is.equal(9, c.y) + assert.is.equal(13, c.z) + assert.is.equal(2, c.w) + assert.is.equal(c, d) end) - it("tests normal, dot", function() + it("subtracts a quaternion from another", function() + local a = quat(2, 3, 4, 1) + local b = quat(3, 6, 9, 1) + local c = quat():sub(a, b) + local d = a - b + assert.is.equal(-1, c.x) + assert.is.equal(-3, c.y) + assert.is.equal(-5, c.z) + assert.is.equal( 0, c.w) + assert.is.equal(c, d) + end) + + it("multiplies a quaternion by another", function() + local a = quat(2, 3, 4, 1) + local b = quat(3, 6, 9, 1) + local c = quat():mul(a, b) + local d = a * b + assert.is.equal( 8, c.x) + assert.is.equal( 3, c.y) + assert.is.equal( 16, c.z) + assert.is.equal(-59, c.w) + assert.is.equal(c, d) + end) + + it("multiplies a quaternion by a scale factor", function() + local a = quat(2, 3, 4, 1) + local s = 3 + local b = quat():scale(a, s) + local c = a * s + assert.is.equal(6, b.x) + assert.is.equal(9, b.y) + assert.is.equal(12, b.z) + assert.is.equal(3, b.w) + assert.is.equal(b, c) + end) + + it("inverts a quaternion", function() + local a = quat(2, 3, 4, 1) + local b = -a + assert.is.equal(-a.x, b.x) + assert.is.equal(-a.y, b.y) + assert.is.equal(-a.z, b.z) + assert.is.equal(-a.w, b.w) + end) + + it("multiplies a quaternion by a vec3", function() + local a = quat(2, 3, 4, 1) + local v = vec3(3, 4, 5) + local b = quat.mul_vec3(vec3(), a, v) + local c = a * v + assert.is.equal(-21, c.x) + assert.is.equal( 4, c.y) + assert.is.equal( 17, c.z) + assert.is.equal(b, c) + end) + + it("multiplies a quaternion by an exponent", function() + local a = quat(2, 3, 4, 1) + local e = 2 + local b = quat():pow(a, e) + local c = a^e + assert.is.equal( 4, b.x) + assert.is.equal( 14, b.y) + assert.is.equal( 24, b.z) + assert.is.equal(-145, b.w) + assert.is.equal(b, c) + end) + + it("inverts a quaternion", function() + local a = quat(1, 1, 1, 1) + local b = quat():inverse(a) + assert.is.equal(-0.5, b.x) + assert.is.equal(-0.5, b.y) + assert.is.equal(-0.5, b.z) + assert.is.equal( 0.5, b.w) + end) + + it("normalizes a quaternion", function() + local a = quat(1, 1, 1, 1) + local b = quat():normalize(a) + assert.is.equal(0.5, b.x) + assert.is.equal(0.5, b.y) + assert.is.equal(0.5, b.z) + assert.is.equal(0.5, b.w) + end) + + it("dots two quaternions", function() local a = quat(1, 1, 1, 1) local b = quat(4, 4, 4, 4) - local c = quat():normalize(a) - local d = a:dot(b) - - assert.is.equal(c.x, 0.5) - assert.is.equal(c.y, 0.5) - assert.is.equal(c.z, 0.5) - assert.is.equal(c.w, 0.5) - assert.is.equal(d, 16) + local c = a:dot(b) + assert.is.equal(16, c) end) - it("tests length", function() + it("gets the length of a quaternion", function() local a = quat(2, 3, 4, 5) local b = a:len() - local c = a:len2() - - assert.is.equal(b, math.sqrt(54)) - assert.is.equal(c, 54) + assert.is.equal(math.sqrt(54), b) end) - it("tests interpolation", function() + it("gets the square length of a quaternion", function() + local a = quat(2, 3, 4, 5) + local b = a:len2() + assert.is.equal(54, b) + end) + + it("interpolates between two quaternions", function() local a = quat(3, 3, 3, 3) local b = quat(6, 6, 6, 6) local s = 0.1 - local c = quat():lerp(a, b, s) - assert.is.equal(c.x, 0.5) - assert.is.equal(c.y, 0.5) - assert.is.equal(c.z, 0.5) - assert.is.equal(c.w, 0.5) - - local d = quat():slerp(a, b, s) - assert.is.equal(d.x, 0.5) - assert.is.equal(d.y, 0.5) - assert.is.equal(d.z, 0.5) - assert.is.equal(d.w, 0.5) + assert.is.equal(0.5, c.x) + assert.is.equal(0.5, c.y) + assert.is.equal(0.5, c.z) + assert.is.equal(0.5, c.w) end) - it("tests extraction", function() + it("interpolates between two quaternions (spherical)", function() + local a = quat(3, 3, 3, 3) + local b = quat(6, 6, 6, 6) + local s = 0.1 + local c = quat():slerp(a, b, s) + assert.is.equal(0.5, c.x) + assert.is.equal(0.5, c.y) + assert.is.equal(0.5, c.z) + assert.is.equal(0.5, c.w) + end) + + it("unpacks a quaternion", function() local a = quat(2, 3, 4, 1) - local x, y, z, w = a:unpack() - assert.is.equal(x, 2) - assert.is.equal(y, 3) - assert.is.equal(z, 4) - assert.is.equal(w, 1) - - local v = a:to_vec3() - assert.is.equal(v.x, 2) - assert.is.equal(v.y, 3) - assert.is.equal(v.z, 4) + assert.is.equal(2, x) + assert.is.equal(3, y) + assert.is.equal(4, z) + assert.is.equal(1, w) end) - it("tests conjugate", function() + it("converts quaternion to a vec3", function() + local a = quat(2, 3, 4, 1) + local v = a:to_vec3() + assert.is.equal(2, v.x) + assert.is.equal(3, v.y) + assert.is.equal(4, v.z) + end) + + it("gets the conjugate quaternion", function() local a = quat(2, 3, 4, 1) local b = quat():conjugate(a) - - assert.is.equal(b.x, -2) - assert.is.equal(b.y, -3) - assert.is.equal(b.z, -4) - assert.is.equal(b.w, 1) + assert.is.equal(-2, b.x) + assert.is.equal(-3, b.y) + assert.is.equal(-4, b.z) + assert.is.equal( 1, b.w) end) - it("tests inverse", function() - local a = quat(1, 1, 1, 1) - local b = quat():inverse(a) - - assert.is.equal(b.x, -0.5) - assert.is.equal(b.y, -0.5) - assert.is.equal(b.z, -0.5) - assert.is.equal(b.w, 0.5) - end) - - it("tests reciprocal", function() + it("gets the reciprocal quaternion", function() local a = quat(1, 1, 1, 1) local b = quat():reciprocal(a) local c = quat():reciprocal(b) - assert.is.equal(c.x, a.x) - assert.is.equal(c.y, a.y) - assert.is.equal(c.z, a.z) - assert.is.equal(c.w, a.w) + assert.is_not.equal(a.x, b.x) + assert.is_not.equal(a.y, b.y) + assert.is_not.equal(a.z, b.z) + assert.is_not.equal(a.w, b.w) + + assert.is.equal(a.x, c.x) + assert.is.equal(a.y, c.y) + assert.is.equal(a.z, c.z) + assert.is.equal(a.w, c.w) + end) + + it("converts between a quaternion and angle/axis", function() + local a = quat.from_angle_axis(math.pi, vec3.unit_z) + local angle, axis = a:to_angle_axis() + assert.is.equal(math.pi, angle) + assert.is.equal(vec3.unit_z, axis) end) end) diff --git a/spec/vec2_spec.lua b/spec/vec2_spec.lua index 1fc6fe9..c743268 100644 --- a/spec/vec2_spec.lua +++ b/spec/vec2_spec.lua @@ -3,171 +3,175 @@ local DBL_EPSILON = require("modules.constants").DBL_EPSILON local abs, sqrt = math.abs, math.sqrt describe("vec2:", function() - it("tests creating vectors", function() - -- new empty vector + it("creates an empty vector", function() local a = vec2() - assert.is.equal(a.x, 0) - assert.is.equal(a.y, 0) + assert.is.equal(0, a.x) + assert.is.equal(0, a.y) assert.is_true(a:is_vec2()) assert.is_true(a:is_zero()) - - -- new vector from table - local b = vec2 { 0, 0 } - assert.is.equal(b.x, 0) - assert.is.equal(b.y, 0) - - local c = vec2 { x=0, y=0 } - assert.is.equal(c.x, 0) - assert.is.equal(c.y, 0) - - -- new vector from numbers - local d = vec2(3.14159, -2.808) - assert.is.equal(d.x, 3.14159) - assert.is.equal(d.y, -2.808) - - -- new vector from other vector - local e = d:clone() - assert.is.equal(d, e) end) - it("tests basic operators", function() + it("creates a vector from numbers", function() + local a = vec2(3, 5) + assert.is.equal(3, a.x) + assert.is.equal(5, a.y) + end) + + it("creates a vector from a list", function() + local a = vec2 { 3, 5 } + assert.is.equal(3, a.x) + assert.is.equal(5, a.y) + end) + + it("creates a vector from a record", function() + local a = vec2 { x=3, y=5 } + assert.is.equal(3, a.x) + assert.is.equal(5, a.y) + end) + + it("clones a vector", function() + local a = vec2(3, 5) + local b = a:clone() + assert.is.equal(a, b) + end) + + it("adds a vector to another", function() local a = vec2(3, 5) local b = vec2(7, 4) - local s = 2 - - -- Add - do - local c = vec2():add(a, b) - local d = a + b - - assert.is.equal(c.x, 10) - assert.is.equal(c.y, 9) - assert.is.equal(c, d) - end - - -- Subtract - do - local c = vec2():sub(a, b) - local d = a - b - - assert.is.equal(c.x, -4) - assert.is.equal(c.y, 1) - assert.is.equal(c, d) - end - - -- Multiply - do - local c = vec2():mul(a, s) - local d = a * s - - assert.is.equal(c.x, 6) - assert.is.equal(c.y, 10) - assert.is.equal(c, d) - end - - -- Divide - do - local c = vec2():div(a, s) - local d = a / s - - assert.is.equal(c.x, 1.5) - assert.is.equal(c.y, 2.5) - assert.is.equal(c, d) - end - - -- Sign flip - do - local c = -a - assert.is.equal(c.x, -3) - assert.is.equal(c.y, -5) - end + local c = vec2():add(a, b) + local d = a + b + assert.is.equal(10, c.x) + assert.is.equal(9, c.y) + assert.is.equal(c, d) end) - it("tests normal, trim, length", function() + it("subracts a vector from another", function() + local a = vec2(3, 5) + local b = vec2(7, 4) + local c = vec2():sub(a, b) + local d = a - b + assert.is.equal(-4, c.x) + assert.is.equal( 1, c.y) + assert.is.equal( c, d) + end) + + it("multiplies a vector by a scale factor", function() + local a = vec2(3, 5) + local s = 2 + local c = vec2():mul(a, s) + local d = a * s + assert.is.equal(6, c.x) + assert.is.equal(10, c.y) + assert.is.equal(c, d) + end) + + it("divides a vector by a scale factor", function() + local a = vec2(3, 5) + local s = 2 + local c = vec2():div(a, s) + local d = a / s + assert.is.equal(1.5, c.x) + assert.is.equal(2.5, c.y) + assert.is.equal(c, d) + end) + + it("inverts a vector", function() + local a = vec2(3, -5) + local b = -a + assert.is.equal(-a.x, b.x) + assert.is.equal(-a.y, b.y) + end) + + it("gets the length of a vector", function() + local a = vec2(3, 5) + assert.is.equal(sqrt(34), a:len()) + end) + + it("gets the square length of a vector", function() + local a = vec2(3, 5) + assert.is.equal(34, a:len2()) + end) + + it("normalizes a vector", function() local a = vec2(3, 5) local b = vec2():normalize(a) - local c = vec2():trim(a, 0.5) + assert.is_true(abs(b:len()-1) < DBL_EPSILON) + end) - assert.is_true(abs(b:len() - 1) < DBL_EPSILON) - assert.is_true(abs(b:len() - 1) < DBL_EPSILON) - assert.is_true(abs(b:len2() - 1) < DBL_EPSILON * 2) - assert.is_true(abs(c:len() - 0.5) < DBL_EPSILON) + it("trims the length of a vector", function() + local a = vec2(3, 5) + local b = vec2():trim(a, 0.5) + assert.is_true(abs(b:len()-0.5) < DBL_EPSILON) end) - it("tests distance", function() + it("gets the distance between two vectors", function() local a = vec2(3, 5) local b = vec2(7, 4) - - -- Distance - do - local c = a:dist(b) - assert.is.equal(c, sqrt(17)) - end - - -- Distance Squared - do - local c = a:dist2(b) - assert.is.equal(c, 17) - end + local c = a:dist(b) + assert.is.equal(sqrt(17), c) end) - it("tests cross product", function() + it("gets the square distance between two vectors", function() + local a = vec2(3, 5) + local b = vec2(7, 4) + local c = a:dist2(b) + assert.is.equal(17, c) + end) + + it("crosses two vectors", function() local a = vec2(3, 5) local b = vec2(7, 4) local c = a:cross(b) - - assert.is.equal(c, -23) + assert.is.equal(-23, c) end) - it("tests dot product", function() + it("dots two vectors", function() local a = vec2(3, 5) local b = vec2(7, 4) local c = a:dot(b) - - assert.is.equal(c, 41) + assert.is.equal(41, c) end) - it("tests lerp", function() + it("interpolates between two vectors", function() local a = vec2(3, 5) local b = vec2(7, 4) local s = 0.1 local c = vec2():lerp(a, b, s) - - assert.is.equal(c.x, 3.4) - assert.is.equal(c.y, 4.9) + assert.is.equal(3.4, c.x) + assert.is.equal(4.9, c.y) end) - it("tests unpack", function() + it("unpacks a vector", function() local a = vec2(3, 5) local x, y = a:unpack() - - assert.is.equal(x, 3) - assert.is.equal(y, 5) + assert.is.equal(3, x) + assert.is.equal(5, y) end) - it("tests rotate", function() + it("rotates a vector", function() local a = vec2(3, 5) local b = vec2():rotate(a, math.pi) local c = vec2():rotate(b, -math.pi) - assert.is.equal(c.x, 3) - assert.is.equal(c.y, 5) + assert.is_not.equal(3, b.x) + assert.is_not.equal(5, b.y) + + assert.is.equal(3, c.x) + assert.is.equal(5, c.y) end) - it("tests converting coordinates", function() + it("converts between polar and cartesian coordinates", function() local a = vec2(3, 5) local r, t = a:to_polar() local b = vec2.from_cartesian(r, t) - - assert.is.equal(b.x, a.x) - assert.is.equal(b.y, a.y) + assert.is.equal(a.x, b.x) + assert.is.equal(a.y, b.y) end) - it("tests perpendicular", function() + it("gets a perpendicular vector", function() local a = vec2(3, 5) local b = vec2():perpendicular(a) - - assert.is.equal(b.x, -5) - assert.is.equal(b.y, 3) + assert.is.equal(-5, b.x) + assert.is.equal( 3, b.y) end) end) diff --git a/spec/vec3_spec.lua b/spec/vec3_spec.lua index 4a5a8bf..aa5e510 100644 --- a/spec/vec3_spec.lua +++ b/spec/vec3_spec.lua @@ -3,176 +3,183 @@ local DBL_EPSILON = require("modules.constants").DBL_EPSILON local abs, sqrt = math.abs, math.sqrt describe("vec3:", function() - it("tests creating vectors", function() - -- new empty vector + it("creates an empty vector", function() local a = vec3() - assert.is.equal(a.x, 0) - assert.is.equal(a.y, 0) - assert.is.equal(a.z, 0) + assert.is.equal(0, a.x) + assert.is.equal(0, a.y) + assert.is.equal(0, a.z) assert.is_true(a:is_vec3()) assert.is_true(a:is_zero()) - - -- new vector from table - local b = vec3 { 0, 0, 0 } - assert.is.equal(b.x, 0) - assert.is.equal(b.y, 0) - assert.is.equal(b.z, 0) - - local c = vec3 { x=0, y=0, z=0 } - assert.is.equal(c.x, 0) - assert.is.equal(c.y, 0) - assert.is.equal(c.z, 0) - - -- new vector from numbers - local d = vec3(3.14159, -2.808, 1.337) - assert.is.equal(d.x, 3.14159) - assert.is.equal(d.y, -2.808) - assert.is.equal(d.z, 1.337) - - -- new vector from other vector - local e = d:clone() - assert.is.equal(d, e) end) - it("tests basic operators", function() + it("creates a vector from numbers", function() + local a = vec3(3, 5, 7) + assert.is.equal(3, a.x) + assert.is.equal(5, a.y) + assert.is.equal(7, a.z) + end) + + it("creates a vector from a list", function() + local a = vec3 { 3, 5, 7 } + assert.is.equal(3, a.x) + assert.is.equal(5, a.y) + assert.is.equal(7, a.z) + end) + + it("creates a vector from a record", function() + local a = vec3 { x=3, y=5, z=7 } + assert.is.equal(3, a.x) + assert.is.equal(5, a.y) + assert.is.equal(7, a.z) + end) + + it("clones a vector", function() + local a = vec3(3, 5, 7) + local b = a:clone() + assert.is.equal(a, b) + end) + + it("adds a vector to another", function() local a = vec3(3, 5, 7) local b = vec3(7, 4, 1) - local s = 2 - - -- Add - do - local c = vec3():add(a, b) - local d = a + b - - assert.is.equal(c.x, 10) - assert.is.equal(c.y, 9) - assert.is.equal(c.z, 8) - assert.is.equal(c, d) - end - - -- Subtract - do - local c = vec3():sub(a, b) - local d = a - b - - assert.is.equal(c.x, -4) - assert.is.equal(c.y, 1) - assert.is.equal(c.z, 6) - assert.is.equal(c, d) - end - - -- Multiply - do - local c = vec3():mul(a, s) - local d = a * s - - assert.is.equal(c.x, 6) - assert.is.equal(c.y, 10) - assert.is.equal(c.z, 14) - assert.is.equal(c, d) - end - - -- Divide - do - local c = vec3():div(a, s) - local d = a / s - - assert.is.equal(c.x, 1.5) - assert.is.equal(c.y, 2.5) - assert.is.equal(c.z, 3.5) - assert.is.equal(c, d) - end - - -- Sign flip - do - local c = -a - assert.is.equal(c.x, -3) - assert.is.equal(c.y, -5) - assert.is.equal(c.z, -7) - end + local c = vec3():add(a, b) + local d = a + b + assert.is.equal(10, c.x) + assert.is.equal(9, c.y) + assert.is.equal(8, c.z) + assert.is.equal(c, d) end) - it("tests normal, trim, length", function() + it("subracts a vector from another", function() + local a = vec3(3, 5, 7) + local b = vec3(7, 4, 1) + local c = vec3():sub(a, b) + local d = a - b + assert.is.equal(-4, c.x) + assert.is.equal( 1, c.y) + assert.is.equal( 6, c.z) + assert.is.equal( c, d) + end) + + it("multiplies a vector by a scale factor", function() + local a = vec3(3, 5, 7) + local s = 2 + local c = vec3():mul(a, s) + local d = a * s + assert.is.equal(6, c.x) + assert.is.equal(10, c.y) + assert.is.equal(14, c.z) + assert.is.equal(c, d) + end) + + it("divides a vector by a scale factor", function() + local a = vec3(3, 5, 7) + local s = 2 + local c = vec3():div(a, s) + local d = a / s + assert.is.equal(1.5, c.x) + assert.is.equal(2.5, c.y) + assert.is.equal(3.5, c.z) + assert.is.equal(c, d) + end) + + it("inverts a vector", function() + local a = vec3(3, -5, 7) + local b = -a + assert.is.equal(-a.x, b.x) + assert.is.equal(-a.y, b.y) + assert.is.equal(-a.z, b.z) + end) + + it("gets the length of a vector", function() + local a = vec3(3, 5, 7) + assert.is.equal(sqrt(83), a:len()) + end) + + it("gets the square length of a vector", function() + local a = vec3(3, 5, 7) + assert.is.equal(83, a:len2()) + end) + + it("normalizes a vector", function() local a = vec3(3, 5, 7) local b = vec3():normalize(a) - local c = vec3():trim(a, 0.5) + assert.is_true(abs(b:len()-1) < DBL_EPSILON) + end) - assert.is_true(abs(b:len() - 1) < DBL_EPSILON) - assert.is_true(abs(b:len() - 1) < DBL_EPSILON) - assert.is_true(abs(b:len2() - 1) < DBL_EPSILON * 2) - assert.is_true(abs(c:len() - 0.5) < DBL_EPSILON) + it("trims the length of a vector", function() + local a = vec3(3, 5, 7) + local b = vec3():trim(a, 0.5) + assert.is_true(abs(b:len()-0.5) < DBL_EPSILON) end) - it("tests distance", function() + it("gets the distance between two vectors", function() local a = vec3(3, 5, 7) local b = vec3(7, 4, 1) - - -- Distance - do - local c = a:dist(b) - assert.is.equal(c, sqrt(53)) - end - - -- Distance Squared - do - local c = a:dist2(b) - assert.is.equal(c, 53) - end + local c = a:dist(b) + assert.is.equal(sqrt(53), c) end) - it("tests cross product", function() + it("gets the square distance between two vectors", function() + local a = vec3(3, 5, 7) + local b = vec3(7, 4, 1) + local c = a:dist2(b) + assert.is.equal(53, c) + end) + + it("crosses two vectors", function() local a = vec3(3, 5, 7) local b = vec3(7, 4, 1) local c = vec3():cross(a, b) - - assert.is.equal(c.x, -23) - assert.is.equal(c.y, 46) - assert.is.equal(c.z, -23) + assert.is.equal(-23, c.x) + assert.is.equal( 46, c.y) + assert.is.equal(-23, c.z) end) - it("tests dot product", function() + it("dots two vectors", function() local a = vec3(3, 5, 7) local b = vec3(7, 4, 1) local c = a:dot(b) - assert.is.equal(c, 48) + assert.is.equal(48, c) end) - it("tests lerp", function() + it("interpolates between two vectors", function() local a = vec3(3, 5, 7) local b = vec3(7, 4, 1) local s = 0.1 local c = vec3():lerp(a, b, s) - - assert.is.equal(c.x, 3.4) - assert.is.equal(c.y, 4.9) - assert.is.equal(c.z, 6.4) + assert.is.equal(3.4, c.x) + assert.is.equal(4.9, c.y) + assert.is.equal(6.4, c.z) end) - it("tests unpack", function() - local a = vec3(3, 5, 7) + it("unpacks a vector", function() + local a = vec3(3, 5, 7) local x, y, z = a:unpack() - - assert.is.equal(x, 3) - assert.is.equal(y, 5) - assert.is.equal(z, 7) + assert.is.equal(3, x) + assert.is.equal(5, y) + assert.is.equal(7, z) end) - it("tests rotate", function() + it("rotates a vector", function() local a = vec3(3, 5, 7) local b = vec3():rotate(a, math.pi, vec3.unit_z) local c = vec3():rotate(b, -math.pi, vec3.unit_z) - assert.is.equal(c.x, 3) - assert.is.equal(c.y, 5) - assert.is.equal(c.z, 7) + assert.is_not.equal(3, b.x) + assert.is_not.equal(5, b.y) + + assert.is.equal(7, b.z) + assert.is.equal(3, c.x) + assert.is.equal(5, c.y) + assert.is.equal(7, c.z) end) - it("tests perpendicular", function() + it("gets a perpendicular vector", function() local a = vec3(3, 5, 7) local b = vec3():perpendicular(a) - - assert.is.equal(b.x, -5) - assert.is.equal(b.y, 3) - assert.is.equal(b.z, 0) + assert.is.equal(-5, b.x) + assert.is.equal( 3, b.y) + assert.is.equal( 0, b.z) end) end)