diff --git a/src/org/joml/Quaterniond.java b/src/org/joml/Quaterniond.java index 54dc14ce..964dd06f 100644 --- a/src/org/joml/Quaterniond.java +++ b/src/org/joml/Quaterniond.java @@ -2775,6 +2775,13 @@ public Vector3d getEulerAnglesZYX(Vector3d eulerAngles) { return eulerAngles; } + public Vector3d getEulerAnglesZXY(Vector3d eulerAngles) { + eulerAngles.x = Math.safeAsin(2.0 * (w * x + y * z)); + eulerAngles.y = Math.atan2(w * y - x * z, 0.5 - y * y - x * x); + eulerAngles.z = Math.atan2(w * z - x * y, 0.5 - z * z - x * x); + return eulerAngles; + } + public Quaterniond rotateAxis(double angle, double axisX, double axisY, double axisZ, Quaterniond dest) { double hangle = angle / 2.0; double sinAngle = Math.sin(hangle); diff --git a/src/org/joml/Quaterniondc.java b/src/org/joml/Quaterniondc.java index e17da5c6..a579f5ef 100644 --- a/src/org/joml/Quaterniondc.java +++ b/src/org/joml/Quaterniondc.java @@ -1774,6 +1774,19 @@ public interface Quaterniondc { */ Vector3d getEulerAnglesZYX(Vector3d eulerAngles); + /** + * Get the euler angles in radians in rotation sequence ZXY of this quaternion and store them in the + * provided parameter eulerAngles. + *

+ * The Euler angles are always returned as the angle around X in the {@link Vector3d#x} field, the angle around Y in the {@link Vector3d#y} + * field and the angle around Z in the {@link Vector3d#z} field of the supplied {@link Vector3d} instance. + * + * @param eulerAngles + * will hold the euler angles in radians + * @return the passed in vector + */ + Vector3d getEulerAnglesZXY(Vector3d eulerAngles); + /** * Apply a rotation to this quaternion rotating the given radians about the specified axis * and store the result in dest. diff --git a/src/org/joml/Quaternionf.java b/src/org/joml/Quaternionf.java index 4b8c25af..72f94f49 100644 --- a/src/org/joml/Quaternionf.java +++ b/src/org/joml/Quaternionf.java @@ -1866,6 +1866,13 @@ public Vector3f getEulerAnglesZYX(Vector3f eulerAngles) { return eulerAngles; } + public Vector3f getEulerAnglesZXY(Vector3f eulerAngles) { + eulerAngles.x = Math.safeAsin(2.0f * (w * x + y * z)); + eulerAngles.y = Math.atan2(w * y - x * z, 0.5f - y * y - x * x); + eulerAngles.z = Math.atan2(w * z - x * y, 0.5f - z * z - x * x); + return eulerAngles; + } + public float lengthSquared() { return Math.fma(x, x, Math.fma(y, y, Math.fma(z, z, w * w))); } diff --git a/src/org/joml/Quaternionfc.java b/src/org/joml/Quaternionfc.java index 4cb13380..fdb93e0a 100644 --- a/src/org/joml/Quaternionfc.java +++ b/src/org/joml/Quaternionfc.java @@ -1580,6 +1580,19 @@ public interface Quaternionfc { */ Vector3f getEulerAnglesZYX(Vector3f eulerAngles); + /** + * Get the euler angles in radians in rotation sequence ZXY of this quaternion and store them in the + * provided parameter eulerAngles. + *

+ * The Euler angles are always returned as the angle around X in the {@link Vector3f#x} field, the angle around Y in the {@link Vector3f#y} + * field and the angle around Z in the {@link Vector3f#z} field of the supplied {@link Vector3f} instance. + * + * @param eulerAngles + * will hold the euler angles in radians + * @return the passed in vector + */ + Vector3f getEulerAnglesZXY(Vector3f eulerAngles); + /** * Return the square of the length of this quaternion. * diff --git a/test/org/joml/test/QuaternionDTest.java b/test/org/joml/test/QuaternionDTest.java index 7f69f257..600babb9 100644 --- a/test/org/joml/test/QuaternionDTest.java +++ b/test/org/joml/test/QuaternionDTest.java @@ -135,4 +135,25 @@ public static void testGetEulerAnglesXYZ() { if ((float)failure / N > 0.0001f) // <- allow for a failure rate of 0.01% throw new AssertionError(); } + + public static void testGetEulerAnglesZXY() { + Random rnd = new Random(1L); + int failure = 0; + int N = 3000000; + 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).rotateX(x).rotateY(y); + Vector3d a = p.getEulerAnglesZXY(new Vector3d()); + Quaterniond q = new Quaterniond().rotateZ(a.z).rotateX(a.x).rotateY(a.y); + 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(); + } } diff --git a/test/org/joml/test/QuaternionfTest.java b/test/org/joml/test/QuaternionfTest.java index acef022f..4b9a8797 100644 --- a/test/org/joml/test/QuaternionfTest.java +++ b/test/org/joml/test/QuaternionfTest.java @@ -194,4 +194,25 @@ public static void testGetEulerAnglesXYZ() { if ((float)failure / N > 0.0001f) // <- allow for a failure rate of 0.01% throw new AssertionError(); } + + public static void testGetEulerAnglesZXY() { + Random rnd = new Random(1L); + int failure = 0; + int N = 3000000; + 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().rotateZ(z).rotateX(x).rotateY(y); + Vector3f a = p.getEulerAnglesZXY(new Vector3f()); + Quaternionf q = new Quaternionf().rotateZ(a.z).rotateX(a.x).rotateY(a.y); + 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(); + } }