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(); + } }