/*========================================================================= Program: Visualization Toolkit Module: vtkQuaternion.txx Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. See Copyright.txt or http://www.kitware.com/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ #include "vtkQuaternion.h" #ifndef vtkQuaternion_txx #define vtkQuaternion_txx #include "vtkMath.h" #include //---------------------------------------------------------------------------- template vtkQuaternion::vtkQuaternion() { this->ToIdentity(); } //---------------------------------------------------------------------------- template vtkQuaternion::vtkQuaternion(const T& w, const T& x, const T& y, const T& z) { this->Data[0] = w; this->Data[1] = x; this->Data[2] = y; this->Data[3] = z; } //---------------------------------------------------------------------------- template T vtkQuaternion::SquaredNorm() const { T result = 0.0; for (int i = 0; i < 4; ++i) { result += this->Data[i] * this->Data[i]; } return result; } //---------------------------------------------------------------------------- template T vtkQuaternion::Norm() const { return sqrt(this->SquaredNorm()); } //---------------------------------------------------------------------------- template void vtkQuaternion::ToIdentity() { this->Set(1.0, 0.0, 0.0, 0.0); } //---------------------------------------------------------------------------- template vtkQuaternion vtkQuaternion::Identity() { vtkQuaternion identity(1.0, 0.0, 0.0, 0.0); return identity; } //---------------------------------------------------------------------------- template T vtkQuaternion::Normalize() { T norm = this->Norm(); if (norm != 0.0) { for (int i = 0; i < 4; ++i) { this->Data[i] /= norm; } } return norm; } //---------------------------------------------------------------------------- template vtkQuaternion vtkQuaternion::Normalized() const { vtkQuaternion temp(*this); temp.Normalize(); return temp; } //---------------------------------------------------------------------------- template void vtkQuaternion::Conjugate() { for (int i = 1; i < 4; ++i) { this->Data[i] *= -1.0; } } //---------------------------------------------------------------------------- template vtkQuaternion vtkQuaternion::Conjugated() const { vtkQuaternion ret(*this); ret.Conjugate(); return ret; } //---------------------------------------------------------------------------- template void vtkQuaternion::Invert() { T squareNorm = this->SquaredNorm(); if (squareNorm != 0.0) { this->Conjugate(); for (int i = 0; i < 4; ++i) { this->Data[i] /= squareNorm; } } } //---------------------------------------------------------------------------- template vtkQuaternion vtkQuaternion::Inverse() const { vtkQuaternion ret(*this); ret.Invert(); return ret; } //---------------------------------------------------------------------------- template template vtkQuaternion vtkQuaternion::Cast() const { vtkQuaternion result; for (int i = 0; i < 4; ++i) { result[i] = static_cast(this->Data[i]); } return result; } //---------------------------------------------------------------------------- template void vtkQuaternion::Set(const T& w, const T& x, const T& y, const T& z) { this->Data[0] = w; this->Data[1] = x; this->Data[2] = y; this->Data[3] = z; } //---------------------------------------------------------------------------- template void vtkQuaternion::Set(T quat[4]) { for (int i = 0; i < 4; ++i) { this->Data[i] = quat[i]; } } //---------------------------------------------------------------------------- template void vtkQuaternion::Get(T quat[4]) const { for (int i = 0; i < 4; ++i) { quat[i] = this->Data[i]; } } //---------------------------------------------------------------------------- template void vtkQuaternion::SetW(const T& w) { this->Data[0] = w; } //---------------------------------------------------------------------------- template const T& vtkQuaternion::GetW() const { return this->Data[0]; } //---------------------------------------------------------------------------- template void vtkQuaternion::SetX(const T& x) { this->Data[1] = x; } //---------------------------------------------------------------------------- template const T& vtkQuaternion::GetX() const { return this->Data[1]; } //---------------------------------------------------------------------------- template void vtkQuaternion::SetY(const T& y) { this->Data[2] = y; } //---------------------------------------------------------------------------- template const T& vtkQuaternion::GetY() const { return this->Data[2]; } //---------------------------------------------------------------------------- template void vtkQuaternion::SetZ(const T& z) { this->Data[3] = z; } //---------------------------------------------------------------------------- template const T& vtkQuaternion::GetZ() const { return this->Data[3]; } //---------------------------------------------------------------------------- template T vtkQuaternion::GetRotationAngleAndAxis(T axis[3]) const { T w = this->GetW(); T x = this->GetX(); T y = this->GetY(); T z = this->GetZ(); T f = sqrt(x * x + y * y + z * z); if (f != 0.0) { axis[0] = x / f; axis[1] = y / f; axis[2] = z / f; } else { w = 1.0; axis[0] = 0.0; axis[1] = 0.0; axis[2] = 0.0; } // atan2() provides a more accurate angle result than acos() return 2.0 * atan2(f, w); } //---------------------------------------------------------------------------- template void vtkQuaternion::SetRotationAngleAndAxis(T angle, T axis[3]) { this->SetRotationAngleAndAxis(angle, axis[0], axis[1], axis[2]); } //---------------------------------------------------------------------------- template void vtkQuaternion::SetRotationAngleAndAxis(const T& angle, const T& x, const T& y, const T& z) { T axisNorm = x * x + y * y + z * z; if (axisNorm != 0.0) { T f = sin(0.5 * angle); this->SetW(cos(0.5 * angle)); this->SetX((x / axisNorm) * f); this->SetY((y / axisNorm) * f); this->SetZ((z / axisNorm) * f); } else { // set the quaternion for "no rotation" this->Set(1.0, 0.0, 0.0, 0.0); } } //---------------------------------------------------------------------------- template vtkQuaternion vtkQuaternion::operator+(const vtkQuaternion& q) const { vtkQuaternion ret; for (int i = 0; i < 4; ++i) { ret[i] = this->Data[i] + q[i]; } return ret; } //---------------------------------------------------------------------------- template vtkQuaternion vtkQuaternion::operator-(const vtkQuaternion& q) const { vtkQuaternion ret; for (int i = 0; i < 4; ++i) { ret[i] = this->Data[i] - q[i]; } return ret; } //---------------------------------------------------------------------------- template vtkQuaternion vtkQuaternion::operator*(const vtkQuaternion& q) const { vtkQuaternion ret; T ww = this->Data[0] * q[0]; T wx = this->Data[0] * q[1]; T wy = this->Data[0] * q[2]; T wz = this->Data[0] * q[3]; T xw = this->Data[1] * q[0]; T xx = this->Data[1] * q[1]; T xy = this->Data[1] * q[2]; T xz = this->Data[1] * q[3]; T yw = this->Data[2] * q[0]; T yx = this->Data[2] * q[1]; T yy = this->Data[2] * q[2]; T yz = this->Data[2] * q[3]; T zw = this->Data[3] * q[0]; T zx = this->Data[3] * q[1]; T zy = this->Data[3] * q[2]; T zz = this->Data[3] * q[3]; ret[0] = ww - xx - yy - zz; ret[1] = wx + xw + yz - zy; ret[2] = wy - xz + yw + zx; ret[3] = wz + xy - yx + zw; return ret; } //---------------------------------------------------------------------------- template vtkQuaternion vtkQuaternion::operator*(const T& scalar) const { vtkQuaternion ret; for (int i = 0; i < 4; ++i) { ret[i] = this->Data[i] * scalar; } return ret; } //---------------------------------------------------------------------------- template void vtkQuaternion::operator*=(const T& scalar) const { for (int i = 0; i < 4; ++i) { this->Data[i] *= scalar; } } //---------------------------------------------------------------------------- template vtkQuaternion vtkQuaternion::operator/(const vtkQuaternion& q) const { vtkQuaternion inverseQuaternion = q.Inverse(); return (*this) * inverseQuaternion; } //---------------------------------------------------------------------------- template vtkQuaternion vtkQuaternion::operator/(const T& scalar) const { vtkQuaternion ret; for (int i = 0; i < 4; ++i) { ret[i] = this->Data[i] / scalar; } return ret; } //---------------------------------------------------------------------------- template void vtkQuaternion::operator/=(const T& scalar) { for (int i = 0; i < 4; ++i) { this->Data[i] /= scalar; } } //---------------------------------------------------------------------------- template void vtkQuaternion::ToMatrix3x3(T A[3][3]) const { T ww = this->Data[0] * this->Data[0]; T wx = this->Data[0] * this->Data[1]; T wy = this->Data[0] * this->Data[2]; T wz = this->Data[0] * this->Data[3]; T xx = this->Data[1] * this->Data[1]; T yy = this->Data[2] * this->Data[2]; T zz = this->Data[3] * this->Data[3]; T xy = this->Data[1] * this->Data[2]; T xz = this->Data[1] * this->Data[3]; T yz = this->Data[2] * this->Data[3]; T rr = xx + yy + zz; // normalization factor, just in case quaternion was not normalized T f; if (ww + rr == 0.0) // means the quaternion is (0, 0, 0, 0) { A[0][0] = 0.0; A[1][0] = 0.0; A[2][0] = 0.0; A[0][1] = 0.0; A[1][1] = 0.0; A[2][1] = 0.0; A[0][2] = 0.0; A[1][2] = 0.0; A[2][2] = 0.0; return; } f = 1.0 / (ww + rr); T s = (ww - rr) * f; f *= 2.0; A[0][0] = xx * f + s; A[1][0] = (xy + wz) * f; A[2][0] = (xz - wy) * f; A[0][1] = (xy - wz) * f; A[1][1] = yy * f + s; A[2][1] = (yz + wx) * f; A[0][2] = (xz + wy) * f; A[1][2] = (yz - wx) * f; A[2][2] = zz * f + s; } //---------------------------------------------------------------------------- // The solution is based on // Berthold K. P. Horn (1987), // "Closed-form solution of absolute orientation using unit quaternions," // Journal of the Optical Society of America A, 4:629-642 template void vtkQuaternion::FromMatrix3x3(const T A[3][3]) { T n[4][4]; // on-diagonal elements n[0][0] = A[0][0] + A[1][1] + A[2][2]; n[1][1] = A[0][0] - A[1][1] - A[2][2]; n[2][2] = -A[0][0] + A[1][1] - A[2][2]; n[3][3] = -A[0][0] - A[1][1] + A[2][2]; // off-diagonal elements n[0][1] = n[1][0] = A[2][1] - A[1][2]; n[0][2] = n[2][0] = A[0][2] - A[2][0]; n[0][3] = n[3][0] = A[1][0] - A[0][1]; n[1][2] = n[2][1] = A[1][0] + A[0][1]; n[1][3] = n[3][1] = A[0][2] + A[2][0]; n[2][3] = n[3][2] = A[2][1] + A[1][2]; T eigenvectors[4][4]; T eigenvalues[4]; // convert into format that JacobiN can use, // then use Jacobi to find eigenvalues and eigenvectors T* nTemp[4]; T* eigenvectorsTemp[4]; for (int i = 0; i < 4; ++i) { nTemp[i] = n[i]; eigenvectorsTemp[i] = eigenvectors[i]; } vtkMath::JacobiN(nTemp, 4, eigenvalues, eigenvectorsTemp); // the first eigenvector is the one we want for (int i = 0; i < 4; ++i) { this->Data[i] = eigenvectors[i][0]; } } //---------------------------------------------------------------------------- // This returns the constant angular velocity interpolation of two quaternions // on the unit quaternion sphere : // http://web.mit.edu/2.998/www/QuaternionReport1.pdf template vtkQuaternion vtkQuaternion::Slerp(T t, const vtkQuaternion& q1) const { // Canonical scalar product on quaternion T cosTheta = this->GetW() * q1.GetW() + this->GetX() * q1.GetX() + this->GetY() * q1.GetY() + this->GetZ() * q1.GetZ(); // To prevent the SLERP interpolation from taking the long path // we first check the relative orientation of the two quaternions // If the angle is superior to 90 degrees we take the opposite quaternion // which is closer and represents the same rotation vtkQuaternion qClosest = q1; if (cosTheta < 0) { cosTheta = -cosTheta; qClosest = qClosest * -1; } // To avoid division by zero, perform a linear interpolation (LERP), if our // quarternions are nearly in the same direction, otherwise resort // to spherical linear interpolation. In the limiting case (for small // angles), SLERP is equivalent to LERP. T t1, t2; if ((1.0 - fabs(cosTheta)) < 1e-6) { t1 = 1.0 - t; t2 = t; } else { // Angle (defined by the canonical scalar product for quaternions) // between the two quaternions const T theta = acos(cosTheta); t1 = sin((1.0 - t) * theta) / sin(theta); t2 = sin(t * theta) / sin(theta); } return (*this) * t1 + qClosest * t2; } //---------------------------------------------------------------------------- template vtkQuaternion vtkQuaternion::InnerPoint( const vtkQuaternion& q1, const vtkQuaternion& q2) const { vtkQuaternion qInv = q1.Inverse(); vtkQuaternion qL = qInv * q2; vtkQuaternion qR = qInv * (*this); vtkQuaternion qLLog = qL.UnitLog(); vtkQuaternion qRLog = qR.UnitLog(); vtkQuaternion qSum = qLLog + qRLog; T w = qSum.GetW(); qSum /= -4.0; qSum.SetW(w); vtkQuaternion qExp = qSum.UnitExp(); return q1 * qExp; } //---------------------------------------------------------------------------- template void vtkQuaternion::ToUnitLog() { T axis[3]; T angle = 0.5 * this->GetRotationAngleAndAxis(axis); this->Set(0.0, angle * axis[0], angle * axis[1], angle * axis[2]); } //---------------------------------------------------------------------------- template vtkQuaternion vtkQuaternion::UnitLog() const { vtkQuaternion unitLog(*this); unitLog.ToUnitLog(); return unitLog; } //---------------------------------------------------------------------------- template void vtkQuaternion::ToUnitExp() { T x = this->GetX(); T y = this->GetY(); T z = this->GetZ(); T angle = sqrt(x * x + y * y + z * z); T sinAngle = sin(angle); T cosAngle = cos(angle); if (angle != 0.0) { x /= angle; y /= angle; z /= angle; } this->Set(cosAngle, sinAngle * x, sinAngle * y, sinAngle * z); } //---------------------------------------------------------------------------- template vtkQuaternion vtkQuaternion::UnitExp() const { vtkQuaternion unitExp(*this); unitExp.ToUnitExp(); return unitExp; } //---------------------------------------------------------------------------- template void vtkQuaternion::NormalizeWithAngleInDegrees() { this->Normalize(); this->SetW(vtkMath::DegreesFromRadians(this->GetW())); } //---------------------------------------------------------------------------- template vtkQuaternion vtkQuaternion::NormalizedWithAngleInDegrees() const { vtkQuaternion unitVTK(*this); unitVTK.Normalize(); unitVTK.SetW(vtkMath::DegreesFromRadians(unitVTK.GetW())); return unitVTK; } #endif