v2 / vlib / math / vec / vec2_test.v
322 lines · 290 sloc · 8.26 KB · a8a5e80f416a09fa60279eea521351a54451bb40
Raw
1import math { close, is_nan, radians, tolerance, veryclose }
2import math.vec
3
4fn test_vec2_int() {
5 mut v1 := vec.vec2(0, 0)
6 mut v2 := vec.vec2(0, 0)
7 assert v1 == v2
8 v1.one()
9 v2.one()
10 assert v1.x == 1
11 assert v1.y == 1
12 assert v1 == v2
13
14 v3 := v1 + v2
15 assert typeof(v3).name == 'vec.Vec2[int]'
16 assert v3.x == 2
17 assert v3.y == 2
18}
19
20fn test_vec2_f32() {
21 mut v1 := vec.vec2(f32(0), 0)
22 mut v2 := vec.vec2(f32(0), 0)
23 assert v1 == v2
24 v1.one()
25 v2.one()
26 assert v1.x == 1
27 assert v1.y == 1
28 assert v1 == v2
29
30 v3 := v1 + v2
31 assert typeof(v3).name == 'vec.Vec2[f32]'
32 assert v3.x == 2
33 assert v3.y == 2
34}
35
36fn test_vec2_f64() {
37 mut v1 := vec.vec2(0.0, 0)
38 mut v2 := vec.vec2(0.0, 0)
39 assert v1 == v2
40 v1.one()
41 v2.one()
42 assert v1.x == 1
43 assert v1.y == 1
44 assert v1 == v2
45
46 v3 := v1 + v2
47 assert typeof(v3).name == 'vec.Vec2[f64]'
48 assert v3.x == 2
49 assert v3.y == 2
50}
51
52fn test_vec2_f64_utils_1() {
53 mut v1 := vec.vec2(2.0, 3.0)
54 mut v2 := vec.vec2(1.0, 4.0)
55
56 mut zv := vec.vec2(5.0, 5.0)
57 zv.zero()
58
59 v3 := v1 + v2
60 assert v3.x == 3
61 assert v3.y == 7
62
63 assert v1.dot(v2) == 14
64 assert v1.cross(v2) == 5
65
66 v1l := vec.vec2(40.0, 9.0)
67 assert v1l.magnitude() == 41
68
69 mut ctv1 := vec.vec2(0.000001, 0.000001)
70 ctv1.clean_tolerance(0.00001)
71 assert ctv1 == zv
72}
73
74fn test_vec2_f64_utils_2() {
75 mut v1 := vec.vec2(4.0, 4.0)
76 assert veryclose(v1.unit().magnitude(), 1)
77 v2 := v1.mul_scalar(0.5)
78 assert v2.x == 2
79 assert v2.y == 2
80 assert veryclose(v2.unit().magnitude(), 1)
81
82 invv2 := v2.inv()
83 assert invv2.x == 0.5
84 assert invv2.y == 0.5
85}
86
87fn fcorrect_angles() []f64 {
88 return [
89 math.pi * 1.0 / 4.0,
90 math.pi * 2.0 / 4.0,
91 math.pi * 3.0 / 4.0,
92 math.pi,
93 math.pi * 5.0 / 4.0 - 2 * math.pi,
94 math.pi * 6.0 / 4.0 - 2 * math.pi,
95 math.pi * 7.0 / 4.0 - 2 * math.pi,
96 ]
97}
98
99fn fsurround() []vec.Vec2[f64] {
100 return [
101 vec.vec2(1.0, 1.0),
102 vec.vec2(0.0, 1.0),
103 vec.vec2(-1.0, 1.0),
104 vec.vec2(-1.0, 0.0),
105 vec.vec2(-1.0, -1.0),
106 vec.vec2(0.0, -1.0),
107 vec.vec2(1.0, -1.0),
108 ]
109}
110
111fn test_vec2_angle_between() {
112 surround := fsurround()
113 correct_angles := fcorrect_angles()
114 v1 := vec.vec2(1.0, 0.0)
115 for i in 0 .. 7 {
116 assert v1.angle_between(surround[i]) == correct_angles[i]
117 // check the angle between non-normalised, scaled vectors too:
118 assert v1.mul_scalar(5.0).angle_between(surround[i].mul_scalar(2.0)) == correct_angles[i]
119 offset := f64(i) + 123.123456
120 assert v1.mul_scalar(offset).angle_between(surround[i].mul_scalar(offset)) == correct_angles[i]
121 // check f32 version:
122 v1_f32 := vec.vec2(f32(v1.x), f32(v1.y))
123 v2_f32 := vec.vec2(f32(surround[i].x), f32(surround[i].y))
124 assert v1_f32.angle_between(v2_f32) == f32(correct_angles[i])
125 // check int version:
126 v1_int := vec.vec2(int(v1.x), int(v1.y))
127 v2_int := vec.vec2(int(surround[i].x), int(surround[i].y))
128 assert v1_int.angle_between(v2_int) == int(correct_angles[i])
129 }
130}
131
132fn test_vec2_angle_towards() {
133 surround := fsurround()
134 correct_angles := fcorrect_angles()
135 // basic case, let p0 be the coordinate origin point:
136 p0 := vec.vec2(0.0, 0.0)
137 for i in 0 .. 7 {
138 assert p0.angle_towards(surround[i]) == correct_angles[i]
139 assert (surround[i] - p0).angle() == p0.angle_towards(surround[i])
140 // check f32 version:
141 p0_f32 := vec.vec2(f32(0), f32(0))
142 s_f32 := vec.vec2(f32(surround[i].x), f32(surround[i].y))
143 assert p0_f32.angle_towards(s_f32) == f32(correct_angles[i])
144 // check int version:
145 p0_int := vec.vec2(int(0), int(0))
146 s_int := vec.vec2(int(surround[i].x), int(surround[i].y))
147 assert p0_int.angle_towards(s_int) == int(correct_angles[i])
148 // check invariants, when the 2 points are moved and translated:
149 for x := -0.5; x <= 0.5; x += 0.1 {
150 for y := -0.5; y <= 0.5; y += 0.1 {
151 p1 := vec.vec2(x, y)
152 p2 := p1.add(surround[i])
153 assert veryclose(p1.angle_towards(p2), correct_angles[i]), 'i: ${i}, p1: ${p1} | p2: ${p2}'
154 offset := f64(i) + 123.123456
155 p1_o := p1.add_scalar(offset)
156 p2_o := p2.add_scalar(offset)
157 assert (p2_o - p1_o).angle() == p1_o.angle_towards(p2_o)
158 assert close(p1_o.angle_towards(p2_o), correct_angles[i]), 'i: ${i}, p1_o: ${p1_o} | p2_o: ${p2_o}'
159 }
160 }
161 }
162}
163
164fn test_vec2_rotate_around_cw() {
165 origin := vec.vec2(0.0, 0.0)
166 mut v := vec.vec2(0.0, 1.0)
167 v = v.rotate_around_cw(origin, radians(90))
168 assert close(v.x, 1.0)
169 assert close(v.y, 0.0)
170 v = v.rotate_around_cw(origin, radians(90))
171 assert close(v.x, 0.0)
172 assert close(v.y, -1.0)
173 v = v.rotate_around_cw(origin, radians(90))
174 assert close(v.x, -1.0)
175 assert close(v.y, 0.0)
176 v = v.rotate_around_cw(origin, radians(90))
177 assert close(v.x, 0.0)
178 assert close(v.y, 1.0)
179}
180
181fn test_vec2_rotate_around_ccw() {
182 origin := vec.vec2(0.0, 0.0)
183 mut v := vec.vec2(0.0, 1.0)
184 v = v.rotate_around_ccw(origin, radians(90))
185 assert close(v.x, -1.0)
186 assert close(v.y, 0.0)
187 v = v.rotate_around_ccw(origin, radians(90))
188 assert close(v.x, 0.0)
189 assert close(v.y, -1.0)
190 v = v.rotate_around_ccw(origin, radians(90))
191 assert close(v.x, 1.0)
192 assert close(v.y, 0.0)
193 v = v.rotate_around_ccw(origin, radians(90))
194 assert close(v.x, 0.0)
195 assert close(v.y, 1.0)
196}
197
198fn test_vec2_rotate_around_cw_2() {
199 origin := vec.vec2(1.0, 1.0)
200 mut v := vec.vec2(1.0, 2.0)
201 v = v.rotate_around_cw(origin, radians(90))
202 assert close(v.x, 2.0)
203 assert close(v.y, 1.0)
204 v = v.rotate_around_cw(origin, radians(90))
205 assert close(v.x, 1.0)
206 assert close(v.y, 0.0)
207 v = v.rotate_around_cw(origin, radians(90))
208 assert close(v.x, 0.0)
209 assert close(v.y, 1.0)
210 v = v.rotate_around_cw(origin, radians(90))
211 assert close(v.x, 1.0)
212 assert close(v.y, 2.0)
213}
214
215fn test_vec2_rotate_around_ccw_2() {
216 origin := vec.vec2(-1.0, 1.0)
217 mut v := vec.vec2(-1.0, -1.0)
218 v = v.rotate_around_ccw(origin, radians(90))
219 assert close(v.x, 1.0)
220 assert close(v.y, 1.0)
221 v = v.rotate_around_ccw(origin, radians(90))
222 assert close(v.x, -1.0)
223 assert close(v.y, 3.0)
224 v = v.rotate_around_ccw(origin, radians(90))
225 assert close(v.x, -3.0)
226 assert close(v.y, 1.0)
227 v = v.rotate_around_ccw(origin, radians(90))
228 assert close(v.x, -1.0)
229 assert close(v.y, -1.0)
230}
231
232// Test for Vec2 projection
233//
234fn test_vec2_project_onto_basic() {
235 v := vec.vec2(5.0, 6.0) // magnitude ~7.81 vector
236 u := vec.vec2(3.0, 4.0) // magnitude 5 vector
237 // hand-computed:
238 // v·u = 5*3 + 6*4 = 39
239 // |u|^2 = 3^2 + 4^2 = 25
240 // scale = 39/25 = 1.56
241 // proj = scale * u = (1.56*3, 1.56*4) = (4.68, 6.24)
242 proj := v.project(u)
243 assert tolerance(proj.x, 4.68, vec.vec_epsilon)
244 assert tolerance(proj.y, 6.24, vec.vec_epsilon)
245}
246
247// Test for Vec2 projection onto zero vector
248// project v into the null vector
249fn test_vec2_project_onto_zero() {
250 v := vec.vec2(5.0, 6.0)
251 u := vec.vec2(0.0, 0.0)
252 proj := v.project(u)
253 // must be nan
254 assert is_nan(proj.x)
255 assert is_nan(proj.y)
256}
257
258// Test for Vec2 projection of zero vector
259// project a null vector
260fn test_vec2_project_zero_vector() {
261 v := vec.vec2(0.0, 0.0)
262 u := vec.vec2(3.0, 4.0)
263 proj := v.project(u)
264 assert proj.x == 0.0
265 assert proj.y == 0.0
266}
267
268// Test for Vec2 projection onto itself
269//
270fn test_vec2_project_onto_self() {
271 u := vec.vec2(3.0, 4.0)
272 proj := u.project(u)
273 assert veryclose(proj.x, u.x)
274 assert veryclose(proj.y, u.y)
275}
276
277// Test for Vec2 projection onto orthogonal vector
278//
279fn test_vec2_project_onto_orthogonal() {
280 v := vec.vec2(0.0, 1.0)
281 u := vec.vec2(1.0, 0.0)
282 proj := u.project(v)
283 // more sensitive to floating point errors so i think close is better here
284 assert close(proj.x, 0.0)
285 assert close(proj.y, 0.0)
286}
287
288// Test for Vec2 projection with negative components
289//
290fn test_vec2_project_negative_components() {
291 v := vec.vec2(5.0, -6.0)
292 u := vec.vec2(-3.0, 4.0)
293 // hand-computed:
294 // v·u = 5*-3 + -6*4 = -15 - 24 = -39
295 // |u|^2 = -3^2 + 4^2 = 9 + 16 = 25
296 // scale = -39/25 = -1.56
297 // proj = scale * u = (-1.56*-3, -1.56*4) = (4.68, -6.24)
298 proj := v.project(u)
299 assert tolerance(proj.x, 4.68, vec.vec_epsilon)
300 assert tolerance(proj.y, -6.24, vec.vec_epsilon)
301}
302
303// Test for perpendicularity
304// 'u' and 'v' are already perpendicular so it must return v
305fn test_vec2_perpendicularity_angle() {
306 u := vec.vec2(1.0, 0.0)
307 v := vec.vec2(0.0, 3.0)
308
309 per := v.perpendicular(u)
310 assert tolerance(per.x, v.x, vec.vec_epsilon)
311 assert tolerance(per.y, v.y, vec.vec_epsilon)
312}
313
314// 'u' and 'v' are collinear so it must return the null vector
315fn test_vec2_collinear() {
316 u := vec.vec2(1.0, 0.0)
317 v := vec.vec2(3.0, 0.0)
318
319 per := v.perpendicular(u)
320 assert tolerance(per.x, 0.0, vec.vec_epsilon)
321 assert tolerance(per.y, 0.0, vec.vec_epsilon)
322}
323