diff --git a/src/main/java/org/joml/Quaterniond.java b/src/main/java/org/joml/Quaterniond.java index 9398577b..16157e84 100644 --- a/src/main/java/org/joml/Quaterniond.java +++ b/src/main/java/org/joml/Quaterniond.java @@ -2847,7 +2847,7 @@ public Vector3d getEulerAnglesXYZ(Vector3d eulerAngles) { } public Vector3d getEulerAnglesZYX(Vector3d eulerAngles) { - eulerAngles.x = Math.atan2(y * z + w * x, 0.5 - x * x + y * y); + eulerAngles.x = Math.atan2(y * z + w * x, 0.5 - x * x - y * y); eulerAngles.y = Math.safeAsin(-2.0 * (x * z - w * y)); eulerAngles.z = Math.atan2(x * y + w * z, 0.5 - y * y - z * z); return eulerAngles; diff --git a/src/main/java/org/joml/Quaternionf.java b/src/main/java/org/joml/Quaternionf.java index b8ad8679..3b79316d 100644 --- a/src/main/java/org/joml/Quaternionf.java +++ b/src/main/java/org/joml/Quaternionf.java @@ -1956,7 +1956,7 @@ public Vector3f getEulerAnglesXYZ(Vector3f eulerAngles) { } public Vector3f getEulerAnglesZYX(Vector3f eulerAngles) { - eulerAngles.x = Math.atan2(y * z + w * x, 0.5f - x * x + y * y); + eulerAngles.x = Math.atan2(y * z + w * x, 0.5f - x * x - y * y); eulerAngles.y = Math.safeAsin(-2.0f * (x * z - w * y)); eulerAngles.z = Math.atan2(x * y + w * z, 0.5f - y * y - z * z); return eulerAngles; diff --git a/src/test/java/org/joml/test/QuaternionDTest.java b/src/test/java/org/joml/test/QuaternionDTest.java index ecde41b9..77ee3435 100644 --- a/src/test/java/org/joml/test/QuaternionDTest.java +++ b/src/test/java/org/joml/test/QuaternionDTest.java @@ -147,6 +147,28 @@ void testGetEulerAnglesXYZ() { throw new AssertionError(); } + @Test + void testGetEulerAnglesZYX() { + Random rnd = new Random(1L); + int failure = 0; + int N = 30000; + for (int i = 0; i < N; i++) { + double x = (rnd.nextFloat() * 2.0 - 1.0) * Math.PI; + double y = (rnd.nextFloat() * 2.0 - 1.0) * Math.PI; + double z = (rnd.nextFloat() * 2.0 - 1.0) * Math.PI; + Quaterniond p = new Quaterniond().rotateZ(z).rotateY(y).rotateX(x); + Vector3d a = p.getEulerAnglesZYX(new Vector3d()); + Quaterniond q = new Quaterniond().rotateZ(a.z).rotateY(a.y).rotateX(a.x); + Vector3d v = new Vector3d(rnd.nextFloat()*2-1, rnd.nextFloat()*2-1, rnd.nextFloat()*2-1); + Vector3d t1 = p.transform(v, new Vector3d()); + Vector3d t2 = q.transform(v, new Vector3d()); + if (!t1.equals(t2, 1E-10f)) + failure++; + } + if ((float)failure / N > 0.0001f) // <- allow for a failure rate of 0.01% + throw new AssertionError(); + } + @Test void testGetEulerAnglesZXY() { Random rnd = new Random(1L); diff --git a/src/test/java/org/joml/test/QuaternionfTest.java b/src/test/java/org/joml/test/QuaternionfTest.java index 1d922fed..be523a9e 100644 --- a/src/test/java/org/joml/test/QuaternionfTest.java +++ b/src/test/java/org/joml/test/QuaternionfTest.java @@ -214,6 +214,28 @@ void testGetEulerAnglesXYZ() { throw new AssertionError(); } + @Test + void testGetEulerAnglesZYX() { + Random rnd = new Random(1L); + int failure = 0; + int N = 30000; + for (int i = 0; i < N; i++) { + float x = (rnd.nextFloat() * 2f - 1f) * (float) Math.PI; + float y = (rnd.nextFloat() * 2f - 1f) * (float) Math.PI; + float z = (rnd.nextFloat() * 2f - 1f) * (float) Math.PI; + Quaternionf p = new Quaternionf().rotateZYX(z, y, x); + Vector3f a = p.getEulerAnglesZYX(new Vector3f()); + Quaternionf q = new Quaternionf().rotateZ(a.z).rotateY(a.y).rotateX(a.x); + Vector3f v = new Vector3f(rnd.nextFloat()*2-1, rnd.nextFloat()*2-1, rnd.nextFloat()*2-1); + Vector3f t1 = p.transform(v, new Vector3f()); + Vector3f t2 = q.transform(v, new Vector3f()); + if (!t1.equals(t2, 1E-3f)) + failure++; + } + if ((float)failure / N > 0.0001f) // <- allow for a failure rate of 0.01% + throw new AssertionError(); + } + @Test void testGetEulerAnglesZXY() { Random rnd = new Random(1L);