template< typename T > void Aftr::QuatT::enforce_imaginary_comp_is_always_positive() noexcept { return; if( this->a < 0 ) { this->a = -this->a; this->b = -this->b; this->c = -this->c; this->d = -this->d; } } template< typename T > Aftr::QuatT::QuatT() noexcept {} template< typename T > Aftr::QuatT::QuatT( T a, T b, T c, T d ) noexcept : a( a ), b( b ), c( c ), d( d ) { this->enforce_imaginary_comp_is_always_positive(); } template< typename T > Aftr::QuatT::QuatT( Aftr::AxisAngleT const& aa ) noexcept { (*this) = QuatImpl::fromAxisAngle( aa ); } template< typename T > Aftr::QuatT::QuatT( Aftr::QuatT const& toCopy ) noexcept { *this = toCopy; } template< typename T > Aftr::QuatT& Aftr::QuatT::operator=( const Aftr::QuatT& q ) { if( this != &q ) { this->a = q.a; this->b = q.b; this->c = q.c; this->d = q.d; this->enforce_imaginary_comp_is_always_positive(); } return *this; } template< typename T > Aftr::QuatT::QuatT( const Mat4T& m ) noexcept { *this = QuatImpl::fromDCM( m ); } template< typename T > Aftr::QuatT::QuatT( const T* m ) : Aftr::QuatT( Mat4T(m) ) {} template< typename T > Aftr::QuatT Aftr::QuatT::negation() const noexcept { Aftr::QuatT temp(-a, -b, -c, -d); return temp; } template< typename T > Aftr::QuatT Aftr::QuatT::conjugate() const noexcept { Aftr::QuatT temp(a, -b, -c, -d); return temp; } template< typename T > Aftr::QuatT Aftr::QuatT::operator +(const Aftr::QuatT &q) const { Aftr::QuatT temp(a + q.a, b + q.b, c + q.c, d + q.d); return temp; } template< typename T > Aftr::QuatT Aftr::QuatT::operator -(const Aftr::QuatT &q) const { Aftr::QuatT temp(a - q.a, b - q.b, c - q.c, d - q.d); return temp; } template< typename T > Aftr::QuatT Aftr::QuatT::operator *(const Aftr::QuatT &q) const { //Exactly the same operations as a grassProduct //Qr = Qa * Qb //Calculates the combined rotation return this->grassmanProduct( q ); } template< typename T > Aftr::QuatT Aftr::QuatT::grassmanProduct(const Aftr::QuatT &q) const { Aftr::QuatT temp( a * q.a - b * q.b - c * q.c - d * q.d, a * q.b + b * q.a + c * q.d - d * q.c, a * q.c - b * q.d + c * q.a + d * q.b, a * q.d + b * q.c - c * q.b + d * q.a ); return temp; } template< typename T > T Aftr::QuatT::innerProduct(const Aftr::QuatT &q) const { return a * q.a + b * q.b + c * q.c + d * q.d; } //template< typename T > //Aftr::QuatT Aftr::QuatT::outerProduct(const Aftr::QuatT &q) const //{ // Vector a(this->b, this->c, this->d); // Vector b(q.b, q.c, q.d); // Vector c; // // c = b * this->a; // c = c - (a * q.a); // c = c - a.crossProduct(b); // // Aftr::QuatT temp(0, c.x, c.y, c.z); // return temp; //} template< typename T > T Aftr::QuatT::magnitude() const { return this->length(); } template< typename T > bool Aftr::QuatT::isEqual( QuatT const& a, T epsilon ) const noexcept { return QuatImpl::isEqual( *this, a, epsilon ); } template< typename T > std::optional< Aftr::QuatT > Aftr::QuatT::fromString_opt( const std::string& str ) { return QuatImpl::fromString_opt( str ); } //template< typename T > //Aftr::QuatT Aftr::QuatT::crossProduct(const Aftr::QuatT &q) const //{ // Vector a(this->b, this->c, this->d); // Vector b(q.b, q.c, q.d); // Vector c = a.crossProduct(b); // // Aftr::QuatT temp(0, c.x, c.y, c.z); // return temp; //} // //template< typename T > //Aftr::QuatT Aftr::QuatT::evenProduct(const Aftr::QuatT &q) const //{ // // QuatT temp(a * q.a - b * q.b - c * q.c - d * q.d, // a * q.b + q.a * b, // a * q.c + q.a * c, // a * q.d + q.a * d); // return temp; //} // //template< typename T > //void Aftr::QuatT::toRepresentationMatrix(T m[]) const //{ // m[0] = m[5] = m[10] = m[15] = a; // m[1] = m[14] = b; // m[2] = m[7] = c; // m[3] = m[9] = d; // m[4] = m[11] = -b; // m[6] = m[12] = -d; // m[8] = m[13] = -c; //} template< typename T > void Aftr::QuatT::toRotationMatrix(T m[]) const { Mat4T mat = QuatImpl::toRotationMatrix( *this ); std::copy( mat.cbegin(), mat.cend(), m ); } template< typename T > Aftr::Mat4T Aftr::QuatT::toRotationMatrix() const { return Aftr::QuatImpl::toRotationMatrix( *this ); } template< typename T > std::string Aftr::QuatT::toString( size_t length) const { return Aftr::QuatImpl::toString( *this, length ); } template< typename T > Aftr::QuatT Aftr::QuatT::normalize() const { T mag = this->magnitude(); T w = a / mag; T x = b / mag; T y = c / mag; T z = d / mag; return QuatT( w, x, y, z ); } template< typename T > T Aftr::QuatT::length() const noexcept { T len = std::sqrt( a * a + b * b + c * c + d * d ); return len; } template< typename T > Aftr::VectorT Aftr::QuatT::getVectorXYZ() const noexcept { return VectorT{b, c, d}; } template< typename T > Aftr::VectorT Aftr::QuatT::getAxisOfRotation() const noexcept { return this->toAxisAngle().axis(); //auto q = this->normalize(); //Code adapted from Eigen3 library //T n = q.getVectorXYZ().length(); //if( q.a < T( 0 ) ) // n = -n; //VectorT axis = q.getVectorXYZ() / n; //return axis; } template< typename T > T Aftr::QuatT::getAngleOfRotationRads() const noexcept { return this->toAxisAngle().rad(); //auto q = this->normalize(); //Code adapted from Eigen3 library //T n = q.getVectorXYZ().length(); //T angleRad = T( 2 ) * std::atan2( n, std::abs( q.a ) );// m_angle = Scalar( 2 ) * atan2( n, abs( q.w() ) ); //return angleRad; } template< typename T > Aftr::AxisAngleT Aftr::QuatT::toAxisAngle() const noexcept { return Aftr::QuatImpl::toAxisAngle( *this ); } template< typename T > T& Aftr::QuatT::operator[]( uint8_t i ) noexcept { switch( i ) { case 0:return this->a; case 1:return this->b; case 2:return this->c; case 3:return this->d; default: std::cout << "ERROR: " << AFTR_FILE_LINE_STR << " - Out of bounds quat access [0,3], cannot access " << i << "\n"; std::abort(); } } template< typename T > T Aftr::QuatT::operator[]( uint8_t i ) const noexcept { switch( i ) { case 0:return this->a; case 1:return this->b; case 2:return this->c; case 3:return this->d; default: std::cout << "ERROR: " << AFTR_FILE_LINE_STR << " - Out of bounds quat access [0,3], cannot access " << i << "\n"; std::abort(); } } template< typename T > Aftr::QuatT Aftr::QuatT::slerp( const Aftr::QuatT& startOrient, const Aftr::QuatT& endOrient, T t ) { //T srcm[16]; //T daftr[16]; //aftrSetIdentityMatrix( srcm ); //aftrSetIdentityMatrix( daftr ); //AftrCoordinateTransforms::rotateAboutArbitraryAxis( Vector(0,1,0), 35.0f * Aftr::DEGtoRAD, daftr ); //AftrCoordinateTransforms::rotateAboutArbitraryAxis( Vector(0,0,1), 90.0f * Aftr::DEGtoRAD, daftr ); //QuatT thisQuat( srcm ); //QuatT input( daftr ); //std::cout << "src quat " << thisQuat.toString() << "...\n"; //std::cout << "input quat " << input.toString() << "...\n"; //for( size_t i = 0; i < 101; ++i ) //{ // T t = (T)i / (T)100; Aftr::QuatT a = startOrient.normalize(); Aftr::QuatT b = endOrient.normalize(); //std::cout << "A norm is " << a.toString() << "...\n"; //std::cout << "B norm is " << b.toString() << "...\n"; //compute "cosine of the angle" between quaternions using dot product T cosOmega = a.innerProduct( b ); //if negative dot prod, negate one of input quaternions if( cosOmega < 0.0f ) { b = b.negation(); cosOmega *= -1.0f; } T k0 = 0.0f; T k1 = 1.0f; if( cosOmega > 0.9999f ) { //very close to 1 (causes a divide by zero) - just use linear interpolation k0 = 1.0f - t; k1 = t; } else { //compute the sin of the angle using //trig identity sin^2 x + cos^2 x = 1 T sinOmega = std::sqrt( 1.0f - cosOmega * cosOmega ); //Compute the angle from its sin and cosine T omega = atan2f( sinOmega, cosOmega ); //compute inverse of denominator, so we only divide once T oneOverSinOmega = 1.0f / sinOmega; //Compute interpolation parameters k0 = sinf( (1.0f - t) * omega ) * oneOverSinOmega; k1 = sinf( t * omega ) * oneOverSinOmega; } //Interpolate Aftr::QuatT c( a.a * k0 + b.a * k1, a.b * k0 + b.b * k1, a.c * k0 + b.c * k1, a.d * k0 + b.d * k1 ); c = c.normalize(); //std::cout << "Interpolated quat is " << c.toString() << "...\n"; //T m[16]; //aftrSetIdentityMatrix( m ); //c.toRotationMatrix( m ); ////std::cout << "matrix version of quat is \n" << print4x4MatrixColumn( m ) << "\n"; //T v[4] = {1,0,0,0}; //T vout[4] = {0,0,0,0}; //multiply4x4MatrixBy4x1Vector( m, v, vout ); ////AftrAffine //Vector xdir( vout[0], vout[1], vout[2] ); //Vector rpy = AftrCoordinateTransforms::transformFromDisplayMatrixToRollPitchYaw( m ).toVecS(); //std::cout << "t " << t << ": " << xdir.toString() << " length is " << xdir.magnitude() << ", rpy is " << rpy.toString() << "...\n"; //} return c; }