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("quat", function() {
24    var out, quatA, quatB, result;
25
26    beforeEach(function() { quatA = [1, 2, 3, 4]; quatB = [5, 6, 7, 8]; out = [0, 0, 0, 0]; });
27
28    describe("create", function() {
29        beforeEach(function() { result = quat.create(); });
30        it("should return a 4 element array initialized to an identity quaternion", function() { expect(result).toBeEqualish([0, 0, 0, 1]); });
31    });
32
33    describe("clone", function() {
34        beforeEach(function() { result = quat.clone(quatA); });
35        it("should return a 4 element array initialized to the values in quatA", function() { expect(result).toBeEqualish(quatA); });
36    });
37
38    describe("fromValues", function() {
39        beforeEach(function() { result = quat.fromValues(1, 2, 3, 4); });
40        it("should return a 4 element array initialized to the values passed", function() { expect(result).toBeEqualish([1, 2, 3, 4]); });
41    });
42
43    describe("copy", function() {
44        beforeEach(function() { result = quat.copy(out, quatA); });
45        it("should place values into out", function() { expect(out).toBeEqualish([1, 2, 3, 4]); });
46        it("should return out", function() { expect(result).toBe(out); });
47    });
48
49    describe("set", function() {
50        beforeEach(function() { result = quat.set(out, 1, 2, 3, 4); });
51        it("should place values into out", function() { expect(out).toBeEqualish([1, 2, 3, 4]); });
52        it("should return out", function() { expect(result).toBe(out); });
53    });
54
55    describe("identity", function() {
56        beforeEach(function() { result = quat.identity(out); });
57        it("should place values into out", function() { expect(result).toBeEqualish([0, 0, 0, 1]); });
58        it("should return out", function() { expect(result).toBe(out); });
59    });
60
61    describe("setAxisAngle", function() {
62        beforeEach(function() { result = quat.setAxisAngle(out, [1, 0, 0], Math.PI * 0.5); });
63        it("should place values into out", function() { expect(result).toBeEqualish([0.707106, 0, 0, 0.707106]); });
64        it("should return out", function() { expect(result).toBe(out); });
65    });
66
67    describe("add", function() {
68        describe("with a separate output quaternion", function() {
69            beforeEach(function() { result = quat.add(out, quatA, quatB); });
70
71            it("should place values into out", function() { expect(out).toBeEqualish([6, 8, 10, 12]); });
72            it("should return out", function() { expect(result).toBe(out); });
73            it("should not modify quatA", function() { expect(quatA).toBeEqualish([1, 2, 3, 4]); });
74            it("should not modify quatB", function() { expect(quatB).toBeEqualish([5, 6, 7, 8]); });
75        });
76
77        describe("when quatA is the output quaternion", function() {
78            beforeEach(function() { result = quat.add(quatA, quatA, quatB); });
79
80            it("should place values into quatA", function() { expect(quatA).toBeEqualish([6, 8, 10, 12]); });
81            it("should return quatA", function() { expect(result).toBe(quatA); });
82            it("should not modify quatB", function() { expect(quatB).toBeEqualish([5, 6, 7, 8]); });
83        });
84
85        describe("when quatB is the output quaternion", function() {
86            beforeEach(function() { result = quat.add(quatB, quatA, quatB); });
87
88            it("should place values into quatB", function() { expect(quatB).toBeEqualish([6, 8, 10, 12]); });
89            it("should return quatB", function() { expect(result).toBe(quatB); });
90            it("should not modify quatA", function() { expect(quatA).toBeEqualish([1, 2, 3, 4]); });
91        });
92    });
93
94    describe("multiply", function() {
95        it("should have an alias called 'mul'", function() { expect(quat.mul).toEqual(quat.multiply); });
96
97        describe("with a separate output quaternion", function() {
98            beforeEach(function() { result = quat.multiply(out, quatA, quatB); });
99
100            it("should place values into out", function() { expect(out).toBeEqualish([24, 48, 48, -6]); });
101            it("should return out", function() { expect(result).toBe(out); });
102            it("should not modify quatA", function() { expect(quatA).toBeEqualish([1, 2, 3, 4]); });
103            it("should not modify quatB", function() { expect(quatB).toBeEqualish([5, 6, 7, 8]); });
104        });
105
106        describe("when quatA is the output quaternion", function() {
107            beforeEach(function() { result = quat.multiply(quatA, quatA, quatB); });
108
109            it("should place values into quatA", function() { expect(quatA).toBeEqualish([24, 48, 48, -6]); });
110            it("should return quatA", function() { expect(result).toBe(quatA); });
111            it("should not modify quatB", function() { expect(quatB).toBeEqualish([5, 6, 7, 8]); });
112        });
113
114        describe("when quatB is the output quaternion", function() {
115            beforeEach(function() { result = quat.multiply(quatB, quatA, quatB); });
116
117            it("should place values into quatB", function() { expect(quatB).toBeEqualish([24, 48, 48, -6]); });
118            it("should return quatB", function() { expect(result).toBe(quatB); });
119            it("should not modify quatA", function() { expect(quatA).toBeEqualish([1, 2, 3, 4]); });
120        });
121    });
122
123    describe("scale", function() {
124        describe("with a separate output quaternion", function() {
125            beforeEach(function() { result = quat.scale(out, quatA, 2); });
126
127            it("should place values into out", function() { expect(out).toBeEqualish([2, 4, 6, 8]); });
128            it("should return out", function() { expect(result).toBe(out); });
129            it("should not modify quatA", function() { expect(quatA).toBeEqualish([1, 2, 3, 4]); });
130        });
131
132        describe("when quatA is the output quaternion", function() {
133            beforeEach(function() { result = quat.scale(quatA, quatA, 2); });
134
135            it("should place values into quatA", function() { expect(quatA).toBeEqualish([2, 4, 6, 8]); });
136            it("should return quatA", function() { expect(result).toBe(quatA); });
137        });
138    });
139
140    describe("length", function() {
141        it("should have an alias called 'len'", function() { expect(quat.len).toEqual(quat.length); });
142
143        beforeEach(function() { result = quat.length(quatA); });
144
145        it("should return the length", function() { expect(result).toBeCloseTo(5.477225); });
146    });
147
148    describe("squaredLength", function() {
149        it("should have an alias called 'sqrLen'", function() { expect(quat.sqrLen).toEqual(quat.squaredLength); });
150
151        beforeEach(function() { result = quat.squaredLength(quatA); });
152
153        it("should return the squared length", function() { expect(result).toEqual(30); });
154    });
155
156    describe("normalize", function() {
157        beforeEach(function() { quatA = [5, 0, 0, 0]; });
158
159        describe("with a separate output quaternion", function() {
160            beforeEach(function() { result = quat.normalize(out, quatA); });
161
162            it("should place values into out", function() { expect(out).toBeEqualish([1, 0, 0, 0]); });
163            it("should return out", function() { expect(result).toBe(out); });
164            it("should not modify quatA", function() { expect(quatA).toBeEqualish([5, 0, 0, 0]); });
165        });
166
167        describe("when quatA is the output quaternion", function() {
168            beforeEach(function() { result = quat.normalize(quatA, quatA); });
169
170            it("should place values into quatA", function() { expect(quatA).toBeEqualish([1, 0, 0, 0]); });
171            it("should return quatA", function() { expect(result).toBe(quatA); });
172        });
173    });
174
175    describe("lerp", function() {
176        describe("with a separate output quaternion", function() {
177            beforeEach(function() { result = quat.lerp(out, quatA, quatB, 0.5); });
178
179            it("should place values into out", function() { expect(out).toBeEqualish([3, 4, 5, 6]); });
180            it("should return out", function() { expect(result).toBe(out); });
181            it("should not modify quatA", function() { expect(quatA).toBeEqualish([1, 2, 3, 4]); });
182            it("should not modify quatB", function() { expect(quatB).toBeEqualish([5, 6, 7, 8]); });
183        });
184
185        describe("when quatA is the output quaternion", function() {
186            beforeEach(function() { result = quat.lerp(quatA, quatA, quatB, 0.5); });
187
188            it("should place values into quatA", function() { expect(quatA).toBeEqualish([3, 4, 5, 6]); });
189            it("should return quatA", function() { expect(result).toBe(quatA); });
190            it("should not modify quatB", function() { expect(quatB).toBeEqualish([5, 6, 7, 8]); });
191        });
192
193        describe("when quatB is the output quaternion", function() {
194            beforeEach(function() { result = quat.lerp(quatB, quatA, quatB, 0.5); });
195
196            it("should place values into quatB", function() { expect(quatB).toBeEqualish([3, 4, 5, 6]); });
197            it("should return quatB", function() { expect(result).toBe(quatB); });
198            it("should not modify quatA", function() { expect(quatA).toBeEqualish([1, 2, 3, 4]); });
199        });
200    });
201
202    /*describe("slerp", function() {
203        describe("with a separate output quaternion", function() {
204            beforeEach(function() { result = quat.slerp(out, quatA, quatB, 0.5); });
205
206            it("should place values into out", function() { expect(out).toBeEqualish([3, 4, 5, 6]); });
207            it("should return out", function() { expect(result).toBe(out); });
208            it("should not modify quatA", function() { expect(quatA).toBeEqualish([1, 2, 3, 4]); });
209            it("should not modify quatB", function() { expect(quatB).toBeEqualish([5, 6, 7, 8]); });
210        });
211
212        describe("when quatA is the output quaternion", function() {
213            beforeEach(function() { result = quat.slerp(quatA, quatA, quatB, 0.5); });
214
215            it("should place values into quatA", function() { expect(quatA).toBeEqualish([3, 4, 5, 6]); });
216            it("should return quatA", function() { expect(result).toBe(quatA); });
217            it("should not modify quatB", function() { expect(quatB).toBeEqualish([5, 6, 7, 8]); });
218        });
219
220        describe("when quatB is the output quaternion", function() {
221            beforeEach(function() { result = quat.slerp(quatB, quatA, quatB, 0.5); });
222
223            it("should place values into quatB", function() { expect(quatB).toBeEqualish([3, 4, 5, 6]); });
224            it("should return quatB", function() { expect(result).toBe(quatB); });
225            it("should not modify quatA", function() { expect(quatA).toBeEqualish([1, 2, 3, 4]); });
226        });
227    });*/
228
229    // TODO: slerp, calcuateW, rotateX, rotateY, rotateZ
230
231    describe("invert", function() {
232        describe("with a separate output quaternion", function() {
233            beforeEach(function() { result = quat.invert(out, quatA); });
234
235            it("should place values into out", function() { expect(out).toBeEqualish([-0.033333, -0.066666, -0.1, 0.133333]); });
236            it("should return out", function() { expect(result).toBe(out); });
237            it("should not modify quatA", function() { expect(quatA).toBeEqualish([1, 2, 3, 4]); });
238        });
239
240        describe("when quatA is the output quaternion", function() {
241            beforeEach(function() { result = quat.invert(quatA, quatA); });
242
243            it("should place values into quatA", function() { expect(quatA).toBeEqualish([-0.033333, -0.066666, -0.1, 0.133333]); });
244            it("should return quatA", function() { expect(result).toBe(quatA); });
245        });
246    });
247
248    describe("conjugate", function() {
249        describe("with a separate output quaternion", function() {
250            beforeEach(function() { result = quat.conjugate(out, quatA); });
251
252            it("should place values into out", function() { expect(out).toBeEqualish([-1, -2, -3, 4]); });
253            it("should return out", function() { expect(result).toBe(out); });
254            it("should not modify quatA", function() { expect(quatA).toBeEqualish([1, 2, 3, 4]); });
255        });
256
257        describe("when quatA is the output quaternion", function() {
258            beforeEach(function() { result = quat.conjugate(quatA, quatA); });
259
260            it("should place values into quatA", function() { expect(quatA).toBeEqualish([-1, -2, -3, 4]); });
261            it("should return quatA", function() { expect(result).toBe(quatA); });
262        });
263    });
264
265    describe("str", function() {
266        beforeEach(function() { result = quat.str(quatA); });
267
268        it("should return a string representation of the quaternion", function() { expect(result).toEqual("quat(1, 2, 3, 4)"); });
269    });
270});
271