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 java.nio.DoubleBuffer;
016import java.nio.FloatBuffer;
017
018import org.junit.Test;
019
020public class TestTransform {
021
022    @Test
023    public void testGetSet() {
024        final Transform trans = new Transform();
025        assertEquals(Transform.IDENTITY, trans);
026
027        final Transform immutable = new Transform(new Matrix3().applyRotationX(MathUtils.QUARTER_PI), new Vector3(0,
028                -1, -2), new Vector3(1, 2, 3), true, true, true);
029        assertTrue(true == immutable.isIdentity());
030        assertTrue(true == immutable.isRotationMatrix());
031        assertTrue(true == immutable.isUniformScale());
032        assertEquals(new Matrix3().applyRotationX(MathUtils.QUARTER_PI), immutable.getMatrix());
033        assertEquals(new Vector3(0, -1, -2), immutable.getScale());
034        assertEquals(new Vector3(1, 2, 3), immutable.getTranslation());
035
036        final Transform trans2 = new Transform(immutable);
037        assertEquals(immutable, trans2);
038        trans2.updateFlags(false);
039
040        trans.set(immutable);
041        assertEquals(Transform.IDENTITY, trans); // because of shortcut flags.
042
043        trans.set(trans2);
044        assertEquals(trans2, trans);
045
046        trans.setIdentity();
047        assertEquals(Transform.IDENTITY, trans);
048
049        final double a = MathUtils.QUARTER_PI;
050        trans.setRotation(new Quaternion().fromAngleAxis(a, Vector3.UNIT_Y));
051
052        assertEquals(new Matrix3( //
053                Math.cos(a), 0, Math.sin(a), //
054                0, 1, 0, //
055                -Math.sin(a), 0, Math.cos(a)), trans.getMatrix());
056
057        trans2.setRotation(new Matrix3().fromAngleAxis(a, Vector3.UNIT_Y));
058        assertEquals(trans.getMatrix(), trans2.getMatrix());
059
060        trans.setScale(1.0);
061        assertEquals(Vector3.ONE, trans.getScale());
062
063        trans.setScale(new Vector3(1, 2, 3));
064        assertEquals(new Vector3(1, 2, 3), trans.getScale());
065
066        trans.setScale(-1, 5, -3);
067        assertEquals(new Vector3(-1, 5, -3), trans.getScale());
068
069        trans.setTranslation(new Vector3(10, 20, 30));
070        assertEquals(new Vector3(10, 20, 30), trans.getTranslation());
071
072        trans.setTranslation(-10, 50, -30);
073        assertEquals(new Vector3(-10, 50, -30), trans.getTranslation());
074
075        trans.setIdentity();
076        trans.setRotation(new Matrix3().fromAngleAxis(a, Vector3.UNIT_Y));
077        trans.setScale(2, 3, 4);
078        trans.setTranslation(5, 10, 15);
079
080        final Matrix4 mat4 = trans.getHomogeneousMatrix(null);
081        assertEquals(new Matrix4( //
082                2 * Math.cos(a), 2 * 0, 2 * Math.sin(a), 5, //
083                3 * 0, 3 * 1, 3 * 0, 10, //
084                4 * -Math.sin(a), 4 * 0, 4 * Math.cos(a), 15, //
085                0, 0, 0, 1), mat4);
086
087        trans2.fromHomogeneousMatrix(mat4);
088        trans2.getHomogeneousMatrix(mat4);
089        assertEquals(new Matrix4( //
090                2 * Math.cos(a), 2 * 0, 2 * Math.sin(a), 5, //
091                3 * 0, 3 * 1, 3 * 0, 10, //
092                4 * -Math.sin(a), 4 * 0, 4 * Math.cos(a), 15, //
093                0, 0, 0, 1), mat4);
094
095        trans.setIdentity();
096        trans.setRotation(new Matrix3(0, 1, 2, 3, 4, 5, 6, 7, 8));
097        trans.setTranslation(10, 11, 12);
098        trans.getHomogeneousMatrix(mat4);
099        assertEquals(new Matrix4( //
100                0, 1, 2, 10, //
101                3, 4, 5, 11, //
102                6, 7, 8, 12, //
103                0, 0, 0, 1), mat4);
104
105    }
106
107    @Test(expected = TransformException.class)
108    public void testFailScale1A() {
109        final Transform trans = new Transform(new Matrix3(), new Vector3(), new Vector3(), false, false, false);
110        trans.setScale(Vector3.ONE);
111    }
112
113    @Test(expected = IllegalArgumentException.class)
114    public void testFailScale1B() {
115        final Transform trans = new Transform();
116        trans.setScale(Vector3.ZERO);
117    }
118
119    @Test(expected = TransformException.class)
120    public void testFailScale2A() {
121        final Transform trans = new Transform(new Matrix3(), new Vector3(), new Vector3(), false, false, false);
122        trans.setScale(1, 1, 1);
123    }
124
125    @Test(expected = IllegalArgumentException.class)
126    public void testFailScale2B() {
127        final Transform trans = new Transform();
128        trans.setScale(0, 0, 0);
129    }
130
131    @Test(expected = TransformException.class)
132    public void testFailScale3A() {
133        final Transform trans = new Transform(new Matrix3(), new Vector3(), new Vector3(), false, false, false);
134        trans.setScale(1);
135    }
136
137    @Test(expected = IllegalArgumentException.class)
138    public void testFailScale3B() {
139        final Transform trans = new Transform();
140        trans.setScale(0);
141    }
142
143    @Test
144    public void testTranslate() {
145        final Transform trans = new Transform();
146        trans.translate(1, 3, 5);
147        assertEquals(new Vector3(1, 3, 5), trans.getTranslation());
148        trans.translate(trans.getTranslation().negate(null));
149        assertEquals(Vector3.ZERO, trans.getTranslation());
150
151        trans.translate(new Vector3(1, 3, 5));
152        assertEquals(new Vector3(1, 3, 5), trans.getTranslation());
153        trans.translate(-1, -3, -5);
154        assertEquals(Vector3.ZERO, trans.getTranslation());
155    }
156
157    @Test
158    public void testApplyVector3() {
159        final Transform trans = new Transform().setRotation(new Matrix3().applyRotationX(MathUtils.HALF_PI)).translate(
160                1, 2, 3);
161        final Vector3 vec3 = new Vector3(0, 1, 0);
162
163        final Vector3 result = trans.applyForward(vec3, null);
164        assertTrue(Math.abs(new Vector3(1, 2, 4).distance(result)) <= MathUtils.EPSILON);
165        trans.applyForward(vec3, result);
166        assertTrue(Math.abs(new Vector3(1, 2, 4).distance(result)) <= MathUtils.EPSILON);
167        trans.applyForward(vec3);
168        assertTrue(Math.abs(new Vector3(1, 2, 4).distance(vec3)) <= MathUtils.EPSILON);
169
170        vec3.set(0, 1, 1);
171        final Vector3 result2 = trans.applyForwardVector(vec3, null);
172        assertTrue(Math.abs(new Vector3(0, -1, 1).distance(result2)) <= MathUtils.EPSILON);
173        trans.applyForwardVector(vec3, result2);
174        assertTrue(Math.abs(new Vector3(0, -1, 1).distance(result2)) <= MathUtils.EPSILON);
175        trans.applyForwardVector(vec3);
176        assertTrue(Math.abs(new Vector3(0, -1, 1).distance(vec3)) <= MathUtils.EPSILON);
177
178        vec3.set(0, 1, 0);
179        final Vector3 result3 = trans.applyInverse(vec3, null);
180        assertTrue(Math.abs(new Vector3(-1, -3, 1).distance(result3)) <= MathUtils.EPSILON);
181        trans.applyInverse(vec3, result3);
182        assertTrue(Math.abs(new Vector3(-1, -3, 1).distance(result3)) <= MathUtils.EPSILON);
183        trans.applyInverse(vec3);
184        assertTrue(Math.abs(new Vector3(-1, -3, 1).distance(vec3)) <= MathUtils.EPSILON);
185
186        vec3.set(0, 1, 1);
187        final Vector3 result4 = trans.applyInverseVector(vec3, null);
188        assertTrue(Math.abs(new Vector3(0, 1, -1).distance(result4)) <= MathUtils.EPSILON);
189        trans.applyInverseVector(vec3, result4);
190        assertTrue(Math.abs(new Vector3(0, 1, -1).distance(result4)) <= MathUtils.EPSILON);
191        trans.applyInverseVector(vec3);
192        assertTrue(Math.abs(new Vector3(0, 1, -1).distance(vec3)) <= MathUtils.EPSILON);
193
194        trans.setRotation(new Matrix3().applyRotationY(MathUtils.PI)).translate(2, 3, -1);
195
196        vec3.set(1, 2, 3).normalizeLocal();
197        final Vector3 orig = new Vector3(vec3);
198        trans.applyForward(vec3);
199        trans.applyInverse(vec3);
200        assertTrue(Math.abs(orig.distance(vec3)) <= 10 * MathUtils.EPSILON); // accumulated error
201
202        vec3.set(orig);
203        trans.applyForwardVector(vec3);
204        trans.applyInverseVector(vec3);
205        assertTrue(Math.abs(orig.distance(vec3)) <= 10 * MathUtils.EPSILON); // accumulated error
206
207        vec3.set(orig);
208        trans.setIdentity();
209        trans.applyForward(vec3);
210        assertEquals(orig, vec3);
211        trans.applyForwardVector(vec3);
212        assertEquals(orig, vec3);
213        trans.applyInverse(vec3);
214        assertEquals(orig, vec3);
215        trans.applyInverseVector(vec3);
216        assertEquals(orig, vec3);
217    }
218
219    @Test(expected = NullPointerException.class)
220    public void testApplyFail1() {
221        final Transform trans = new Transform();
222        trans.applyForward(null);
223    }
224
225    @Test(expected = NullPointerException.class)
226    public void testApplyFail2() {
227        final Transform trans = new Transform();
228        trans.applyForwardVector(null);
229    }
230
231    @Test(expected = NullPointerException.class)
232    public void testApplyFail3() {
233        final Transform trans = new Transform();
234        trans.applyInverse(null);
235    }
236
237    @Test(expected = NullPointerException.class)
238    public void testApplyFail4() {
239        final Transform trans = new Transform();
240        trans.applyInverseVector(null);
241    }
242
243    @Test
244    public void testMultiply() {
245        final Transform trans1 = new Transform();
246        final Transform trans2 = new Transform();
247        assertEquals(Transform.IDENTITY, trans1.multiply(trans2, null));
248
249        trans1.setTranslation(1, 2, 3);
250        final Transform trans3 = trans1.multiply(trans2, null);
251        assertEquals(trans1, trans3);
252
253        trans2.setTranslation(-1, -2, -3);
254        trans1.multiply(trans2, trans3);
255        assertEquals(Transform.IDENTITY, trans3);
256        assertTrue(trans3.isRotationMatrix());
257        assertTrue(trans3.isIdentity());
258        assertTrue(trans3.isUniformScale());
259
260        trans2.setScale(1, 2, 1);
261        trans1.multiply(trans2, trans3);
262        assertEquals(new Transform().setScale(1, 2, 1), trans3);
263        assertTrue(trans3.isRotationMatrix());
264        assertFalse(trans3.isIdentity());
265        assertFalse(trans3.isUniformScale());
266
267        trans1.setScale(1, 2, 1);
268        trans1.multiply(trans2, trans3);
269        assertEquals(new Transform().setRotation(new Matrix3(1, 0, 0, 0, 4, 0, 0, 0, 1)).setTranslation(0, -2, 0),
270                trans3);
271        assertFalse(trans3.isRotationMatrix());
272        assertFalse(trans3.isIdentity());
273        assertFalse(trans3.isUniformScale());
274    }
275
276    @Test
277    public void testInvert() {
278        final Transform trans1 = new Transform();
279        trans1.setRotation(new Matrix3().applyRotationZ(3 * MathUtils.QUARTER_PI));
280        final Transform trans2 = trans1.invert(null);
281        assertEquals(Transform.IDENTITY, trans1.multiply(trans2, null));
282
283        trans1.setIdentity().invert(trans1);
284        assertEquals(Transform.IDENTITY, trans1);
285    }
286
287    @Test
288    public void testClone() {
289        final Transform trans1 = new Transform();
290        final Transform trans2 = trans1.clone();
291        assertEquals(trans1, trans2);
292        assertNotSame(trans1, trans2);
293    }
294
295    @Test
296    public void testValid() {
297        final Transform trans = new Transform();
298        assertTrue(Transform.isValid(trans));
299        trans.setIdentity();
300        trans.setRotation(new Matrix3(Double.NaN, 0, 0, 0, 0, 0, 0, 0, 0));
301        assertFalse(Transform.isValid(trans));
302        trans.setIdentity();
303        trans.setScale(Double.NaN, 0, 0);
304        assertFalse(Transform.isValid(trans));
305        trans.setScale(Double.NaN);
306        assertFalse(Transform.isValid(trans));
307        trans.setIdentity();
308        trans.setTranslation(Double.NaN, 0, 0);
309        assertFalse(Transform.isValid(trans));
310
311        trans.setIdentity();
312        assertTrue(Transform.isValid(trans));
313
314        assertFalse(Transform.isValid(null));
315
316        // couple of equals validity tests
317        assertEquals(trans, trans);
318        assertTrue(trans.strictEquals(trans));
319        assertFalse(trans.equals(null));
320        assertFalse(trans.strictEquals(null));
321        assertFalse(trans.equals(new Vector2()));
322        assertFalse(trans.strictEquals(new Vector2()));
323
324        // throw in a couple pool accesses for coverage
325        final Transform transTemp = Transform.fetchTempInstance();
326        transTemp.set(trans);
327        assertEquals(trans, transTemp);
328        assertNotSame(trans, transTemp);
329        Transform.releaseTempInstance(transTemp);
330
331        // cover more of equals
332        trans.setScale(1, 2, 3);
333        trans.setRotation(new Matrix3(0, 1, 2, 3, 4, 5, 6, 7, 8));
334        trans.setTranslation(1, 2, 3);
335        final Transform comp = new Transform();
336        final Matrix3 mat3 = new Matrix3(-1, -1, -1, -1, -1, -1, -1, -1, -1);
337        comp.setScale(-1, -1, -1);
338        comp.setRotation(mat3);
339        comp.setTranslation(-1, -1, -1);
340        assertFalse(trans.equals(comp));
341        assertFalse(trans.strictEquals(comp));
342        for (int i = 0; i < 8; i++) {
343            mat3.setValue(i / 3, i % 3, i);
344            comp.setRotation(mat3);
345            assertFalse(trans.equals(comp));
346            assertFalse(trans.strictEquals(comp));
347        }
348        // test translation
349        trans.setRotation(Matrix3.IDENTITY);
350        comp.setRotation(Matrix3.IDENTITY);
351        comp.setTranslation(1, -1, -1);
352        assertFalse(trans.equals(comp));
353        assertFalse(trans.strictEquals(comp));
354        comp.setTranslation(1, 2, -1);
355        assertFalse(trans.equals(comp));
356        assertFalse(trans.strictEquals(comp));
357        comp.setTranslation(1, 2, 3);
358        assertFalse(trans.equals(comp));
359        assertFalse(trans.strictEquals(comp));
360
361        // test scale
362        comp.setScale(1, -1, -1);
363        assertFalse(trans.equals(comp));
364        assertFalse(trans.strictEquals(comp));
365        comp.setScale(1, 2, -1);
366        assertFalse(trans.equals(comp));
367        assertFalse(trans.strictEquals(comp));
368        comp.setScale(1, 2, 3);
369        assertTrue(trans.equals(comp));
370        assertTrue(trans.strictEquals(comp));
371    }
372
373    @Test
374    public void testSimpleHash() {
375        // Just a simple sanity check.
376        final Transform trans1 = new Transform().setTranslation(1, 2, 3);
377        final Transform trans2 = new Transform().setTranslation(1, 2, 3);
378        final Transform trans3 = new Transform().setTranslation(1, 2, 0);
379
380        assertTrue(trans1.hashCode() == trans2.hashCode());
381        assertTrue(trans1.hashCode() != trans3.hashCode());
382    }
383
384    @Test
385    public void testGLApplyMatrix() {
386        final Transform trans = new Transform();
387
388        // non-rotational
389        trans.setRotation(new Matrix3(0, 1, 2, 3, 4, 5, 6, 7, 8));
390        trans.setTranslation(10, 11, 12);
391        final DoubleBuffer db = DoubleBuffer.allocate(16);
392        trans.getGLApplyMatrix(db);
393        for (final double val : new double[] { 0, 3, 6, 0, 1, 4, 7, 0, 2, 5, 8, 0, 10, 11, 12, 1 }) {
394            assertTrue(val == db.get());
395        }
396        final FloatBuffer fb = FloatBuffer.allocate(16);
397        trans.getGLApplyMatrix(fb);
398        for (final float val : new float[] { 0, 3, 6, 0, 1, 4, 7, 0, 2, 5, 8, 0, 10, 11, 12, 1 }) {
399            assertTrue(val == fb.get());
400        }
401
402        // rotational
403        final double a = MathUtils.QUARTER_PI;
404        trans.setRotation(new Matrix3().applyRotationY(a));
405        trans.setTranslation(10, 11, 12);
406        trans.setScale(2, 3, 4);
407        db.rewind();
408        trans.getGLApplyMatrix(db);
409        for (final double val : new double[] { 2 * Math.cos(a), 2 * 0, 2 * -Math.sin(a), 0, //
410                3 * 0, 3 * 1, 3 * 0, 0, //
411                4 * Math.sin(a), 4 * 0, 4 * Math.cos(a), 0, //
412                10, 11, 12, 1 }) {
413            assertTrue(val == db.get());
414        }
415        fb.rewind();
416        trans.getGLApplyMatrix(fb);
417        for (final float val : new float[] { (float) (2 * Math.cos(a)), 2 * 0, (float) (2 * -Math.sin(a)), 0, //
418                3 * 0, 3 * 1, 3 * 0, 0, //
419                (float) (4 * Math.sin(a)), 4 * 0, (float) (4 * Math.cos(a)), 0, //
420                10, 11, 12, 1 }) {
421            assertTrue(val == fb.get());
422        }
423    }
424}