eryar@163.com

Abstract. The quaternions are members of a noncommutative division algebra first invented by William Rowan Hamilton. The idea for quaternions occurred to him while he was walking along the Royal Cannal on his way to a meeting of the Irish Academy, and Hamilton was so pleased with his discovery that he scratched the fundamental formula of quaternion algebra. There are several different ways we can express orientation and angular displacement in 3D. Here we discuss the three most important methods-matrices, Euler angles, and quaternions.

Key Words. OpenCASCADE, Quaternion, Euler angles, Rotation, Transformation

1. Introduction

Figure 1.1 Modify the location of a Valve

2.Rotation in Matrix Form

Figure 2.1 Defining an orientation using a matrix

Figure 2.2  A Valve Orientation in PDMS

Figure 2.3 Rotating a vector about an arbitrary axis

```void gp_Mat::SetRotation (const gp_XYZ& Axis,
const Standard_Real Ang)
{
//    Rot = I + sin(Ang) * M + (1. - cos(Ang)) * M*M
//    avec  M . XYZ = Axis ^ XYZ

gp_XYZ V = Axis.Normalized();
SetCross (V);
Multiply (sin(Ang));
gp_Mat Temp;
Temp.SetScale (1.0);
Standard_Real A = V.X();
Standard_Real B = V.Y();
Standard_Real C = V.Z();
Temp.SetRow (1, gp_XYZ(- C*C - B*B,      A*B,           A*C     ));
Temp.SetRow (2, gp_XYZ(     A*B,      -A*A - C*C,        B*C    ));
Temp.SetRow (3, gp_XYZ(     A*C,          B*C,       - A*A - B*B));
Temp.Multiply (1.0 - cos(Ang));
}```

3.Rotation with Euler Angles

Euler
Angles的基本思想是将角位移分解为绕三个互相垂直的三个旋转组成的序列。这听起来有点复杂，其实是非常直观的，这也正是Euler
Angle易于使用的优点之一。Euler
Angle将方位Orientation分解为绕三个垂直轴的旋转，那么是哪三个轴？按什么顺序？其实任意三个轴和任意顺序都可以，但最有意义的是使用笛

bank角度后，可以用四步法来确定Euler Angle对应的Orientation：

Figure 3.1 Step 1: An object in its identity orientation

Figure 3.2 Step 2: Heading is the first rotation and rotates about the vertical axis(y-axis)

Figure 3.3 Step 3: Pitch is the second rotation and rotates about the object laterial axis(x-axis)

Figure 3.4 Step 4: Bank is the third and rotates about the object longitudinal axis(z-axis)

pitch-bank系统不是唯一的Euler
Angle系统。绕任意三个互相垂直的任意旋转序列都能定义一个方位orientation。所以多种选择导致了Euler

Angle的易用性，只需要约定旋转序列和三个角度即可表示方位了。可以仿照上述变换过程应用Euler
Angle来实现模型旋转编辑的交互操作，实现交互方式友好的操作。即当鼠标移动到高亮的旋转handle上时，就可以绕一个轴旋转一定角度，不一定是按

Figure 5. Model Editor in PDMS

Figure 6. Euler Angle in Model Editor

Figure 7. Orientation Properties in AVEVA Plant/PDMS

4.Quaternions

Quaternion能被解释为角位移的轴－角对方式。然而，旋转轴和角度不是直接存储在Quaternion的四个数中，它们的确在Quaternion中，但是不是那么直接，其关系式为：

```//=======================================================================
//function : SetVectorAndAngle
//purpose  :
//=======================================================================
void gp_Quaternion::SetVectorAndAngle (const gp_Vec& theAxis,
const Standard_Real theAngle)
{
gp_Vec anAxis = theAxis.Normalized();
Standard_Real anAngleHalf = 0.5 * theAngle;
Standard_Real sin_a = Sin (anAngleHalf);
Set (anAxis.X() * sin_a, anAxis.Y() * sin_a, anAxis.Z() * sin_a, Cos (anAngleHalf));
}```

R(n,θ)变换到R(w,x,y,z)是一个技巧性很强的推导，如果只是为了使用矩阵，那么就不必理解矩阵是如何推导的。如果对推导过程感兴趣，可参考
Fletcher Dunn,Ian Parberry.所著《3D Math Primer for Graphics and Game
Development》，其中有详细推导说明。下面就直接给出推导结果：

Figure 4.1 Converting a quaternion to a 3×3 matrix

