//using namespace Aftr; //template bool Mat4::is_T_Equal( float x, float y, float epsilon ); //template bool Mat4::is_T_Equal( double x, double y, double epsilon ); template< typename T > bool Aftr::Mat4T::is_T_Equal( T x, T y, T epsilon ) noexcept { if( std::abs( x - y ) <= epsilon ) return true; return false; } template< typename T > void Aftr::Mat4T::setToIdentity4x4( std::array& m ) noexcept { m[0] = 1; m[4] = 0; m[8] = 0; m[12] = 0; m[1] = 0; m[5] = 1; m[9] = 0; m[13] = 0; m[2] = 0; m[6] = 0; m[10] = 1; m[14] = 0; m[3] = 0; m[7] = 0; m[11] = 0; m[15] = 1; } template< typename T > Aftr::Mat4T::Mat4T() noexcept { Aftr::Mat4T::setToIdentity4x4( this->m ); static_assert( sizeof( Mat4T ) == sizeof( T ) * 16, "Aftr Compiler Halt: Mat4T MUST not change its size of 16*T.\n" \ "This would cause float* mat to have a different layout than std::vector>.\n" \ "This would ultimately hurt performance." ); } template< typename T > Aftr::Mat4T::Mat4T( const Mat4T& m ) noexcept { *this = m; } template< typename T > Aftr::Mat4T::Mat4T( const T* mat4ToBeCopied ) noexcept { if( mat4ToBeCopied != nullptr ) std::copy( mat4ToBeCopied, mat4ToBeCopied + this->m.size(), this->m.begin() ); else Aftr::Mat4T::setToIdentity4x4( this->m ); } template< typename T > Aftr::Mat4T::Mat4T( Aftr::AxisAngleT const& a ) noexcept { *this = Mat4T::rotateIdentityMat( a.axis(), a.rad() ); } template< typename T > Aftr::Mat4T::Mat4T( Aftr::QuatT const& q ) noexcept { *this = q.toRotationMatrix(); } template< typename T > Aftr::Mat4T::Mat4T( const VectorT& x, const VectorT& y, const VectorT& z ) noexcept { this->m[0] = x.x; this->m[4] = y.x; this->m[8] = z.x; this->m[12] = 0; this->m[1] = x.y; this->m[5] = y.y; this->m[9] = z.y; this->m[13] = 0; this->m[2] = x.z; this->m[6] = y.z; this->m[10] = z.z; this->m[14] = 0; this->m[3] = 0; this->m[7] = 0; this->m[11] = 0; this->m[15] = 1; } template< typename T > Aftr::Mat4T::Mat4T( const VectorT& x, const VectorT& y, const VectorT& z, const VectorT& translation ) noexcept { this->m[0] = x.x; this->m[4] = y.x; this->m[8] = z.x; this->m[12] = translation.x; this->m[1] = x.y; this->m[5] = y.y; this->m[9] = z.y; this->m[13] = translation.y; this->m[2] = x.z; this->m[6] = y.z; this->m[10] = z.z; this->m[14] = translation.z; this->m[3] = 0; this->m[7] = 0; this->m[11] = 0; this->m[15] = 1; } template< typename T > Aftr::Mat4T::Mat4T( std::array const& mat ) noexcept { std::copy( mat.cbegin(), mat.cend(), this->m.begin()); } template< typename T > Aftr::Mat4T::Mat4T( const std::string& str, bool inputStringIsColumnMajor ) noexcept { Mat4T mat = Mat4T::fromString( str, inputStringIsColumnMajor ); *this = mat; } template< typename T > void Aftr::Mat4T::set( const T* mat4x4 ) { std::copy( mat4x4, mat4x4 + this->m.size(), this->m.begin() ); } template< typename T > void Aftr::Mat4T::set( Mat4T const& m ) noexcept { this->m = m.m; } template< typename T > void Aftr::Mat4T::setMeToIdentity() noexcept { Mat4T::setToIdentity4x4( this->m ); } template< typename T > Aftr::VectorT Aftr::Mat4T::operator *( const Aftr::VectorT& v ) const noexcept { Aftr::VectorT w = { 0, 0, 0 }; w.x = m[0] * v.x + m[4] * v.y + m[8] * v.z; w.y = m[1] * v.x + m[5] * v.y + m[9] * v.z; w.z = m[2] * v.x + m[6] * v.y + m[10] * v.z; return w; } template< typename T > std::pair< Aftr::VectorT, T > Aftr::Mat4T::operator *( const std::pair< const Aftr::VectorT&, T >& homogeniusCoord ) const noexcept { //return std::make_pair( 1, 1.3f ); const VectorT& v = homogeniusCoord.first; T h = homogeniusCoord.second; Aftr::VectorT w = { 0, 0, 0 }; T s = 0; w.x = m[0] * v.x + m[4] * v.y + m[8] * v.z + m[12] * h; w.y = m[1] * v.x + m[5] * v.y + m[9] * v.z + m[13] * h; w.z = m[2] * v.x + m[6] * v.y + m[10] * v.z + m[14] * h; s = m[3] * v.x + m[7] * v.y + m[11] * v.z + m[15] * h; return std::make_pair( w, s ); //return std::make_pair( w, h ); } template< typename T > Aftr::Mat4T Aftr::Mat4T::operator *( const Aftr::Mat4T& m ) const noexcept { Aftr::Mat4T result; T* p = result.m.data(); const T* b = this->m.data(); const T* a = m.m.data(); //we want to post multiply a by b. So we swap the order the matrices are multiplied above (ie, b is "this" and a is "m") for( size_t r = 0; r < 4; ++r ) for( size_t c = 0; c < 4; ++c ) p[r * 4 + c] = a[r * 4 + 0] * b[0 * 4 + c] + a[r * 4 + 1] * b[1 * 4 + c] + a[r * 4 + 2] * b[2 * 4 + c] + a[r * 4 + 3] * b[3 * 4 + c]; return result; } template Aftr::Mat4T Aftr::Mat4T::operator*( T const scalar ) const noexcept { Mat4T scaled; std::transform( this->m.cbegin(), this->m.cend(), scaled.begin(), [scalar](auto const v) {return v * scalar; }); return scaled; } template Aftr::Mat4T Aftr::Mat4T::operator/( T const scalar ) const noexcept { Mat4T divided; std::transform( this->m.cbegin(), this->m.cend(), divided.begin(), [scalar]( auto const v ) {return v / scalar; } ); return divided; } template< typename T > Aftr::Mat4T& Aftr::Mat4T::operator *=( const Aftr::Mat4T& m) noexcept { *this = m * ( *this ); return *this; } template< typename T > Aftr::Mat4T Aftr::Mat4T::operator +(const Aftr::Mat4T& m) const noexcept { Aftr::Mat4T result; std::transform( this->m.cbegin(), this->m.cend(), m.cbegin(), result.begin(), std::plus{} ); return result; } template< typename T > Aftr::Mat4T Aftr::Mat4T::operator -( const Aftr::Mat4T& m ) const noexcept { Aftr::Mat4T result; std::transform( this->m.cbegin(), this->m.cend(), m.cbegin(), result.begin(), std::minus{} ); return result; } template< typename T > Aftr::Mat4T& Aftr::Mat4T::operator =( const Aftr::Mat4T& m ) noexcept { if( this != &m ) this->m = m.m; return *this; } template< typename T > bool Aftr::Mat4T::operator ==( const Aftr::Mat4T& m ) const noexcept { for( size_t i = 0; i < this->m.size(); ++i ) { if( std::abs( this->m[i] - m.m[i] ) > ZERO_THRESHOLD() ) return false; } return true; } template< typename T > bool Aftr::Mat4T::operator !=( const Aftr::Mat4T& m ) const noexcept { if( (*this) == m ) return false; return true; } template< typename T > T& Aftr::Mat4T::operator [] ( size_t i ) noexcept { return this->m[i]; } template< typename T > T Aftr::Mat4T::operator [] ( size_t i ) const noexcept { return this->m[i]; } template< typename T > T& Aftr::Mat4T::at( size_t i ) noexcept { if( i > 15 ) { //ERROR, replace with exception at some point std::cout << "ERROR: " << AFTR_FILE_LINE_STR << " User attempted to access a component outside the interval [0,15]. Exiting. \n"; std::abort(); } return this->m[i]; } template< typename T > T Aftr::Mat4T::at( size_t i ) const noexcept { if( i > 15 ) { //ERROR, replace with exception at some point std::cout << "ERROR: " << AFTR_FILE_LINE_STR << " User attempted to access a component outside the interval [0,15]. Exiting. \n"; std::abort(); } return this->m[i]; } template< typename T > T& Aftr::Mat4T::at( int row, int col ) noexcept { if( row < 0 || row > 3 || col < 0 || col > 3 ) { //ERROR, replace with exception at some point std::cout << "ERROR: " << AFTR_FILE_LINE_STR << " User attempted to access a component ("<m[ row * 4 + col]; } template< typename T > T Aftr::Mat4T::at( int row, int col ) const noexcept { if( row < 0 || row > 3 || col < 0 || col > 3 ) { //ERROR, replace with exception at some point std::cout << "ERROR: " << AFTR_FILE_LINE_STR << " User attempted to access a component (" << row << "," << col << "). This is outside Mat4x4. Exiting. \n"; std::abort(); } return this->m[row * 4 + col]; } template< typename T > bool Aftr::Mat4T::isEqual( const Aftr::Mat4T& m, T tolerance ) const noexcept { for( size_t i = 0; i < this->m.size(); ++i ) { if( std::abs( this->m[i] - m[i] ) > tolerance ) return false; } return true; } template< typename T > bool Aftr::Mat4T::isValidForEachComponent() const noexcept { for( size_t i = 0; i < 16; ++i ) if( std::isinf( this->m[i] ) || std::isnan( this->m[i] ) ) return false; return true; } template< typename T > T Aftr::Mat4T::trace3x3() const noexcept { return this->m[0] + this->m[5] + this->m[10]; } template< typename T > T Aftr::Mat4T::trace4x4() const noexcept { return this->m[0] + this->m[5] + this->m[10] + this->m[15]; } template< typename T > Aftr::Mat4T Aftr::Mat4T::rotate( const Aftr::VectorT& axisNormalized, T angleRads ) const noexcept { return Aftr::Mat4T::rotateIdentityMat( axisNormalized, angleRads ) * ( *this ); // transform current matrix by rotated identity } template< typename T > Aftr::Mat4T Aftr::Mat4T::rotateAboutRelX( T angleRads ) const noexcept { VectorT relX{ this->m[0], this->m[1], this->m[2] }; return Aftr::Mat4T::rotateIdentityMat( relX, angleRads ) * (*this); } template< typename T > Aftr::Mat4T Aftr::Mat4T::rotateAboutRelY( T angleRads ) const noexcept { VectorT relY{ this->m[4], this->m[5], this->m[6] }; return Aftr::Mat4T::rotateIdentityMat( relY, angleRads ) * (*this); } template< typename T > Aftr::Mat4T Aftr::Mat4T::rotateAboutRelZ( T angleRads ) const noexcept { VectorT relZ{ this->m[8], this->m[9], this->m[10] }; return Aftr::Mat4T::rotateIdentityMat( relZ, angleRads ) * (*this); } template< typename T > Aftr::Mat4T Aftr::Mat4T::rotateIdentityMat( const Aftr::VectorT& axisNormalized, T angleRads ) noexcept { if( ! Aftr::Mat4T::is_T_Equal( axisNormalized.magnitudeSquared(), static_cast(1.0), static_cast(0.0001) ) ) { std::cout << "ERROR:" << AFTR_FILE_LINE_STR << ": Expected rotation axis must be unit length, but was passed " << axisNormalized.toString( 5 ) << " (magnitude " << axisNormalized.magnitude() << ")...\n" << "Ensure rotation axis is unit length."; std::abort(); } Aftr::Mat4T r; T* m = r.m.data(); T u = axisNormalized.x; T v = axisNormalized.y; T w = axisNormalized.z; T t = angleRads; T cost = std::cos( t ); T sint = std::sin( t ); m[0] = u*u + ( 1.0f - u*u ) * cost; m[4] = u*v*( 1.0f - cost ) - w * sint; m[8] = u*w*( 1.0f - cost ) + v * sint; m[12] = 0.0f; m[1] = u*v*( 1.0f - cost ) + w * sint; m[5] = v*v + ( 1.0f - v*v ) * cost; m[9] = v*w*( 1.0f - cost ) - u * sint; m[13] = 0.0f; m[2] = u*w*( 1.0f - cost ) - v * sint; m[6] = v*w*( 1.0f - cost ) + u*sint; m[10] = w*w + ( 1.0f - w*w )* cost; m[14] = 0.0f; m[3] = 0.0f; m[7] = 0.0f; m[11] = 0.0f; m[15] = 1.0f; return r; //Since this static method only rotates the identity matrix, we do not need to accumulate r with the current matrix } template< typename T > Aftr::Mat4T Aftr::Mat4T::translate( const Aftr::VectorT& t ) const noexcept { Aftr::Mat4T trM( *this ); VectorT tr = ( *this ) * t; //transform t through upper 3x3 matrix trM.setPosition( trM.getPosition() + tr ); //add new translate amount to the existing translate amount return trM; } template< typename T > Aftr::Mat4T Aftr::Mat4T::translateIdentityMat( const Aftr::VectorT& t ) noexcept { Aftr::Mat4T tr; tr[12] = t.x; tr[13] = t.y; tr[14] = t.z; return tr; } template< typename T > Aftr::Mat4T Aftr::Mat4T::scale( const Aftr::VectorT& s ) const noexcept { Aftr::Mat4T sc( *this ); sc.setX( sc.getX() * s.x ); sc.setY( sc.getY() * s.y ); sc.setZ( sc.getZ() * s.z ); return sc; } template< typename T > Aftr::Mat4T Aftr::Mat4T::scaleIdentityMat( const Aftr::VectorT& s ) noexcept { Aftr::Mat4T sc; sc[0] = s.x; sc[5] = s.y; sc[10] = s.z; return sc; } template< typename T > Aftr::Mat4T Aftr::Mat4T::transposeUpperLeft3x3() const noexcept { Aftr::Mat4T transpose(*this); T* t = transpose.m.data(); const T* s = this->m.data(); t[0] = s[0]; t[4] = s[1]; t[8] = s[2]; t[1] = s[4]; t[5] = s[5]; t[9] = s[6]; t[2] = s[8]; t[6] = s[9]; t[10] = s[10]; return transpose; } template< typename T > Aftr::Mat4T Aftr::Mat4T::transpose4x4() const noexcept { Aftr::Mat4T t; for( size_t r = 0; r < 4; ++r ) for( size_t c = 0; c < 4; ++c ) t[ r * 4 + c ] = ( *this )[r + c * 4 ]; return t; } template< typename T > std::string Aftr::Mat4T::toString() const { return this->toString( 3 ); } template< typename T > Aftr::Mat4T Aftr::Mat4T::fromString( const std::string& str, bool inputStringIsColumnMajor ) noexcept { return Aftr::Mat4Impl::make_Mat4T( str, inputStringIsColumnMajor ); } template< typename T > std::optional< Aftr::Mat4T > Aftr::Mat4T::fromString_opt( const std::string& str, bool inputStringIsColumnMajor ) noexcept { return Aftr::Mat4Impl::make_Mat4T_opt( str, inputStringIsColumnMajor ); } template< typename T > std::string Aftr::Mat4T::toString( size_t length ) const { return Aftr::Mat4Impl::toString( *this, length ); } template< typename T > std::string Aftr::Mat4T::toStringWithVecMagnitudes( size_t length ) const { return Aftr::Mat4Impl::toStringWithVecMagnitudes( *this, length ); } template< typename T > Aftr::Mat4T Aftr::Mat4T::toMatD() const noexcept { Aftr::Mat4T dm; for( size_t i = 0; i < 16; ++i ) dm.m[i] = static_cast( this->m[i] ); return dm; } template< typename T > Aftr::Mat4T Aftr::Mat4T::toMatS() const noexcept { Aftr::Mat4T fm; for( size_t i = 0; i < 16; ++i ) fm.m[i] = static_cast( this->m[i] ); return fm; } template< typename T > Aftr::VectorT Aftr::Mat4T::getX() const noexcept { Aftr::VectorT x( this->m[0], this->m[1], this->m[2] ); return x; } template< typename T > Aftr::VectorT Aftr::Mat4T::getY() const noexcept { Aftr::VectorT y( this->m[4], this->m[5], this->m[6] ); return y; } template< typename T > Aftr::VectorT Aftr::Mat4T::getZ() const noexcept { Aftr::VectorT z( this->m[8], this->m[9], this->m[10] ); return z; } template< typename T > Aftr::VectorT Aftr::Mat4T::getPosition() const noexcept { Aftr::VectorT trans( this->m[12], this->m[13], this->m[14] ); return trans; } template< typename T > Aftr::VectorT Aftr::Mat4T::getVecCol( int i ) const noexcept { if( i < 0 || i > 3 ) { std::cout << "ERROR: " << AFTR_FILE_LINE_STR << ": cannot extract column " << i << ", out of bounds...\n"; std::abort(); } if( i == 0 ) return this->getX(); else if( i == 1 ) return this->getY(); else if( i == 2 ) return this->getZ(); else //( i == 3 ); return this->getPosition(); } template< typename T > Aftr::Mat4T Aftr::Mat4T::getUpperLeft3x3AsMat4() const noexcept { Aftr::Mat4T mat( *this ); T* t = mat.m.data(); t[12] = 0; t[13] = 0; t[14] = 0; t[3] = 0; t[7] = 0; t[11] = 0; t[15] = 1; return mat; } template< typename T > void Aftr::Mat4T::setX( const Aftr::VectorT& xVec ) noexcept { this->m[0] = xVec.x; this->m[1] = xVec.y; this->m[2] = xVec.z; } template< typename T > void Aftr::Mat4T::setY( const Aftr::VectorT& yVec ) noexcept { this->m[4] = yVec.x; this->m[5] = yVec.y; this->m[6] = yVec.z; } template< typename T > void Aftr::Mat4T::setZ( const Aftr::VectorT& zVec ) noexcept { this->m[8] = zVec.x; this->m[9] = zVec.y; this->m[10] = zVec.z; } template< typename T > void Aftr::Mat4T::setXYZ( const VectorT& xCol, const VectorT& yCol, const VectorT& zCol ) noexcept { this->setX( xCol ); this->setY( yCol ); this->setZ( zCol ); } template< typename T > void Aftr::Mat4T::setPosition( const Aftr::VectorT& trans ) noexcept { this->m[12] = trans.x; this->m[13] = trans.y; this->m[14] = trans.z; } template< typename T > void Aftr::Mat4T::setVecCol( int i, const VectorT& vec ) noexcept { if( i < 0 || i > 3 ) { std::cout << "ERROR: " << AFTR_FILE_LINE_STR << ": cannot insert column " << i << ", out of bounds...\n"; std::abort(); } if( i == 0 ) return this->setX(vec); else if( i == 1 ) return this->setY(vec); else if( i == 2 ) return this->setZ(vec); else //( i == 3 ); return this->setPosition(vec); } template< typename T > void Aftr::Mat4T::setScale( const Aftr::VectorT& scale ) noexcept { this->m[0] = scale.x; this->m[5] = scale.y; this->m[10] = scale.z; } template< typename T > void Aftr::Mat4T::multScale(const Aftr::VectorT& scale) noexcept { this->m[0] *= scale.x; this->m[5] *= scale.y; this->m[10] *= scale.z; } template< typename T > T* Aftr::Mat4T::getPtr() noexcept { return this->m.data(); } template< typename T > const T* Aftr::Mat4T::getPtr() const noexcept { return this->m.data(); } template< typename T > T* Aftr::Mat4T::data() noexcept { return this->m.data(); } template< typename T > const T* Aftr::Mat4T::data() const noexcept { return this->m.data(); } template< typename T > Aftr::Mat4T Aftr::Mat4T::orthogonalize_3x3() const noexcept { return Aftr::Mat4Impl::orthogonalize_3x3( *this ); } template< typename T> Aftr::Mat4T Aftr::Mat4T::toRotationMat_fromSkewSymmetricMat() const noexcept { // | 0 -z y 0 | This is the expected form of the skew symmetric matrix. // | z 0 -x 0 | If the matrix does *not* have this form, this // | -y x 0 0 | function ***WILL NOT RETURN A VALID VALUE***. // | 0 0 0 1 | // | 0 4 8 12 | // | 1 5 9 13 | // | 2 6 10 14 | // | 3 7 11 15 | if( !this->isSkewSymmetric() ) { std::cout << "ERROR: " << AFTR_FILE_LINE_STR << ": Called on non-skewsymmetric matrix will produce garbage... Opting to crash here for fun...\n"; std::abort(); } T x( this->m[6] ); T y( this->m[8] ); T z( this->m[1] ); VectorT axis{ x,y,z }; if( axis.isEqual( { 0,0,0 } ) ) return {};//return identity if passed no angular rotation T mag_rad = axis.magnitude(); Mat4T r = Mat4T::rotateIdentityMat( axis.normalizeMe(), mag_rad ); return r; } template< typename T> Aftr::Mat4T Aftr::Mat4T::toSkewSymmetricMat_fromRotationMat() const noexcept { // | 0 -z y 0 | This is the expected form of the skew symmetric matrix. // | z 0 -x 0 | If the matrix does *not* have this form, this // | -y x 0 0 | function ***WILL NOT RETURN A VALID VALUE***. // | 0 0 0 1 | return this->toRodriguesVec_fromRotationMat().toSkewSymmetric3x3(); } template< typename T> Aftr::VectorT Aftr::Mat4T::toRodriguesVec_fromSkewSymmetricMat() const noexcept { // | 0 -z y 0 | This is the expected form of the skew symmetric matrix. // | z 0 -x 0 | If the matrix does *not* have this form, this // | -y x 0 0 | function ***WILL NOT RETURN A VALID VALUE***. // | 0 0 0 1 | // | 0 4 8 12 | // | 1 5 9 13 | // | 2 6 10 14 | // | 3 7 11 15 | if( !this->isSkewSymmetric() ) { std::cout << "ERROR: " << AFTR_FILE_LINE_STR << ": Called on non-skewsymmetric matrix will produce garbage... Opting to crash here for fun...\n"; std::abort(); } T x( this->m[6] ); T y( this->m[8] ); T z( this->m[1] ); return VectorT{x, y, z}; } template< typename T> bool Aftr::Mat4T::isSkewSymmetric() const noexcept { // | 0 -z y 0 | This is the expected form of the skew symmetric matrix. // | z 0 -x 0 | If the matrix does *not* have this form, this // | -y x 0 0 | function ***WILL NOT RETURN A VALID VALUE***. // | 0 0 0 1 | // | 0 4 8 12 | // | 1 5 9 13 | // | 2 6 10 14 | // | 3 7 11 15 | return Mat4Impl::isSkewSymmetric( *this ); } template< typename T> Aftr::VectorT Aftr::Mat4T::toRodriguesVec_fromRotationMat() const noexcept { // | 0 4 8 12 | // | 1 5 9 13 | // | 2 6 10 14 | // | 3 7 11 15 | return Mat4Impl::toRodriguesVec_fromRotationMat( *this ); //https://courses.cs.duke.edu/fall13/compsci527/notes/rodrigues.pdf //https://docs.opencv.org/3.4/d9/d0c/group__calib3d.html#ga61585db663d9da06b68e70cfbf6a1eac // | 0 -z y 0 | This is the expected form of the skew symmetric matrix. // | z 0 -x 0 | If the matrix does *not* have this form, this // | -y x 0 0 | function ***WILL NOT RETURN A VALID VALUE***. // | 0 0 0 1 | Mat4T A = ( (*this) - this->transpose4x4()) / T( 2.0 ); VectorT rho( A[6], A[8], A[1] ); //ax,ay,az from matrix A T s = rho.length(); //length in radians of rho T c = (m[0] + m[5] + m[10] - T( 1.0 )) / T( 2.0 ); //trace of original R matrix minus 1, divided by two if( std::abs( s ) <= T( 0.00001 ) && std::abs( c - T( 1.0 ) ) <= T( 0.00001 ) ) { //if s == 0 and c == 1, then r = 0 (rotMat is identity) VectorT r( 0, 0, 1 );//identity return r; //sln not thoroguly tested, if you hit this line of code, I bet your input is not a valid ortho DCM } //if( std::abs( s ) <= T( 0.00001 ) && // std::abs( c + T( 1.0 ) ) <= T( 0.00001 ) ) //{ //if s == 0 and c == -1, then r = 0 (rotMat is identity) // const Mat4T reflectionMat = ((*this) + Mat4T()); // VectorT u = reflectionMat.getX().normalizeMe(); // if( u.magnitudeSquared() == 0 ) //normalizeMe() returns EXACTLY 0,0,0 when vector is empty! // u = reflectionMat.getY().normalizeMe(); // if( u.magnitudeSquared() == 0 ) //normalizeMe() returns EXACTLY 0,0,0 when vector is empty! // u = reflectionMat.getZ().normalizeMe(); //Z *must* be non-zero if X and Y were zero // if( ( u.x == u.y && u.z < 0 ) || //flips the sign of u based on location in half-hemisphere // ( u.x == 0 && u.y < 0 ) || // ( u.x < 0 ) ) // { // u = -u; //sln not thoroguly tested, if you hit this line of code, I bet your input is not a valid ortho DCM // } // VectorT r = u * T( Aftr::PId ); //normalize unit length u to length PI // return r; //} //finally, if sin theta != 0 VectorT u = rho / s; T theta = std::atan2( s, c ); VectorT r = u * theta; return r; } template< typename T > T Aftr::Mat4T::getAngleOfRotationAboutAxisOfRotationRads() const noexcept { T cos_theta = (m[0] + m[5] + m[10] - T( 1.0 )) / T( 2.0 ); //trace of original R matrix minus 1, divided by two T theta = std::acos( cos_theta ); return theta; } template< typename T > Aftr::VectorT Aftr::Mat4T::getAxisOfRotation_fromRotationMat() const noexcept { return Aftr::Mat4Impl::getAxisOfRotation_fromRotationMat( *this ); } template< typename T > Aftr::AxisAngleT Aftr::Mat4T::toAxisAngle_fromRotationMat() const noexcept { return Aftr::Mat4Impl::toAxisAngle_fromRotationMat( *this ); } template< typename T > Aftr::AxisAngleT Aftr::Mat4T::toAxisAngle_fromSkewSymmetricMat() const noexcept { return Aftr::Mat4Impl::toAxisAngle_fromSkewSymmetricMat( *this ); } template Aftr::QuatT Aftr::Mat4T::toQuat_fromRotationMat() const noexcept { if( this->isSkewSymmetric() ) { std::cout << "ERROR: " << AFTR_FILE_LINE_STR << " This current Mat4 is *not* a rotation matrix -- it\n" "looks like a skew symmetric, perhaps you meant to call toQuat_fromSkewSymmetricMat() or try\n" "reorthogonalizing the Mat4 prior to calling this method... Perhaps numerical accuracy is\n" "creeping into this matrix... Gonna crash here for your inspection / callstack... Have fun...\n"; //matrix, this quaternion will not represent the proper orientation. std::abort(); } Aftr::QuatT q( *this ); return q; } template Aftr::QuatT Aftr::Mat4T::toQuat_fromSkewSymmetricMat() const noexcept { auto aa = this->toAxisAngle_fromSkewSymmetricMat(); Aftr::QuatT q( aa ); return q; } template< typename T > Aftr::VectorD Aftr::Mat4T::getRPY_deg_clockwise() const noexcept { return Aftr::Mat4Impl::getRPY_deg_clockwise( *this ); } template< typename T > Aftr::VectorD Aftr::Mat4T::getRPY_deg_counter_clockwise() const noexcept { return Aftr::Mat4Impl::getRPY_deg_counter_clockwise( *this ); } template< typename T > Aftr::Mat4T Aftr::Mat4T::fromRPY_deg_clockwise( const VectorT& rpyDeg ) noexcept { return Aftr::Mat4Impl::fromRPY_deg_clockwise( rpyDeg ); } template< typename T > Aftr::Mat4T Aftr::Mat4T::swizzle_upper_3x3( swizzle_to const swiz ) const noexcept { Mat4 cpy{ *this }; switch( swiz ) { case swizzle_to::xyz: cpy.setXYZ( getX(), getY(), getZ() ); return cpy; case swizzle_to::xzy: cpy.setXYZ( getX(), getZ(), getY() ); return cpy; case swizzle_to::yxz: cpy.setXYZ( getY(), getX(), getZ() ); return cpy; case swizzle_to::yzx: cpy.setXYZ( getY(), getZ(), getX() ); return cpy; case swizzle_to::zxy: cpy.setXYZ( getZ(), getX(), getY() ); return cpy; case swizzle_to::zyx: cpy.setXYZ( getZ(), getY(), getX() ); return cpy; default: std::cout << "ERROR: " << AFTR_FILE_LINE_STR << " INVALID swizzle_to param. Exiting. \n"; std::abort(); } }