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("vec2", function() {
24    var out, vecA, vecB, result;
25
26    beforeEach(function() { vecA = [1, 2]; vecB = [3, 4]; out = [0, 0]; });
27
28    describe("create", function() {
29        beforeEach(function() { result = vec2.create(); });
30        it("should return a 2 element array initialized to 0s", function() { expect(result).toBeEqualish([0, 0]); });
31    });
32
33    describe("clone", function() {
34        beforeEach(function() { result = vec2.clone(vecA); });
35        it("should return a 2 element array initialized to the values in vecA", function() { expect(result).toBeEqualish(vecA); });
36    });
37
38    describe("fromValues", function() {
39        beforeEach(function() { result = vec2.fromValues(1, 2); });
40        it("should return a 2 element array initialized to the values passed", function() { expect(result).toBeEqualish([1, 2]); });
41    });
42
43    describe("copy", function() {
44        beforeEach(function() { result = vec2.copy(out, vecA); });
45        it("should place values into out", function() { expect(out).toBeEqualish([1, 2]); });
46        it("should return out", function() { expect(result).toBe(out); });
47    });
48
49    describe("set", function() {
50        beforeEach(function() { result = vec2.set(out, 1, 2); });
51        it("should place values into out", function() { expect(out).toBeEqualish([1, 2]); });
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 = vec2.add(out, vecA, vecB); });
58
59            it("should place values into out", function() { expect(out).toBeEqualish([4, 6]); });
60            it("should return out", function() { expect(result).toBe(out); });
61            it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 2]); });
62            it("should not modify vecB", function() { expect(vecB).toBeEqualish([3, 4]); });
63        });
64
65        describe("when vecA is the output vector", function() {
66            beforeEach(function() { result = vec2.add(vecA, vecA, vecB); });
67
68            it("should place values into vecA", function() { expect(vecA).toBeEqualish([4, 6]); });
69            it("should return vecA", function() { expect(result).toBe(vecA); });
70            it("should not modify vecB", function() { expect(vecB).toBeEqualish([3, 4]); });
71        });
72
73        describe("when vecB is the output vector", function() {
74            beforeEach(function() { result = vec2.add(vecB, vecA, vecB); });
75
76            it("should place values into vecB", function() { expect(vecB).toBeEqualish([4, 6]); });
77            it("should return vecB", function() { expect(result).toBe(vecB); });
78            it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 2]); });
79        });
80    });
81
82    describe("subtract", function() {
83        it("should have an alias called 'sub'", function() { expect(vec2.sub).toEqual(vec2.subtract); });
84
85        describe("with a separate output vector", function() {
86            beforeEach(function() { result = vec2.subtract(out, vecA, vecB); });
87
88            it("should place values into out", function() { expect(out).toBeEqualish([-2, -2]); });
89            it("should return out", function() { expect(result).toBe(out); });
90            it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 2]); });
91            it("should not modify vecB", function() { expect(vecB).toBeEqualish([3, 4]); });
92        });
93
94        describe("when vecA is the output vector", function() {
95            beforeEach(function() { result = vec2.subtract(vecA, vecA, vecB); });
96
97            it("should place values into vecA", function() { expect(vecA).toBeEqualish([-2, -2]); });
98            it("should return vecA", function() { expect(result).toBe(vecA); });
99            it("should not modify vecB", function() { expect(vecB).toBeEqualish([3, 4]); });
100        });
101
102        describe("when vecB is the output vector", function() {
103            beforeEach(function() { result = vec2.subtract(vecB, vecA, vecB); });
104
105            it("should place values into vecB", function() { expect(vecB).toBeEqualish([-2, -2]); });
106            it("should return vecB", function() { expect(result).toBe(vecB); });
107            it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 2]); });
108        });
109    });
110
111    describe("multiply", function() {
112        it("should have an alias called 'mul'", function() { expect(vec2.mul).toEqual(vec2.multiply); });
113
114        describe("with a separate output vector", function() {
115            beforeEach(function() { result = vec2.multiply(out, vecA, vecB); });
116
117            it("should place values into out", function() { expect(out).toBeEqualish([3, 8]); });
118            it("should return out", function() { expect(result).toBe(out); });
119            it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 2]); });
120            it("should not modify vecB", function() { expect(vecB).toBeEqualish([3, 4]); });
121        });
122
123        describe("when vecA is the output vector", function() {
124            beforeEach(function() { result = vec2.multiply(vecA, vecA, vecB); });
125
126            it("should place values into vecA", function() { expect(vecA).toBeEqualish([3, 8]); });
127            it("should return vecA", function() { expect(result).toBe(vecA); });
128            it("should not modify vecB", function() { expect(vecB).toBeEqualish([3, 4]); });
129        });
130
131        describe("when vecB is the output vector", function() {
132            beforeEach(function() { result = vec2.multiply(vecB, vecA, vecB); });
133
134            it("should place values into vecB", function() { expect(vecB).toBeEqualish([3, 8]); });
135            it("should return vecB", function() { expect(result).toBe(vecB); });
136            it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 2]); });
137        });
138    });
139
140    describe("divide", function() {
141        it("should have an alias called 'div'", function() { expect(vec2.div).toEqual(vec2.divide); });
142
143        describe("with a separate output vector", function() {
144            beforeEach(function() { result = vec2.divide(out, vecA, vecB); });
145
146            it("should place values into out", function() { expect(out).toBeEqualish([0.3333333, 0.5]); });
147            it("should return out", function() { expect(result).toBe(out); });
148            it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 2]); });
149            it("should not modify vecB", function() { expect(vecB).toBeEqualish([3, 4]); });
150        });
151
152        describe("when vecA is the output vector", function() {
153            beforeEach(function() { result = vec2.divide(vecA, vecA, vecB); });
154
155            it("should place values into vecA", function() { expect(vecA).toBeEqualish([0.3333333, 0.5]); });
156            it("should return vecA", function() { expect(result).toBe(vecA); });
157            it("should not modify vecB", function() { expect(vecB).toBeEqualish([3, 4]); });
158        });
159
160        describe("when vecB is the output vector", function() {
161            beforeEach(function() { result = vec2.divide(vecB, vecA, vecB); });
162
163            it("should place values into vecB", function() { expect(vecB).toBeEqualish([0.3333333, 0.5]); });
164            it("should return vecB", function() { expect(result).toBe(vecB); });
165            it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 2]); });
166        });
167    });
168
169    describe("min", function() {
170        beforeEach(function() { vecA = [1, 4]; vecB = [3, 2]; });
171
172        describe("with a separate output vector", function() {
173            beforeEach(function() { result = vec2.min(out, vecA, vecB); });
174
175            it("should place values into out", function() { expect(out).toBeEqualish([1, 2]); });
176            it("should return out", function() { expect(result).toBe(out); });
177            it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 4]); });
178            it("should not modify vecB", function() { expect(vecB).toBeEqualish([3, 2]); });
179        });
180
181        describe("when vecA is the output vector", function() {
182            beforeEach(function() { result = vec2.min(vecA, vecA, vecB); });
183
184            it("should place values into vecA", function() { expect(vecA).toBeEqualish([1, 2]); });
185            it("should return vecA", function() { expect(result).toBe(vecA); });
186            it("should not modify vecB", function() { expect(vecB).toBeEqualish([3, 2]); });
187        });
188
189        describe("when vecB is the output vector", function() {
190            beforeEach(function() { result = vec2.min(vecB, vecA, vecB); });
191
192            it("should place values into vecB", function() { expect(vecB).toBeEqualish([1, 2]); });
193            it("should return vecB", function() { expect(result).toBe(vecB); });
194            it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 4]); });
195        });
196    });
197
198    describe("max", function() {
199        beforeEach(function() { vecA = [1, 4]; vecB = [3, 2]; });
200
201        describe("with a separate output vector", function() {
202            beforeEach(function() { result = vec2.max(out, vecA, vecB); });
203
204            it("should place values into out", function() { expect(out).toBeEqualish([3, 4]); });
205            it("should return out", function() { expect(result).toBe(out); });
206            it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 4]); });
207            it("should not modify vecB", function() { expect(vecB).toBeEqualish([3, 2]); });
208        });
209
210        describe("when vecA is the output vector", function() {
211            beforeEach(function() { result = vec2.max(vecA, vecA, vecB); });
212
213            it("should place values into vecA", function() { expect(vecA).toBeEqualish([3, 4]); });
214            it("should return vecA", function() { expect(result).toBe(vecA); });
215            it("should not modify vecB", function() { expect(vecB).toBeEqualish([3, 2]); });
216        });
217
218        describe("when vecB is the output vector", function() {
219            beforeEach(function() { result = vec2.max(vecB, vecA, vecB); });
220
221            it("should place values into vecB", function() { expect(vecB).toBeEqualish([3, 4]); });
222            it("should return vecB", function() { expect(result).toBe(vecB); });
223            it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 4]); });
224        });
225    });
226
227    describe("scale", function() {
228        describe("with a separate output vector", function() {
229            beforeEach(function() { result = vec2.scale(out, vecA, 2); });
230
231            it("should place values into out", function() { expect(out).toBeEqualish([2, 4]); });
232            it("should return out", function() { expect(result).toBe(out); });
233            it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 2]); });
234        });
235
236        describe("when vecA is the output vector", function() {
237            beforeEach(function() { result = vec2.scale(vecA, vecA, 2); });
238
239            it("should place values into vecA", function() { expect(vecA).toBeEqualish([2, 4]); });
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(vec2.dist).toEqual(vec2.distance); });
246
247        beforeEach(function() { result = vec2.distance(vecA, vecB); });
248
249        it("should return the distance", function() { expect(result).toBeCloseTo(2.828427); });
250    });
251
252    describe("squaredDistance", function() {
253        it("should have an alias called 'sqrDist'", function() { expect(vec2.sqrDist).toEqual(vec2.squaredDistance); });
254
255        beforeEach(function() { result = vec2.squaredDistance(vecA, vecB); });
256
257        it("should return the squared distance", function() { expect(result).toEqual(8); });
258    });
259
260    describe("length", function() {
261        it("should have an alias called 'len'", function() { expect(vec2.len).toEqual(vec2.length); });
262
263        beforeEach(function() { result = vec2.length(vecA); });
264
265        it("should return the length", function() { expect(result).toBeCloseTo(2.236067); });
266    });
267
268    describe("squaredLength", function() {
269        it("should have an alias called 'sqrLen'", function() { expect(vec2.sqrLen).toEqual(vec2.squaredLength); });
270
271        beforeEach(function() { result = vec2.squaredLength(vecA); });
272
273        it("should return the squared length", function() { expect(result).toEqual(5); });
274    });
275
276    describe("negate", function() {
277        describe("with a separate output vector", function() {
278            beforeEach(function() { result = vec2.negate(out, vecA); });
279
280            it("should place values into out", function() { expect(out).toBeEqualish([-1, -2]); });
281            it("should return out", function() { expect(result).toBe(out); });
282            it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 2]); });
283        });
284
285        describe("when vecA is the output vector", function() {
286            beforeEach(function() { result = vec2.negate(vecA, vecA); });
287
288            it("should place values into vecA", function() { expect(vecA).toBeEqualish([-1, -2]); });
289            it("should return vecA", function() { expect(result).toBe(vecA); });
290        });
291    });
292
293    describe("normalize", function() {
294        beforeEach(function() { vecA = [5, 0]; });
295
296        describe("with a separate output vector", function() {
297            beforeEach(function() { result = vec2.normalize(out, vecA); });
298
299            it("should place values into out", function() { expect(out).toBeEqualish([1, 0]); });
300            it("should return out", function() { expect(result).toBe(out); });
301            it("should not modify vecA", function() { expect(vecA).toBeEqualish([5, 0]); });
302        });
303
304        describe("when vecA is the output vector", function() {
305            beforeEach(function() { result = vec2.normalize(vecA, vecA); });
306
307            it("should place values into vecA", function() { expect(vecA).toBeEqualish([1, 0]); });
308            it("should return vecA", function() { expect(result).toBe(vecA); });
309        });
310    });
311
312    describe("dot", function() {
313        beforeEach(function() { result = vec2.dot(vecA, vecB); });
314
315        it("should return the dot product", function() { expect(result).toEqual(11); });
316        it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 2]); });
317        it("should not modify vecB", function() { expect(vecB).toBeEqualish([3, 4]); });
318    });
319
320    describe("cross", function() {
321        var out3;
322
323        beforeEach(function() {
324            out3 = [0, 0, 0];
325            result = vec2.cross(out3, vecA, vecB);
326        });
327
328        it("should place values into out", function() { expect(out3).toBeEqualish([0, 0, -2]); });
329        it("should return out", function() { expect(result).toBe(out3); });
330        it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 2]); });
331        it("should not modify vecB", function() { expect(vecB).toBeEqualish([3, 4]); });
332    });
333
334    describe("lerp", function() {
335        describe("with a separate output vector", function() {
336            beforeEach(function() { result = vec2.lerp(out, vecA, vecB, 0.5); });
337
338            it("should place values into out", function() { expect(out).toBeEqualish([2, 3]); });
339            it("should return out", function() { expect(result).toBe(out); });
340            it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 2]); });
341            it("should not modify vecB", function() { expect(vecB).toBeEqualish([3, 4]); });
342        });
343
344        describe("when vecA is the output vector", function() {
345            beforeEach(function() { result = vec2.lerp(vecA, vecA, vecB, 0.5); });
346
347            it("should place values into vecA", function() { expect(vecA).toBeEqualish([2, 3]); });
348            it("should return vecA", function() { expect(result).toBe(vecA); });
349            it("should not modify vecB", function() { expect(vecB).toBeEqualish([3, 4]); });
350        });
351
352        describe("when vecB is the output vector", function() {
353            beforeEach(function() { result = vec2.lerp(vecB, vecA, vecB, 0.5); });
354
355            it("should place values into vecB", function() { expect(vecB).toBeEqualish([2, 3]); });
356            it("should return vecB", function() { expect(result).toBe(vecB); });
357            it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 2]); });
358        });
359    });
360
361    describe("transformMat2", function() {
362        var matA;
363        beforeEach(function() { matA = [1, 2, 3, 4]; });
364
365        describe("with a separate output vector", function() {
366            beforeEach(function() { result = vec2.transformMat2(out, vecA, matA); });
367
368            it("should place values into out", function() { expect(out).toBeEqualish([7, 10]); });
369            it("should return out", function() { expect(result).toBe(out); });
370            it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 2]); });
371            it("should not modify matA", function() { expect(matA).toBeEqualish([1, 2, 3, 4]); });
372        });
373
374        describe("when vecA is the output vector", function() {
375            beforeEach(function() { result = vec2.transformMat2(vecA, vecA, matA); });
376
377            it("should place values into vecA", function() { expect(vecA).toBeEqualish([7, 10]); });
378            it("should return vecA", function() { expect(result).toBe(vecA); });
379            it("should not modify matA", function() { expect(matA).toBeEqualish([1, 2, 3, 4]); });
380        });
381    });
382
383    describe("transformMat2d", function() {
384        var matA;
385        beforeEach(function() { matA = [1, 2, 3, 4, 5, 6]; });
386
387        describe("with a separate output vector", function() {
388            beforeEach(function() { result = vec2.transformMat2d(out, vecA, matA); });
389
390            it("should place values into out", function() { expect(out).toBeEqualish([12, 16]); });
391            it("should return out", function() { expect(result).toBe(out); });
392            it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 2]); });
393            it("should not modify matA", function() { expect(matA).toBeEqualish([1, 2, 3, 4, 5, 6]); });
394        });
395
396        describe("when vecA is the output vector", function() {
397            beforeEach(function() { result = vec2.transformMat2d(vecA, vecA, matA); });
398
399            it("should place values into vecA", function() { expect(vecA).toBeEqualish([12, 16]); });
400            it("should return vecA", function() { expect(result).toBe(vecA); });
401            it("should not modify matA", function() { expect(matA).toBeEqualish([1, 2, 3, 4, 5, 6]); });
402        });
403    });
404
405    describe("forEach", function() {
406        var vecArray;
407
408        beforeEach(function() {
409            vecArray = [
410                1, 2,
411                3, 4,
412                0, 0
413            ];
414        });
415
416        describe("when performing operations that take no extra arguments", function() {
417            beforeEach(function() { result = vec2.forEach(vecArray, 0, 0, 0, vec2.normalize); });
418
419            it("should update all values", function() {
420                expect(vecArray).toBeEqualish([
421                    0.447214, 0.894427,
422                    0.6, 0.8,
423                    0, 0
424                ]);
425            });
426            it("should return vecArray", function() { expect(result).toBe(vecArray); });
427        });
428
429        describe("when performing operations that takes one extra arguments", function() {
430            beforeEach(function() { result = vec2.forEach(vecArray, 0, 0, 0, vec2.add, vecA); });
431
432            it("should update all values", function() {
433                expect(vecArray).toBeEqualish([
434                    2, 4,
435                    4, 6,
436                    1, 2
437                ]);
438            });
439            it("should return vecArray", function() { expect(result).toBe(vecArray); });
440            it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 2]); });
441        });
442
443        describe("when specifying an offset", function() {
444            beforeEach(function() { result = vec2.forEach(vecArray, 0, 2, 0, vec2.add, vecA); });
445
446            it("should update all values except the first vector", function() {
447                expect(vecArray).toBeEqualish([
448                    1, 2,
449                    4, 6,
450                    1, 2
451                ]);
452            });
453            it("should return vecArray", function() { expect(result).toBe(vecArray); });
454            it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 2]); });
455        });
456
457        describe("when specifying a count", function() {
458            beforeEach(function() { result = vec2.forEach(vecArray, 0, 0, 2, vec2.add, vecA); });
459
460            it("should update all values except the last vector", function() {
461                expect(vecArray).toBeEqualish([
462                    2, 4,
463                    4, 6,
464                    0, 0
465                ]);
466            });
467            it("should return vecArray", function() { expect(result).toBe(vecArray); });
468            it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 2]); });
469        });
470
471        describe("when specifying a stride", function() {
472            beforeEach(function() { result = vec2.forEach(vecArray, 4, 0, 0, vec2.add, vecA); });
473
474            it("should update all values except the second vector", function() {
475                expect(vecArray).toBeEqualish([
476                    2, 4,
477                    3, 4,
478                    1, 2
479                ]);
480            });
481            it("should return vecArray", function() { expect(result).toBe(vecArray); });
482            it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 2]); });
483        });
484
485        describe("when calling a function that does not modify the out variable", function() {
486            beforeEach(function() {
487                result = vec2.forEach(vecArray, 0, 0, 0, function(out, vec) {});
488            });
489
490            it("values should remain unchanged", function() {
491                expect(vecArray).toBeEqualish([
492                    1, 2,
493                    3, 4,
494                    0, 0,
495                ]);
496            });
497            it("should return vecArray", function() { expect(result).toBe(vecArray); });
498        });
499    });
500
501    describe("str", function() {
502        beforeEach(function() { result = vec2.str(vecA); });
503
504        it("should return a string representation of the vector", function() { expect(result).toEqual("vec2(1, 2)"); });
505    });
506});
507