/* LA: linear algebra C++ interface library Copyright (C) 2020 Jiri Pittner or This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "vecmat3.h" #ifndef QUAT_NO_RANDOM #include "la_random.h" #endif namespace LA_Vecmat3 { //http://en.wikipedia.org/wiki/Fast_inverse_square_root float fast_sqrtinv(float number ) { long i; float x2, y; const float threehalfs = 1.5F; x2 = number * 0.5F; y = number; i = * ( long * ) &y; // evil floating point bit level hacking i = 0x5f3759df - ( i >> 1 ); // what the fuck? y = * ( float * ) &i; y = y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration y = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration, this can be removed return y; } template<> Vec3 & Vec3::fast_normalize(void) {*this *= fast_sqrtinv(normsqr()); return *this;}; template Vec3& Vec3::fast_normalize(void) {normalize(); return *this;}; template Mat3 Vec3::outer(const Vec3 &rhs) const { Mat3 m; m[0][0]=q[0]*rhs.q[0]; m[0][1]=q[0]*rhs.q[1]; m[0][2]=q[0]*rhs.q[2]; m[1][0]=q[1]*rhs.q[0]; m[1][1]=q[1]*rhs.q[1]; m[1][2]=q[1]*rhs.q[2]; m[2][0]=q[2]*rhs.q[0]; m[2][1]=q[2]*rhs.q[1]; m[2][2]=q[2]*rhs.q[2]; return m; } template void Vec3::addouter(Mat3 &m, const Vec3 &rhs, const T weight) const { m[0][0]+=weight* q[0]*rhs.q[0]; m[0][1]+=weight* q[0]*rhs.q[1]; m[0][2]+=weight* q[0]*rhs.q[2]; m[1][0]+=weight* q[1]*rhs.q[0]; m[1][1]+=weight* q[1]*rhs.q[1]; m[1][2]+=weight* q[1]*rhs.q[2]; m[2][0]+=weight* q[2]*rhs.q[0]; m[2][1]+=weight* q[2]*rhs.q[1]; m[2][2]+=weight* q[2]*rhs.q[2]; } template void Vec3::inertia(Mat3 &m, const T weight) const { T r2 = q[0]*q[0]+q[1]*q[1]+q[2]*q[2]; T tmp; m[0][0] -= weight*(q[0]*q[0]-r2); tmp= weight*q[0]*q[1]; m[0][1] -= tmp; m[1][0] -= tmp; tmp = weight*q[0]*q[2]; m[0][2] -= tmp; m[2][0] -= tmp; m[1][1] -= weight*(q[1]*q[1]-r2); tmp = weight*q[1]*q[2]; m[1][2] -= tmp; m[2][1] -= tmp; m[2][2] -= weight*(q[2]*q[2]-r2); } template const Vec3 Vec3::operator*(const Mat3 &rhs) const { Vec3 r; r[0] = q[0]*rhs.q[0][0] + q[1]*rhs.q[1][0] + q[2]*rhs.q[2][0]; r[1] = q[0]*rhs.q[0][1] + q[1]*rhs.q[1][1] + q[2]*rhs.q[2][1]; r[2] = q[0]*rhs.q[0][2] + q[1]*rhs.q[1][2] + q[2]*rhs.q[2][2]; return r; }; template const Vec3 Vec3::timesT(const Mat3 &rhs) const { Vec3 r; r[0] = q[0]*rhs.q[0][0] + q[1]*rhs.q[0][1] + q[2]*rhs.q[0][2]; r[1] = q[0]*rhs.q[1][0] + q[1]*rhs.q[1][1] + q[2]*rhs.q[1][2]; r[2] = q[0]*rhs.q[2][0] + q[1]*rhs.q[2][1] + q[2]*rhs.q[2][2]; return r; }; template Mat3& Mat3::operator+=(const Mat3 &rhs) { q[0][0]+=rhs.q[0][0];q[0][1]+=rhs.q[0][1];q[0][2]+=rhs.q[0][2]; q[1][0]+=rhs.q[1][0];q[1][1]+=rhs.q[1][1];q[1][2]+=rhs.q[1][2]; q[2][0]+=rhs.q[2][0];q[2][1]+=rhs.q[2][1];q[2][2]+=rhs.q[2][2]; return *this; } template Mat3& Mat3::operator-=(const Mat3 &rhs) { q[0][0]-=rhs.q[0][0];q[0][1]-=rhs.q[0][1];q[0][2]-=rhs.q[0][2]; q[1][0]-=rhs.q[1][0];q[1][1]-=rhs.q[1][1];q[1][2]-=rhs.q[1][2]; q[2][0]-=rhs.q[2][0];q[2][1]-=rhs.q[2][1];q[2][2]-=rhs.q[2][2]; return *this; } template const Mat3 Mat3::operator*(const Mat3 &rhs) const { Mat3 r; r[0][0]= q[0][0]*rhs.q[0][0] + q[0][1]*rhs.q[1][0] + q[0][2]*rhs.q[2][0]; r[0][1]= q[0][0]*rhs.q[0][1] + q[0][1]*rhs.q[1][1] + q[0][2]*rhs.q[2][1]; r[0][2]= q[0][0]*rhs.q[0][2] + q[0][1]*rhs.q[1][2] + q[0][2]*rhs.q[2][2]; r[1][0]= q[1][0]*rhs.q[0][0] + q[1][1]*rhs.q[1][0] + q[1][2]*rhs.q[2][0]; r[1][1]= q[1][0]*rhs.q[0][1] + q[1][1]*rhs.q[1][1] + q[1][2]*rhs.q[2][1]; r[1][2]= q[1][0]*rhs.q[0][2] + q[1][1]*rhs.q[1][2] + q[1][2]*rhs.q[2][2]; r[2][0]= q[2][0]*rhs.q[0][0] + q[2][1]*rhs.q[1][0] + q[2][2]*rhs.q[2][0]; r[2][1]= q[2][0]*rhs.q[0][1] + q[2][1]*rhs.q[1][1] + q[2][2]*rhs.q[2][1]; r[2][2]= q[2][0]*rhs.q[0][2] + q[2][1]*rhs.q[1][2] + q[2][2]*rhs.q[2][2]; return r; } template const Mat3 Mat3::Ttimes(const Mat3 &rhs) const { Mat3 r; r[0][0]= q[0][0]*rhs.q[0][0] + q[1][0]*rhs.q[1][0] + q[2][0]*rhs.q[2][0]; r[0][1]= q[0][0]*rhs.q[0][1] + q[1][0]*rhs.q[1][1] + q[2][0]*rhs.q[2][1]; r[0][2]= q[0][0]*rhs.q[0][2] + q[1][0]*rhs.q[1][2] + q[2][0]*rhs.q[2][2]; r[1][0]= q[0][1]*rhs.q[0][0] + q[1][1]*rhs.q[1][0] + q[2][1]*rhs.q[2][0]; r[1][1]= q[0][1]*rhs.q[0][1] + q[1][1]*rhs.q[1][1] + q[2][1]*rhs.q[2][1]; r[1][2]= q[0][1]*rhs.q[0][2] + q[1][1]*rhs.q[1][2] + q[2][1]*rhs.q[2][2]; r[2][0]= q[0][2]*rhs.q[0][0] + q[1][2]*rhs.q[1][0] + q[2][2]*rhs.q[2][0]; r[2][1]= q[0][2]*rhs.q[0][1] + q[1][2]*rhs.q[1][1] + q[2][2]*rhs.q[2][1]; r[2][2]= q[0][2]*rhs.q[0][2] + q[1][2]*rhs.q[1][2] + q[2][2]*rhs.q[2][2]; return r; } template const Mat3 Mat3::timesT(const Mat3 &rhs) const { Mat3 r; r[0][0]= q[0][0]*rhs.q[0][0] + q[0][1]*rhs.q[0][1] + q[0][2]*rhs.q[0][2]; r[0][1]= q[0][0]*rhs.q[1][0] + q[0][1]*rhs.q[1][1] + q[0][2]*rhs.q[1][2]; r[0][2]= q[0][0]*rhs.q[2][0] + q[0][1]*rhs.q[2][1] + q[0][2]*rhs.q[2][2]; r[1][0]= q[1][0]*rhs.q[0][0] + q[1][1]*rhs.q[0][1] + q[1][2]*rhs.q[0][2]; r[1][1]= q[1][0]*rhs.q[1][0] + q[1][1]*rhs.q[1][1] + q[1][2]*rhs.q[1][2]; r[1][2]= q[1][0]*rhs.q[2][0] + q[1][1]*rhs.q[2][1] + q[1][2]*rhs.q[2][2]; r[2][0]= q[2][0]*rhs.q[0][0] + q[2][1]*rhs.q[0][1] + q[2][2]*rhs.q[0][2]; r[2][1]= q[2][0]*rhs.q[1][0] + q[2][1]*rhs.q[1][1] + q[2][2]*rhs.q[1][2]; r[2][2]= q[2][0]*rhs.q[2][0] + q[2][1]*rhs.q[2][1] + q[2][2]*rhs.q[2][2]; return r; } template const Mat3 Mat3::TtimesT(const Mat3 &rhs) const { Mat3 r; r[0][0]= q[0][0]*rhs.q[0][0] + q[1][0]*rhs.q[0][1] + q[2][0]*rhs.q[0][2]; r[0][1]= q[0][0]*rhs.q[1][0] + q[1][0]*rhs.q[1][1] + q[2][0]*rhs.q[1][2]; r[0][2]= q[0][0]*rhs.q[2][0] + q[1][0]*rhs.q[2][1] + q[2][0]*rhs.q[2][2]; r[1][0]= q[0][1]*rhs.q[0][0] + q[1][1]*rhs.q[0][1] + q[2][1]*rhs.q[0][2]; r[1][1]= q[0][1]*rhs.q[1][0] + q[1][1]*rhs.q[1][1] + q[2][1]*rhs.q[1][2]; r[1][2]= q[0][1]*rhs.q[2][0] + q[1][1]*rhs.q[2][1] + q[2][1]*rhs.q[2][2]; r[2][0]= q[0][2]*rhs.q[0][0] + q[1][2]*rhs.q[0][1] + q[2][2]*rhs.q[0][2]; r[2][1]= q[0][2]*rhs.q[1][0] + q[1][2]*rhs.q[1][1] + q[2][2]*rhs.q[1][2]; r[2][2]= q[0][2]*rhs.q[2][0] + q[1][2]*rhs.q[2][1] + q[2][2]*rhs.q[2][2]; return r; } template T Mat3::determinant() const { return q[0][0]*(q[2][2]*q[1][1]-q[2][1]*q[1][2])-q[1][0]*(q[2][2]*q[0][1]-q[2][1]*q[0][2])+q[2][0]*(q[1][2]*q[0][1]-q[1][1]*q[0][2]); } template void Mat3::transposeme() {T t; t=q[0][1]; q[0][1]=q[1][0]; q[1][0]=t; t=q[0][2]; q[0][2]=q[2][0]; q[2][0]=t; t=q[1][2]; q[1][2]=q[2][1]; q[2][1]=t;}; template const Mat3 Mat3::inverse(T *det) const { Mat3 r; r[0][0]= q[2][2]*q[1][1]-q[2][1]*q[1][2]; r[0][1]= -q[2][2]*q[0][1]+q[2][1]*q[0][2]; r[0][2]= q[1][2]*q[0][1]-q[1][1]*q[0][2]; r[1][0]= -q[2][2]*q[1][0]+q[2][0]*q[1][2]; r[1][1]= q[2][2]*q[0][0]-q[2][0]*q[0][2]; r[1][2]= -q[1][2]*q[0][0]+q[1][0]*q[0][2]; r[2][0]= q[2][1]*q[1][0]-q[2][0]*q[1][1]; r[2][1]= -q[2][1]*q[0][0]+q[2][0]*q[0][1]; r[2][2]= q[1][1]*q[0][0]-q[1][0]*q[0][1]; T d=determinant(); if(det) *det=d; return r/d; } template const Vec3 Mat3::linear_solve(const Vec3 &rhs, T *det) const { Vec3 r; r[0]= ( q[2][2]*q[1][1]-q[2][1]*q[1][2]) * rhs[0] +( -q[2][2]*q[0][1]+q[2][1]*q[0][2]) * rhs[1] +( +q[1][2]*q[0][1]-q[1][1]*q[0][2]) * rhs[2]; r[1]= (-q[2][2]*q[1][0]+q[2][0]*q[1][2]) * rhs[0] +( +q[2][2]*q[0][0]-q[2][0]*q[0][2]) * rhs[1] +( -q[1][2]*q[0][0]+q[1][0]*q[0][2]) * rhs[2]; r[2]= ( q[2][1]*q[1][0]-q[2][0]*q[1][1]) * rhs[0] +( -q[2][1]*q[0][0]+q[2][0]*q[0][1]) *rhs[1] +( q[1][1]*q[0][0]-q[1][0]*q[0][1]) * rhs[2]; T d=determinant(); if(det) *det=d; return r/d; } template const Vec3 Mat3::operator*(const Vec3 &rhs) const { Vec3 r; r[0] = q[0][0]*rhs.q[0] + q[0][1]*rhs.q[1] + q[0][2]*rhs.q[2]; r[1] = q[1][0]*rhs.q[0] + q[1][1]*rhs.q[1] + q[1][2]*rhs.q[2]; r[2] = q[2][0]*rhs.q[0] + q[2][1]*rhs.q[1] + q[2][2]*rhs.q[2]; return r; } template const Vec3 Mat3::Ttimes(const Vec3 &rhs) const { Vec3 r; r[0] = q[0][0]*rhs.q[0] + q[1][0]*rhs.q[1] + q[2][0]*rhs.q[2]; r[1] = q[0][1]*rhs.q[0] + q[1][1]*rhs.q[1] + q[2][1]*rhs.q[2]; r[2] = q[0][2]*rhs.q[0] + q[1][2]*rhs.q[1] + q[2][2]*rhs.q[2]; return r; } //cf. https://en.wikipedia.org/wiki/Euler_angles and NASA paper cited therein template void euler2rotmat(const T *eul, Mat3 &a, const char *type, bool transpose, bool direction, bool reverse) { T c2=cos(eul[1]); T s2=sin(eul[1]); T c1=cos(eul[reverse?2:0]); T s1=sin(eul[reverse?2:0]); T c3=cos(eul[reverse?0:2]); T s3=sin(eul[reverse?0:2]); if(direction) {s1= -s1; s2= -s2; s3= -s3;} switch(Euler_case(type[0],type[1],type[2])) { case Euler_case('x','z','x'): { a[0][0]= c2; a[0][1]= -c3*s2; a[0][2]= s2*s3; a[1][0]= c1*s2; a[1][1]= c1*c2*c3-s1*s3; a[1][2]= -c3*s1-c1*c2*s3; a[2][0]= s1*s2; a[2][1]= c1*s3+c2*c3*s1; a[2][2]= c1*c3-c2*s1*s3; } break; case Euler_case('x','y','x'): { a[0][0]= c2; a[0][1]= s2*s3; a[0][2]= c3*s2; a[1][0]= s1*s2; a[1][1]= c1*c3-c2*s1*s3; a[1][2]= -c1*s3-c2*c3*s1; a[2][0]= -c1*s2; a[2][1]= c3*s1+c1*c2*s3; a[2][2]= c1*c2*c3-s1*s3; } break; case Euler_case('y','x','y'): { a[0][0]= c1*c3-c2*s1*s3; a[0][1]= s1*s2; a[0][2]= c1*s3+c2*c3*s1; a[1][0]= s2*s3; a[1][1]= c2; a[1][2]= -c3*s2; a[2][0]= -c3*s1-c1*c2*s3; a[2][1]= c1*s2; a[2][2]= c1*c2*c3-s1*s3; } break; case Euler_case('y','z','y'): { a[0][0]= c1*c2*c3-s1*s3; a[0][1]= -c1*s2; a[0][2]= c3*s1+c1*c2*s3; a[1][0]= c3*s2; a[1][1]= c2; a[1][2]= s2*s3; a[2][0]= -c1*s3; a[2][1]= s1*s2; a[2][2]= c1*c3-c2*s1*s3; } break; case Euler_case('z','y','z'): { a[0][0]= c1*c2*c3-s1*s3; a[0][1]= -c3*s1-c1*c2*s3; a[0][2]= c1*s2; a[1][0]= c1*s3+c2*c3*s1; a[1][1]= c1*c3-c2*s1*s3; a[1][2]= s1*s2; a[2][0]= -c3*s2; a[2][1]= s2*s3; a[2][2]= c2; } break; case Euler_case('z','x','z'): { a[0][0]= c1*c3-c2*s1*s3; a[0][1]= -c1*s3-c2*c3*s1; a[0][2]= s1*s2; a[1][0]= c3*s1+c1*c2*s3; a[1][1]= c1*c2*c3-s1*s3; a[1][2]= -c1*s2; a[2][0]= s2*s3; a[2][1]= c3*s2; a[2][2]= c2; } break; case Euler_case('x','z','y'): { a[0][0]= c2*c3; a[0][1]= -s2; a[0][2]= c2*s3; a[1][0]= s1*s3+c1*c3*s2; a[1][1]= c1*c2; a[1][2]= c1*s2*s3-c3*s1; a[2][0]= c3*s1*s2-c1*s3; a[2][1]= c2*s1; a[2][2]= c1*c3+s1*s2*s3; } break; case Euler_case('x','y','z'): { a[0][0]= c2*c3; a[0][1]= -c2*s3; a[0][2]= s2; a[1][0]= c1*s3+c3*s1*s2; a[1][1]= c1*c3-s1*s2*s3; a[1][2]= -c2*s1; a[2][0]= s1*s3-c1*c3*s2; a[2][1]= c3*s1+c1*s2*s3; a[2][2]= c1*c2; } break; case Euler_case('y','x','z'): { a[0][0]= c1*c3+s1*s2*s3; a[0][1]= c3*s1*s2-c1*s3; a[0][2]= c2*s1; a[1][0]= c2*s3; a[1][1]= c2*c3; a[1][2]= -s2; a[2][0]= c1*s2*s3-c3*s1; a[2][1]= c1*c3*s2+s1*s3; a[2][2]= c1*c2; } break; case Euler_case('y','z','x'): { a[0][0]= c1*c2; a[0][1]= s1*s3-c1*c3*s2; a[0][2]= c3*s1+c1*s2*s3; a[1][0]= s2; a[1][1]= c2*c3; a[1][2]= -c2*s3; a[2][0]= -c2*s1; a[2][1]= c1*s3+c3*s1*s2; a[2][2]= c1*c3-s1*s2*s3; } break; case Euler_case('z','y','x'): { a[0][0]= c1*c2; a[0][1]= c1*s2*s3-c3*s1; a[0][2]= s1*s2+c1*c3*s2; a[1][0]= c2*s1; a[1][1]= c1*c3+s1*s2*s3; a[1][2]= c3*s1*s2-c1*s3; a[2][0]= -s2; a[2][1]= c2*s3; a[2][2]= c2*c3; } break; case Euler_case('z','x','y'): { a[0][0]= c1*c3-s1*s2*s3; a[0][1]= -c2*s1; a[0][2]= c1*s3+c3*s1*s2; a[1][0]= c3*s1+c1*s2*s3; a[1][1]= c1*c2; a[1][2]= s1*s3-c1*c3*s2; a[2][0]= -c2*s3; a[2][1]= s2; a[2][2]= c2*c3; } break; }//switch if(transpose) a.transposeme(); } template void rotmat2euler(T *eul, const Mat3 &a, const char *type, bool transpose, bool direction, bool reverse) { T m11=a[0][0]; T m22=a[1][1]; T m33=a[2][2]; T m12=transpose?a[1][0]:a[0][1]; T m21=transpose?a[0][1]:a[1][0]; T m13=transpose?a[2][0]:a[0][2]; T m31=transpose?a[0][2]:a[2][0]; T m23=transpose?a[2][1]:a[1][2]; T m32=transpose?a[1][2]:a[2][1]; switch(Euler_case(type[0],type[1],type[2])) { case Euler_case('x','z','x'): { eul[0]=atan2(m31,m21); eul[1]=atan2(sqrt(1-m11*m11),m11); eul[2]=atan2(m13,-m12); } break; case Euler_case('x','y','x'): { eul[0]=atan2(m21,-m31); eul[1]=atan2(sqrt(1-m11*m11),m11); eul[2]=atan2(m12,m13); } break; case Euler_case('y','x','y'): { eul[0]=atan2(m12,m32); eul[1]=atan2(sqrt(1-m22*m22),m22); eul[2]=atan2(m21,-m23); } break; case Euler_case('y','z','y'): { eul[0]=atan2(m32,-m12); eul[1]=atan2(sqrt(1-m22*m22),m22); eul[2]=atan2(m23,m21); } break; case Euler_case('z','y','z'): { eul[0]=atan2(m23,m13); eul[1]=atan2(sqrt(1-m33*m33),m33); eul[2]=atan2(m32,-m31); } break; case Euler_case('z','x','z'): { eul[0]=atan2(m13,-m23); eul[1]=atan2(sqrt(1-m33*m33),m33); eul[2]=atan2(m31,m32); } break; case Euler_case('x','z','y'): { eul[0]=atan2(m32,m22); eul[1]=atan2(-m12,sqrt(1-m12*m12)); eul[2]=atan2(m13,m11); } break; case Euler_case('x','y','z'): { eul[0]=atan2(-m23,m33); eul[1]=atan2(m13,sqrt(1-m13*m13)); eul[2]=atan2(-m12,m11); } break; case Euler_case('y','x','z'): { eul[0]=atan2(m31,m33); eul[1]=atan2(-m23,sqrt(1-m23*m23)); eul[2]=atan2(m21,m22); } break; case Euler_case('y','z','x'): { eul[0]=atan2(-m31,m11); eul[1]=atan2(m21,sqrt(1-m21*m21)); eul[2]=atan2(-m23,m22); } break; case Euler_case('z','y','x'): { eul[0]=atan2(m21,m11); eul[1]=atan2(-m31,sqrt(1-m31*m31)); eul[2]=atan2(m32,m33); } break; case Euler_case('z','x','y'): { eul[0]=atan2(-m12,m22); eul[1]=atan2(m32,sqrt(1-m32*m32)); eul[2]=atan2(-m31,m33); } break; }//switch if(reverse) { T t=eul[0]; eul[0]=eul[2]; eul[2]=t; } if(direction) { eul[0] *= (T)-1; eul[1] *= (T)-1; eul[2] *= (T)-1; } } //stream I/O #ifndef AVOID_STDSTREAM template std::istream& operator>>(std::istream &s, Vec3 &x) { s >> x.q[0]; s >> x.q[1]; s >> x.q[2]; return s; } template std::ostream& operator<<(std::ostream &s, const Vec3 &x) { s << x.q[0]<<" "; s << x.q[1]<<" "; s << x.q[2]; return s; } template std::istream& operator>>(std::istream &s, Mat3 &x) { s >> x.q[0][0]; s >> x.q[0][1]; s >> x.q[0][2]; s >> x.q[1][0]; s >> x.q[1][1]; s >> x.q[1][2]; s >> x.q[2][0]; s >> x.q[2][1]; s >> x.q[2][2]; return s; } template std::ostream& operator<<(std::ostream &s, const Mat3 &x) { s << x.q[0][0]<<" "<< x.q[0][1]<<" " << x.q[0][2]< void Mat3::symmetrize() { T tmp=(q[0][1]+q[1][0])/2; q[0][1]=q[1][0]=tmp; tmp=(q[0][2]+q[2][0])/2; q[0][2]=q[2][0]=tmp; tmp=(q[2][1]+q[1][2])/2; q[2][1]=q[1][2]=tmp; } #ifndef QUAT_NO_RANDOM template <> void Vec3::randomize(const double x) { for(int i=0;i<3;++i) q[i]=x*RANDDOUBLESIGNED(); } template <> void Mat3::randomize(const double x, const bool symmetric) { if(symmetric) for(int i=0;i<3;++i) for(int j=0; j<=i; ++j) q[j][i]=q[i][j]=x*RANDDOUBLESIGNED(); else for(int i=0;i<3;++i) for(int j=0; j<3; ++j) q[i][j]=x*RANDDOUBLESIGNED(); } #endif #if 0 //the Kopp's method is numerically unstable if the matrix is already close to diagonal //for example repeated diagonalization of inertia tensor and performing a body rotation //yields nonsense on the second run //I switched to Jacobi // //eigensolver for 3x3 matrix by Joachim Kopp - analytic formula version, //might be unstable for ill-conditioned ones, then use other methods //cf. arxiv physics 0610206v3 // //// Numerical diagonalization of 3x3 matrcies //// Copyright (C) 2006 Joachim Kopp //// ---------------------------------------------------------------------------- //// This library is free software; you can redistribute it and/or //// modify it under the terms of the GNU Lesser General Public //// License as published by the Free Software Foundation; either //// version 2.1 of the License, or (at your option) any later version. //// //// This library is distributed in the hope that it will be useful, //// but WITHOUT ANY WARRANTY; without even the implied warranty of //// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU //// Lesser General Public License for more details. //// //// You should have received a copy of the GNU Lesser General Public //// License along with this library; if not, write to the Free Software //// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA //// ---------------------------------------------------------------------------- // //numeric_limits not available on some crosscompilers for small MCUs #ifdef QUAT_NO_DOUBLE #define NO_NUMERIC_LIMITS #endif #define M_SQRT3 1.73205080756887729352744634151 // sqrt(3) #define SQR(x) ((x)*(x)) // x^2 // template static void eival_sym(const Mat3 &q, Vec3 &w, const bool sortdown) { T m, c1, c0; // Determine coefficients of characteristic poynomial. We write // | a d f | // A = | d* b e | // | f* e* c | T de = q[0][1] * q[1][2]; // d * e T dd = SQR(q[0][1]); // d^2 T ee = SQR(q[1][2]); // e^2 T ff = SQR(q[0][2]); // f^2 m = q[0][0] + q[1][1] + q[2][2]; c1 = (q[0][0]*q[1][1] + q[0][0]*q[2][2] + q[1][1]*q[2][2]) // a*b + a*c + b*c - d^2 - e^2 - f^2 - (dd + ee + ff); c0 = q[2][2]*dd + q[0][0]*ee + q[1][1]*ff - q[0][0]*q[1][1]*q[2][2] - 2.0 * q[0][2]*de; // c*d^2 + a*e^2 + b*f^2 - a*b*c - 2*f*d*e) T p, sqrt_p, Q, c, s, phi; p = SQR(m) - 3.0*c1; Q = m*(p - (3.0/2.0)*c1) - (27.0/2.0)*c0; sqrt_p = sqrt(abs(p)); phi = 27.0 * ( 0.25*SQR(c1)*(p - c1) + c0*(Q + 27.0/4.0*c0)); phi = (1.0/3.0) * atan2(sqrt(abs(phi)), Q); c = sqrt_p*cos(phi); s = (1.0/M_SQRT3)*sqrt_p*sin(phi); w[1] = (1.0/3.0)*(m - c); w[2] = w[1] + s; w[0] = w[1] + c; w[1] -= s; if(sortdown) { if(w[0]w[1]) {T tmp=w[0]; w[0]=w[1]; w[1]=tmp;} if(w[0]>w[2]) {T tmp=w[0]; w[0]=w[2]; w[2]=tmp;} if(w[1]>w[2]) {T tmp=w[1]; w[1]=w[2]; w[2]=tmp;} } } // Calculates the eigenvalues and normalized eigenvectors of a symmetric 3x3 // matrix A using Cardano's method for the eigenvalues and an analytical // method based on vector cross products for the eigenvectors. // Only the diagonal and upper triangular parts of A need to contain meaningful // values. However, all of A may be used as temporary storage and may hence be // destroyed. // ---------------------------------------------------------------------------- // Parameters: // A: The symmetric input matrix // Q: Storage buffer for eigenvectors // w: Storage buffer for eigenvalues // ---------------------------------------------------------------------------- // Return value: // 0: Success // -1: Error // ---------------------------------------------------------------------------- // Dependencies: // dsyevc3() // ---------------------------------------------------------------------------- // Version history: // v1.1 (12 Mar 2012): Removed access to lower triangualr part of A // (according to the documentation, only the upper triangular part needs // to be filled) // v1.0: First released version // ---------------------------------------------------------------------------- template bool Mat3::eivec_sym(Vec3 &w, Mat3 &v, const bool sortdown) const { T norm; // Squared norm or inverse norm of current eigenvector T n0, n1; // Norm of first and second columns of A T n0tmp, n1tmp; // "Templates" for the calculation of n0/n1 - saves a few FLOPS T thresh; // Small number used as threshold for floating point comparisons T error; // Estimated maximum roundoff error in some steps T wmax; // The eigenvalue of maximum modulus T f, t; // Intermediate storage int i, j; // Loop counters // Calculate eigenvalues eival_sym(*this, w,sortdown); Mat3 A(*this); //scratch copy wmax = abs(w[0]); if ((t=abs(w[1])) > wmax) wmax = t; if ((t=abs(w[2])) > wmax) wmax = t; thresh = SQR(8.0 * DBL_EPSILON * wmax); // Prepare calculation of eigenvectors n0tmp = SQR(A[0][1]) + SQR(A[0][2]); n1tmp = SQR(A[0][1]) + SQR(A[1][2]); v[0][1] = A[0][1]*A[1][2] - A[0][2]*A[1][1]; v[1][1] = A[0][2]*A[0][1] - A[1][2]*A[0][0]; v[2][1] = SQR(A[0][1]); // Calculate first eigenvector by the formula // v[0] = (A - w[0]).e1 x (A - w[0]).e2 A[0][0] -= w[0]; A[1][1] -= w[0]; v[0][0] = v[0][1] + A[0][2]*w[0]; v[1][0] = v[1][1] + A[1][2]*w[0]; v[2][0] = A[0][0]*A[1][1] - v[2][1]; norm = SQR(v[0][0]) + SQR(v[1][0]) + SQR(v[2][0]); n0 = n0tmp + SQR(A[0][0]); n1 = n1tmp + SQR(A[1][1]); error = n0 * n1; if (n0 <= thresh) // If the first column is zero, then (1,0,0) is an eigenvector { v[0][0] = 1.0; v[1][0] = 0.0; v[2][0] = 0.0; } else if (n1 <= thresh) // If the second column is zero, then (0,1,0) is an eigenvector { v[0][0] = 0.0; v[1][0] = 1.0; v[2][0] = 0.0; } else if (norm < SQR(64.0 * DBL_EPSILON) * error) { // If angle between A[0] and A[1] is too small, don't use t = SQR(A[0][1]); // cross product, but calculate v ~ (1, -A0/A1, 0) f = -A[0][0] / A[0][1]; if (SQR(A[1][1]) > t) { t = SQR(A[1][1]); f = -A[0][1] / A[1][1]; } if (SQR(A[1][2]) > t) f = -A[0][2] / A[1][2]; norm = 1.0/sqrt(1 + SQR(f)); v[0][0] = norm; v[1][0] = f * norm; v[2][0] = 0.0; } else // This is the standard branch { norm = sqrt(1.0 / norm); for (j=0; j < 3; j++) v[j][0] = v[j][0] * norm; } // Prepare calculation of second eigenvector t = w[0] - w[1]; if (abs(t) > 8.0 * DBL_EPSILON * wmax) { // For non-degenerate eigenvalue, calculate second eigenvector by the formula // v[1] = (A - w[1]).e1 x (A - w[1]).e2 A[0][0] += t; A[1][1] += t; v[0][1] = v[0][1] + A[0][2]*w[1]; v[1][1] = v[1][1] + A[1][2]*w[1]; v[2][1] = A[0][0]*A[1][1] - v[2][1]; norm = SQR(v[0][1]) + SQR(v[1][1]) + SQR(v[2][1]); n0 = n0tmp + SQR(A[0][0]); n1 = n1tmp + SQR(A[1][1]); error = n0 * n1; if (n0 <= thresh) // If the first column is zero, then (1,0,0) is an eigenvector { v[0][1] = 1.0; v[1][1] = 0.0; v[2][1] = 0.0; } else if (n1 <= thresh) // If the second column is zero, then (0,1,0) is an eigenvector { v[0][1] = 0.0; v[1][1] = 1.0; v[2][1] = 0.0; } else if (norm < SQR(64.0 * DBL_EPSILON) * error) { // If angle between A[0] and A[1] is too small, don't use t = SQR(A[0][1]); // cross product, but calculate v ~ (1, -A0/A1, 0) f = -A[0][0] / A[0][1]; if (SQR(A[1][1]) > t) { t = SQR(A[1][1]); f = -A[0][1] / A[1][1]; } if (SQR(A[1][2]) > t) f = -A[0][2] / A[1][2]; norm = 1.0/sqrt(1 + SQR(f)); v[0][1] = norm; v[1][1] = f * norm; v[2][1] = 0.0; } else { norm = sqrt(1.0 / norm); for (j=0; j < 3; j++) v[j][1] = v[j][1] * norm; } } else { // For degenerate eigenvalue, calculate second eigenvector according to // v[1] = v[0] x (A - w[1]).e[i] // // This would really get to complicated if we could not assume all of A to // contain meaningful values. A[1][0] = A[0][1]; A[2][0] = A[0][2]; A[2][1] = A[1][2]; A[0][0] += w[0]; A[1][1] += w[0]; for (i=0; i < 3; i++) { A[i][i] -= w[1]; n0 = SQR(A[0][i]) + SQR(A[1][i]) + SQR(A[2][i]); if (n0 > thresh) { v[0][1] = v[1][0]*A[2][i] - v[2][0]*A[1][i]; v[1][1] = v[2][0]*A[0][i] - v[0][0]*A[2][i]; v[2][1] = v[0][0]*A[1][i] - v[1][0]*A[0][i]; norm = SQR(v[0][1]) + SQR(v[1][1]) + SQR(v[2][1]); if (norm > SQR(256.0 * DBL_EPSILON) * n0) // Accept cross product only if the angle between { // the two vectors was not too small norm = sqrt(1.0 / norm); for (j=0; j < 3; j++) v[j][1] = v[j][1] * norm; break; } } } if (i == 3) // This means that any vector orthogonal to v[0] is an EV. { for (j=0; j < 3; j++) if (v[j][0] != 0.0) // Find nonzero element of v[0] ... { // ... and swap it with the next one norm = 1.0 / sqrt(SQR(v[j][0]) + SQR(v[(j+1)%3][0])); v[j][1] = v[(j+1)%3][0] * norm; v[(j+1)%3][1] = -v[j][0] * norm; v[(j+2)%3][1] = 0.0; break; } } } // Calculate third eigenvector according to // v[2] = v[0] x v[1] v[0][2] = v[1][0]*v[2][1] - v[2][0]*v[1][1]; v[1][2] = v[2][0]*v[0][1] - v[0][0]*v[2][1]; v[2][2] = v[0][0]*v[1][1] - v[1][0]*v[0][1]; return 0; } #undef SQR //end Kopp's eigensolver for 3x3 matrix ///////////////////////////////////////////////////////////////////////////////////////////// #else //begin Jacobi's eigensolver for 3x3 matrix #define ROTATE(a,i,j,k,l) g=a[i][j];h=a[k][l];a[i][j]=g-s*(h+g*tau); a[k][l]=h+s*(g-h*tau); #define SWAP(i,j) {T tmp; tmp=d[i]; d[i]=d[j]; d[j]=tmp; for(int ii=0; ii bool Mat3::eivec_sym(Vec3 &d, Mat3 &v, const bool sortdown) const { Mat3 a(*this);//will be overwritten but we are const const int n=3; int j,iq,ip,i; T tresh,theta,tau,t,sm,s,h,g,c; Vec3 b,z; for (ip=0;ipd[1]) SWAP(0,1) if(d[0]>d[2]) SWAP(0,2) if(d[1]>d[2]) SWAP(1,2) } return 0; } if (i < 4) tresh=0.2*sm/(n*n); else tresh=0.0; for (ip=0;ip 4 && abs(d[ip])+g == abs(d[ip]) && abs(d[iq])+g == abs(d[iq])) a[ip][iq]=0.0; else if (abs(a[ip][iq]) > tresh) { h=d[iq]-d[ip]; if (abs(h)+g == abs(h)) t=(a[ip][iq])/h; else { theta=0.5*h/(a[ip][iq]); t=1.0/(abs(theta)+sqrt(1.0+theta*theta)); if (theta < 0.0) t = -t; } c=1.0/sqrt(1+t*t); s=t*c; tau=s/(1.0+c); h=t*a[ip][iq]; z[ip] -= h; z[iq] += h; d[ip] -= h; d[iq] += h; a[ip][iq]=0.0; for (j=0;j<=ip-1;j++) { ROTATE(a,j,ip,j,iq) } for (j=ip+1;j<=iq-1;j++) { ROTATE(a,ip,j,j,iq) } for (j=iq+1;j T Mat3::norm(const T scalar) const { T sum(0); for(int i=0; i<3; i++) for(int j=0; j<3; j++) { T tmp = q[i][j]; if(i == j) tmp -= scalar; sum += tmp*tmp; } return sqrt(sum); } //efficient SVD for 3x3 matrices cf. Mc.Adams et al. UWM technical report 1690 (2011) // template static inline T ABS(const T a) { return a<0?-a:a; } template static inline T MAX(const T a, const T b) { return a>b ? a : b; } template void QRGivensQuaternion(T a1, T a2, T &ch, T &sh) { // a1 = pivot point on diagonal // a2 = lower triangular entry we want to annihilate T rho = sqrt(a1*a1 + a2*a2); sh = rho > DBL_EPSILON ? a2 : 0; ch = ABS(a1) + MAX(rho,DBL_EPSILON); if(a1 < 0) { T tmp=sh; sh=ch; ch=tmp; } T w = (T)1/sqrt(ch*ch+sh*sh); ch *= w; sh *= w; } template void Mat3::qrd(Mat3 &Q, Mat3 &R) { T ch1,sh1,ch2,sh2,ch3,sh3; T a,b; // first givens rotation (ch,0,0,sh) QRGivensQuaternion(q[0][0],q[1][0],ch1,sh1); a=1-2*sh1*sh1; b=2*ch1*sh1; // apply B = Q' * B R[0][0]= a*q[0][0]+b*q[1][0]; R[0][1]= a*q[0][1]+b*q[1][1]; R[0][2]= a*q[0][2]+b*q[1][2]; R[1][0]= -b*q[0][0]+a*q[1][0]; R[1][1]= -b*q[0][1]+a*q[1][1]; R[1][2]= -b*q[0][2]+a*q[1][2]; R[2][0]= q[2][0]; R[2][1]= q[2][1]; R[2][2]= q[2][2]; // second givens rotation (ch,0,-sh,0) QRGivensQuaternion(R[0][0],R[2][0],ch2,sh2); a= 1-2*sh2*sh2; b= 2*ch2*sh2; // apply B = Q' * B; q[0][0]= a*R[0][0]+b*R[2][0]; q[0][1]= a*R[0][1]+b*R[2][1]; q[0][2]= a*R[0][2]+b*R[2][2]; q[1][0]= R[1][0]; q[1][1]= R[1][1]; q[1][2]= R[1][2]; q[2][0]= -b*R[0][0]+a*R[2][0]; q[2][1]= -b*R[0][1]+a*R[2][1]; q[2][2]= -b*R[0][2]+a*R[2][2]; // third givens rotation (ch,sh,0,0) QRGivensQuaternion(q[1][1],q[2][1],ch3,sh3); a= 1-2*sh3*sh3; b= 2*ch3*sh3; // R is now set to desired value R[0][0]= q[0][0]; R[0][1]= q[0][1]; R[0][2]= q[0][2]; R[1][0]= a*q[1][0]+b*q[2][0]; R[1][1]= a*q[1][1]+b*q[2][1]; R[1][2]= a*q[1][2]+b*q[2][2]; R[2][0]= -b*q[1][0]+a*q[2][0]; R[2][1]= -b*q[1][1]+a*q[2][1]; R[2][2]= -b*q[1][2]+a*q[2][2]; // construct the cumulative rotation Q=Q1 * Q2 * Q3 // the number of floating point operations for three quaternion multiplications // is more or less comparable to the explicit form of the joined matrix. // certainly more memory-efficient! T sh12= sh1*sh1; T sh22= sh2*sh2; T sh32= sh3*sh3; Q[0][0]= (-1+2*sh12)*(-1+2*sh22); Q[0][1]= 4*ch2*ch3*(-1+2*sh12)*sh2*sh3+2*ch1*sh1*(-1+2*sh32); Q[0][2]= 4*ch1*ch3*sh1*sh3-2*ch2*(-1+2*sh12)*sh2*(-1+2*sh32); Q[1][0]= 2*ch1*sh1*(1-2*sh22); Q[1][1]= -8*ch1*ch2*ch3*sh1*sh2*sh3+(-1+2*sh12)*(-1+2*sh32); Q[1][2]= -2*ch3*sh3+4*sh1*(ch3*sh1*sh3+ch1*ch2*sh2*(-1+2*sh32)); Q[2][0]= 2*ch2*sh2; Q[2][1]= 2*ch3*(1-2*sh22)*sh3; Q[2][2]= (-1+2*sh22)*(-1+2*sh32); } template void Mat3::svd(Mat3 &u, Vec3 &w, Mat3 &v, bool proper_rotations) const { Mat3 ata= this->Ttimes(*this); Vec3 ww; //actually not needed squares of singular values ata.eivec_sym(ww,v,true); Mat3 uw = (*this)*v; Mat3 r; uw.qrd(u,r); w[0]= r[0][0]; w[1]= r[1][1]; w[2]= r[2][2]; if(proper_rotations) return; if(w[2]<0) { w[2] = -w[2]; u[0][2] = -u[0][2]; u[1][2] = -u[1][2]; u[2][2] = -u[2][2]; } } template void Mat3::diagmultl(const Vec3 &rhs) { for(int i=0; i<3; ++i) for(int j=0; j<3; ++j) q[i][j] *= rhs[i]; } template void Mat3::diagmultr(const Vec3 &rhs) { for(int i=0; i<3; ++i) for(int j=0; j<3; ++j) q[i][j] *= rhs[j]; } template const Mat3 Mat3::svdinverse(const T thr) const { Mat3 u,v; Vec3 w; svd(u,w,v); for(int i=0; i<3; ++i) w[i] = (w[i] void perspective(T *proj_xy, const Vec3 &point, const Mat3 &rot_angle, const Vec3 &camera, const Vec3 &plane_to_camera) { Vec3 d=rot_angle*point-camera; T scale = plane_to_camera[2]/d[2]; for(int i=0; i<2; ++i) proj_xy[i]= scale*d[i] + plane_to_camera[i]; } //force instantization #define INSTANTIZE(T) \ template class Vec3; \ template class Mat3; \ template void euler2rotmat(const T *eul, Mat3 &a, const char *type, bool transpose=0, bool direction=0, bool reverse=0); \ template void rotmat2euler(T *eul, const Mat3 &a, const char *type, bool transpose=0, bool direction=0, bool reverse=0); \ template void perspective(T *proj_xy, const Vec3 &point, const Mat3 &rot_angle, const Vec3 &camera, const Vec3 &plane_to_camera); \ #ifndef AVOID_STDSTREAM #define INSTANTIZE2(T) \ template std::istream& operator>>(std::istream &s, Vec3 &x); \ template std::ostream& operator<<(std::ostream &s, const Vec3 &x); \ template std::istream& operator>>(std::istream &s, Mat3 &x); \ template std::ostream& operator<<(std::ostream &s, const Mat3 &x); \ #endif INSTANTIZE(float) #ifndef QUAT_NO_DOUBLE INSTANTIZE(double) #endif #ifndef AVOID_STDSTREAM INSTANTIZE2(float) #ifndef QUAT_NO_DOUBLE INSTANTIZE2(double) #endif #endif }//namespace