001/**
002 * Copyright (c) 2008-2014 Ardor Labs, Inc.
003 *
004 * This file is part of Ardor3D.
005 *
006 * Ardor3D is free software: you can redistribute it and/or modify it 
007 * under the terms of its license which may be found in the accompanying
008 * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
009 */
010
011package com.ardor3d.math;
012
013import static org.junit.Assert.*;
014
015import org.junit.Test;
016
017import com.ardor3d.math.type.ReadOnlyVector3;
018
019public class TestQuaternion {
020
021    @Test
022    public void testGetSet() {
023        final Quaternion quat1 = new Quaternion();
024        assertEquals(Quaternion.IDENTITY, quat1);
025        assertTrue(quat1.isIdentity());
026
027        quat1.setX(1);
028        assertTrue(quat1.getX() == 1.0);
029        quat1.setX(Double.POSITIVE_INFINITY);
030        assertTrue(quat1.getX() == Double.POSITIVE_INFINITY);
031        quat1.setX(Double.NEGATIVE_INFINITY);
032        assertTrue(quat1.getX() == Double.NEGATIVE_INFINITY);
033
034        quat1.setY(1);
035        assertTrue(quat1.getY() == 1.0);
036        quat1.setY(Double.POSITIVE_INFINITY);
037        assertTrue(quat1.getY() == Double.POSITIVE_INFINITY);
038        quat1.setY(Double.NEGATIVE_INFINITY);
039        assertTrue(quat1.getY() == Double.NEGATIVE_INFINITY);
040
041        quat1.setZ(1);
042        assertTrue(quat1.getZ() == 1.0);
043        quat1.setZ(Double.POSITIVE_INFINITY);
044        assertTrue(quat1.getZ() == Double.POSITIVE_INFINITY);
045        quat1.setZ(Double.NEGATIVE_INFINITY);
046        assertTrue(quat1.getZ() == Double.NEGATIVE_INFINITY);
047
048        quat1.setW(1);
049        assertTrue(quat1.getW() == 1.0);
050        quat1.setW(Double.POSITIVE_INFINITY);
051        assertTrue(quat1.getW() == Double.POSITIVE_INFINITY);
052        quat1.setW(Double.NEGATIVE_INFINITY);
053        assertTrue(quat1.getW() == Double.NEGATIVE_INFINITY);
054
055        quat1.set(Math.PI, Math.PI, Math.PI, Math.PI);
056        assertTrue(quat1.getXf() == (float) Math.PI);
057        assertTrue(quat1.getYf() == (float) Math.PI);
058        assertTrue(quat1.getZf() == (float) Math.PI);
059        assertTrue(quat1.getWf() == (float) Math.PI);
060
061        final Quaternion quat2 = new Quaternion();
062        quat2.set(quat1);
063        assertEquals(quat1, quat2);
064    }
065
066    @Test
067    public void testToArray() {
068        final Quaternion quat1 = new Quaternion();
069        quat1.set(Math.PI, Double.MAX_VALUE, 42, -1);
070        final double[] array = quat1.toArray(null);
071        final double[] array2 = quat1.toArray(new double[4]);
072        assertNotNull(array);
073        assertTrue(array.length == 4);
074        assertTrue(array[0] == Math.PI);
075        assertTrue(array[1] == Double.MAX_VALUE);
076        assertTrue(array[2] == 42);
077        assertTrue(array[3] == -1);
078        assertNotNull(array2);
079        assertTrue(array2.length == 4);
080        assertTrue(array2[0] == Math.PI);
081        assertTrue(array2[1] == Double.MAX_VALUE);
082        assertTrue(array2[2] == 42);
083        assertTrue(array2[3] == -1);
084    }
085
086    @Test(expected = ArrayIndexOutOfBoundsException.class)
087    public void testBadArray() {
088        final Quaternion quat = new Quaternion();
089        quat.toArray(new double[2]);
090    }
091
092    @Test(expected = ArrayIndexOutOfBoundsException.class)
093    public void testBadAxesArray() {
094        final Quaternion quat = new Quaternion();
095        quat.toAxes(new Vector3[2]);
096    }
097
098    @Test(expected = ArrayIndexOutOfBoundsException.class)
099    public void testBadEuler1() {
100        new Quaternion().fromEulerAngles(new double[2]);
101    }
102
103    @Test(expected = ArrayIndexOutOfBoundsException.class)
104    public void testBadEuler2() {
105        final Quaternion quat = new Quaternion();
106        quat.toEulerAngles(new double[2]);
107    }
108
109    @Test
110    public void testEulerAngles() {
111        final Quaternion quat = new Quaternion().fromEulerAngles(new double[] { MathUtils.HALF_PI, 0, 0 });
112        assertTrue(1.0 == quat.magnitude());
113        assertTrue(Math.abs(Vector3.NEG_UNIT_Z.distance(quat.apply(Vector3.UNIT_X, null))) <= MathUtils.EPSILON);
114
115        quat.fromEulerAngles(0, -MathUtils.HALF_PI, 0);
116        assertTrue(1.0 == quat.magnitude());
117        assertTrue(Math.abs(Vector3.NEG_UNIT_Y.distance(quat.apply(Vector3.UNIT_X, null))) <= MathUtils.EPSILON);
118
119        quat.fromEulerAngles(0, 0, MathUtils.HALF_PI);
120        assertTrue(1.0 == quat.magnitude());
121        assertTrue(Math.abs(Vector3.UNIT_Z.distance(quat.apply(Vector3.UNIT_Y, null))) <= MathUtils.EPSILON);
122
123        quat.fromEulerAngles(0, MathUtils.HALF_PI, 0);
124        double[] angles = quat.toEulerAngles(null);
125        final Quaternion quat2 = new Quaternion().fromEulerAngles(angles);
126        assertEquals(quat, quat2);
127        quat.fromEulerAngles(0, -MathUtils.HALF_PI, 0);
128        angles = quat.toEulerAngles(null);
129        quat2.fromEulerAngles(angles);
130        assertEquals(quat, quat2);
131        quat.fromEulerAngles(0, 0, MathUtils.HALF_PI);
132        angles = quat.toEulerAngles(null);
133        quat2.fromEulerAngles(angles);
134        assertEquals(quat, quat2);
135    }
136
137    @Test
138    public void testMatrix3() {
139        double a = MathUtils.HALF_PI;
140        final Quaternion quat = new Quaternion();
141        quat.fromRotationMatrix( //
142                1, 0, 0, //
143                0, Math.cos(a), -Math.sin(a), //
144                0, Math.sin(a), Math.cos(a));
145
146        assertTrue(Math.abs(Vector3.UNIT_Z.distance(quat.apply(Vector3.UNIT_Y, null))) <= MathUtils.EPSILON);
147        final Matrix3 mat = quat.toRotationMatrix((Matrix3) null);
148        assertTrue(Math.abs(quat.apply(Vector3.NEG_ONE, null).distance(mat.applyPost(Vector3.NEG_ONE, null))) <= MathUtils.EPSILON);
149
150        a = MathUtils.PI;
151        quat.fromRotationMatrix( //
152                1, 0, 0, //
153                0, Math.cos(a), -Math.sin(a), //
154                0, Math.sin(a), Math.cos(a));
155
156        assertTrue(Math.abs(Vector3.NEG_UNIT_Y.distance(quat.apply(Vector3.UNIT_Y, null))) <= MathUtils.EPSILON);
157        quat.toRotationMatrix(mat);
158        assertTrue(Math.abs(quat.apply(Vector3.ONE, null).distance(mat.applyPost(Vector3.ONE, null))) <= MathUtils.EPSILON);
159
160        quat.set(0, 0, 0, 0);
161        assertEquals(Matrix3.IDENTITY, quat.toRotationMatrix((Matrix3) null));
162
163        a = MathUtils.PI;
164        quat.fromRotationMatrix( //
165                Math.cos(a), 0, Math.sin(a), //
166                0, 1, 0, //
167                -Math.sin(a), 0, Math.cos(a));
168
169        assertTrue(Math.abs(Vector3.NEG_UNIT_X.distance(quat.apply(Vector3.UNIT_X, null))) <= MathUtils.EPSILON);
170        final Matrix4 mat4 = quat.toRotationMatrix((Matrix4) null);
171        assertTrue(Math.abs(quat.apply(Vector3.NEG_ONE, null).distance(mat4.applyPostVector(Vector3.NEG_ONE, null))) <= MathUtils.EPSILON);
172
173        a = MathUtils.PI;
174        quat.fromRotationMatrix(new Matrix3(//
175                Math.cos(a), -Math.sin(a), 0, //
176                Math.sin(a), Math.cos(a), 0, //
177                0, 0, 1));
178
179        assertTrue(Math.abs(Vector3.NEG_UNIT_X.distance(quat.apply(Vector3.UNIT_X, null))) <= MathUtils.EPSILON);
180        quat.toRotationMatrix(mat4);
181        assertTrue(Math.abs(quat.apply(Vector3.ONE, null).distance(mat4.applyPostVector(Vector3.ONE, null))) <= MathUtils.EPSILON);
182
183        quat.set(0, 0, 0, 0);
184        assertEquals(Matrix4.IDENTITY, quat.toRotationMatrix((Matrix4) null));
185    }
186
187    @Test
188    public void testRotations() {
189        final double a = MathUtils.QUARTER_PI;
190        final Quaternion quat = new Quaternion().fromRotationMatrix(new Matrix3(//
191                Math.cos(a), -Math.sin(a), 0, //
192                Math.sin(a), Math.cos(a), 0, //
193                0, 0, 1));
194        final Vector3 column = quat.getRotationColumn(0, null);
195        assertTrue(Math.abs(new Vector3(Math.cos(a), Math.sin(a), 0).distance(column)) <= MathUtils.EPSILON);
196        quat.getRotationColumn(1, column);
197        assertTrue(Math.abs(new Vector3(-Math.sin(a), Math.sin(a), 0).distance(column)) <= MathUtils.EPSILON);
198        quat.getRotationColumn(2, column);
199        assertTrue(Math.abs(new Vector3(0, 0, 1).distance(column)) <= MathUtils.EPSILON);
200
201        quat.set(0, 0, 0, 0);
202        assertEquals(Vector3.UNIT_X, quat.getRotationColumn(0, null));
203
204        // Try a new way with new angles...
205        quat.fromEulerAngles(MathUtils.QUARTER_PI, MathUtils.PI, MathUtils.HALF_PI);
206        final Vector3 rotated = new Vector3(1, 1, 1);
207        quat.apply(rotated, rotated);
208
209        // expected
210        final Vector3 expected = new Vector3(1, 1, 1);
211        final Quaternion worker = new Quaternion();
212        // put together matrix, then apply to vector, so YZX
213        worker.applyRotationY(MathUtils.QUARTER_PI);
214        worker.applyRotationZ(MathUtils.PI);
215        worker.applyRotationX(MathUtils.HALF_PI);
216        worker.apply(expected, expected);
217
218        // test how close it came out
219        assertTrue(rotated.distance(expected) <= Quaternion.ALLOWED_DEVIANCE);
220
221        // test axis rotation methods against general purpose
222        // X AXIS
223        expected.set(1, 1, 1);
224        rotated.set(1, 1, 1);
225        worker.setIdentity().applyRotationX(MathUtils.QUARTER_PI).apply(expected, expected);
226        worker.setIdentity().applyRotation(MathUtils.QUARTER_PI, 1, 0, 0).apply(rotated, rotated);
227        assertTrue(rotated.distance(expected) <= MathUtils.EPSILON);
228
229        // Y AXIS
230        expected.set(1, 1, 1);
231        rotated.set(1, 1, 1);
232        worker.setIdentity().applyRotationY(MathUtils.QUARTER_PI).apply(expected, expected);
233        worker.setIdentity().applyRotation(MathUtils.QUARTER_PI, 0, 1, 0).apply(rotated, rotated);
234        assertTrue(rotated.distance(expected) <= MathUtils.EPSILON);
235
236        // Z AXIS
237        expected.set(1, 1, 1);
238        rotated.set(1, 1, 1);
239        worker.setIdentity().applyRotationZ(MathUtils.QUARTER_PI).apply(expected, expected);
240        worker.setIdentity().applyRotation(MathUtils.QUARTER_PI, 0, 0, 1).apply(rotated, rotated);
241        assertTrue(rotated.distance(expected) <= MathUtils.EPSILON);
242
243        quat.set(worker);
244        worker.applyRotation(0, 0, 0, 0);
245        assertEquals(quat, worker);
246    }
247
248    @Test(expected = IllegalArgumentException.class)
249    public void testBadRotationColumn1() {
250        new Quaternion().getRotationColumn(-1, null);
251    }
252
253    @Test(expected = IllegalArgumentException.class)
254    public void testBadRotationColumn2() {
255        new Quaternion().getRotationColumn(4, null);
256    }
257
258    @Test
259    public void testAngleAxis() {
260        final Quaternion quat = new Quaternion().fromAngleAxis(MathUtils.HALF_PI, new Vector3(2, 0, 0));
261        final Quaternion quat2 = new Quaternion().fromAngleNormalAxis(MathUtils.HALF_PI, new Vector3(1, 0, 0));
262
263        assertEquals(quat2, quat);
264        assertTrue(1 - quat.magnitude() <= MathUtils.EPSILON);
265
266        assertEquals(quat.apply(Vector3.ONE, null), quat2.apply(Vector3.ONE, null));
267        assertTrue(Math.abs(new Vector3(0, -1, 0).distance(quat.apply(new Vector3(0, 0, 1), null))) <= MathUtils.EPSILON);
268
269        assertEquals(Quaternion.IDENTITY,
270                new Quaternion(1, 2, 3, 4).fromAngleAxis(MathUtils.HALF_PI, new Vector3(0, 0, 0)));
271
272        final Vector3 axisStore = new Vector3();
273        double angle = quat.toAngleAxis(axisStore);
274        assertEquals(quat, new Quaternion().fromAngleAxis(angle, axisStore));
275
276        quat.set(0, 0, 0, 0);
277        angle = quat.toAngleAxis(axisStore);
278        assertTrue(0.0 == angle);
279        assertEquals(Vector3.UNIT_X, axisStore);
280    }
281
282    @Test
283    public void testFromVectorToVector() {
284        final Quaternion quat = new Quaternion().fromVectorToVector(Vector3.UNIT_Z, Vector3.UNIT_X);
285        assertEquals(new Quaternion().fromAngleAxis(MathUtils.HALF_PI, Vector3.UNIT_Y), quat);
286
287        quat.fromVectorToVector(Vector3.UNIT_Z, Vector3.NEG_UNIT_Z);
288        assertTrue(Math.abs(new Vector3(0, 0, -1).distance(quat.apply(new Vector3(0, 0, 1), null))) <= Quaternion.ALLOWED_DEVIANCE);
289
290        quat.fromVectorToVector(Vector3.UNIT_X, Vector3.NEG_UNIT_X);
291        assertTrue(Math.abs(new Vector3(-1, 0, 0).distance(quat.apply(new Vector3(1, 0, 0), null))) <= Quaternion.ALLOWED_DEVIANCE);
292
293        quat.fromVectorToVector(Vector3.UNIT_Y, Vector3.NEG_UNIT_Y);
294        assertTrue(Math.abs(new Vector3(0, -1, 0).distance(quat.apply(new Vector3(0, 1, 0), null))) <= Quaternion.ALLOWED_DEVIANCE);
295
296        quat.fromVectorToVector(Vector3.ONE, Vector3.NEG_ONE);
297        assertTrue(Math.abs(new Vector3(-1, -1, -1).distance(quat.apply(new Vector3(1, 1, 1), null))) <= Quaternion.ALLOWED_DEVIANCE);
298
299        quat.fromVectorToVector(Vector3.ZERO, Vector3.ZERO);
300        assertEquals(Quaternion.IDENTITY, quat);
301    }
302
303    @Test
304    public void testNormalize() {
305        final Quaternion quat = new Quaternion(0, 1, 2, 3);
306        final Quaternion quat2 = quat.normalize(null);
307        assertEquals(quat2, quat.normalizeLocal());
308        assertTrue(Math.abs(1 - quat.magnitude()) <= MathUtils.EPSILON);
309        assertTrue(Math.abs(1 - quat2.magnitude()) <= MathUtils.EPSILON);
310    }
311
312    @Test
313    public void testApplyToZero() {
314        assertEquals(Vector3.ZERO, new Quaternion().apply(new Vector3(0, 0, 0), null));
315    }
316
317    @Test
318    public void testInvert() {
319        final Quaternion quat1 = new Quaternion(0, 1, 2, 3);
320        final Quaternion quat2 = quat1.invert(null);
321        assertEquals(Quaternion.IDENTITY, quat1.multiply(quat2, null));
322        assertEquals(quat1, quat2.invert(new Quaternion()));
323        assertEquals(quat1, quat2.invertLocal());
324
325        // normalized version
326        quat1.fromAngleAxis(MathUtils.QUARTER_PI, Vector3.UNIT_Y);
327        quat1.invert(quat2);
328        assertEquals(Quaternion.IDENTITY, quat1.multiply(quat2, null));
329        assertEquals(quat1, quat2.invert(new Quaternion()));
330        assertEquals(quat1, quat2.invertLocal());
331
332        // conjugate check
333        assertEquals(new Quaternion(-1, -2, -3, 4), new Quaternion(1, 2, 3, 4).conjugate(null));
334    }
335
336    @Test
337    public void testAddSubtract() {
338        final Quaternion quat1 = new Quaternion(0, 1, 2, 3);
339        final Quaternion quat2 = new Quaternion(1, 1, 1, 1);
340        assertEquals(new Quaternion(1, 2, 3, 4), quat1.add(quat2, null));
341        assertEquals(new Quaternion(1, 2, 3, 4), quat1.add(quat2, new Quaternion()));
342        assertEquals(new Quaternion(1, 2, 3, 4), quat1.addLocal(quat2));
343
344        quat1.set(0, 1, 2, 3);
345        quat2.set(1, 1, 1, 1);
346        assertEquals(new Quaternion(-1, 0, 1, 2), quat1.subtract(quat2, null));
347        assertEquals(new Quaternion(-1, 0, 1, 2), quat1.subtract(quat2, new Quaternion()));
348        assertEquals(new Quaternion(-1, 0, 1, 2), quat1.subtractLocal(quat2));
349    }
350
351    @Test
352    public void testMultiply() {
353        final Quaternion quat1 = new Quaternion(0.5, 1, 2, 3);
354        final Quaternion quat2 = new Quaternion();
355        assertEquals(new Quaternion(1, 2, 4, 6), quat1.multiply(2, null));
356        assertEquals(new Quaternion(2, 4, 8, 12), quat1.multiply(4, quat2));
357        assertEquals(new Quaternion(1, 2, 4, 6), quat1.multiplyLocal(2));
358
359        quat1.fromAngleNormalAxis(MathUtils.QUARTER_PI, Vector3.UNIT_Y);
360        quat1.multiply(quat1, quat2);
361
362        final ReadOnlyVector3 vec = Vector3.UNIT_Z;
363        assertTrue(Math.abs(Vector3.UNIT_X.distance(quat2.apply(vec, null))) <= Quaternion.ALLOWED_DEVIANCE);
364        quat1.multiplyLocal(quat1.getX(), quat1.getY(), quat1.getZ(), quat1.getW());
365        assertTrue(Math.abs(Vector3.UNIT_X.distance(quat1.apply(vec, null))) <= Quaternion.ALLOWED_DEVIANCE);
366        quat2.fromAngleNormalAxis(MathUtils.HALF_PI, Vector3.UNIT_Y);
367        quat1.multiplyLocal(quat2);
368        assertTrue(Math.abs(Vector3.NEG_UNIT_Z.distance(quat1.apply(vec, null))) <= Quaternion.ALLOWED_DEVIANCE);
369
370        quat1.multiplyLocal(new Matrix3().applyRotationY(MathUtils.HALF_PI));
371        assertTrue(Math.abs(Vector3.NEG_UNIT_X.distance(quat1.apply(vec, null))) <= Quaternion.ALLOWED_DEVIANCE);
372    }
373
374    @Test
375    public void testAxes() {
376        final Matrix3 rot = new Matrix3().applyRotationX(MathUtils.QUARTER_PI).applyRotationY(MathUtils.HALF_PI);
377        final Quaternion quat1 = new Quaternion().fromAxes(rot.getColumn(0, null), rot.getColumn(1, null),
378                rot.getColumn(2, null));
379        final Quaternion quat2 = new Quaternion().fromRotationMatrix(rot);
380        assertEquals(quat2, quat1);
381
382        final Vector3[] axes = quat1.toAxes(new Vector3[3]);
383        quat1.fromAxes(axes[0], axes[1], axes[2]);
384        assertEquals(quat2, quat1);
385    }
386
387    @Test
388    public void testSlerp() {
389        final Quaternion quat = new Quaternion();
390        final Quaternion quat2 = new Quaternion().applyRotationY(MathUtils.HALF_PI);
391        final Quaternion store = quat.slerp(quat2, .5, null);
392        assertTrue(Math.abs(new Vector3(Math.sin(MathUtils.QUARTER_PI), 0, Math.sin(MathUtils.QUARTER_PI))
393                .distance(store.apply(Vector3.UNIT_Z, null))) <= Quaternion.ALLOWED_DEVIANCE);
394
395        // delta == 100%
396        quat2.setIdentity().applyRotationZ(MathUtils.PI);
397        quat.slerp(quat2, 1.0, store);
398        assertTrue(Math.abs(new Vector3(-1, 0, 0).distance(store.apply(Vector3.UNIT_X, null))) <= Quaternion.ALLOWED_DEVIANCE);
399
400        quat2.setIdentity().applyRotationZ(MathUtils.PI);
401        quat.slerp(quat2, .5, store);
402        assertTrue(Math.abs(new Vector3(0, 1, 0).distance(store.apply(Vector3.UNIT_X, null))) <= Quaternion.ALLOWED_DEVIANCE);
403
404        // delta == 0%
405        quat2.setIdentity().applyRotationZ(MathUtils.PI);
406        quat.slerp(quat2, 0, store);
407        assertTrue(Math.abs(new Vector3(1, 0, 0).distance(store.apply(Vector3.UNIT_X, null))) <= Quaternion.ALLOWED_DEVIANCE);
408
409        // a==b
410        quat2.setIdentity();
411        quat.slerp(quat2, 0.25, store);
412        assertTrue(Math.abs(new Vector3(1, 0, 0).distance(store.apply(Vector3.UNIT_X, null))) <= Quaternion.ALLOWED_DEVIANCE);
413
414        // negative dot product
415        quat.setIdentity().applyRotationX(-2 * MathUtils.HALF_PI);
416        quat2.setIdentity().applyRotationX(MathUtils.HALF_PI);
417        quat.slerp(quat2, 0.5, store);
418        assertTrue(Math.abs(new Vector3(0, -Math.sin(MathUtils.QUARTER_PI), Math.sin(MathUtils.QUARTER_PI))
419                .distance(store.apply(Vector3.UNIT_Y, null))) <= Quaternion.ALLOWED_DEVIANCE);
420
421        // LOCAL
422        // delta == 100%
423        quat2.setIdentity().applyRotationX(MathUtils.PI);
424        quat.slerpLocal(quat2, 1.0);
425        assertTrue(Math.abs(new Vector3(0, -1, 0).distance(quat.apply(Vector3.UNIT_Y, null))) <= Quaternion.ALLOWED_DEVIANCE);
426
427        quat.setIdentity();
428        quat.slerpLocal(quat2, .5);
429        assertTrue(Math.abs(new Vector3(0, 0, 1).distance(quat.apply(Vector3.UNIT_Y, null))) <= Quaternion.ALLOWED_DEVIANCE);
430
431        // delta == 0%
432        quat.setIdentity();
433        quat.slerpLocal(quat2, 0);
434        assertTrue(Math.abs(new Vector3(0, 1, 0).distance(quat.apply(Vector3.UNIT_Y, null))) <= Quaternion.ALLOWED_DEVIANCE);
435
436        // a==b
437        quat.setIdentity();
438        quat2.setIdentity();
439        quat.slerpLocal(quat2, 0.25);
440        assertTrue(Math.abs(new Vector3(0, 1, 0).distance(quat.apply(Vector3.UNIT_Y, null))) <= Quaternion.ALLOWED_DEVIANCE);
441
442        // negative dot product
443        quat.setIdentity().applyRotationX(-2 * MathUtils.HALF_PI);
444        quat2.setIdentity().applyRotationX(MathUtils.HALF_PI);
445        quat.slerpLocal(quat2, 0.5);
446        assertTrue(Math.abs(new Vector3(0, -Math.sin(MathUtils.QUARTER_PI), Math.sin(MathUtils.QUARTER_PI))
447                .distance(quat.apply(Vector3.UNIT_Y, null))) <= Quaternion.ALLOWED_DEVIANCE);
448    }
449
450    @Test
451    public void testLookAt() {
452        final Vector3 direction = new Vector3(-1, 0, 0);
453        final Quaternion quat = new Quaternion().lookAt(direction, Vector3.UNIT_Y);
454        assertTrue(Math.abs(direction.distance(quat.apply(Vector3.UNIT_Z, null))) <= Quaternion.ALLOWED_DEVIANCE);
455
456        direction.set(1, 1, 1).normalizeLocal();
457        quat.lookAt(direction, Vector3.UNIT_Y);
458        assertTrue(Math.abs(direction.distance(quat.apply(Vector3.UNIT_Z, null))) <= Quaternion.ALLOWED_DEVIANCE);
459
460        direction.set(-1, 2, -1).normalizeLocal();
461        quat.lookAt(direction, Vector3.UNIT_Y);
462        assertTrue(Math.abs(direction.distance(quat.apply(Vector3.UNIT_Z, null))) <= Quaternion.ALLOWED_DEVIANCE);
463    }
464
465    @Test
466    public void testDot() {
467        final Quaternion quat = new Quaternion(7, 2, 5, -1);
468        assertTrue(35.0 == quat.dot(3, 1, 2, -2));
469
470        assertTrue(-11.0 == quat.dot(new Quaternion(-1, 1, -1, 1)));
471    }
472
473    @Test
474    public void testClone() {
475        final Quaternion quat1 = new Quaternion();
476        final Quaternion quat2 = quat1.clone();
477        assertEquals(quat1, quat2);
478        assertNotSame(quat1, quat2);
479    }
480
481    @Test
482    public void testValid() {
483        final Quaternion quat = new Quaternion();
484        assertTrue(Quaternion.isValid(quat));
485
486        quat.set(Double.NaN, 0, 0, 0);
487        assertFalse(Quaternion.isValid(quat));
488        quat.set(0, Double.NaN, 0, 0);
489        assertFalse(Quaternion.isValid(quat));
490        quat.set(0, 0, Double.NaN, 0);
491        assertFalse(Quaternion.isValid(quat));
492        quat.set(0, 0, 0, Double.NaN);
493        assertFalse(Quaternion.isValid(quat));
494
495        quat.set(Double.NEGATIVE_INFINITY, 0, 0, 0);
496        assertFalse(Quaternion.isValid(quat));
497        quat.set(0, Double.NEGATIVE_INFINITY, 0, 0);
498        assertFalse(Quaternion.isValid(quat));
499        quat.set(0, 0, Double.NEGATIVE_INFINITY, 0);
500        assertFalse(Quaternion.isValid(quat));
501        quat.set(0, 0, 0, Double.NEGATIVE_INFINITY);
502        assertFalse(Quaternion.isValid(quat));
503
504        quat.setIdentity();
505        assertTrue(Quaternion.isValid(quat));
506
507        assertFalse(Quaternion.isValid(null));
508
509        // couple of equals validity tests
510        assertEquals(quat, quat);
511        assertTrue(quat.strictEquals(quat));
512        assertFalse(quat.equals(null));
513        assertFalse(quat.strictEquals(null));
514        assertFalse(quat.equals(new Vector2()));
515        assertFalse(quat.strictEquals(new Vector2()));
516
517        // throw in a couple pool accesses for coverage
518        final Quaternion quatTemp = Quaternion.fetchTempInstance();
519        quatTemp.set(quat);
520        assertEquals(quat, quatTemp);
521        assertNotSame(quat, quatTemp);
522        Quaternion.releaseTempInstance(quatTemp);
523
524        // cover more of equals
525        quat.set(0, 1, 2, 3);
526        final Quaternion comp = new Quaternion(-1, -1, -1, -1);
527        assertFalse(quat.equals(comp));
528        assertFalse(quat.strictEquals(comp));
529        comp.setX(0);
530        assertFalse(quat.equals(comp));
531        assertFalse(quat.strictEquals(comp));
532        comp.setY(1);
533        assertFalse(quat.equals(comp));
534        assertFalse(quat.strictEquals(comp));
535        comp.setZ(2);
536        assertFalse(quat.equals(comp));
537        assertFalse(quat.strictEquals(comp));
538        comp.setW(3);
539        assertEquals(quat, comp);
540        assertTrue(quat.strictEquals(comp));
541    }
542
543    @Test
544    public void testSimpleHash() {
545        // Just a simple sanity check.
546        final Quaternion quat1 = new Quaternion(1, 2, 3, 4);
547        final Quaternion quat2 = new Quaternion(1, 2, 3, 4);
548        final Quaternion quat3 = new Quaternion(1, 2, 3, 0);
549
550        assertTrue(quat1.hashCode() == quat2.hashCode());
551        assertTrue(quat1.hashCode() != quat3.hashCode());
552    }
553
554}