// // SPDX-License-Identifier: BSD-3-Clause // Copyright Contributors to the OpenEXR Project. // // // A quaternion // // "Quaternions came from Hamilton ... and have been an unmixed // evil to those who have touched them in any way. Vector is a // useless survival ... and has never been of the slightest use // to any creature." // // - Lord Kelvin // #ifndef INCLUDED_IMATHQUAT_H #define INCLUDED_IMATHQUAT_H #include "ImathExport.h" #include "ImathNamespace.h" #include "ImathMatrix.h" #include IMATH_INTERNAL_NAMESPACE_HEADER_ENTER #if (defined _WIN32 || defined _WIN64) && defined _MSC_VER // Disable MS VC++ warnings about conversion from double to float # pragma warning(push) # pragma warning(disable : 4244) #endif /// /// The Quat class implements the quaternion numerical type -- you /// will probably want to use this class to represent orientations /// in R3 and to convert between various euler angle reps. You /// should probably use Imath::Euler<> for that. /// template class IMATH_EXPORT_TEMPLATE_TYPE Quat { public: /// @{ /// @name Direct access to elements /// The real part T r; /// The imaginary vector Vec3 v; /// @} /// Element access: q[0] is the real part, (q[1],q[2],q[3]) is the /// imaginary part. IMATH_HOSTDEVICE IMATH_CONSTEXPR14 T& operator[] (int index) IMATH_NOEXCEPT; // as 4D vector /// Element access: q[0] is the real part, (q[1],q[2],q[3]) is the /// imaginary part. IMATH_HOSTDEVICE constexpr T operator[] (int index) const IMATH_NOEXCEPT; /// @{ /// @name Constructors /// Default constructor is the identity quat IMATH_HOSTDEVICE constexpr Quat() IMATH_NOEXCEPT; /// Copy constructor IMATH_HOSTDEVICE constexpr Quat (const Quat& q) IMATH_NOEXCEPT; /// Construct from a quaternion of a another base type template IMATH_HOSTDEVICE IMATH_CONSTEXPR14 Quat (const Quat& q) IMATH_NOEXCEPT; /// Initialize with real part `s` and imaginary vector 1(i,j,k)` IMATH_HOSTDEVICE constexpr Quat (T s, T i, T j, T k) IMATH_NOEXCEPT; /// Initialize with real part `s` and imaginary vector `d` IMATH_HOSTDEVICE constexpr Quat (T s, Vec3 d) IMATH_NOEXCEPT; /// The identity quaternion IMATH_HOSTDEVICE constexpr static Quat identity() IMATH_NOEXCEPT; /// Assignment IMATH_HOSTDEVICE IMATH_CONSTEXPR14 const Quat& operator= (const Quat& q) IMATH_NOEXCEPT; /// Destructor ~Quat () IMATH_NOEXCEPT = default; /// @} /// @{ /// @name Basic Algebra /// /// Note that the operator return values are *NOT* normalized // /// Quaternion multiplication IMATH_HOSTDEVICE IMATH_CONSTEXPR14 const Quat& operator*= (const Quat& q) IMATH_NOEXCEPT; /// Scalar multiplication: multiply both real and imaginary parts /// by the given scalar. IMATH_HOSTDEVICE IMATH_CONSTEXPR14 const Quat& operator*= (T t) IMATH_NOEXCEPT; /// Quaterion division, using the inverse() IMATH_HOSTDEVICE IMATH_CONSTEXPR14 const Quat& operator/= (const Quat& q) IMATH_NOEXCEPT; /// Scalar division: multiply both real and imaginary parts /// by the given scalar. IMATH_HOSTDEVICE IMATH_CONSTEXPR14 const Quat& operator/= (T t) IMATH_NOEXCEPT; /// Quaternion addition IMATH_HOSTDEVICE IMATH_CONSTEXPR14 const Quat& operator+= (const Quat& q) IMATH_NOEXCEPT; /// Quaternion subtraction IMATH_HOSTDEVICE IMATH_CONSTEXPR14 const Quat& operator-= (const Quat& q) IMATH_NOEXCEPT; /// Equality template IMATH_HOSTDEVICE constexpr bool operator== (const Quat& q) const IMATH_NOEXCEPT; /// Inequality template IMATH_HOSTDEVICE constexpr bool operator!= (const Quat& q) const IMATH_NOEXCEPT; /// @} /// @{ /// @name Query /// Return the R4 length IMATH_HOSTDEVICE constexpr T length() const IMATH_NOEXCEPT; // in R4 /// Return the angle of the axis/angle representation IMATH_HOSTDEVICE constexpr T angle() const IMATH_NOEXCEPT; /// Return the axis of the axis/angle representation IMATH_HOSTDEVICE constexpr Vec3 axis() const IMATH_NOEXCEPT; /// Return a 3x3 rotation matrix IMATH_HOSTDEVICE constexpr Matrix33 toMatrix33() const IMATH_NOEXCEPT; /// Return a 4x4 rotation matrix IMATH_HOSTDEVICE constexpr Matrix44 toMatrix44() const IMATH_NOEXCEPT; /// Return the logarithm of the quaterion IMATH_HOSTDEVICE Quat log() const IMATH_NOEXCEPT; /// Return the exponent of the quaterion IMATH_HOSTDEVICE Quat exp() const IMATH_NOEXCEPT; /// @} /// @{ /// @name Utility Methods /// Invert in place: this = 1 / this. /// @return const reference to this. IMATH_HOSTDEVICE IMATH_CONSTEXPR14 Quat& invert() IMATH_NOEXCEPT; /// Return 1/this, leaving this unchanged. IMATH_HOSTDEVICE IMATH_CONSTEXPR14 Quat inverse() const IMATH_NOEXCEPT; /// Normalize in place /// @return const reference to this. IMATH_HOSTDEVICE IMATH_CONSTEXPR14 Quat& normalize() IMATH_NOEXCEPT; /// Return a normalized quaternion, leaving this unmodified. IMATH_HOSTDEVICE IMATH_CONSTEXPR14 Quat normalized() const IMATH_NOEXCEPT; /// Rotate the given point by the quaterion. IMATH_HOSTDEVICE IMATH_CONSTEXPR14 Vec3 rotateVector (const Vec3& original) const IMATH_NOEXCEPT; /// Return the Euclidean inner product. IMATH_HOSTDEVICE constexpr T euclideanInnerProduct (const Quat& q) const IMATH_NOEXCEPT; /// Set the quaterion to be a rotation around the given axis by the /// given angle. /// @return const reference to this. IMATH_HOSTDEVICE IMATH_CONSTEXPR14 Quat& setAxisAngle (const Vec3& axis, T radians) IMATH_NOEXCEPT; /// Set the quaternion to be a rotation that transforms the /// direction vector `fromDirection` to `toDirection` /// @return const reference to this. IMATH_HOSTDEVICE IMATH_CONSTEXPR14 Quat& setRotation (const Vec3& fromDirection, const Vec3& toDirection) IMATH_NOEXCEPT; /// @} /// The base type: In templates that accept a parameter `V`, you /// can refer to `T` as `V::BaseType` typedef T BaseType; private: IMATH_HOSTDEVICE void setRotationInternal (const Vec3& f0, const Vec3& t0, Quat& q) IMATH_NOEXCEPT; }; template IMATH_HOSTDEVICE IMATH_CONSTEXPR14 Quat slerp (const Quat& q1, const Quat& q2, T t) IMATH_NOEXCEPT; template IMATH_HOSTDEVICE IMATH_CONSTEXPR14 Quat slerpShortestArc (const Quat& q1, const Quat& q2, T t) IMATH_NOEXCEPT; template IMATH_HOSTDEVICE IMATH_CONSTEXPR14 Quat squad (const Quat& q1, const Quat& q2, const Quat& qa, const Quat& qb, T t) IMATH_NOEXCEPT; /// /// From advanced Animation and Rendering Techniques by Watt and Watt, /// Page 366: /// /// computing the inner quadrangle points (qa and qb) to guarantee /// tangent continuity. template IMATH_HOSTDEVICE void intermediate (const Quat& q0, const Quat& q1, const Quat& q2, const Quat& q3, Quat& qa, Quat& qb) IMATH_NOEXCEPT; template IMATH_HOSTDEVICE constexpr Matrix33 operator* (const Matrix33& M, const Quat& q) IMATH_NOEXCEPT; template IMATH_HOSTDEVICE constexpr Matrix33 operator* (const Quat& q, const Matrix33& M) IMATH_NOEXCEPT; template std::ostream& operator<< (std::ostream& o, const Quat& q); template IMATH_HOSTDEVICE constexpr Quat operator* (const Quat& q1, const Quat& q2) IMATH_NOEXCEPT; template IMATH_HOSTDEVICE constexpr Quat operator/ (const Quat& q1, const Quat& q2) IMATH_NOEXCEPT; template IMATH_HOSTDEVICE constexpr Quat operator/ (const Quat& q, T t) IMATH_NOEXCEPT; template IMATH_HOSTDEVICE constexpr Quat operator* (const Quat& q, T t) IMATH_NOEXCEPT; template IMATH_HOSTDEVICE constexpr Quat operator* (T t, const Quat& q) IMATH_NOEXCEPT; template IMATH_HOSTDEVICE constexpr Quat operator+ (const Quat& q1, const Quat& q2) IMATH_NOEXCEPT; template IMATH_HOSTDEVICE constexpr Quat operator- (const Quat& q1, const Quat& q2) IMATH_NOEXCEPT; template IMATH_HOSTDEVICE constexpr Quat operator~ (const Quat& q) IMATH_NOEXCEPT; template IMATH_HOSTDEVICE constexpr Quat operator- (const Quat& q) IMATH_NOEXCEPT; template IMATH_HOSTDEVICE IMATH_CONSTEXPR14 Vec3 operator* (const Vec3& v, const Quat& q) IMATH_NOEXCEPT; /// Quaternion of type float typedef Quat Quatf; /// Quaternion of type double typedef Quat Quatd; //--------------- // Implementation //--------------- template IMATH_HOSTDEVICE constexpr inline Quat::Quat() IMATH_NOEXCEPT : r (1), v (0, 0, 0) { // empty } template template IMATH_HOSTDEVICE IMATH_CONSTEXPR14 inline Quat::Quat (const Quat& q) IMATH_NOEXCEPT : r (q.r), v (q.v) { // empty } template IMATH_HOSTDEVICE constexpr inline Quat::Quat (T s, T i, T j, T k) IMATH_NOEXCEPT : r (s), v (i, j, k) { // empty } template IMATH_HOSTDEVICE constexpr inline Quat::Quat (T s, Vec3 d) IMATH_NOEXCEPT : r (s), v (d) { // empty } template IMATH_HOSTDEVICE constexpr inline Quat::Quat (const Quat& q) IMATH_NOEXCEPT : r (q.r), v (q.v) { // empty } template IMATH_HOSTDEVICE constexpr inline Quat Quat::identity() IMATH_NOEXCEPT { return Quat(); } template IMATH_HOSTDEVICE IMATH_CONSTEXPR14 inline const Quat& Quat::operator= (const Quat& q) IMATH_NOEXCEPT { r = q.r; v = q.v; return *this; } template IMATH_HOSTDEVICE IMATH_CONSTEXPR14 inline const Quat& Quat::operator*= (const Quat& q) IMATH_NOEXCEPT { T rtmp = r * q.r - (v ^ q.v); v = r * q.v + v * q.r + v % q.v; r = rtmp; return *this; } template IMATH_HOSTDEVICE IMATH_CONSTEXPR14 inline const Quat& Quat::operator*= (T t) IMATH_NOEXCEPT { r *= t; v *= t; return *this; } template IMATH_HOSTDEVICE IMATH_CONSTEXPR14 inline const Quat& Quat::operator/= (const Quat& q) IMATH_NOEXCEPT { *this = *this * q.inverse(); return *this; } template IMATH_HOSTDEVICE IMATH_CONSTEXPR14 inline const Quat& Quat::operator/= (T t) IMATH_NOEXCEPT { r /= t; v /= t; return *this; } template IMATH_HOSTDEVICE IMATH_CONSTEXPR14 inline const Quat& Quat::operator+= (const Quat& q) IMATH_NOEXCEPT { r += q.r; v += q.v; return *this; } template IMATH_HOSTDEVICE IMATH_CONSTEXPR14 inline const Quat& Quat::operator-= (const Quat& q) IMATH_NOEXCEPT { r -= q.r; v -= q.v; return *this; } template IMATH_HOSTDEVICE IMATH_CONSTEXPR14 inline T& Quat::operator[] (int index) IMATH_NOEXCEPT { return index ? v[index - 1] : r; } template IMATH_HOSTDEVICE constexpr inline T Quat::operator[] (int index) const IMATH_NOEXCEPT { return index ? v[index - 1] : r; } template template IMATH_HOSTDEVICE constexpr inline bool Quat::operator== (const Quat& q) const IMATH_NOEXCEPT { return r == q.r && v == q.v; } template template IMATH_HOSTDEVICE constexpr inline bool Quat::operator!= (const Quat& q) const IMATH_NOEXCEPT { return r != q.r || v != q.v; } /// 4D dot product template IMATH_HOSTDEVICE constexpr inline T operator^ (const Quat& q1, const Quat& q2) IMATH_NOEXCEPT { return q1.r * q2.r + (q1.v ^ q2.v); } template IMATH_HOSTDEVICE constexpr inline T Quat::length() const IMATH_NOEXCEPT { return std::sqrt (r * r + (v ^ v)); } template IMATH_HOSTDEVICE IMATH_CONSTEXPR14 inline Quat& Quat::normalize() IMATH_NOEXCEPT { if (T l = length()) { r /= l; v /= l; } else { r = 1; v = Vec3 (0); } return *this; } template IMATH_HOSTDEVICE IMATH_CONSTEXPR14 inline Quat Quat::normalized() const IMATH_NOEXCEPT { if (T l = length()) return Quat (r / l, v / l); return Quat(); } template IMATH_HOSTDEVICE IMATH_CONSTEXPR14 inline Quat Quat::inverse() const IMATH_NOEXCEPT { // // 1 Q* // - = ---- where Q* is conjugate (operator~) // Q Q* Q and (Q* Q) == Q ^ Q (4D dot) // T qdot = *this ^ *this; return Quat (r / qdot, -v / qdot); } template IMATH_HOSTDEVICE IMATH_CONSTEXPR14 inline Quat& Quat::invert() IMATH_NOEXCEPT { T qdot = (*this) ^ (*this); r /= qdot; v = -v / qdot; return *this; } template IMATH_HOSTDEVICE IMATH_CONSTEXPR14 inline Vec3 Quat::rotateVector (const Vec3& original) const IMATH_NOEXCEPT { // // Given a vector p and a quaternion q (aka this), // calculate p' = qpq* // // Assumes unit quaternions (because non-unit // quaternions cannot be used to rotate vectors // anyway). // Quat vec (0, original); // temporarily promote grade of original Quat inv (*this); inv.v *= -1; // unit multiplicative inverse Quat result = *this * vec * inv; return result.v; } template IMATH_HOSTDEVICE constexpr inline T Quat::euclideanInnerProduct (const Quat& q) const IMATH_NOEXCEPT { return r * q.r + v.x * q.v.x + v.y * q.v.y + v.z * q.v.z; } /// /// Compute the angle between two quaternions, /// interpreting the quaternions as 4D vectors. template IMATH_HOSTDEVICE IMATH_CONSTEXPR14 inline T angle4D (const Quat& q1, const Quat& q2) IMATH_NOEXCEPT { Quat d = q1 - q2; T lengthD = std::sqrt (d ^ d); Quat s = q1 + q2; T lengthS = std::sqrt (s ^ s); return 2 * std::atan2 (lengthD, lengthS); } /// /// Spherical linear interpolation. /// Assumes q1 and q2 are normalized and that q1 != -q2. /// /// This method does *not* interpolate along the shortest /// arc between q1 and q2. If you desire interpolation /// along the shortest arc, and q1^q2 is negative, then /// consider calling slerpShortestArc(), below, or flipping /// the second quaternion explicitly. /// /// The implementation of squad() depends on a slerp() /// that interpolates as is, without the automatic /// flipping. /// /// Don Hatch explains the method we use here on his /// web page, The Right Way to Calculate Stuff, at /// http://www.plunk.org/~hatch/rightway.php template IMATH_HOSTDEVICE IMATH_CONSTEXPR14 inline Quat slerp (const Quat& q1, const Quat& q2, T t) IMATH_NOEXCEPT { T a = angle4D (q1, q2); T s = 1 - t; Quat q = sinx_over_x (s * a) / sinx_over_x (a) * s * q1 + sinx_over_x (t * a) / sinx_over_x (a) * t * q2; return q.normalized(); } /// /// Spherical linear interpolation along the shortest /// arc from q1 to either q2 or -q2, whichever is closer. /// Assumes q1 and q2 are unit quaternions. template IMATH_HOSTDEVICE IMATH_CONSTEXPR14 inline Quat slerpShortestArc (const Quat& q1, const Quat& q2, T t) IMATH_NOEXCEPT { if ((q1 ^ q2) >= 0) return slerp (q1, q2, t); else return slerp (q1, -q2, t); } /// /// Spherical Cubic Spline Interpolation - from Advanced Animation and /// Rendering Techniques by Watt and Watt, Page 366: /// /// A spherical curve is constructed using three spherical linear /// interpolations of a quadrangle of unit quaternions: q1, qa, qb, /// q2. Given a set of quaternion keys: q0, q1, q2, q3, this routine /// does the interpolation between q1 and q2 by constructing two /// intermediate quaternions: qa and qb. The qa and qb are computed by /// the intermediate function to guarantee the continuity of tangents /// across adjacent cubic segments. The qa represents in-tangent for /// q1 and the qb represents the out-tangent for q2. /// /// The q1 q2 is the cubic segment being interpolated. /// /// The q0 is from the previous adjacent segment and q3 is from the /// next adjacent segment. The q0 and q3 are used in computing qa and /// qb. template IMATH_HOSTDEVICE IMATH_CONSTEXPR14 inline Quat spline (const Quat& q0, const Quat& q1, const Quat& q2, const Quat& q3, T t) IMATH_NOEXCEPT { Quat qa = intermediate (q0, q1, q2); Quat qb = intermediate (q1, q2, q3); Quat result = squad (q1, qa, qb, q2, t); return result; } /// /// Spherical Quadrangle Interpolation - from Advanced Animation and /// Rendering Techniques by Watt and Watt, Page 366: /// /// It constructs a spherical cubic interpolation as a series of three /// spherical linear interpolations of a quadrangle of unit /// quaternions. template IMATH_HOSTDEVICE IMATH_CONSTEXPR14 inline Quat squad (const Quat& q1, const Quat& qa, const Quat& qb, const Quat& q2, T t) IMATH_NOEXCEPT { Quat r1 = slerp (q1, q2, t); Quat r2 = slerp (qa, qb, t); Quat result = slerp (r1, r2, 2 * t * (1 - t)); return result; } /// Compute the intermediate point between three quaternions `q0`, `q1`, /// and `q2`. template IMATH_HOSTDEVICE IMATH_CONSTEXPR14 inline Quat intermediate (const Quat& q0, const Quat& q1, const Quat& q2) IMATH_NOEXCEPT { Quat q1inv = q1.inverse(); Quat c1 = q1inv * q2; Quat c2 = q1inv * q0; Quat c3 = (T) (-0.25) * (c2.log() + c1.log()); Quat qa = q1 * c3.exp(); qa.normalize(); return qa; } template IMATH_HOSTDEVICE inline Quat Quat::log() const IMATH_NOEXCEPT { // // For unit quaternion, from Advanced Animation and // Rendering Techniques by Watt and Watt, Page 366: // T theta = std::acos (std::min (r, (T) 1.0)); if (theta == 0) return Quat (0, v); T sintheta = std::sin (theta); T k; if (std::abs(sintheta) < 1 && std::abs(theta) >= std::numeric_limits::max() * std::abs(sintheta)) k = 1; else k = theta / sintheta; return Quat ((T) 0, v.x * k, v.y * k, v.z * k); } template IMATH_HOSTDEVICE inline Quat Quat::exp() const IMATH_NOEXCEPT { // // For pure quaternion (zero scalar part): // from Advanced Animation and Rendering // Techniques by Watt and Watt, Page 366: // T theta = v.length(); T sintheta = std::sin (theta); T k; if (abs (theta) < 1 && abs (sintheta) >= std::numeric_limits::max() * abs (theta)) k = 1; else k = sintheta / theta; T costheta = std::cos (theta); return Quat (costheta, v.x * k, v.y * k, v.z * k); } template IMATH_HOSTDEVICE constexpr inline T Quat::angle() const IMATH_NOEXCEPT { return 2 * std::atan2 (v.length(), r); } template IMATH_HOSTDEVICE constexpr inline Vec3 Quat::axis() const IMATH_NOEXCEPT { return v.normalized(); } template IMATH_HOSTDEVICE IMATH_CONSTEXPR14 inline Quat& Quat::setAxisAngle (const Vec3& axis, T radians) IMATH_NOEXCEPT { r = std::cos (radians / 2); v = axis.normalized() * std::sin (radians / 2); return *this; } template IMATH_HOSTDEVICE IMATH_CONSTEXPR14 inline Quat& Quat::setRotation (const Vec3& from, const Vec3& to) IMATH_NOEXCEPT { // // Create a quaternion that rotates vector from into vector to, // such that the rotation is around an axis that is the cross // product of from and to. // // This function calls function setRotationInternal(), which is // numerically accurate only for rotation angles that are not much // greater than pi/2. In order to achieve good accuracy for angles // greater than pi/2, we split large angles in half, and rotate in // two steps. // // // Normalize from and to, yielding f0 and t0. // Vec3 f0 = from.normalized(); Vec3 t0 = to.normalized(); if ((f0 ^ t0) >= 0) { // // The rotation angle is less than or equal to pi/2. // setRotationInternal (f0, t0, *this); } else { // // The angle is greater than pi/2. After computing h0, // which is halfway between f0 and t0, we rotate first // from f0 to h0, then from h0 to t0. // Vec3 h0 = (f0 + t0).normalized(); if ((h0 ^ h0) != 0) { setRotationInternal (f0, h0, *this); Quat q; setRotationInternal (h0, t0, q); *this *= q; } else { // // f0 and t0 point in exactly opposite directions. // Pick an arbitrary axis that is orthogonal to f0, // and rotate by pi. // r = T (0); Vec3 f02 = f0 * f0; if (f02.x <= f02.y && f02.x <= f02.z) v = (f0 % Vec3 (1, 0, 0)).normalized(); else if (f02.y <= f02.z) v = (f0 % Vec3 (0, 1, 0)).normalized(); else v = (f0 % Vec3 (0, 0, 1)).normalized(); } } return *this; } template IMATH_HOSTDEVICE inline void Quat::setRotationInternal (const Vec3& f0, const Vec3& t0, Quat& q) IMATH_NOEXCEPT { // // The following is equivalent to setAxisAngle(n,2*phi), // where the rotation axis, n, is orthogonal to the f0 and // t0 vectors, and 2*phi is the angle between f0 and t0. // // This function is called by setRotation(), above; it assumes // that f0 and t0 are normalized and that the angle between // them is not much greater than pi/2. This function becomes // numerically inaccurate if f0 and t0 point into nearly // opposite directions. // // // Find a normalized vector, h0, that is halfway between f0 and t0. // The angle between f0 and h0 is phi. // Vec3 h0 = (f0 + t0).normalized(); // // Store the rotation axis and rotation angle. // q.r = f0 ^ h0; // f0 ^ h0 == cos (phi) q.v = f0 % h0; // (f0 % h0).length() == sin (phi) } template IMATH_HOSTDEVICE constexpr inline Matrix33 Quat::toMatrix33() const IMATH_NOEXCEPT { return Matrix33 (1 - 2 * (v.y * v.y + v.z * v.z), 2 * (v.x * v.y + v.z * r), 2 * (v.z * v.x - v.y * r), 2 * (v.x * v.y - v.z * r), 1 - 2 * (v.z * v.z + v.x * v.x), 2 * (v.y * v.z + v.x * r), 2 * (v.z * v.x + v.y * r), 2 * (v.y * v.z - v.x * r), 1 - 2 * (v.y * v.y + v.x * v.x)); } template IMATH_HOSTDEVICE constexpr inline Matrix44 Quat::toMatrix44() const IMATH_NOEXCEPT { return Matrix44 (1 - 2 * (v.y * v.y + v.z * v.z), 2 * (v.x * v.y + v.z * r), 2 * (v.z * v.x - v.y * r), 0, 2 * (v.x * v.y - v.z * r), 1 - 2 * (v.z * v.z + v.x * v.x), 2 * (v.y * v.z + v.x * r), 0, 2 * (v.z * v.x + v.y * r), 2 * (v.y * v.z - v.x * r), 1 - 2 * (v.y * v.y + v.x * v.x), 0, 0, 0, 0, 1); } /// Transform the quaternion by the matrix /// @return M * q template IMATH_HOSTDEVICE constexpr inline Matrix33 operator* (const Matrix33& M, const Quat& q) IMATH_NOEXCEPT { return M * q.toMatrix33(); } /// Transform the matrix by the quaterion: /// @return q * M template IMATH_HOSTDEVICE constexpr inline Matrix33 operator* (const Quat& q, const Matrix33& M) IMATH_NOEXCEPT { return q.toMatrix33() * M; } /// Stream output as "(r x y z)" template std::ostream& operator<< (std::ostream& o, const Quat& q) { return o << "(" << q.r << " " << q.v.x << " " << q.v.y << " " << q.v.z << ")"; } /// Quaterion multiplication template IMATH_HOSTDEVICE constexpr inline Quat operator* (const Quat& q1, const Quat& q2) IMATH_NOEXCEPT { return Quat (q1.r * q2.r - (q1.v ^ q2.v), q1.r * q2.v + q1.v * q2.r + q1.v % q2.v); } /// Quaterion division template IMATH_HOSTDEVICE constexpr inline Quat operator/ (const Quat& q1, const Quat& q2) IMATH_NOEXCEPT { return q1 * q2.inverse(); } /// Quaterion division template IMATH_HOSTDEVICE constexpr inline Quat operator/ (const Quat& q, T t) IMATH_NOEXCEPT { return Quat (q.r / t, q.v / t); } /// Quaterion*scalar multiplication /// @return q * t template IMATH_HOSTDEVICE constexpr inline Quat operator* (const Quat& q, T t) IMATH_NOEXCEPT { return Quat (q.r * t, q.v * t); } /// Quaterion*scalar multiplication /// @return q * t template IMATH_HOSTDEVICE constexpr inline Quat operator* (T t, const Quat& q) IMATH_NOEXCEPT { return Quat (q.r * t, q.v * t); } /// Quaterion addition template IMATH_HOSTDEVICE constexpr inline Quat operator+ (const Quat& q1, const Quat& q2) IMATH_NOEXCEPT { return Quat (q1.r + q2.r, q1.v + q2.v); } /// Quaterion subtraction template IMATH_HOSTDEVICE constexpr inline Quat operator- (const Quat& q1, const Quat& q2) IMATH_NOEXCEPT { return Quat (q1.r - q2.r, q1.v - q2.v); } /// Compute the conjugate template IMATH_HOSTDEVICE constexpr inline Quat operator~ (const Quat& q) IMATH_NOEXCEPT { return Quat (q.r, -q.v); } /// Negate the quaterion template IMATH_HOSTDEVICE constexpr inline Quat operator- (const Quat& q) IMATH_NOEXCEPT { return Quat (-q.r, -q.v); } /// Quaterion*vector multiplcation /// @return v * q template IMATH_HOSTDEVICE IMATH_CONSTEXPR14 inline Vec3 operator* (const Vec3& v, const Quat& q) IMATH_NOEXCEPT { Vec3 a = q.v % v; Vec3 b = q.v % a; return v + T (2) * (q.r * a + b); } #if (defined _WIN32 || defined _WIN64) && defined _MSC_VER # pragma warning(pop) #endif IMATH_INTERNAL_NAMESPACE_HEADER_EXIT #endif // INCLUDED_IMATHQUAT_H