Get Euler angles from gp_Trsf with gp_Quaternion::getEulerAngles()


I am creating a transformation (rotation only) by rotating around three fixed axes X, Y, Z by three angles c, b, a. As the result I am getting a gp_Quaternion "q".

Now I want to extract three euler angles "aProbe, bProbe, cProbe" from "q" in order to create the same transformation again (rotate around fixed axes X, Y, Z with cProbe, bProbe, aProbe). This results in a second gp_Quaternion "qProbe".

After all the "q" and "aProbe" should be equal.

The question is: How do I extract "cProbe, bProbe, aProbe" from "q"? Or: what "gp_EulerSequence sequence" do I have to use?

I thought "Extrinsic = fixed Axes, XYZ = order of Axes" and tried these two possibilities:

q.GetEulerAngles(gp_Extrinsic_XYZ, cProbe, bProbe, aProbe);
q.GetEulerAngles(gp_Extrinsic_XYZ, aProbe, bProbe, cProbe);

But both fails (q != qProbe). :-(

Ok, I can try all possibilities, but I want to understand what I am doing.

Here is my sample code:
const double TO_RAD = M_PI / 180.0;
const double TO_DEG = 1.0 / TO_RAD;

double a = 10.0 * TO_RAD; // degrees
double b = 20.0 * TO_RAD; // degrees
double c = 30.0 * TO_RAD; // degrees

// Calculate trsf from three angles a, b, c
gp_Trsf trsf1, trsf2, trsf3, trsf;
trsf1.SetRotation(gp::OX(), c); // rotate around fixed X-Axis with c
trsf2.SetRotation(gp::OY(), b); // rotate around fixed Y-Axis with b
trsf3.SetRotation(gp::OZ(), a); // rotate around fixed Z-Axis with a
trsf = trsf1 * trsf2 * trsf3;
gp_Quaternion q = trsf.GetRotation();

// Check angles
double aProbe, bProbe, cProbe;
q.GetEulerAngles(sequence, aProbe, bProbe, cProbe);
gp_Trsf trsf1Probe, trsf2Probe, trsf3Probe, trsfProbe;
trsf1Probe.SetRotation(gp::OX(), cProbe);
trsf2Probe.SetRotation(gp::OY(), bProbe);
trsf3Probe.SetRotation(gp::OZ(), aProbe);
trsfProbe = trsf1Probe * trsf2Probe * trsf3Probe;
gp_Quaternion qProbe = trsfProbe.GetRotation();


Thorsten H's picture

I stepped into the source code and compared the OCCT method with the algorithm by Ken Shoemake mentioned in gp_Quaternion.cxx:

//function : translateEulerSequence
//purpose :
// Code supporting conversion between quaternion and generalized
// Euler angles (sequence of three rotations) is based on
// algorithm by Ken Shoemake, published in Graphics Gems IV, p. 222-22

I am doing the following rotation:
with c around the worlds x-Axis
with b around the worlds y-Axis
with a around the worlds z-Axis

In "Graphics Gems IV" this is EulOrdXYZs, which means:
- Initial axis: X
- parity of axis permutation: even
- repetition of initial axis as last: no
- take axes from initial frame (static axes): yes

The same euler parameters in OCCT results in the euler sequence "gp_Intrinsic_XYZ". But the equivalent gp_EulerSequence for EulOrdXYZs should be "gp_Extrinsic_XYZ", because I am rotating around static axes! I integrated the "Graphics Gems IV" sources into my tests and the results confirmed this: EulOrdXYZs is gp_Intrinsic_XYZ in OCCT!

Now I am using gp_Intrinsic_XYZ everywhere I rotate around static XYZ. Now all my algorithms are working correctly, but the naming of the "gp_EulerSequence"s in OCCT is obviously WRONG!

Is there any OCCT developer who can confirm this as a BUG (or a feature...)?

Nicholas Fette's picture

I'm not developing OCCT now, but I also noticed similar problems, and insufficient documentation to clarify what the code intended to do. I'm getting an axes instead of a transformation, eg.:

gp_EulerSequence mode = gp_Intrinsic_XYZ;
double alpha = 1.5,
beta = 0,
gamma = 0;
gp_Ax2 the_ax2;

gp_Quaternion q;
q.SetEulerAngles(mode, alpha, beta, gamma);
gp_Mat mat = q.GetMatrix();
gp_Dir dir(mat.Column(3));
gp_Dir Vx(mat.Column(1));

Something is wrong because when I make only alpha nonzero (that is a rotation about x axis), even the x direction of the_ax2 changes, which should happen for neither extrinsic nor intrinsic rotation about just the x axis. However, if I reverse the order of alpha, beta, gamma, or use mode = gp_Intrinsic_ZYX, I get what I expect.

Nicholas Fette's picture

Also, the comments in gp_TrsfForm.hxx really belong to the enum gp_EulerSequence in its own header file.

Forum supervisor's picture


OCCT 7.0 includes a fix for incorrect interpretation of intrinsic Tait-Bryan angles (including gp_YawPitchRoll), see #25574 issue in OCCT Mantis Bug Tracker.
We suppose that this fixes the problems described above.
Wrong comment in gp_TrsfForm.hxx should be fixed in master this week, see #27602.

Best Regards,