vector_spec.lua 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. _G.vector = {}
  2. dofile("builtin/common/vector.lua")
  3. describe("vector", function()
  4. describe("new()", function()
  5. it("constructs", function()
  6. assert.same({ x = 0, y = 0, z = 0 }, vector.new())
  7. assert.same({ x = 1, y = 2, z = 3 }, vector.new(1, 2, 3))
  8. assert.same({ x = 3, y = 2, z = 1 }, vector.new({ x = 3, y = 2, z = 1 }))
  9. local input = vector.new({ x = 3, y = 2, z = 1 })
  10. local output = vector.new(input)
  11. assert.same(input, output)
  12. assert.are_not.equal(input, output)
  13. end)
  14. it("throws on invalid input", function()
  15. assert.has.errors(function()
  16. vector.new({ x = 3 })
  17. end)
  18. assert.has.errors(function()
  19. vector.new({ d = 3 })
  20. end)
  21. end)
  22. end)
  23. it("equal()", function()
  24. local function assertE(a, b)
  25. assert.is_true(vector.equals(a, b))
  26. end
  27. local function assertNE(a, b)
  28. assert.is_false(vector.equals(a, b))
  29. end
  30. assertE({x = 0, y = 0, z = 0}, {x = 0, y = 0, z = 0})
  31. assertE({x = -1, y = 0, z = 1}, {x = -1, y = 0, z = 1})
  32. local a = { x = 2, y = 4, z = -10 }
  33. assertE(a, a)
  34. assertNE({x = -1, y = 0, z = 1}, a)
  35. end)
  36. it("add()", function()
  37. assert.same({ x = 2, y = 4, z = 6 }, vector.add(vector.new(1, 2, 3), { x = 1, y = 2, z = 3 }))
  38. end)
  39. it("offset()", function()
  40. assert.same({ x = 41, y = 52, z = 63 }, vector.offset(vector.new(1, 2, 3), 40, 50, 60))
  41. end)
  42. -- This function is needed because of floating point imprecision.
  43. local function almost_equal(a, b)
  44. if type(a) == "number" then
  45. return math.abs(a - b) < 0.00000000001
  46. end
  47. return vector.distance(a, b) < 0.000000000001
  48. end
  49. describe("rotate_around_axis()", function()
  50. it("rotates", function()
  51. assert.True(almost_equal({x = -1, y = 0, z = 0},
  52. vector.rotate_around_axis({x = 1, y = 0, z = 0}, {x = 0, y = 1, z = 0}, math.pi)))
  53. assert.True(almost_equal({x = 0, y = 1, z = 0},
  54. vector.rotate_around_axis({x = 0, y = 0, z = 1}, {x = 1, y = 0, z = 0}, math.pi / 2)))
  55. assert.True(almost_equal({x = 4, y = 1, z = 1},
  56. vector.rotate_around_axis({x = 4, y = 1, z = 1}, {x = 4, y = 1, z = 1}, math.pi / 6)))
  57. end)
  58. it("keeps distance to axis", function()
  59. local rotate1 = {x = 1, y = 3, z = 1}
  60. local axis1 = {x = 1, y = 3, z = 2}
  61. local rotated1 = vector.rotate_around_axis(rotate1, axis1, math.pi / 13)
  62. assert.True(almost_equal(vector.distance(axis1, rotate1), vector.distance(axis1, rotated1)))
  63. local rotate2 = {x = 1, y = 1, z = 3}
  64. local axis2 = {x = 2, y = 6, z = 100}
  65. local rotated2 = vector.rotate_around_axis(rotate2, axis2, math.pi / 23)
  66. assert.True(almost_equal(vector.distance(axis2, rotate2), vector.distance(axis2, rotated2)))
  67. local rotate3 = {x = 1, y = -1, z = 3}
  68. local axis3 = {x = 2, y = 6, z = 100}
  69. local rotated3 = vector.rotate_around_axis(rotate3, axis3, math.pi / 2)
  70. assert.True(almost_equal(vector.distance(axis3, rotate3), vector.distance(axis3, rotated3)))
  71. end)
  72. it("rotates back", function()
  73. local rotate1 = {x = 1, y = 3, z = 1}
  74. local axis1 = {x = 1, y = 3, z = 2}
  75. local rotated1 = vector.rotate_around_axis(rotate1, axis1, math.pi / 13)
  76. rotated1 = vector.rotate_around_axis(rotated1, axis1, -math.pi / 13)
  77. assert.True(almost_equal(rotate1, rotated1))
  78. local rotate2 = {x = 1, y = 1, z = 3}
  79. local axis2 = {x = 2, y = 6, z = 100}
  80. local rotated2 = vector.rotate_around_axis(rotate2, axis2, math.pi / 23)
  81. rotated2 = vector.rotate_around_axis(rotated2, axis2, -math.pi / 23)
  82. assert.True(almost_equal(rotate2, rotated2))
  83. local rotate3 = {x = 1, y = -1, z = 3}
  84. local axis3 = {x = 2, y = 6, z = 100}
  85. local rotated3 = vector.rotate_around_axis(rotate3, axis3, math.pi / 2)
  86. rotated3 = vector.rotate_around_axis(rotated3, axis3, -math.pi / 2)
  87. assert.True(almost_equal(rotate3, rotated3))
  88. end)
  89. it("is right handed", function()
  90. local v_before1 = {x = 0, y = 1, z = -1}
  91. local v_after1 = vector.rotate_around_axis(v_before1, {x = 1, y = 0, z = 0}, math.pi / 4)
  92. assert.True(almost_equal(vector.normalize(vector.cross(v_after1, v_before1)), {x = 1, y = 0, z = 0}))
  93. local v_before2 = {x = 0, y = 3, z = 4}
  94. local v_after2 = vector.rotate_around_axis(v_before2, {x = 1, y = 0, z = 0}, 2 * math.pi / 5)
  95. assert.True(almost_equal(vector.normalize(vector.cross(v_after2, v_before2)), {x = 1, y = 0, z = 0}))
  96. local v_before3 = {x = 1, y = 0, z = -1}
  97. local v_after3 = vector.rotate_around_axis(v_before3, {x = 0, y = 1, z = 0}, math.pi / 4)
  98. assert.True(almost_equal(vector.normalize(vector.cross(v_after3, v_before3)), {x = 0, y = 1, z = 0}))
  99. local v_before4 = {x = 3, y = 0, z = 4}
  100. local v_after4 = vector.rotate_around_axis(v_before4, {x = 0, y = 1, z = 0}, 2 * math.pi / 5)
  101. assert.True(almost_equal(vector.normalize(vector.cross(v_after4, v_before4)), {x = 0, y = 1, z = 0}))
  102. local v_before5 = {x = 1, y = -1, z = 0}
  103. local v_after5 = vector.rotate_around_axis(v_before5, {x = 0, y = 0, z = 1}, math.pi / 4)
  104. assert.True(almost_equal(vector.normalize(vector.cross(v_after5, v_before5)), {x = 0, y = 0, z = 1}))
  105. local v_before6 = {x = 3, y = 4, z = 0}
  106. local v_after6 = vector.rotate_around_axis(v_before6, {x = 0, y = 0, z = 1}, 2 * math.pi / 5)
  107. assert.True(almost_equal(vector.normalize(vector.cross(v_after6, v_before6)), {x = 0, y = 0, z = 1}))
  108. end)
  109. end)
  110. describe("rotate()", function()
  111. it("rotates", function()
  112. assert.True(almost_equal({x = -1, y = 0, z = 0},
  113. vector.rotate({x = 1, y = 0, z = 0}, {x = 0, y = math.pi, z = 0})))
  114. assert.True(almost_equal({x = 0, y = -1, z = 0},
  115. vector.rotate({x = 1, y = 0, z = 0}, {x = 0, y = 0, z = math.pi / 2})))
  116. assert.True(almost_equal({x = 1, y = 0, z = 0},
  117. vector.rotate({x = 1, y = 0, z = 0}, {x = math.pi / 123, y = 0, z = 0})))
  118. end)
  119. it("is counterclockwise", function()
  120. local v_before1 = {x = 0, y = 1, z = -1}
  121. local v_after1 = vector.rotate(v_before1, {x = math.pi / 4, y = 0, z = 0})
  122. assert.True(almost_equal(vector.normalize(vector.cross(v_after1, v_before1)), {x = 1, y = 0, z = 0}))
  123. local v_before2 = {x = 0, y = 3, z = 4}
  124. local v_after2 = vector.rotate(v_before2, {x = 2 * math.pi / 5, y = 0, z = 0})
  125. assert.True(almost_equal(vector.normalize(vector.cross(v_after2, v_before2)), {x = 1, y = 0, z = 0}))
  126. local v_before3 = {x = 1, y = 0, z = -1}
  127. local v_after3 = vector.rotate(v_before3, {x = 0, y = math.pi / 4, z = 0})
  128. assert.True(almost_equal(vector.normalize(vector.cross(v_after3, v_before3)), {x = 0, y = 1, z = 0}))
  129. local v_before4 = {x = 3, y = 0, z = 4}
  130. local v_after4 = vector.rotate(v_before4, {x = 0, y = 2 * math.pi / 5, z = 0})
  131. assert.True(almost_equal(vector.normalize(vector.cross(v_after4, v_before4)), {x = 0, y = 1, z = 0}))
  132. local v_before5 = {x = 1, y = -1, z = 0}
  133. local v_after5 = vector.rotate(v_before5, {x = 0, y = 0, z = math.pi / 4})
  134. assert.True(almost_equal(vector.normalize(vector.cross(v_after5, v_before5)), {x = 0, y = 0, z = 1}))
  135. local v_before6 = {x = 3, y = 4, z = 0}
  136. local v_after6 = vector.rotate(v_before6, {x = 0, y = 0, z = 2 * math.pi / 5})
  137. assert.True(almost_equal(vector.normalize(vector.cross(v_after6, v_before6)), {x = 0, y = 0, z = 1}))
  138. end)
  139. end)
  140. it("dir_to_rotation()", function()
  141. -- Comparing rotations (pitch, yaw, roll) is hard because of certain ambiguities,
  142. -- e.g. (pi, 0, pi) looks exactly the same as (0, pi, 0)
  143. -- So instead we convert the rotation back to vectors and compare these.
  144. local function forward_at_rot(rot)
  145. return vector.rotate(vector.new(0, 0, 1), rot)
  146. end
  147. local function up_at_rot(rot)
  148. return vector.rotate(vector.new(0, 1, 0), rot)
  149. end
  150. local rot1 = vector.dir_to_rotation({x = 1, y = 0, z = 0}, {x = 0, y = 1, z = 0})
  151. assert.True(almost_equal({x = 1, y = 0, z = 0}, forward_at_rot(rot1)))
  152. assert.True(almost_equal({x = 0, y = 1, z = 0}, up_at_rot(rot1)))
  153. local rot2 = vector.dir_to_rotation({x = 1, y = 1, z = 0}, {x = 0, y = 0, z = 1})
  154. assert.True(almost_equal({x = 1/math.sqrt(2), y = 1/math.sqrt(2), z = 0}, forward_at_rot(rot2)))
  155. assert.True(almost_equal({x = 0, y = 0, z = 1}, up_at_rot(rot2)))
  156. for i = 1, 1000 do
  157. local rand_vec = vector.new(math.random(), math.random(), math.random())
  158. if vector.length(rand_vec) ~= 0 then
  159. local rot_1 = vector.dir_to_rotation(rand_vec)
  160. local rot_2 = {
  161. x = math.atan2(rand_vec.y, math.sqrt(rand_vec.z * rand_vec.z + rand_vec.x * rand_vec.x)),
  162. y = -math.atan2(rand_vec.x, rand_vec.z),
  163. z = 0
  164. }
  165. assert.True(almost_equal(rot_1, rot_2))
  166. end
  167. end
  168. end)
  169. end)