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