quat_spec.lua 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. local quat = require "modules.quat"
  2. local vec3 = require "modules.vec3"
  3. local utils = require "modules.utils"
  4. describe("quat:", function()
  5. it("creates an identity quaternion", function()
  6. local a = quat()
  7. assert.is.equal(0, a.x)
  8. assert.is.equal(0, a.y)
  9. assert.is.equal(0, a.z)
  10. assert.is.equal(1, a.w)
  11. assert.is_true(a:is_quat())
  12. assert.is_true(a:is_real())
  13. end)
  14. it("creates a quaternion from numbers", function()
  15. local a = quat(0, 0, 0, 0)
  16. assert.is.equal(0, a.x)
  17. assert.is.equal(0, a.y)
  18. assert.is.equal(0, a.z)
  19. assert.is.equal(0, a.w)
  20. assert.is_true(a:is_zero())
  21. assert.is_true(a:is_imaginary())
  22. end)
  23. it("creates a quaternion from a list", function()
  24. local a = quat { 2, 3, 4, 1 }
  25. assert.is.equal(2, a.x)
  26. assert.is.equal(3, a.y)
  27. assert.is.equal(4, a.z)
  28. assert.is.equal(1, a.w)
  29. end)
  30. it("creates a quaternion from a record", function()
  31. local a = quat { x=2, y=3, z=4, w=1 }
  32. assert.is.equal(2, a.x)
  33. assert.is.equal(3, a.y)
  34. assert.is.equal(4, a.z)
  35. assert.is.equal(1, a.w)
  36. end)
  37. it("creates a quaternion from a direction", function()
  38. local v = vec3(-80, 80, -80):normalize()
  39. local a = quat.from_direction(v, vec3.unit_z)
  40. assert.is_true(utils.tolerance(-0.577-a.x, 0.001))
  41. assert.is_true(utils.tolerance(-0.577-a.y, 0.001))
  42. assert.is_true(utils.tolerance( 0 -a.z, 0.001))
  43. assert.is_true(utils.tolerance( 0.423-a.w, 0.001))
  44. end)
  45. it("clones a quaternion", function()
  46. local a = quat()
  47. local b = a:clone()
  48. assert.is.equal(a.x, b.x)
  49. assert.is.equal(a.y, b.y)
  50. assert.is.equal(a.z, b.z)
  51. assert.is.equal(a.w, b.w)
  52. end)
  53. it("adds a quaternion to another", function()
  54. local a = quat(2, 3, 4, 1)
  55. local b = quat(3, 6, 9, 1)
  56. local c = a:add(b)
  57. local d = a + b
  58. assert.is.equal(5, c.x)
  59. assert.is.equal(9, c.y)
  60. assert.is.equal(13, c.z)
  61. assert.is.equal(2, c.w)
  62. assert.is.equal(c, d)
  63. end)
  64. it("subtracts a quaternion from another", function()
  65. local a = quat(2, 3, 4, 1)
  66. local b = quat(3, 6, 9, 1)
  67. local c = a:sub(b)
  68. local d = a - b
  69. assert.is.equal(-1, c.x)
  70. assert.is.equal(-3, c.y)
  71. assert.is.equal(-5, c.z)
  72. assert.is.equal( 0, c.w)
  73. assert.is.equal(c, d)
  74. end)
  75. it("multiplies a quaternion by another", function()
  76. local a = quat(2, 3, 4, 1)
  77. local b = quat(3, 6, 9, 1)
  78. local c = a:mul(b)
  79. local d = a * b
  80. assert.is.equal( 8, c.x)
  81. assert.is.equal( 3, c.y)
  82. assert.is.equal( 16, c.z)
  83. assert.is.equal(-59, c.w)
  84. assert.is.equal(c, d)
  85. end)
  86. it("multiplies a quaternion by a scale factor", function()
  87. local a = quat(2, 3, 4, 1)
  88. local s = 3
  89. local b = a:scale(s)
  90. local c = a * s
  91. assert.is.equal(6, b.x)
  92. assert.is.equal(9, b.y)
  93. assert.is.equal(12, b.z)
  94. assert.is.equal(3, b.w)
  95. assert.is.equal(b, c)
  96. end)
  97. it("inverts a quaternion", function()
  98. local a = quat(2, 3, 4, 1)
  99. local b = -a
  100. assert.is.equal(-a.x, b.x)
  101. assert.is.equal(-a.y, b.y)
  102. assert.is.equal(-a.z, b.z)
  103. assert.is.equal(-a.w, b.w)
  104. end)
  105. it("multiplies a quaternion by a vec3", function()
  106. local a = quat(2, 3, 4, 1)
  107. local v = vec3(3, 4, 5)
  108. local b = a:mul_vec3(v)
  109. local c = a * v
  110. assert.is.equal(-21, c.x)
  111. assert.is.equal( 4, c.y)
  112. assert.is.equal( 17, c.z)
  113. assert.is.equal(b, c)
  114. end)
  115. it("multiplies a quaternion by an exponent of 0", function()
  116. local a = quat(2, 3, 4, 1):normalize()
  117. local e = 0
  118. local b = a:pow(e)
  119. local c = a^e
  120. assert.is.equal(0, b.x)
  121. assert.is.equal(0, b.y)
  122. assert.is.equal(0, b.z)
  123. assert.is.equal(1, b.w)
  124. assert.is.equal(b, c)
  125. end)
  126. it("multiplies a quaternion by a positive exponent", function()
  127. local a = quat(2, 3, 4, 1):normalize()
  128. local e = 0.75
  129. local b = a:pow(e)
  130. local c = a^e
  131. assert.is_true(utils.tolerance(-0.3204+b.x, 0.0001))
  132. assert.is_true(utils.tolerance(-0.4805+b.y, 0.0001))
  133. assert.is_true(utils.tolerance(-0.6407+b.z, 0.0001))
  134. assert.is_true(utils.tolerance(-0.5059+b.w, 0.0001))
  135. assert.is.equal( b, c)
  136. end)
  137. it("multiplies a quaternion by a negative exponent", function()
  138. local a = quat(2, 3, 4, 1):normalize()
  139. local e = -1
  140. local b = a:pow(e)
  141. local c = a^e
  142. assert.is_true(utils.tolerance( 0.3651+b.x, 0.0001))
  143. assert.is_true(utils.tolerance( 0.5477+b.y, 0.0001))
  144. assert.is_true(utils.tolerance( 0.7303+b.z, 0.0001))
  145. assert.is_true(utils.tolerance(-0.1826+b.w, 0.0001))
  146. assert.is.equal(b, c)
  147. end)
  148. it("inverts a quaternion", function()
  149. local a = quat(1, 1, 1, 1):inverse()
  150. assert.is.equal(-0.5, a.x)
  151. assert.is.equal(-0.5, a.y)
  152. assert.is.equal(-0.5, a.z)
  153. assert.is.equal( 0.5, a.w)
  154. end)
  155. it("normalizes a quaternion", function()
  156. local a = quat(1, 1, 1, 1):normalize()
  157. assert.is.equal(0.5, a.x)
  158. assert.is.equal(0.5, a.y)
  159. assert.is.equal(0.5, a.z)
  160. assert.is.equal(0.5, a.w)
  161. end)
  162. it("dots two quaternions", function()
  163. local a = quat(1, 1, 1, 1)
  164. local b = quat(4, 4, 4, 4)
  165. local c = a:dot(b)
  166. assert.is.equal(16, c)
  167. end)
  168. it("dots two quaternions (negative)", function()
  169. local a = quat(-1, 1, 1, 1)
  170. local b = quat(4, 4, 4, 4)
  171. local c = a:dot(b)
  172. assert.is.equal(8, c)
  173. end)
  174. it("dots two quaternions (tiny)", function()
  175. local a = quat(0.1, 0.1, 0.1, 0.1)
  176. local b = quat(0.4, 0.4, 0.4, 0.4)
  177. local c = a:dot(b)
  178. assert.is_true(utils.tolerance(0.16-c, 0.001))
  179. end)
  180. it("gets the length of a quaternion", function()
  181. local a = quat(2, 3, 4, 5):len()
  182. assert.is.equal(math.sqrt(54), a)
  183. end)
  184. it("gets the square length of a quaternion", function()
  185. local a = quat(2, 3, 4, 5):len2()
  186. assert.is.equal(54, a)
  187. end)
  188. it("interpolates between two quaternions", function()
  189. local a = quat(3, 3, 3, 3)
  190. local b = quat(6, 6, 6, 6)
  191. local s = 0.1
  192. local c = a:lerp(b, s)
  193. assert.is.equal(0.5, c.x)
  194. assert.is.equal(0.5, c.y)
  195. assert.is.equal(0.5, c.z)
  196. assert.is.equal(0.5, c.w)
  197. end)
  198. it("interpolates between two quaternions (spherical)", function()
  199. local a = quat(3, 3, 3, 3)
  200. local b = quat(6, 6, 6, 6)
  201. local s = 0.1
  202. local c = a:slerp(b, s)
  203. assert.is.equal(0.5, c.x)
  204. assert.is.equal(0.5, c.y)
  205. assert.is.equal(0.5, c.z)
  206. assert.is.equal(0.5, c.w)
  207. end)
  208. it("unpacks a quaternion", function()
  209. local x, y, z, w = quat(2, 3, 4, 1):unpack()
  210. assert.is.equal(2, x)
  211. assert.is.equal(3, y)
  212. assert.is.equal(4, z)
  213. assert.is.equal(1, w)
  214. end)
  215. it("converts quaternion to a vec3", function()
  216. local v = quat(2, 3, 4, 1):to_vec3()
  217. assert.is.equal(2, v.x)
  218. assert.is.equal(3, v.y)
  219. assert.is.equal(4, v.z)
  220. end)
  221. it("gets the conjugate quaternion", function()
  222. local a = quat(2, 3, 4, 1):conjugate()
  223. assert.is.equal(-2, a.x)
  224. assert.is.equal(-3, a.y)
  225. assert.is.equal(-4, a.z)
  226. assert.is.equal( 1, a.w)
  227. end)
  228. it("gets the reciprocal quaternion", function()
  229. local a = quat(1, 1, 1, 1)
  230. local b = a:reciprocal()
  231. local c = b:reciprocal()
  232. assert.is_not.equal(a.x, b.x)
  233. assert.is_not.equal(a.y, b.y)
  234. assert.is_not.equal(a.z, b.z)
  235. assert.is_not.equal(a.w, b.w)
  236. assert.is.equal(a.x, c.x)
  237. assert.is.equal(a.y, c.y)
  238. assert.is.equal(a.z, c.z)
  239. assert.is.equal(a.w, c.w)
  240. end)
  241. it("converts between a quaternion and angle/axis", function()
  242. local a = quat.from_angle_axis(math.pi, vec3.unit_z)
  243. local angle, axis = a:to_angle_axis()
  244. assert.is.equal(math.pi, angle)
  245. assert.is.equal(vec3.unit_z, axis)
  246. end)
  247. it("converts between a quaternion and angle/axis (specify by component)", function()
  248. local a = quat.from_angle_axis(math.pi, vec3.unit_z.x, vec3.unit_z.y, vec3.unit_z.z)
  249. local angle, axis = a:to_angle_axis()
  250. assert.is.equal(math.pi, angle)
  251. assert.is.equal(vec3.unit_z, axis)
  252. end)
  253. it("converts between a quaternion and angle/axis (w=2)", function()
  254. local angle, axis = quat(1, 1, 1, 2):to_angle_axis()
  255. assert.is_true(utils.tolerance(1.427-angle, 0.001))
  256. assert.is_true(utils.tolerance(0.577-axis.x, 0.001))
  257. assert.is_true(utils.tolerance(0.577-axis.y, 0.001))
  258. assert.is_true(utils.tolerance(0.577-axis.z, 0.001))
  259. end)
  260. it("converts between a quaternion and angle/axis (w=2) (by component)", function()
  261. local angle, x,y,z = quat(1, 1, 1, 2):to_angle_axis_unpack()
  262. assert.is_true(utils.tolerance(1.427-angle, 0.001))
  263. assert.is_true(utils.tolerance(0.577-x, 0.001))
  264. assert.is_true(utils.tolerance(0.577-y, 0.001))
  265. assert.is_true(utils.tolerance(0.577-z, 0.001))
  266. end)
  267. it("converts between a quaternion and angle/axis (w=1)", function()
  268. local angle, axis = quat(1, 2, 3, 1):to_angle_axis()
  269. assert.is.equal(0, angle)
  270. assert.is.equal(1, axis.x)
  271. assert.is.equal(2, axis.y)
  272. assert.is.equal(3, axis.z)
  273. end)
  274. it("gets a string representation of a quaternion", function()
  275. local a = quat():to_string()
  276. assert.is.equal("(+0.000,+0.000,+0.000,+1.000)", a)
  277. end)
  278. end)