```//=======================================================================
//function : GetMatrix
//purpose  :
//=======================================================================
gp_Mat gp_Quaternion::GetMatrix () const
{
Standard_Real wx, wy, wz, xx, yy, yz, xy, xz, zz, x2, y2, z2;
Standard_Real s  = 2.0 / SquareNorm();
x2 = x * s;    y2 = y * s;    z2 = z * s;
xx = x * x2;   xy = x * y2;   xz = x * z2;
yy = y * y2;   yz = y * z2;   zz = z * z2;
wx = w * x2;   wy = w * y2;   wz = w * z2;

gp_Mat aMat;

aMat (1, 1) = 1.0 - (yy + zz);
aMat (1, 2) = xy - wz;
aMat (1, 3) = xz + wy;

aMat (2, 1) = xy + wz;
aMat (2, 2) = 1.0 - (xx + zz);
aMat (2, 3) = yz - wx;

aMat (3, 1) = xz - wy;
aMat (3, 2) = yz + wx;
aMat (3, 3) = 1.0 - (xx + yy);
// 1 division    16 multiplications    15 addidtions    12 variables

return aMat;
}```

```//=======================================================================
//function : GetEulerAngles
//purpose  :
//=======================================================================
void gp_Quaternion::GetEulerAngles (const gp_EulerSequence theOrder,
Standard_Real& theAlpha,
Standard_Real& theBeta,
Standard_Real& theGamma) const
{
gp_Mat M = GetMatrix();

gp_EulerSequence_Parameters o = translateEulerSequence (theOrder);
if ( o.isTwoAxes )
{
double sy = sqrt (M(o.i, o.j) * M(o.i, o.j) + M(o.i, o.k) * M(o.i, o.k));
if (sy > 16 * DBL_EPSILON)
{
theAlpha = ATan2 (M(o.i, o.j),  M(o.i, o.k));
theGamma = ATan2 (M(o.j, o.i), -M(o.k, o.i));
}
else
{
theAlpha = ATan2 (-M(o.j, o.k), M(o.j, o.j));
theGamma = 0.;
}
theBeta = ATan2 (sy, M(o.i, o.i));
}
else
{
double cy = sqrt (M(o.i, o.i) * M(o.i, o.i) + M(o.j, o.i) * M(o.j, o.i));
if (cy > 16 * DBL_EPSILON)
{
theAlpha = ATan2 (M(o.k, o.j), M(o.k, o.k));
theGamma = ATan2 (M(o.j, o.i), M(o.i, o.i));
}
else
{
theAlpha = ATan2 (-M(o.j, o.k), M(o.j, o.j));
theGamma = 0.;
}
theBeta = ATan2 (-M(o.k, o.i), cy);
}
if ( o.isOdd )
{
theAlpha = -theAlpha;
theBeta  = -theBeta;
theGamma = -theGamma;
}
if ( ! o.isExtrinsic )
{
Standard_Real aFirst = theAlpha;
theAlpha = theGamma;
theGamma = aFirst;
}
}```

```/*
*
*           File : Main.cpp
*         Author : eryar@163.com
*           Date : 2014-11-29 10:18
*
*    Description : Test OpenCASCADE quaternion.
*
*      Key Words : OpenCASCADE, Quaternion
*
*/

#define WNT
#include <gp_Quaternion.hxx>

#pragma comment(lib, \"TKernel.lib\")
#pragma comment(lib, \"TKMath.lib\")

void TestQuaternion(void)
{
gp_Quaternion aQuaternion;

// create quaternion by axis-angle.
aQuaternion.SetVectorAndAngle(gp_Vec(1.0, 0.0, 0.0), M_PI_2);

// convert quaternion to matrix.
gp_Mat aMatrix = aQuaternion.GetMatrix();

Standard_Real aYaw = 0.0;
Standard_Real aPitch = 0.0;
Standard_Real aRoll = 0.0;

// convert quaternion to Euler Angles.
aQuaternion.GetEulerAngles(gp_YawPitchRoll, aYaw, aPitch, aRoll);

}

int main(int argc, char* argv[])
{
TestQuaternion();

return 0;
}```

5.Conclusions

Euler Angles作为方位的“主拷贝”，并同时维护一个旋转矩阵，当Euler Angles发生变化时矩阵也同时进行更新。

6. References

1. WolframMathWorld, http://mathworld.wolfram.com/Quaternion.html

2. Ken Shoemake. Conversion between quaternion and Euler angles. Graphics Gems IV, P222-22

3. 苏步青, 华宣积. 应用几何教程. 复旦大学出版计. 2012

4. 丘维声. 解析几何. 北京大学出版社. 1996

5. 同济大学应用数学系编. 线性代数（第四版）. 高等教育出版社. 2003

6. Fletcher Dunn,Ian Parberry. 3D Math Primer for Graphics and Game Development. CRC Press

7. 史银雪,陈洪,王荣静. 3D数学基础：图形与游戏开发. 清华大学出版社. 2005

8. 苏步青. 神奇的符号. 湖南少年儿童出版社. 2010