123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829 |
- // David Eberly, Geometric Tools, Redmond WA 98052
- // Copyright (c) 1998-2020
- // Distributed under the Boost Software License, Version 1.0.
- // https://www.boost.org/LICENSE_1_0.txt
- // https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
- // Version: 4.0.2019.08.13
- #pragma once
- #include <Mathematics/AxisAngle.h>
- #include <Mathematics/EulerAngles.h>
- #include <Mathematics/Matrix.h>
- #include <Mathematics/Quaternion.h>
- namespace WwiseGTE
- {
- // Conversions among various representations of rotations. The value of
- // N must be 3 or 4. The latter case supports affine algebra when you use
- // 4-tuple vectors (w-component is 1 for points and 0 for vector) and 4x4
- // matrices for affine transformations. Rotation axes must be unit
- // length. The angles are in radians. The Euler angles are in world
- // coordinates; we have not yet added support for body coordinates.
- template <int N, typename Real>
- class Rotation
- {
- public:
- // Create rotations from various representations.
- Rotation(Matrix<N, N, Real> const& matrix)
- :
- mType(IS_MATRIX),
- mMatrix(matrix)
- {
- static_assert(N == 3 || N == 4, "Dimension must be 3 or 4.");
- }
- Rotation(Quaternion<Real> const& quaternion)
- :
- mType(IS_QUATERNION),
- mQuaternion(quaternion)
- {
- static_assert(N == 3 || N == 4, "Dimension must be 3 or 4.");
- }
- Rotation(AxisAngle<N, Real> const& axisAngle)
- :
- mType(IS_AXIS_ANGLE),
- mAxisAngle(axisAngle)
- {
- static_assert(N == 3 || N == 4, "Dimension must be 3 or 4.");
- }
- Rotation(EulerAngles<Real> const& eulerAngles)
- :
- mType(IS_EULER_ANGLES),
- mEulerAngles(eulerAngles)
- {
- static_assert(N == 3 || N == 4, "Dimension must be 3 or 4.");
- }
- // Convert one representation to another.
- operator Matrix<N, N, Real>() const
- {
- static_assert(N == 3 || N == 4, "Dimension must be 3 or 4.");
- switch (mType)
- {
- case IS_MATRIX:
- break;
- case IS_QUATERNION:
- Convert(mQuaternion, mMatrix);
- break;
- case IS_AXIS_ANGLE:
- Convert(mAxisAngle, mMatrix);
- break;
- case IS_EULER_ANGLES:
- Convert(mEulerAngles, mMatrix);
- break;
- }
- return mMatrix;
- }
- operator Quaternion<Real>() const
- {
- static_assert(N == 3 || N == 4, "Dimension must be 3 or 4.");
- switch (mType)
- {
- case IS_MATRIX:
- Convert(mMatrix, mQuaternion);
- break;
- case IS_QUATERNION:
- break;
- case IS_AXIS_ANGLE:
- Convert(mAxisAngle, mQuaternion);
- break;
- case IS_EULER_ANGLES:
- Convert(mEulerAngles, mQuaternion);
- break;
- }
- return mQuaternion;
- }
- operator AxisAngle<N, Real>() const
- {
- static_assert(N == 3 || N == 4, "Dimension must be 3 or 4.");
- switch (mType)
- {
- case IS_MATRIX:
- Convert(mMatrix, mAxisAngle);
- break;
- case IS_QUATERNION:
- Convert(mQuaternion, mAxisAngle);
- break;
- case IS_AXIS_ANGLE:
- break;
- case IS_EULER_ANGLES:
- Convert(mEulerAngles, mAxisAngle);
- break;
- }
- return mAxisAngle;
- }
- EulerAngles<Real> const& operator()(int i0, int i1, int i2) const
- {
- static_assert(N == 3 || N == 4, "Dimension must be 3 or 4.");
- mEulerAngles.axis[0] = i0;
- mEulerAngles.axis[1] = i1;
- mEulerAngles.axis[2] = i2;
- switch (mType)
- {
- case IS_MATRIX:
- Convert(mMatrix, mEulerAngles);
- break;
- case IS_QUATERNION:
- Convert(mQuaternion, mEulerAngles);
- break;
- case IS_AXIS_ANGLE:
- Convert(mAxisAngle, mEulerAngles);
- break;
- case IS_EULER_ANGLES:
- break;
- }
- return mEulerAngles;
- }
- private:
- enum RepresentationType
- {
- IS_MATRIX,
- IS_QUATERNION,
- IS_AXIS_ANGLE,
- IS_EULER_ANGLES
- };
- RepresentationType mType;
- mutable Matrix<N, N, Real> mMatrix;
- mutable Quaternion<Real> mQuaternion;
- mutable AxisAngle<N, Real> mAxisAngle;
- mutable EulerAngles<Real> mEulerAngles;
- // Convert a rotation matrix to a quaternion.
- //
- // x^2 = (+r00 - r11 - r22 + 1)/4
- // y^2 = (-r00 + r11 - r22 + 1)/4
- // z^2 = (-r00 - r11 + r22 + 1)/4
- // w^2 = (+r00 + r11 + r22 + 1)/4
- // x^2 + y^2 = (1 - r22)/2
- // z^2 + w^2 = (1 + r22)/2
- // y^2 - x^2 = (r11 - r00)/2
- // w^2 - z^2 = (r11 + r00)/2
- // x*y = (r01 + r10)/4
- // x*z = (r02 + r20)/4
- // y*z = (r12 + r21)/4
- // [GTE_USE_MAT_VEC]
- // x*w = (r21 - r12)/4
- // y*w = (r02 - r20)/4
- // z*w = (r10 - r01)/4
- // [GTE_USE_VEC_MAT]
- // x*w = (r12 - r21)/4
- // y*w = (r20 - r02)/4
- // z*w = (r01 - r10)/4
- //
- // If Q is the 4x1 column vector (x,y,z,w), the previous equations
- // give us
- // +- -+
- // | x*x x*y x*z x*w |
- // Q*Q^T = | y*x y*y y*z y*w |
- // | z*x z*y z*z z*w |
- // | w*x w*y w*z w*w |
- // +- -+
- // The code extracts the row of maximum length, normalizing it to
- // obtain the result q.
- static void Convert(Matrix<N, N, Real> const& r, Quaternion<Real>& q)
- {
- static_assert(N == 3 || N == 4, "Dimension must be 3 or 4.");
- Real r22 = r(2, 2);
- if (r22 <= (Real)0) // x^2 + y^2 >= z^2 + w^2
- {
- Real dif10 = r(1, 1) - r(0, 0);
- Real omr22 = (Real)1 - r22;
- if (dif10 <= (Real)0) // x^2 >= y^2
- {
- Real fourXSqr = omr22 - dif10;
- Real inv4x = ((Real)0.5) / std::sqrt(fourXSqr);
- q[0] = fourXSqr * inv4x;
- q[1] = (r(0, 1) + r(1, 0)) * inv4x;
- q[2] = (r(0, 2) + r(2, 0)) * inv4x;
- #if defined(GTE_USE_MAT_VEC)
- q[3] = (r(2, 1) - r(1, 2)) * inv4x;
- #else
- q[3] = (r(1, 2) - r(2, 1)) * inv4x;
- #endif
- }
- else // y^2 >= x^2
- {
- Real fourYSqr = omr22 + dif10;
- Real inv4y = ((Real)0.5) / std::sqrt(fourYSqr);
- q[0] = (r(0, 1) + r(1, 0)) * inv4y;
- q[1] = fourYSqr * inv4y;
- q[2] = (r(1, 2) + r(2, 1)) * inv4y;
- #if defined(GTE_USE_MAT_VEC)
- q[3] = (r(0, 2) - r(2, 0)) * inv4y;
- #else
- q[3] = (r(2, 0) - r(0, 2)) * inv4y;
- #endif
- }
- }
- else // z^2 + w^2 >= x^2 + y^2
- {
- Real sum10 = r(1, 1) + r(0, 0);
- Real opr22 = (Real)1 + r22;
- if (sum10 <= (Real)0) // z^2 >= w^2
- {
- Real fourZSqr = opr22 - sum10;
- Real inv4z = ((Real)0.5) / std::sqrt(fourZSqr);
- q[0] = (r(0, 2) + r(2, 0)) * inv4z;
- q[1] = (r(1, 2) + r(2, 1)) * inv4z;
- q[2] = fourZSqr * inv4z;
- #if defined(GTE_USE_MAT_VEC)
- q[3] = (r(1, 0) - r(0, 1)) * inv4z;
- #else
- q[3] = (r(0, 1) - r(1, 0)) * inv4z;
- #endif
- }
- else // w^2 >= z^2
- {
- Real fourWSqr = opr22 + sum10;
- Real inv4w = ((Real)0.5) / std::sqrt(fourWSqr);
- #if defined(GTE_USE_MAT_VEC)
- q[0] = (r(2, 1) - r(1, 2)) * inv4w;
- q[1] = (r(0, 2) - r(2, 0)) * inv4w;
- q[2] = (r(1, 0) - r(0, 1)) * inv4w;
- #else
- q[0] = (r(1, 2) - r(2, 1)) * inv4w;
- q[1] = (r(2, 0) - r(0, 2)) * inv4w;
- q[2] = (r(0, 1) - r(1, 0)) * inv4w;
- #endif
- q[3] = fourWSqr * inv4w;
- }
- }
- }
- // Convert a quaterion q = x*i + y*j + z*k + w to a rotation matrix.
- // [GTE_USE_MAT_VEC]
- // +- -+ +- -+
- // R = | r00 r01 r02 | = | 1-2y^2-2z^2 2(xy-zw) 2(xz+yw) |
- // | r10 r11 r12 | | 2(xy+zw) 1-2x^2-2z^2 2(yz-xw) |
- // | r20 r21 r22 | | 2(xz-yw) 2(yz+xw) 1-2x^2-2y^2 |
- // +- -+ +- -+
- // [GTE_USE_VEC_MAT]
- // +- -+ +- -+
- // R = | r00 r01 r02 | = | 1-2y^2-2z^2 2(xy+zw) 2(xz-yw) |
- // | r10 r11 r12 | | 2(xy-zw) 1-2x^2-2z^2 2(yz+xw) |
- // | r20 r21 r22 | | 2(xz+yw) 2(yz-xw) 1-2x^2-2y^2 |
- // +- -+ +- -+
- static void Convert(Quaternion<Real> const& q, Matrix<N, N, Real>& r)
- {
- static_assert(N == 3 || N == 4, "Dimension must be 3 or 4.");
- r.MakeIdentity();
- Real twoX = ((Real)2) * q[0];
- Real twoY = ((Real)2) * q[1];
- Real twoZ = ((Real)2) * q[2];
- Real twoXX = twoX * q[0];
- Real twoXY = twoX * q[1];
- Real twoXZ = twoX * q[2];
- Real twoXW = twoX * q[3];
- Real twoYY = twoY * q[1];
- Real twoYZ = twoY * q[2];
- Real twoYW = twoY * q[3];
- Real twoZZ = twoZ * q[2];
- Real twoZW = twoZ * q[3];
- #if defined(GTE_USE_MAT_VEC)
- r(0, 0) = (Real)1 - twoYY - twoZZ;
- r(0, 1) = twoXY - twoZW;
- r(0, 2) = twoXZ + twoYW;
- r(1, 0) = twoXY + twoZW;
- r(1, 1) = (Real)1 - twoXX - twoZZ;
- r(1, 2) = twoYZ - twoXW;
- r(2, 0) = twoXZ - twoYW;
- r(2, 1) = twoYZ + twoXW;
- r(2, 2) = (Real)1 - twoXX - twoYY;
- #else
- r(0, 0) = (Real)1 - twoYY - twoZZ;
- r(1, 0) = twoXY - twoZW;
- r(2, 0) = twoXZ + twoYW;
- r(0, 1) = twoXY + twoZW;
- r(1, 1) = (Real)1 - twoXX - twoZZ;
- r(2, 1) = twoYZ - twoXW;
- r(0, 2) = twoXZ - twoYW;
- r(1, 2) = twoYZ + twoXW;
- r(2, 2) = (Real)1 - twoXX - twoYY;
- #endif
- }
- // Convert a rotation matrix to an axis-angle pair. Let (x0,x1,x2) be
- // the axis let t be an angle of rotation. The rotation matrix is
- // [GTE_USE_MAT_VEC]
- // R = I + sin(t)*S + (1-cos(t))*S^2
- // or
- // [GTE_USE_VEC_MAT]
- // R = I - sin(t)*S + (1-cos(t))*S^2
- // where I is the identity and S = {{0,-x2,x1},{x2,0,-x0},{-x1,x0,0}}
- // where the inner-brace triples are the rows of the matrix. If
- // t > 0, R represents a counterclockwise rotation; see the comments
- // for the constructor Matrix3x3(axis,angle). It may be shown that
- // cos(t) = (trace(R)-1)/2 and R - Transpose(R) = 2*sin(t)*S. As long
- // as sin(t) is not zero, we may solve for S in the second equation,
- // which produces the axis direction U = (S21,S02,S10). When t = 0,
- // the rotation is the identity, in which case any axis direction is
- // valid; we choose (1,0,0). When t = pi, it must be that
- // R - Transpose(R) = 0, which prevents us from extracting the axis.
- // Instead, note that (R+I)/2 = I+S^2 = U*U^T, where U is a
- // unit-length axis direction.
- static void Convert(Matrix<N, N, Real> const& r, AxisAngle<N, Real>& a)
- {
- static_assert(N == 3 || N == 4, "Dimension must be 3 or 4.");
- Real trace = r(0, 0) + r(1, 1) + r(2, 2);
- Real half = (Real)0.5;
- Real cs = half * (trace - (Real)1);
- cs = std::max(std::min(cs, (Real)1), (Real)-1);
- a.angle = std::acos(cs); // The angle is in [0,pi].
- a.axis.MakeZero();
- if (a.angle > (Real)0)
- {
- if (a.angle < (Real)GTE_C_PI)
- {
- // The angle is in (0,pi).
- #if defined(GTE_USE_MAT_VEC)
- a.axis[0] = r(2, 1) - r(1, 2);
- a.axis[1] = r(0, 2) - r(2, 0);
- a.axis[2] = r(1, 0) - r(0, 1);
- Normalize(a.axis);
- #else
- a.axis[0] = r(1, 2) - r(2, 1);
- a.axis[1] = r(2, 0) - r(0, 2);
- a.axis[2] = r(0, 1) - r(1, 0);
- Normalize(a.axis);
- #endif
- }
- else
- {
- // The angle is pi, in which case R is symmetric and
- // R+I = 2*(I+S^2) = 2*U*U^T, where U = (u0,u1,u2) is the
- // unit-length direction of the rotation axis. Determine
- // the largest diagonal entry of R+I and normalize the
- // corresponding row to produce U. It does not matter the
- // sign on u[d] for chosen diagonal d, because
- // R(U,pi) = R(-U,pi).
- Real one = (Real)1;
- if (r(0, 0) >= r(1, 1))
- {
- if (r(0, 0) >= r(2, 2))
- {
- // r00 is maximum diagonal term
- a.axis[0] = r(0, 0) + one;
- a.axis[1] = half * (r(0, 1) + r(1, 0));
- a.axis[2] = half * (r(0, 2) + r(2, 0));
- }
- else
- {
- // r22 is maximum diagonal term
- a.axis[0] = half * (r(2, 0) + r(0, 2));
- a.axis[1] = half * (r(2, 1) + r(1, 2));
- a.axis[2] = r(2, 2) + one;
- }
- }
- else
- {
- if (r(1, 1) >= r(2, 2))
- {
- // r11 is maximum diagonal term
- a.axis[0] = half * (r(1, 0) + r(0, 1));
- a.axis[1] = r(1, 1) + one;
- a.axis[2] = half * (r(1, 2) + r(2, 1));
- }
- else
- {
- // r22 is maximum diagonal term
- a.axis[0] = half * (r(2, 0) + r(0, 2));
- a.axis[1] = half * (r(2, 1) + r(1, 2));
- a.axis[2] = r(2, 2) + one;
- }
- }
- Normalize(a.axis);
- }
- }
- else
- {
- // The angle is 0 and the matrix is the identity. Any axis
- // will work, so choose the Unit(0) axis.
- a.axis[0] = (Real)1;
- }
- }
- // Convert an axis-angle pair to a rotation matrix. Assuming
- // (x0,x1,x2) is for a right-handed world (x0 to right, x1 up, x2 out
- // of plane of page), a positive angle corresponds to a
- // counterclockwise rotation from the perspective of an observer
- // looking at the origin of the plane of rotation and having view
- // direction the negative of the axis direction. The coordinate-axis
- // rotations are the following, where unit(0) = (1,0,0),
- // unit(1) = (0,1,0), unit(2) = (0,0,1),
- // [GTE_USE_MAT_VEC]
- // R(unit(0),t) = {{ 1, 0, 0}, { 0, c,-s}, { 0, s, c}}
- // R(unit(1),t) = {{ c, 0, s}, { 0, 1, 0}, {-s, 0, c}}
- // R(unit(2),t) = {{ c,-s, 0}, { s, c, 0}, { 0, 0, 1}}
- // or
- // [GTE_USE_VEC_MAT]
- // R(unit(0),t) = {{ 1, 0, 0}, { 0, c, s}, { 0,-s, c}}
- // R(unit(1),t) = {{ c, 0,-s}, { 0, 1, 0}, { s, 0, c}}
- // R(unit(2),t) = {{ c, s, 0}, {-s, c, 0}, { 0, 0, 1}}
- // where c = cos(t), s = sin(t), and the inner-brace triples are rows
- // of the matrix. The general matrix is
- // [GTE_USE_MAT_VEC]
- // +- -+
- // R = | (1-c)*x0^2 + c (1-c)*x0*x1 - s*x2 (1-c)*x0*x2 + s*x1 |
- // | (1-c)*x0*x1 + s*x2 (1-c)*x1^2 + c (1-c)*x1*x2 - s*x0 |
- // | (1-c)*x0*x2 - s*x1 (1-c)*x1*x2 + s*x0 (1-c)*x2^2 + c |
- // +- -+
- // [GTE_USE_VEC_MAT]
- // +- -+
- // R = | (1-c)*x0^2 + c (1-c)*x0*x1 + s*x2 (1-c)*x0*x2 - s*x1 |
- // | (1-c)*x0*x1 - s*x2 (1-c)*x1^2 + c (1-c)*x1*x2 + s*x0 |
- // | (1-c)*x0*x2 + s*x1 (1-c)*x1*x2 - s*x0 (1-c)*x2^2 + c |
- // +- -+
- static void Convert(AxisAngle<N, Real> const& a, Matrix<N, N, Real>& r)
- {
- static_assert(N == 3 || N == 4, "Dimension must be 3 or 4.");
- r.MakeIdentity();
- Real cs = std::cos(a.angle);
- Real sn = std::sin(a.angle);
- Real oneMinusCos = ((Real)1) - cs;
- Real x0sqr = a.axis[0] * a.axis[0];
- Real x1sqr = a.axis[1] * a.axis[1];
- Real x2sqr = a.axis[2] * a.axis[2];
- Real x0x1m = a.axis[0] * a.axis[1] * oneMinusCos;
- Real x0x2m = a.axis[0] * a.axis[2] * oneMinusCos;
- Real x1x2m = a.axis[1] * a.axis[2] * oneMinusCos;
- Real x0Sin = a.axis[0] * sn;
- Real x1Sin = a.axis[1] * sn;
- Real x2Sin = a.axis[2] * sn;
- #if defined(GTE_USE_MAT_VEC)
- r(0, 0) = x0sqr * oneMinusCos + cs;
- r(0, 1) = x0x1m - x2Sin;
- r(0, 2) = x0x2m + x1Sin;
- r(1, 0) = x0x1m + x2Sin;
- r(1, 1) = x1sqr * oneMinusCos + cs;
- r(1, 2) = x1x2m - x0Sin;
- r(2, 0) = x0x2m - x1Sin;
- r(2, 1) = x1x2m + x0Sin;
- r(2, 2) = x2sqr * oneMinusCos + cs;
- #else
- r(0, 0) = x0sqr * oneMinusCos + cs;
- r(1, 0) = x0x1m - x2Sin;
- r(2, 0) = x0x2m + x1Sin;
- r(0, 1) = x0x1m + x2Sin;
- r(1, 1) = x1sqr * oneMinusCos + cs;
- r(2, 1) = x1x2m - x0Sin;
- r(0, 2) = x0x2m - x1Sin;
- r(1, 2) = x1x2m + x0Sin;
- r(2, 2) = x2sqr * oneMinusCos + cs;
- #endif
- }
- // Convert a rotation matrix to Euler angles. Factorization into
- // Euler angles is not necessarily unique. If the result is
- // ER_NOT_UNIQUE_SUM, then the multiple solutions occur because
- // angleN2+angleN0 is constant. If the result is ER_NOT_UNIQUE_DIF,
- // then the multiple solutions occur because angleN2-angleN0 is
- // constant. In either type of nonuniqueness, the function returns
- // angleN0=0.
- static void Convert(Matrix<N, N, Real> const& r, EulerAngles<Real>& e)
- {
- static_assert(N == 3 || N == 4, "Dimension must be 3 or 4.");
- if (0 <= e.axis[0] && e.axis[0] < 3
- && 0 <= e.axis[1] && e.axis[1] < 3
- && 0 <= e.axis[2] && e.axis[2] < 3
- && e.axis[1] != e.axis[0]
- && e.axis[1] != e.axis[2])
- {
- if (e.axis[0] != e.axis[2])
- {
- #if defined(GTE_USE_MAT_VEC)
- // Map (0,1,2), (1,2,0), and (2,0,1) to +1.
- // Map (0,2,1), (2,1,0), and (1,0,2) to -1.
- int parity = (((e.axis[2] | (e.axis[1] << 2)) >> e.axis[0]) & 1);
- Real const sgn = (parity & 1 ? (Real)-1 : (Real)+1);
- if (r(e.axis[2], e.axis[0]) < (Real)1)
- {
- if (r(e.axis[2], e.axis[0]) > (Real)-1)
- {
- e.angle[2] = std::atan2(sgn * r(e.axis[1], e.axis[0]),
- r(e.axis[0], e.axis[0]));
- e.angle[1] = std::asin(-sgn * r(e.axis[2], e.axis[0]));
- e.angle[0] = std::atan2(sgn * r(e.axis[2], e.axis[1]),
- r(e.axis[2], e.axis[2]));
- e.result = ER_UNIQUE;
- }
- else
- {
- e.angle[2] = (Real)0;
- e.angle[1] = sgn * (Real)GTE_C_HALF_PI;
- e.angle[0] = std::atan2(-sgn * r(e.axis[1], e.axis[2]),
- r(e.axis[1], e.axis[1]));
- e.result = ER_NOT_UNIQUE_DIF;
- }
- }
- else
- {
- e.angle[2] = (Real)0;
- e.angle[1] = -sgn * (Real)GTE_C_HALF_PI;
- e.angle[0] = std::atan2(-sgn * r(e.axis[1], e.axis[2]),
- r(e.axis[1], e.axis[1]));
- e.result = ER_NOT_UNIQUE_SUM;
- }
- #else
- // Map (0,1,2), (1,2,0), and (2,0,1) to +1.
- // Map (0,2,1), (2,1,0), and (1,0,2) to -1.
- int parity = (((e.axis[0] | (e.axis[1] << 2)) >> e.axis[2]) & 1);
- Real const sgn = (parity & 1 ? (Real)+1 : (Real)-1);
- if (r(e.axis[0], e.axis[2]) < (Real)1)
- {
- if (r(e.axis[0], e.axis[2]) > (Real)-1)
- {
- e.angle[0] = std::atan2(sgn * r(e.axis[1], e.axis[2]),
- r(e.axis[2], e.axis[2]));
- e.angle[1] = std::asin(-sgn * r(e.axis[0], e.axis[2]));
- e.angle[2] = std::atan2(sgn * r(e.axis[0], e.axis[1]),
- r(e.axis[0], e.axis[0]));
- e.result = ER_UNIQUE;
- }
- else
- {
- e.angle[0] = (Real)0;
- e.angle[1] = sgn * (Real)GTE_C_HALF_PI;
- e.angle[2] = std::atan2(-sgn * r(e.axis[1], e.axis[0]),
- r(e.axis[1], e.axis[1]));
- e.result = ER_NOT_UNIQUE_DIF;
- }
- }
- else
- {
- e.angle[0] = (Real)0;
- e.angle[1] = -sgn * (Real)GTE_C_HALF_PI;
- e.angle[2] = std::atan2(-sgn * r(e.axis[1], e.axis[0]),
- r(e.axis[1], e.axis[1]));
- e.result = ER_NOT_UNIQUE_SUM;
- }
- #endif
- }
- else
- {
- #if defined(GTE_USE_MAT_VEC)
- // Map (0,2,0), (1,0,1), and (2,1,2) to +1.
- // Map (0,1,0), (1,2,1), and (2,0,2) to -1.
- int b0 = 3 - e.axis[1] - e.axis[2];
- int parity = (((b0 | (e.axis[1] << 2)) >> e.axis[2]) & 1);
- Real const sgn = (parity & 1 ? (Real)+1 : (Real)-1);
- if (r(e.axis[2], e.axis[2]) < (Real)1)
- {
- if (r(e.axis[2], e.axis[2]) > (Real)-1)
- {
- e.angle[2] = std::atan2(r(e.axis[1], e.axis[2]),
- sgn * r(b0, e.axis[2]));
- e.angle[1] = std::acos(r(e.axis[2], e.axis[2]));
- e.angle[0] = std::atan2(r(e.axis[2], e.axis[1]),
- -sgn * r(e.axis[2], b0));
- e.result = ER_UNIQUE;
- }
- else
- {
- e.angle[2] = (Real)0;
- e.angle[1] = (Real)GTE_C_PI;
- e.angle[0] = std::atan2(sgn * r(e.axis[1], b0),
- r(e.axis[1], e.axis[1]));
- e.result = ER_NOT_UNIQUE_DIF;
- }
- }
- else
- {
- e.angle[2] = (Real)0;
- e.angle[1] = (Real)0;
- e.angle[0] = std::atan2(sgn * r(e.axis[1], b0),
- r(e.axis[1], e.axis[1]));
- e.result = ER_NOT_UNIQUE_SUM;
- }
- #else
- // Map (0,2,0), (1,0,1), and (2,1,2) to -1.
- // Map (0,1,0), (1,2,1), and (2,0,2) to +1.
- int b2 = 3 - e.axis[0] - e.axis[1];
- int parity = (((b2 | (e.axis[1] << 2)) >> e.axis[0]) & 1);
- Real const sgn = (parity & 1 ? (Real)-1 : (Real)+1);
- if (r(e.axis[0], e.axis[0]) < (Real)1)
- {
- if (r(e.axis[0], e.axis[0]) > (Real)-1)
- {
- e.angle[0] = std::atan2(r(e.axis[1], e.axis[0]),
- sgn * r(b2, e.axis[0]));
- e.angle[1] = std::acos(r(e.axis[0], e.axis[0]));
- e.angle[2] = std::atan2(r(e.axis[0], e.axis[1]),
- -sgn * r(e.axis[0], b2));
- e.result = ER_UNIQUE;
- }
- else
- {
- e.angle[0] = (Real)0;
- e.angle[1] = (Real)GTE_C_PI;
- e.angle[2] = std::atan2(sgn * r(e.axis[1], b2),
- r(e.axis[1], e.axis[1]));
- e.result = ER_NOT_UNIQUE_DIF;
- }
- }
- else
- {
- e.angle[0] = (Real)0;
- e.angle[1] = (Real)0;
- e.angle[2] = std::atan2(sgn * r(e.axis[1], b2),
- r(e.axis[1], e.axis[1]));
- e.result = ER_NOT_UNIQUE_SUM;
- }
- #endif
- }
- }
- else
- {
- // Invalid angles.
- e.angle[0] = (Real)0;
- e.angle[1] = (Real)0;
- e.angle[2] = (Real)0;
- e.result = ER_INVALID;
- }
- }
- // Convert Euler angles to a rotation matrix. The three integer
- // inputs are in {0,1,2} and correspond to world directions
- // unit(0) = (1,0,0), unit(1) = (0,1,0), or unit(2) = (0,0,1). The
- // triples (N0,N1,N2) must be in the following set,
- // {(0,1,2),(0,2,1),(1,0,2),(1,2,0),(2,0,1),(2,1,0),
- // (0,1,0),(0,2,0),(1,0,1),(1,2,1),(2,0,2),(2,1,2)}
- // The rotation matrix is
- // [GTE_USE_MAT_VEC]
- // R(unit(N2),angleN2)*R(unit(N1),angleN1)*R(unit(N0),angleN0)
- // or
- // [GTE_USE_VEC_MAT]
- // R(unit(N0),angleN0)*R(unit(N1),angleN1)*R(unit(N2),angleN2)
- // The conventions of constructor Matrix3(axis,angle) apply here as
- // well.
- //
- // NOTE: The reversal of order is chosen so that a rotation matrix
- // built with one multiplication convention is the transpose of the
- // rotation matrix built with the other multiplication convention.
- // Thus,
- // [GTE_USE_MAT_VEC]
- // Matrix3x3<Real> R_mvconvention(N0,N1,N2,angleN0,angleN1,angleN2);
- // Vector3<Real> V(...);
- // Vector3<Real> U = R_mvconvention*V; // (u0,u1,u2) = R2*R1*R0*V
- // [GTE_USE_VEC_MAT]
- // Matrix3x3<Real> R_vmconvention(N0,N1,N2,angleN0,angleN1,angleN2);
- // Vector3<Real> V(...);
- // Vector3<Real> U = R_mvconvention*V; // (u0,u1,u2) = V*R0*R1*R2
- // In either convention, you get the same 3-tuple U.
- static void Convert(EulerAngles<Real> const& e, Matrix<N, N, Real>& r)
- {
- static_assert(N == 3 || N == 4, "Dimension must be 3 or 4.");
- if (0 <= e.axis[0] && e.axis[0] < 3
- && 0 <= e.axis[1] && e.axis[1] < 3
- && 0 <= e.axis[2] && e.axis[2] < 3
- && e.axis[1] != e.axis[0]
- && e.axis[1] != e.axis[2])
- {
- Matrix<N, N, Real> r0, r1, r2;
- Convert(AxisAngle<N, Real>(Vector<N, Real>::Unit(e.axis[0]),
- e.angle[0]), r0);
- Convert(AxisAngle<N, Real>(Vector<N, Real>::Unit(e.axis[1]),
- e.angle[1]), r1);
- Convert(AxisAngle<N, Real>(Vector<N, Real>::Unit(e.axis[2]),
- e.angle[2]), r2);
- #if defined(GTE_USE_MAT_VEC)
- r = r2 * r1 * r0;
- #else
- r = r0 * r1 * r2;
- #endif
- }
- else
- {
- // Invalid angles.
- r.MakeIdentity();
- }
- }
- // Convert a quaternion to an axis-angle pair, where
- // q = sin(angle/2)*(axis[0]*i+axis[1]*j+axis[2]*k)+cos(angle/2)
- static void Convert(Quaternion<Real> const& q, AxisAngle<N, Real>& a)
- {
- static_assert(N == 3 || N == 4, "Dimension must be 3 or 4.");
- a.axis.MakeZero();
- Real axisSqrLen = q[0] * q[0] + q[1] * q[1] + q[2] * q[2];
- if (axisSqrLen > (Real)0)
- {
- #if defined(GTE_USE_MAT_VEC)
- Real adjust = ((Real)1) / std::sqrt(axisSqrLen);
- #else
- Real adjust = ((Real)-1) / std::sqrt(axisSqrLen);
- #endif
- a.axis[0] = q[0] * adjust;
- a.axis[1] = q[1] * adjust;
- a.axis[2] = q[2] * adjust;
- Real cs = std::max(std::min(q[3], (Real)1), (Real)-1);
- a.angle = (Real)2 * std::acos(cs);
- }
- else
- {
- // The angle is 0 (modulo 2*pi). Any axis will work, so choose
- // the Unit(0) axis.
- a.axis[0] = (Real)1;
- a.angle = (Real)0;
- }
- }
- // Convert an axis-angle pair to a quaternion, where
- // q = sin(angle/2)*(axis[0]*i+axis[1]*j+axis[2]*k)+cos(angle/2)
- static void Convert(AxisAngle<N, Real> const& a, Quaternion<Real>& q)
- {
- static_assert(N == 3 || N == 4, "Dimension must be 3 or 4.");
- #if defined(GTE_USE_MAT_VEC)
- Real halfAngle = (Real)0.5 * a.angle;
- #else
- Real halfAngle = (Real)-0.5 * a.angle;
- #endif
- Real sn = std::sin(halfAngle);
- q[0] = sn * a.axis[0];
- q[1] = sn * a.axis[1];
- q[2] = sn * a.axis[2];
- q[3] = std::cos(halfAngle);
- }
- // Convert a quaternion to Euler angles. The quaternion is converted
- // to a matrix which is then converted to Euler angles.
- static void Convert(Quaternion<Real> const& q, EulerAngles<Real>& e)
- {
- static_assert(N == 3 || N == 4, "Dimension must be 3 or 4.");
- Matrix<N, N, Real> r;
- Convert(q, r);
- Convert(r, e);
- }
- // Convert Euler angles to a quaternion. The Euler angles are
- // converted to a matrix which is then converted to a quaternion.
- static void Convert(EulerAngles<Real> const& e, Quaternion<Real>& q)
- {
- static_assert(N == 3 || N == 4, "Dimension must be 3 or 4.");
- Matrix<N, N, Real> r;
- Convert(e, r);
- Convert(r, q);
- }
- // Convert an axis-angle pair to Euler angles. The axis-angle pair
- // is converted to a quaternion which is then converted to Euler
- // angles.
- static void Convert(AxisAngle<N, Real> const& a, EulerAngles<Real>& e)
- {
- static_assert(N == 3 || N == 4, "Dimension must be 3 or 4.");
- Quaternion<Real> q;
- Convert(a, q);
- Convert(q, e);
- }
- // Convert Euler angles to an axis-angle pair. The Euler angles are
- // converted to a quaternion which is then converted to an axis-angle
- // pair.
- static void Convert(EulerAngles<Real> const& e, AxisAngle<N, Real>& a)
- {
- static_assert(N == 3 || N == 4, "Dimension must be 3 or 4.");
- Quaternion<Real> q;
- Convert(e, q);
- Convert(q, a);
- }
- };
- }
|