local quat = require "modules.quat" local vec3 = require "modules.vec3" local utils = require "modules.utils" describe("quat:", function() it("creates an identity quaternion", function() local a = quat() 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()) end) 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) 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("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 of 0", function() local a = quat(2, 3, 4, 1) local e = 0 local b = quat():pow(a, e) local c = a^e assert.is.equal(0, b.x) assert.is.equal(0, b.y) assert.is.equal(0, b.z) assert.is.equal(1, b.w) assert.is.equal(b, c) end) it("multiplies a quaternion by a positive 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("multiplies a quaternion by a negative exponent", function() local a = quat(2, 3, 4, 1) local e = -2 local b = quat():pow(a, e) local c = a^e assert.is_true(utils.tolerance(0.004-b.x, 0.001)) assert.is_true(utils.tolerance(0.01 -b.y, 0.001)) assert.is_true(utils.tolerance(0.018-b.z, 0.001)) assert.is_true(utils.tolerance(0.001-b.w, 0.001)) 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 = a:dot(b) assert.is.equal(16, c) end) it("dots two quaternions (negative)", function() local a = quat(-1, 1, 1, 1) local b = quat(4, 4, 4, 4) local c = a:dot(b) assert.is.equal(8, c) end) it("dots two quaternions (tiny)", function() local a = quat(0.1, 0.1, 0.1, 0.1) local b = quat(0.4, 0.4, 0.4, 0.4) local c = a:dot(b) assert.is_true(utils.tolerance(0.16-c, 0.001)) end) it("gets the length of a quaternion", function() local a = quat(2, 3, 4, 5) local b = a:len() assert.is.equal(math.sqrt(54), b) end) 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(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("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(2, x) assert.is.equal(3, y) assert.is.equal(4, z) assert.is.equal(1, w) end) 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(-2, b.x) assert.is.equal(-3, b.y) assert.is.equal(-4, b.z) assert.is.equal( 1, b.w) end) 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_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) it("converts between a quaternion and angle/axis (w=2)", function() local a = quat(1, 1, 1, 2) local _, axis = a:to_angle_axis() assert.is_true(utils.tolerance(0.378-a.x, 0.001)) assert.is_true(utils.tolerance(0.378-a.y, 0.001)) assert.is_true(utils.tolerance(0.378-a.z, 0.001)) assert.is_true(utils.tolerance(0.756-a.w, 0.001)) end) it("converts between a quaternion and angle/axis (w=1)", function() local a = quat(1, 2, 3, 1) local _, axis = a:to_angle_axis() assert.is.equal(1, axis.x) assert.is.equal(2, axis.y) assert.is.equal(3, axis.z) end) it("gets a string representation of a quaternion", function() local a = quat() local b = a:to_string() assert.is.equal("(+0.000,+0.000,+0.000,+1.000)", b) end) end)