1/* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved.
2
3Redistribution and use in source and binary forms, with or without modification,
4are permitted provided that the following conditions are met:
5
6  * Redistributions of source code must retain the above copyright notice, this
7    list of conditions and the following disclaimer.
8  * Redistributions in binary form must reproduce the above copyright notice,
9    this list of conditions and the following disclaimer in the documentation
10    and/or other materials provided with the distribution.
11
12THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
13ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
14WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
15DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
16ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
17(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
18LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
19ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
20(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
21SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
22
23describe("vec3", function() {
24    var out, vecA, vecB, result;
25
26    beforeEach(function() { vecA = [1, 2, 3]; vecB = [4, 5, 6]; out = [0, 0, 0]; });
27
28    describe("create", function() {
29        beforeEach(function() { result = vec3.create(); });
30        it("should return a 3 element array initialized to 0s", function() { expect(result).toBeEqualish([0, 0, 0]); });
31    });
32
33    describe("clone", function() {
34        beforeEach(function() { result = vec3.clone(vecA); });
35        it("should return a 3 element array initialized to the values in vecA", function() { expect(result).toBeEqualish(vecA); });
36    });
37
38    describe("fromValues", function() {
39        beforeEach(function() { result = vec3.fromValues(1, 2, 3); });
40        it("should return a 3 element array initialized to the values passed", function() { expect(result).toBeEqualish([1, 2, 3]); });
41    });
42
43    describe("copy", function() {
44        beforeEach(function() { result = vec3.copy(out, vecA); });
45        it("should place values into out", function() { expect(out).toBeEqualish([1, 2, 3]); });
46        it("should return out", function() { expect(result).toBe(out); });
47    });
48
49    describe("set", function() {
50        beforeEach(function() { result = vec3.set(out, 1, 2, 3); });
51        it("should place values into out", function() { expect(out).toBeEqualish([1, 2, 3]); });
52        it("should return out", function() { expect(result).toBe(out); });
53    });
54
55    describe("add", function() {
56        describe("with a separate output vector", function() {
57            beforeEach(function() { result = vec3.add(out, vecA, vecB); });
58
59            it("should place values into out", function() { expect(out).toBeEqualish([5, 7, 9]); });
60            it("should return out", function() { expect(result).toBe(out); });
61            it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 2, 3]); });
62            it("should not modify vecB", function() { expect(vecB).toBeEqualish([4, 5, 6]); });
63        });
64
65        describe("when vecA is the output vector", function() {
66            beforeEach(function() { result = vec3.add(vecA, vecA, vecB); });
67
68            it("should place values into vecA", function() { expect(vecA).toBeEqualish([5, 7, 9]); });
69            it("should return vecA", function() { expect(result).toBe(vecA); });
70            it("should not modify vecB", function() { expect(vecB).toBeEqualish([4, 5, 6]); });
71        });
72
73        describe("when vecB is the output vector", function() {
74            beforeEach(function() { result = vec3.add(vecB, vecA, vecB); });
75
76            it("should place values into vecB", function() { expect(vecB).toBeEqualish([5, 7, 9]); });
77            it("should return vecB", function() { expect(result).toBe(vecB); });
78            it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 2, 3]); });
79        });
80    });
81
82    describe("subtract", function() {
83        it("should have an alias called 'sub'", function() { expect(vec3.sub).toEqual(vec3.subtract); });
84
85        describe("with a separate output vector", function() {
86            beforeEach(function() { result = vec3.subtract(out, vecA, vecB); });
87
88            it("should place values into out", function() { expect(out).toBeEqualish([-3, -3, -3]); });
89            it("should return out", function() { expect(result).toBe(out); });
90            it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 2, 3]); });
91            it("should not modify vecB", function() { expect(vecB).toBeEqualish([4, 5, 6]); });
92        });
93
94        describe("when vecA is the output vector", function() {
95            beforeEach(function() { result = vec3.subtract(vecA, vecA, vecB); });
96
97            it("should place values into vecA", function() { expect(vecA).toBeEqualish([-3, -3, -3]); });
98            it("should return vecA", function() { expect(result).toBe(vecA); });
99            it("should not modify vecB", function() { expect(vecB).toBeEqualish([4, 5, 6]); });
100        });
101
102        describe("when vecB is the output vector", function() {
103            beforeEach(function() { result = vec3.subtract(vecB, vecA, vecB); });
104
105            it("should place values into vecB", function() { expect(vecB).toBeEqualish([-3, -3, -3]); });
106            it("should return vecB", function() { expect(result).toBe(vecB); });
107            it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 2, 3]); });
108        });
109    });
110
111    describe("multiply", function() {
112        it("should have an alias called 'mul'", function() { expect(vec3.mul).toEqual(vec3.multiply); });
113
114        describe("with a separate output vector", function() {
115            beforeEach(function() { result = vec3.multiply(out, vecA, vecB); });
116
117            it("should place values into out", function() { expect(out).toBeEqualish([4, 10, 18]); });
118            it("should return out", function() { expect(result).toBe(out); });
119            it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 2, 3]); });
120            it("should not modify vecB", function() { expect(vecB).toBeEqualish([4, 5, 6]); });
121        });
122
123        describe("when vecA is the output vector", function() {
124            beforeEach(function() { result = vec3.multiply(vecA, vecA, vecB); });
125
126            it("should place values into vecA", function() { expect(vecA).toBeEqualish([4, 10, 18]); });
127            it("should return vecA", function() { expect(result).toBe(vecA); });
128            it("should not modify vecB", function() { expect(vecB).toBeEqualish([4, 5, 6]); });
129        });
130
131        describe("when vecB is the output vector", function() {
132            beforeEach(function() { result = vec3.multiply(vecB, vecA, vecB); });
133
134            it("should place values into vecB", function() { expect(vecB).toBeEqualish([4, 10, 18]); });
135            it("should return vecB", function() { expect(result).toBe(vecB); });
136            it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 2, 3]); });
137        });
138    });
139
140    describe("divide", function() {
141        it("should have an alias called 'div'", function() { expect(vec3.div).toEqual(vec3.divide); });
142
143        describe("with a separate output vector", function() {
144            beforeEach(function() { result = vec3.divide(out, vecA, vecB); });
145
146            it("should place values into out", function() { expect(out).toBeEqualish([0.25, 0.4, 0.5]); });
147            it("should return out", function() { expect(result).toBe(out); });
148            it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 2, 3]); });
149            it("should not modify vecB", function() { expect(vecB).toBeEqualish([4, 5, 6]); });
150        });
151
152        describe("when vecA is the output vector", function() {
153            beforeEach(function() { result = vec3.divide(vecA, vecA, vecB); });
154
155            it("should place values into vecA", function() { expect(vecA).toBeEqualish([0.25, 0.4, 0.5]); });
156            it("should return vecA", function() { expect(result).toBe(vecA); });
157            it("should not modify vecB", function() { expect(vecB).toBeEqualish([4, 5, 6]); });
158        });
159
160        describe("when vecB is the output vector", function() {
161            beforeEach(function() { result = vec3.divide(vecB, vecA, vecB); });
162
163            it("should place values into vecB", function() { expect(vecB).toBeEqualish([0.25, 0.4, 0.5]); });
164            it("should return vecB", function() { expect(result).toBe(vecB); });
165            it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 2, 3]); });
166        });
167    });
168
169    describe("min", function() {
170        beforeEach(function() { vecA = [1, 3, 1]; vecB = [3, 1, 3]; });
171
172        describe("with a separate output vector", function() {
173            beforeEach(function() { result = vec3.min(out, vecA, vecB); });
174
175            it("should place values into out", function() { expect(out).toBeEqualish([1, 1, 1]); });
176            it("should return out", function() { expect(result).toBe(out); });
177            it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 3, 1]); });
178            it("should not modify vecB", function() { expect(vecB).toBeEqualish([3, 1, 3]); });
179        });
180
181        describe("when vecA is the output vector", function() {
182            beforeEach(function() { result = vec3.min(vecA, vecA, vecB); });
183
184            it("should place values into vecA", function() { expect(vecA).toBeEqualish([1, 1, 1]); });
185            it("should return vecA", function() { expect(result).toBe(vecA); });
186            it("should not modify vecB", function() { expect(vecB).toBeEqualish([3, 1, 3]); });
187        });
188
189        describe("when vecB is the output vector", function() {
190            beforeEach(function() { result = vec3.min(vecB, vecA, vecB); });
191
192            it("should place values into vecB", function() { expect(vecB).toBeEqualish([1, 1, 1]); });
193            it("should return vecB", function() { expect(result).toBe(vecB); });
194            it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 3, 1]); });
195        });
196    });
197
198    describe("max", function() {
199        beforeEach(function() { vecA = [1, 3, 1]; vecB = [3, 1, 3]; });
200
201        describe("with a separate output vector", function() {
202            beforeEach(function() { result = vec3.max(out, vecA, vecB); });
203
204            it("should place values into out", function() { expect(out).toBeEqualish([3, 3, 3]); });
205            it("should return out", function() { expect(result).toBe(out); });
206            it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 3, 1]); });
207            it("should not modify vecB", function() { expect(vecB).toBeEqualish([3, 1, 3]); });
208        });
209
210        describe("when vecA is the output vector", function() {
211            beforeEach(function() { result = vec3.max(vecA, vecA, vecB); });
212
213            it("should place values into vecA", function() { expect(vecA).toBeEqualish([3, 3, 3]); });
214            it("should return vecA", function() { expect(result).toBe(vecA); });
215            it("should not modify vecB", function() { expect(vecB).toBeEqualish([3, 1, 3]); });
216        });
217
218        describe("when vecB is the output vector", function() {
219            beforeEach(function() { result = vec3.max(vecB, vecA, vecB); });
220
221            it("should place values into vecB", function() { expect(vecB).toBeEqualish([3, 3, 3]); });
222            it("should return vecB", function() { expect(result).toBe(vecB); });
223            it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 3, 1]); });
224        });
225    });
226
227    describe("scale", function() {
228        describe("with a separate output vector", function() {
229            beforeEach(function() { result = vec3.scale(out, vecA, 2); });
230
231            it("should place values into out", function() { expect(out).toBeEqualish([2, 4, 6]); });
232            it("should return out", function() { expect(result).toBe(out); });
233            it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 2, 3]); });
234        });
235
236        describe("when vecA is the output vector", function() {
237            beforeEach(function() { result = vec3.scale(vecA, vecA, 2); });
238
239            it("should place values into vecA", function() { expect(vecA).toBeEqualish([2, 4, 6]); });
240            it("should return vecA", function() { expect(result).toBe(vecA); });
241        });
242    });
243
244    describe("distance", function() {
245        it("should have an alias called 'dist'", function() { expect(vec3.dist).toEqual(vec3.distance); });
246
247        beforeEach(function() { result = vec3.distance(vecA, vecB); });
248
249        it("should return the distance", function() { expect(result).toBeCloseTo(5.196152); });
250    });
251
252    describe("squaredDistance", function() {
253        it("should have an alias called 'sqrDist'", function() { expect(vec3.sqrDist).toEqual(vec3.squaredDistance); });
254
255        beforeEach(function() { result = vec3.squaredDistance(vecA, vecB); });
256
257        it("should return the squared distance", function() { expect(result).toEqual(27); });
258    });
259
260    describe("length", function() {
261        it("should have an alias called 'len'", function() { expect(vec3.len).toEqual(vec3.length); });
262
263        beforeEach(function() { result = vec3.length(vecA); });
264
265        it("should return the length", function() { expect(result).toBeCloseTo(3.741657); });
266    });
267
268    describe("squaredLength", function() {
269        it("should have an alias called 'sqrLen'", function() { expect(vec3.sqrLen).toEqual(vec3.squaredLength); });
270
271        beforeEach(function() { result = vec3.squaredLength(vecA); });
272
273        it("should return the squared length", function() { expect(result).toEqual(14); });
274    });
275
276    describe("negate", function() {
277        describe("with a separate output vector", function() {
278            beforeEach(function() { result = vec3.negate(out, vecA); });
279
280            it("should place values into out", function() { expect(out).toBeEqualish([-1, -2, -3]); });
281            it("should return out", function() { expect(result).toBe(out); });
282            it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 2, 3]); });
283        });
284
285        describe("when vecA is the output vector", function() {
286            beforeEach(function() { result = vec3.negate(vecA, vecA); });
287
288            it("should place values into vecA", function() { expect(vecA).toBeEqualish([-1, -2, -3]); });
289            it("should return vecA", function() { expect(result).toBe(vecA); });
290        });
291    });
292
293    describe("normalize", function() {
294        beforeEach(function() { vecA = [5, 0, 0]; });
295
296        describe("with a separate output vector", function() {
297            beforeEach(function() { result = vec3.normalize(out, vecA); });
298
299            it("should place values into out", function() { expect(out).toBeEqualish([1, 0, 0]); });
300            it("should return out", function() { expect(result).toBe(out); });
301            it("should not modify vecA", function() { expect(vecA).toBeEqualish([5, 0, 0]); });
302        });
303
304        describe("when vecA is the output vector", function() {
305            beforeEach(function() { result = vec3.normalize(vecA, vecA); });
306
307            it("should place values into vecA", function() { expect(vecA).toBeEqualish([1, 0, 0]); });
308            it("should return vecA", function() { expect(result).toBe(vecA); });
309        });
310    });
311
312    describe("dot", function() {
313        beforeEach(function() { result = vec3.dot(vecA, vecB); });
314
315        it("should return the dot product", function() { expect(result).toEqual(32); });
316        it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 2, 3]); });
317        it("should not modify vecB", function() { expect(vecB).toBeEqualish([4, 5, 6]); });
318    });
319
320    describe("cross", function() {
321        describe("with a separate output vector", function() {
322            beforeEach(function() { result = vec3.cross(out, vecA, vecB); });
323
324            it("should place values into out", function() { expect(out).toBeEqualish([-3, 6, -3]); });
325            it("should return out", function() { expect(result).toBe(out); });
326            it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 2, 3]); });
327            it("should not modify vecB", function() { expect(vecB).toBeEqualish([4, 5, 6]); });
328        });
329
330        describe("when vecA is the output vector", function() {
331            beforeEach(function() { result = vec3.cross(vecA, vecA, vecB); });
332
333            it("should place values into vecA", function() { expect(vecA).toBeEqualish([-3, 6, -3]); });
334            it("should return vecA", function() { expect(result).toBe(vecA); });
335            it("should not modify vecB", function() { expect(vecB).toBeEqualish([4, 5, 6]); });
336        });
337
338        describe("when vecB is the output vector", function() {
339            beforeEach(function() { result = vec3.cross(vecB, vecA, vecB); });
340
341            it("should place values into vecB", function() { expect(vecB).toBeEqualish([-3, 6, -3]); });
342            it("should return vecB", function() { expect(result).toBe(vecB); });
343            it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 2, 3]); });
344        });
345    });
346
347    describe("lerp", function() {
348        describe("with a separate output vector", function() {
349            beforeEach(function() { result = vec3.lerp(out, vecA, vecB, 0.5); });
350
351            it("should place values into out", function() { expect(out).toBeEqualish([2.5, 3.5, 4.5]); });
352            it("should return out", function() { expect(result).toBe(out); });
353            it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 2, 3]); });
354            it("should not modify vecB", function() { expect(vecB).toBeEqualish([4, 5, 6]); });
355        });
356
357        describe("when vecA is the output vector", function() {
358            beforeEach(function() { result = vec3.lerp(vecA, vecA, vecB, 0.5); });
359
360            it("should place values into vecA", function() { expect(vecA).toBeEqualish([2.5, 3.5, 4.5]); });
361            it("should return vecA", function() { expect(result).toBe(vecA); });
362            it("should not modify vecB", function() { expect(vecB).toBeEqualish([4, 5, 6]); });
363        });
364
365        describe("when vecB is the output vector", function() {
366            beforeEach(function() { result = vec3.lerp(vecB, vecA, vecB, 0.5); });
367
368            it("should place values into vecB", function() { expect(vecB).toBeEqualish([2.5, 3.5, 4.5]); });
369            it("should return vecB", function() { expect(result).toBe(vecB); });
370            it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 2, 3]); });
371        });
372    });
373
374    describe("forEach", function() {
375        var vecArray;
376
377        beforeEach(function() {
378            vecArray = [
379                1, 2, 3,
380                4, 5, 6,
381                0, 0, 0
382            ];
383        });
384
385        describe("when performing operations that take no extra arguments", function() {
386            beforeEach(function() { result = vec3.forEach(vecArray, 0, 0, 0, vec3.normalize); });
387
388            it("should update all values", function() {
389                expect(vecArray).toBeEqualish([
390                    0.267261, 0.534522, 0.801783,
391                    0.455842, 0.569802, 0.683763,
392                    0, 0, 0
393                ]);
394            });
395            it("should return vecArray", function() { expect(result).toBe(vecArray); });
396        });
397
398        describe("when performing operations that takes one extra arguments", function() {
399            beforeEach(function() { result = vec3.forEach(vecArray, 0, 0, 0, vec3.add, vecA); });
400
401            it("should update all values", function() {
402                expect(vecArray).toBeEqualish([
403                    2, 4, 6,
404                    5, 7, 9,
405                    1, 2, 3
406                ]);
407            });
408            it("should return vecArray", function() { expect(result).toBe(vecArray); });
409            it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 2, 3]); });
410        });
411
412        describe("when specifying an offset", function() {
413            beforeEach(function() { result = vec3.forEach(vecArray, 0, 3, 0, vec3.add, vecA); });
414
415            it("should update all values except the first vector", function() {
416                expect(vecArray).toBeEqualish([
417                    1, 2, 3,
418                    5, 7, 9,
419                    1, 2, 3
420                ]);
421            });
422            it("should return vecArray", function() { expect(result).toBe(vecArray); });
423            it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 2, 3]); });
424        });
425
426        describe("when specifying a count", function() {
427            beforeEach(function() { result = vec3.forEach(vecArray, 0, 0, 2, vec3.add, vecA); });
428
429            it("should update all values except the last vector", function() {
430                expect(vecArray).toBeEqualish([
431                    2, 4, 6,
432                    5, 7, 9,
433                    0, 0, 0
434                ]);
435            });
436            it("should return vecArray", function() { expect(result).toBe(vecArray); });
437            it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 2, 3]); });
438        });
439
440        describe("when specifying a stride", function() {
441            beforeEach(function() { result = vec3.forEach(vecArray, 6, 0, 0, vec3.add, vecA); });
442
443            it("should update all values except the second vector", function() {
444                expect(vecArray).toBeEqualish([
445                    2, 4, 6,
446                    4, 5, 6,
447                    1, 2, 3
448                ]);
449            });
450            it("should return vecArray", function() { expect(result).toBe(vecArray); });
451            it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 2, 3]); });
452        });
453
454        describe("when calling a function that does not modify the out variable", function() {
455            beforeEach(function() {
456                result = vec3.forEach(vecArray, 0, 0, 0, function(out, vec) {});
457            });
458
459            it("values should remain unchanged", function() {
460                expect(vecArray).toBeEqualish([
461                    1, 2, 3,
462                    4, 5, 6,
463                    0, 0, 0
464                ]);
465            });
466            it("should return vecArray", function() { expect(result).toBe(vecArray); });
467        });
468    });
469
470    describe("str", function() {
471        beforeEach(function() { result = vec3.str(vecA); });
472
473        it("should return a string representation of the vector", function() { expect(result).toEqual("vec3(1, 2, 3)"); });
474    });
475});
476