1 module tests.quaterniond_test;
2 
3 import Math = math;
4 import quaternion_d;
5 import matrix_4d;
6 import vector_3d;
7 import tests.dunit_tests;
8 import std.stdio;
9 
10 /*
11  * The MIT License
12  *
13  * Copyright (c) 2015-2021 JOML.
14  %$#% Translated by jordan4ibanez
15  *
16  * Permission is hereby granted, free of charge, to any person obtaining a copy
17  * of this software and associated documentation files (the "Software"), to deal
18  * in the Software without restriction, including without limitation the rights
19  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
20  * copies of the Software, and to permit persons to whom the Software is
21  * furnished to do so, subject to the following conditions:
22  *
23  * The above copyright notice and this permission notice shall be included in
24  * all copies or substantial portions of the Software.
25  *
26  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
29  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
31  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
32  * THE SOFTWARE.
33  */
34 
35 
36 /**
37  * Test class for {@link Quaterniond}.
38  * @author Sebastian Fellner
39  */
40 unittest {
41     
42     writeln("\nSTARING QUATERNIOND TESTING\n");
43 
44     // testMulQuaternionDQuaternionDQuaternionD
45     {
46         // Multiplication with the identity quaternion should change nothing
47         Quaterniond testQuat = Quaterniond(1, 23.3, -7.57, 2.1);
48         Quaterniond identityQuat = Quaterniond().identity();
49         Quaterniond resultQuat = Quaterniond();
50         
51         testQuat.mul(identityQuat, resultQuat);
52         assertTrue(quatEqual(testQuat, resultQuat, MANY_OPS_AROUND_ZERO_PRECISION_DOUBLE));
53         
54         identityQuat.mul(testQuat, resultQuat);
55         assertTrue(quatEqual(testQuat, resultQuat, STANDARD_AROUND_ZERO_PRECISION_DOUBLE));
56         
57         // Multiplication with conjugate should give (0, 0, 0, dot(this, this))
58         Quaterniond conjugate = Quaterniond();
59         testQuat.conjugate(conjugate);
60         testQuat.mul(conjugate, resultQuat);
61         
62         Quaterniond wantedResultQuat = Quaterniond(0, 0, 0, testQuat.dot(testQuat));
63         assertTrue(quatEqual(resultQuat, wantedResultQuat, MANY_OPS_AROUND_ZERO_PRECISION_DOUBLE));
64     }
65 
66     // testRotationXYZ
67     {
68         Quaterniond v = Quaterniond().rotationXYZ(0.12f, 0.521f, 0.951f);
69         Matrix4d m = Matrix4d().rotateXYZ(0.12f, 0.521f, 0.951f);
70         Matrix4d n = Matrix4d().set(v);
71         assertMatrix4dEquals(m, n, 1E-6f);
72     }
73 
74     // testRotationZYX
75     {
76         Quaterniond v = Quaterniond().rotationZYX(0.12f, 0.521f, 0.951f);
77         Matrix4d m = Matrix4d().rotateZYX(0.12f, 0.521f, 0.951f);
78         Matrix4d n = Matrix4d().set(v);
79         assertMatrix4dEquals(m, n, 1E-6f);
80     }
81 
82     // testRotationYXZ
83     {
84         Quaterniond v = Quaterniond().rotationYXZ(0.12f, 0.521f, 0.951f);
85         Matrix4d m = Matrix4d().rotationYXZ(0.12f, 0.521f, 0.951f);
86         Matrix4d n = Matrix4d().set(v);
87         assertMatrix4dEquals(m, n, 1E-6f);
88     }
89 
90     // testRotateXYZ
91     {
92         Quaterniond v = Quaterniond().rotateXYZ(0.12f, 0.521f, 0.951f);
93         Matrix4d m = Matrix4d().rotateXYZ(0.12f, 0.521f, 0.951f);
94         Matrix4d n = Matrix4d().set(v);
95         assertMatrix4dEquals(m, n, 1E-6f);
96     }
97 
98     // testRotateXYZ
99     {
100         Quaterniond v = Quaterniond().rotateZYX(0.12f, 0.521f, 0.951f);
101         Matrix4d m = Matrix4d().rotateZYX(0.12f, 0.521f, 0.951f);
102         Matrix4d n = Matrix4d().set(v);
103         assertMatrix4dEquals(m, n, 1E-6f);
104     }
105 
106     // testRotateYXZ
107     {
108         Quaterniond v = Quaterniond().rotateYXZ(0.12f, 0.521f, 0.951f);
109         Matrix4d m = Matrix4d().rotateYXZ(0.12f, 0.521f, 0.951f);
110         Matrix4d n = Matrix4d().set(v);
111         assertMatrix4dEquals(m, n, 1E-6f);
112     }
113 
114     // testRotateToReturnsDestination
115     {
116         Quaterniond rotation = Quaterniond();
117         Quaterniond destination = Quaterniond();
118         Quaterniond result = rotation.rotateTo(0, 1, 0, 0, 1, 0, destination);
119         assertEquals(destination, result); // Assert same?
120     }
121 
122     // testFromAxisAngle
123     {
124         Vector3d axis = Vector3d(1.0, 0.0, 0.0);
125         double angleDeg = 45.0;
126         double angleRad = Math.toRadians(angleDeg);
127         Quaterniond fromRad1 = Quaterniond().fromAxisAngleRad(axis, angleRad);
128         Quaterniond fromRad2 = Quaterniond().fromAxisAngleRad(axis.x, axis.y, axis.z, angleRad);
129         Quaterniond fromDeg = Quaterniond().fromAxisAngleDeg(axis, angleDeg);
130         assertEquals(fromRad1, fromRad2);
131         assertEquals(fromRad2, fromDeg);
132     }
133 
134     // testGetEulerAnglesXYZ
135     {        
136         int failure = 0;
137         int N = 3_000_000;
138         for (int i = 0; i <= N; i++) {
139             if (i % 100_000  == 0) {
140                 // This is here because it takes a long time
141                 writeln("Batch test: ", (cast(double)i / 3_000_000.0) * 100, "%" );
142             }
143             double x = (Math.random() * 2.0 - 1.0) * Math.PI;
144             double y = (Math.random() * 2.0 - 1.0) * Math.PI;
145             double z = (Math.random() * 2.0 - 1.0) * Math.PI;
146             Quaterniond p = Quaterniond().rotateXYZ(x, y, z);
147             Vector3d a = p.getEulerAnglesXYZ(Vector3d());
148             Quaterniond q = Quaterniond().rotateX(a.x).rotateY(a.y).rotateZ(a.z);
149             Vector3d v = Vector3d(Math.random()*2-1, Math.random()*2-1, Math.random()*2-1);
150             Vector3d testunit = Vector3d();
151             Vector3d t1 = p.transform(v, testunit);
152             Vector3d t2 = q.transform(v, testunit);
153             if (!t1.equals(t2, 1E-10f))
154                 failure++;
155         }
156         if (cast(float)failure / N > 0.0001f) // <- allow for a failure rate of 0.01%
157             assert(true == false);
158     }
159 
160     writeln("PASSED!");
161 
162     // testGetEulerAnglesZXY
163     {
164         int failure = 0;
165         int N = 3_000_000;
166         for (int i = 0; i <= N; i++) {
167             if (i % 100_000  == 0) {
168                 // This is here because it takes a long time
169                 writeln("Batch test: ", (cast(double)i / 3_000_000.0) * 100, "%" );
170             }
171             double x = (Math.random() * 2.0 - 1.0) * Math.PI;
172             double y = (Math.random() * 2.0 - 1.0) * Math.PI;
173             double z = (Math.random() * 2.0 - 1.0) * Math.PI;
174             Quaterniond p = Quaterniond().rotateZ(z).rotateX(x).rotateY(y);
175             Vector3d a = p.getEulerAnglesZXY(Vector3d());
176             Quaterniond q = Quaterniond().rotateZ(a.z).rotateX(a.x).rotateY(a.y);
177             Vector3d v = Vector3d(Math.random()*2-1, Math.random()*2-1, Math.random()*2-1);
178             Vector3d testunit = Vector3d();
179             Vector3d t1 = p.transform(v, testunit);
180             Vector3d t2 = q.transform(v, testunit);
181             if (!t1.equals(t2, 1E-10f))
182                 failure++;
183         }
184         if (cast(float)failure / N > 0.0001f) // <- allow for a failure rate of 0.01%
185             assert(true == false);
186     }
187 
188     writeln("PASSED!");
189 
190     // testGetEulerAnglesYXZ
191     {
192         int failure = 0;
193         int N = 3_000_000;
194         for (int i = 0; i <= N; i++) {
195             if (i % 100_000  == 0) {
196                 // This is here because it takes a long time
197                 writeln("Batch test: ", (cast(double)i / 3_000_000.0) * 100, "%" );
198             }
199             double x = (Math.random() * 2.0 - 1.0) * Math.PI;
200             double y = (Math.random() * 2.0 - 1.0) * Math.PI;
201             double z = (Math.random() * 2.0 - 1.0) * Math.PI;
202 
203             Vector3d testunit = Vector3d();
204             Quaterniond p = Quaterniond().rotateY(y).rotateX(x).rotateZ(z);
205             Vector3d a = p.getEulerAnglesYXZ(Vector3d());
206             Quaterniond q = Quaterniond().rotateY(a.y).rotateX(a.x).rotateZ(a.z);
207             Vector3d v = Vector3d(Math.random()*2-1, Math.random()*2-1, Math.random()*2-1);
208             Vector3d t1 = p.transform(v, testunit);
209             Vector3d t2 = q.transform(v, testunit);
210             if (!t1.equals(t2, 1E-10f))
211                 failure++;
212         }
213         if (cast(float)failure / N > 0.0001f) // <- allow for a failure rate of 0.01%
214             assert(true == false);
215     }
216 
217     writeln("PASSED!");
218 }