// D E F I N E S /////////////////////////////////////////////////// // S T R U C T S /////////////////////////////////////////////////// // G L O B A L S /////////////////////////////////////////////////// namespace SEACAVE { // C L A S S ////////////////////////////////////////////////////// template inline TQuaternion::~TQuaternion() {} template inline TQuaternion::TQuaternion() : Base() {} template inline TQuaternion::TQuaternion(const Base& q) : Base(q) {} template inline TQuaternion::TQuaternion(TYPE i, TYPE j, TYPE k, TYPE r) : Base(cv::Vec(i, j, k, r)) {} template inline TQuaternion::TQuaternion(const Rot& R) : Base(((const TRMatrixBase&)R).GetQuaternion()) {} template inline void TQuaternion::Invert() { Base::operator()(0) *=-1; Base::operator()(1) *=-1; Base::operator()(2) *=-1; } template inline TQuaternion TQuaternion::Inverse() const { TQuaternion res(*this); res.Invert(); return res; } template inline void TQuaternion::MakeUnique() { //project quaternion to upper hemisphere (first ima.part >= 0) TYPE *qThis = this->val; if ((qThis[3] <= QUATERNION_EPSILON) &&(qThis[3] >= -QUATERNION_EPSILON)) { if ((qThis[1] <= QUATERNION_EPSILON) &&(qThis[1] >= -QUATERNION_EPSILON)) { if ((qThis[2] <= QUATERNION_EPSILON) &&(qThis[2] >= -QUATERNION_EPSILON)) { if (qThis[0] < 0.0) (*this) *= -1.0; } else if (qThis[2] < 0.0) { (*this) *= -1.0; } } else if (qThis[1] < 0.0) { (*this) *= -1.0; } } else if (qThis[3] < 0.0) { (*this) *= -1.0; } } template inline void TQuaternion::Mult(const TQuaternion &quat) { TYPE s1, s2, s3, s4, s5, s6, s7, s8, s9, t; TYPE *qThis = this->val; s1 = (qThis[2] - qThis[1]) * (quat[1] - quat[2]); s2 = (qThis[3] + qThis[0]) * (quat[3] + quat[0]); s3 = (qThis[3] - qThis[0]) * (quat[1] + quat[2]); s4 = (qThis[2] + qThis[1]) * (quat[3] - quat[0]); s5 = (qThis[2] - qThis[0]) * (quat[0] - quat[1]); s6 = (qThis[2] + qThis[0]) * (quat[0] + quat[1]); s7 = (qThis[3] + qThis[1]) * (quat[3] - quat[2]); s8 = (qThis[3] - qThis[1]) * (quat[3] + quat[2]); s9 = s6 + s7 + s8; t = (s5 + s9) / 2.0f; qThis[3] = s1 + t - s6; qThis[0] = s2 + t - s9; qThis[1] = s3 + t - s8; qThis[2] = s4 + t - s7; } template inline void TQuaternion::Mult(const TQuaternion &arg, TQuaternion &res) const { res = *this; res.Mult(arg); } template inline void TQuaternion::MultLeft(const TQuaternion &quatLeft) { TYPE s1, s2, s3, s4, s5, s6, s7, s8, s9, t; TYPE *qThis = this->val; s1 = (quatLeft[2] - quatLeft[1]) * (qThis[1] - qThis[2]); s2 = (quatLeft[3] + quatLeft[0]) * (qThis[3] + qThis[0]); s3 = (quatLeft[3] - quatLeft[0]) * (qThis[1] + qThis[2]); s4 = (quatLeft[2] + quatLeft[1]) * (qThis[3] - qThis[0]); s5 = (quatLeft[2] - quatLeft[0]) * (qThis[0] - qThis[1]); s6 = (quatLeft[2] + quatLeft[0]) * (qThis[0] + qThis[1]); s7 = (quatLeft[3] + quatLeft[1]) * (qThis[3] - qThis[2]); s8 = (quatLeft[3] - quatLeft[1]) * (qThis[3] + qThis[2]); s9 = s6 + s7 + s8; t = (s5 + s9) / 2.0f; qThis[3] = s1 + t - s6; qThis[0] = s2 + t - s9; qThis[1] = s3 + t - s8; qThis[2] = s4 + t - s7; } /** rotates the given Vector with the quaternion ( q v q* ) the resulting vector is given in res @returns 0 in case of no error @author Daniel Grest, June 2003 */ template inline int TQuaternion::MultVec(const Vec &vec, Vec &res) const { TYPE rx,ry,rz; TYPE Vx= vec[0], Vy=vec[1], Vz=vec[2]; const TYPE *q = this->val; { TYPE QwQx, QwQy, QwQz, QxQy, QxQz, QyQz; QwQx = q[3] * q[0]; QwQy = q[3] * q[1]; QwQz = q[3] * q[2]; QxQy = q[0] * q[1]; QxQz = q[0] * q[2]; QyQz = q[1] * q[2]; rx = 2* (Vy * (-QwQz + QxQy) + Vz *( QwQy + QxQz)); ry = 2* (Vx * ( QwQz + QxQy) + Vz *(-QwQx + QyQz)); rz = 2* (Vx * (-QwQy + QxQz) + Vy *( QwQx + QyQz)); } TYPE QwQw, QxQx, QyQy, QzQz; QwQw= q[3]*q[3]; QxQx = q[0]*q[0]; QyQy = q[1]*q[1]; QzQz= q[2]*q[2]; rx+= Vx * (QwQw + QxQx - QyQy - QzQz); ry+= Vy * (QwQw - QxQx + QyQy - QzQz); rz+= Vz * (QwQw - QxQx - QyQy + QzQz); res[0]=rx; res[1]=ry; res[2]=rz; return 0; } template inline typename TQuaternion::Vec TQuaternion::MultVec(const Vec &vec) const { Vec r; MultVec(vec,r); return r; } template inline void TQuaternion::SetIdentity() { Base::operator()(0) = Base::operator()(1) = Base::operator()(2) = 0; Base::operator()(3) = 1; } template inline void TQuaternion::SetQuaternion(TYPE real, TYPE i, TYPE j, TYPE k) { //Components are stored in Vec4 in order imaginary part 1,2,3 and real part this->Set(i,j,k,real); } template inline void TQuaternion::SetValueAsAxisRad(const Vec &axis, TYPE angle) { SetValueAsAxisRad(axis[0], axis[1], axis[2], angle); } template inline void TQuaternion::SetValueAsAxisRad(TYPE x, TYPE y, TYPE z, TYPE w) { TYPE rTmp = TYPE(sqrt(x * x + y * y + z * z)); if(rTmp > QUATERNION_EPSILON) { rTmp = TYPE(sin(w / 2.0f)) / rTmp; Base::operator()(0) = x * rTmp; Base::operator()(1) = y * rTmp; Base::operator()(2) = z * rTmp; Base::operator()(3) = TYPE(cos(w / 2.0f)); } else { SetIdentity(); } } template typename TQuaternion::Mat TQuaternion::GetQuaternionMultMatrixLeft() const { Mat res; res(0,0) = (*this)[3]; res(0,1) = -(*this)[2]; res(0,2) = (*this)[1]; res(0,3) = (*this)[0]; res(1,0) = (*this)[2]; res(1,1) = (*this)[3]; res(1,2) = -(*this)[0]; res(1,3) = (*this)[1]; res(2,0) = -(*this)[1]; res(2,1) = (*this)[0]; res(2,2) = (*this)[3]; res(2,3) = (*this)[2]; res(3,0) = -(*this)[0]; res(3,1) = -(*this)[1]; res(3,2) = -(*this)[2]; res(3,3) = (*this)[3]; return res; } template typename TQuaternion::Mat TQuaternion::GetQuaternionMultMatrixRight() const { Mat res; res(0,0) = (*this)[3]; res(0,1) = (*this)[2]; res(0,2) = -(*this)[1]; res(0,3) = (*this)[0]; res(1,0) = -(*this)[2]; res(1,1) = (*this)[3]; res(1,2) = (*this)[0]; res(1,3) = (*this)[1]; res(2,0) = (*this)[1]; res(2,1) = -(*this)[0]; res(2,2) = (*this)[3]; res(2,3) = (*this)[2]; res(3,0) = -(*this)[0]; res(3,1) = -(*this)[1]; res(3,2) = -(*this)[2]; res(3,3) = (*this)[3]; return res; } template int TQuaternion::GetAxisAngle(Vec& axis, TYPE& angle) const { // nicked from OpenSG int res=0; Vec q(cv::Vec((*this)[0], (*this)[1], (*this)[2])); TYPE len = TYPE(norm(q)); if(len > QUATERNION_EPSILON) { q *= TYPE(1.0) / len; axis[0] = q[0]; axis[1] = q[1]; axis[2] = q[2]; angle = TYPE(2.0 * acos((*this)[3])); } else { axis[0] = 0.0; axis[1] = 0.0; axis[2] = 1.0; angle = 0.0; } return res; } template typename TQuaternion::Vec TQuaternion::GetRotationAxis() const { Vec r(cv::Vec((*this)[0], (*this)[1], (*this)[2])); TYPE len = TYPE(norm(r)); if (len > QUATERNION_EPSILON) return r * (1.0 / len); else return Vec(cv::Vec(0,0,1)); } template TYPE TQuaternion::GetRotationAngle() const { const TYPE angle = TYPE(2 * acos((*this)[3])); return std::isnan(angle) && ABS(ABS((*this)[3]) - TYPE(1)) < QUATERNION_EPSILON ? TYPE(0) : angle; } template TQuaternion TQuaternion::Power(const TYPE & scale) const { TQuaternion res = *this; if ((scale < 1e-7) && (scale > -1e-7)) { //scale near zero -> return identity res.SetIdentity(); } else if ((res[3] > 0.999999) || (res[3] < -0.999999)) { //this is near identity (numerical unstable) -> return identity //res = *this } else { //else use slerp interpolation between identity and this with scale TYPE temp1 = sin(scale * acos(res[3]))/sin(acos(res[3])); TYPE res3 = res[3]; res *= temp1; res[3] += TYPE(sin((1.0-scale) * acos(res3))/sin(acos(res3))); } return res; } template TQuaternion TQuaternion::InterpolateLinear(const TQuaternion &to, const TYPE & t) const { BIASASSERT(t >= 0.0 && t <= 1.0); TQuaternion p = *this; TQuaternion q = to; //p.Normalize(); //q.Normalize(); if (t < 1e-7) { //t near zero -> return p return p; } else if (1.0-t < 1e-7) { //t near one -> return q return q; } //else use linear interpolation between this and q TQuaternion res; const TYPE cosPhi = (TYPE)p.ScalarProduct(q); const TYPE a = (TYPE)1.0 - t; const TYPE b = (cosPhi < 0 ? -t : t); res[0] = a*p[0] + b*q[0]; res[1] = a*p[1] + b*q[1]; res[2] = a*p[2] + b*q[2]; res[3] = a*p[3] + b*q[3]; res.Normalize(); return res; } template TQuaternion TQuaternion:: Interpolate(const TQuaternion &to, const TYPE & t) const { BIASASSERT(t >= 0.0 && t <= 1.0); TQuaternion p = *this; TQuaternion q = to; //p.Normalize(); //q.Normalize(); if (t < 1e-7) { //t near zero -> return p return p; } else if (1.0-t < 1e-7) { //t near one -> return q return q; } TQuaternion res; TYPE a, b; TYPE cosPhi = (TYPE)p.ScalarProduct(q); //adjust angle if necessary if (cosPhi < 0) { q.MultiplyIP(-1.0); cosPhi = -cosPhi; } if (cosPhi > 0.9999 || cosPhi < -0.9999) { // p is very close to q, do linear interpolation instead of slerp a = (TYPE)1.0 - t; b = (cosPhi < 0 ? -t : t); } else { //else use slerp interpolation between p and q const TYPE phi = acos(cosPhi < 0 ? -cosPhi : cosPhi); const TYPE sinPhi = sin(phi); a = sin(((TYPE)1.0 - t) * phi) / sinPhi; b = sin(t * phi) / sinPhi; if (cosPhi < 0) b = -b; } res[0] = a*p[0] + b*q[0]; res[1] = a*p[1] + b*q[1]; res[2] = a*p[2] + b*q[2]; res[3] = a*p[3] + b*q[3]; res.Normalize(); return res; } template int TQuaternion::SetXYZ(TYPE radX, TYPE radY, TYPE radZ) { TQuaternion q_x; TQuaternion q_y; TQuaternion q_z; q_x.SetValueAsAxisRad( 1.,0.,0., radX ); q_y.SetValueAsAxisRad( 0.,1.,0., radY ); q_z.SetValueAsAxisRad( 0.,0.,1., radZ ); q_x.Mult(q_y); q_x.Mult(q_z); *this = q_x; return 0; } template int TQuaternion::SetZYX(TYPE radX, TYPE radY, TYPE radZ) { TQuaternion q_x; TQuaternion q_y; TQuaternion q_z; q_x.SetValueAsAxisRad( 1.,0.,0., radX ); q_y.SetValueAsAxisRad( 0.,1.,0., radY ); q_z.SetValueAsAxisRad( 0.,0.,1., radZ ); q_z.Mult(q_y); q_z.Mult(q_x); *this = q_z; return 0; } template TQuaternion& TQuaternion::operator=(const TQuaternion& vec) { memcpy((void *)val, (void *)vec.val, sizeof(TQuaternion)); return *this; } template void TQuaternion::EnforceRigidCouplingConstraint(TQuaternion &other) { // @todo check unit norm constraint of this and other quaternion!! // make this and other quaternion unique MakeUnique(); other.MakeUnique(); // get aliases for both quaternions TQuaternion &q0 = *this, &q1 = other; // interpolate equal angle q0[3] = q1[3] = TYPE(0.5) * (q0[3] + q1[3]); // re-enforce unit length constraint for q0 and q1 TYPE q0norm = q0[0]*q0[0] + q0[1]*q0[1] + q0[2]*q0[2]; TYPE q1norm = q1[0]*q1[0] + q1[1]*q1[1] + q1[2]*q1[2]; if (q0norm > TYPE(1e-8) && q1norm > TYPE(1e-8)) { TYPE qscale0 = (TYPE)sqrt(double((1-q0[3]*q0[3])/q0norm)); TYPE qscale1 = (TYPE)sqrt(double((1-q1[3]*q1[3])/q1norm)); for (int l = 0; l < 3; l++) { q0[l] *= qscale0; q1[l] *= qscale1; } } // @todo check unit norm constraint of resulting quaternions!! } template void TQuaternion::EnforceRigidCouplingConstraint(std::vector > &quats) { // @todo check unit norm constraint of all quaternions!! // make all quaternions unique const int n = quats.size(); for (int i = 0; i < n; i++) quats[i].MakeUnique(); // interpolate equal angle double a = 0; for (int i = 0; i < n; i++) a += quats[i][3]; a /= TYPE(n); for (int i = 0; i < n; i++) quats[i][3] = a; // re-enforce unit length constraint for all quaternions for (int i = 0; i < n; i++) { TQuaternion &q = quats[i]; TYPE qnorm = q[0]*q[0] + q[1]*q[1] + q[2]*q[2]; if (qnorm > TYPE(1e-8)) { TYPE qscale = (TYPE)sqrt(double((1-q[3]*q[3])/qnorm)); for (int l = 0; l < 3; l++) q[l] *= qscale; } } // @todo check unit norm constraint of resulting quaternions!! } /*----------------------------------------------------------------*/ // C L A S S ////////////////////////////////////////////////////// // accuracy for constraint check in Check() function calls #define R_CONSTRAINT_ACCURACY 1e-5 //ZEROTOLERANCE() // if rotation angle is below this value, assume zero rotation #define MINIMUM_ROTATION 1e-7 template inline TRMatrixBase::TRMatrixBase() : Base(Base::IDENTITY) {} template inline TRMatrixBase::TRMatrixBase(const Mat& mat) : Base(mat) { ASSERT(Check(R_CONSTRAINT_ACCURACY)); } template inline TRMatrixBase::TRMatrixBase(const Vec& w, TYPE phi) { Set(w, phi); } template inline TRMatrixBase::TRMatrixBase(const Vec& rot) { SetRotationAxisAngle(rot); } template inline TRMatrixBase::TRMatrixBase(const Quat& q) { SetFromQuaternion(q); } template inline TRMatrixBase::TRMatrixBase(TYPE roll, TYPE pitch, TYPE yaw) { SetXYZ(roll, pitch, yaw); } template inline TRMatrixBase::TRMatrixBase(const Vec& dir0, const Vec& dir1) { SetFromDir2Dir(dir0, dir1); } template void TRMatrixBase::SetXYZ(TYPE PhiX, TYPE PhiY, TYPE PhiZ) { #if 0 TRMatrixBase Rx(TRMatrixBase::IDENTITY); TRMatrixBase Ry(TRMatrixBase::IDENTITY); TRMatrixBase Rz(TRMatrixBase::IDENTITY); /* set Rz, Ry, Rx as rotation matrices */ Rz(0,0) = cos(PhiZ); Rz(0,1) = -sin(PhiZ); Rz(1,0) = -Rz(0,1); Rz(1,1) = Rz(0,0); //Rz(2,2) = 1; Ry(0,0) = cos(PhiY); Ry(0,2) = sin(PhiY); //Ry(1,1) = 1; Ry(2,0) = -Ry(0,2); //-sin(PhiY); Ry(2,2) = Ry(0,0); //cos(PhiY); //Rx(0,0) = 1; Rx(1,1) = cos(PhiX); Rx(1,2) = -sin(PhiX); Rx(2,1) = -Rx(1,2); //sin(PhiX); Rx(2,2) = Rx(1,1); //cos(PhiX); *this = Rx*Ry*Rz; #else const TYPE sin_x = sin(PhiX); const TYPE sin_y = sin(PhiY); const TYPE sin_z = sin(PhiZ); const TYPE cos_x = cos(PhiX); const TYPE cos_y = cos(PhiY); const TYPE cos_z = cos(PhiZ); val[0] = cos_y * cos_z; val[1] = -cos_y * sin_z; val[2] = sin_y; val[3] = cos_x * sin_z + cos_z * sin_x * sin_y; val[4] = cos_x * cos_z - sin_x * sin_y * sin_z; val[5] = -cos_y * sin_x; val[6] = sin_x * sin_z - cos_x * cos_z * sin_y; val[7] = cos_z * sin_x + cos_x * sin_y * sin_z; val[8] = cos_x * cos_y; #endif } template void TRMatrixBase::SetZYX(TYPE PhiX, TYPE PhiY, TYPE PhiZ) { TRMatrixBase Rx(TRMatrixBase::IDENTITY); TRMatrixBase Ry(TRMatrixBase::IDENTITY); TRMatrixBase Rz(TRMatrixBase::IDENTITY); /* set Rz, Ry, Rx as rotation matrices */ Rz(0,0) = cos(PhiZ); Rz(0,1) = -sin(PhiZ); Rz(1,0) = -Rz(0,1); Rz(1,1) = Rz(0,0); //Rz(2,2) = 1; Ry(0,0) = cos(PhiY); Ry(0,2) = sin(PhiY); //Ry(1,1) = 1; Ry(2,0) = -Ry(0,2); //-sin(PhiY); Ry(2,2) = Ry(0,0); //cos(PhiY); //Rx(0,0) = 1; Rx(1,1) = cos(PhiX); Rx(1,2) = -sin(PhiX); Rx(2,1) = -Rx(1,2); //sin(PhiX); Rx(2,2) = Rx(1,1); //cos(PhiX); *this = Rz*Ry*Rx; } template void TRMatrixBase::SetYXZ(TYPE PhiY, TYPE PhiX, TYPE PhiZ) { TRMatrixBase Rx(TRMatrixBase::IDENTITY); TRMatrixBase Ry(TRMatrixBase::IDENTITY); TRMatrixBase Rz(TRMatrixBase::IDENTITY); /* set Rz, Ry, Rx as rotation matrices */ Rz(0,0) = cos(PhiZ); Rz(0,1) = -sin(PhiZ); Rz(1,0) = -Rz(0,1); Rz(1,1) = Rz(0,0); Ry(0,0) = cos(PhiY); Ry(0,2) = sin(PhiY); Ry(2,0) = -Ry(0,2); //-sin(PhiY); Ry(2,2) = Ry(0,0); //cos(PhiY); Rx(1,1) = cos(PhiX); Rx(1,2) = -sin(PhiX); Rx(2,1) = -Rx(1,2); //sin(PhiX); Rx(2,2) = Rx(1,1); //cos(PhiX); *this = Ry*Rx*Rz; } template void TRMatrixBase::SetZXY(TYPE PhiX, TYPE PhiY, TYPE PhiZ) { TRMatrixBase Rx(TRMatrixBase::IDENTITY); TRMatrixBase Ry(TRMatrixBase::IDENTITY); TRMatrixBase Rz(TRMatrixBase::IDENTITY); /* set Rz, Ry, Rx as rotation matrices */ Rz(0,0) = cos(PhiZ); Rz(0,1) = -sin(PhiZ); Rz(1,0) = -Rz(0,1); Rz(1,1) = Rz(0,0); Ry(0,0) = cos(PhiY); Ry(0,2) = sin(PhiY); Ry(2,0) = -Ry(0,2); //-sin(PhiY); Ry(2,2) = Ry(0,0); //cos(PhiY); Rx(1,1) = cos(PhiX); Rx(1,2) = -sin(PhiX); Rx(2,1) = -Rx(1,2); //sin(PhiX); Rx(2,2) = Rx(1,1); //cos(PhiX); *this = Rz*Rx*Ry; } template void TRMatrixBase::Set(const Vec& wa, TYPE phi) { // zero rotation results in identity matrix if (ISZERO(phi)) { *this = Base::IDENTITY; return; } const TYPE wnorm(norm(wa)); if (wnorm < std::numeric_limits::epsilon()) { CPC_ERROR("Vector "< void TRMatrixBase::SetFromColumnVectors(const Vec& v0, const Vec& v1, const Vec& v2) { val[0] = v0[0]; val[1] = v1[0]; val[2] = v2[0]; val[3] = v0[1]; val[4] = v1[1]; val[5] = v2[1]; val[6] = v0[2]; val[7] = v1[2]; val[8] = v2[2]; ASSERT(Check(R_CONSTRAINT_ACCURACY)); } template void TRMatrixBase::SetFromRowVectors(const Vec& v0, const Vec& v1, const Vec& v2) { val[0] = v0[0]; val[1] = v0[1]; val[2] = v0[2]; val[3] = v1[0]; val[4] = v1[1]; val[5] = v1[2]; val[6] = v2[0]; val[7] = v2[1]; val[8] = v2[2]; ASSERT(Check(R_CONSTRAINT_ACCURACY)); } template void TRMatrixBase::SetFromAxisAngle(const Vec& w) { #if 0 const TYPE dAngle(norm(w)); Set(w*INVERT(dAngle), dAngle); #else SetRotationAxisAngle(w); #endif } template void TRMatrixBase::GetQuaternion(Quat& quat) const { ASSERT(Check(R_CONSTRAINT_ACCURACY)); #if 1 // Conversion method from OpenSG // @attention Removed old conversion in Linux build which had numerical // issues for rotations close to 180 degree! (esquivel 03/2011) TYPE tr = Base::operator()(0,0) + Base::operator()(1,1) + Base::operator()(2,2); if (tr > 0.0) { // Use default formula if trace is large enough TYPE s = sqrt(tr + 1.0); quat[3] = s * 0.5; s *= 2.0; quat[0] = (Base::operator()(2,1) - Base::operator()(1,2)) / s; quat[1] = (Base::operator()(0,2) - Base::operator()(2,0)) / s; quat[2] = (Base::operator()(1,0) - Base::operator()(0,1)) / s; } else { // Use alternate formula using largest trace element TYPE s; unsigned i; unsigned j; unsigned k; // Find largest trace element i and its successors j, k if(Base::operator()(1,1) > Base::operator()(0,0)) i = 1; else i = 0; if(Base::operator()(2,2) > Base::operator()(i,i)) i = 2; j = (i + 1) % 3; k = (j + 1) % 3; // Compute discriminator using largest trace element s = sqrt(Base::operator()(i,i) - (Base::operator()(j,j) + Base::operator()(k,k)) + 1.0); // Compute quaternion entries quat[i] = s * 0.5; s *= 2.0; quat[j] = (Base::operator()(i,j) + Base::operator()(j,i)) / s; quat[k] = (Base::operator()(i,k) + Base::operator()(k,i)) / s; quat[3] = (Base::operator()(k,j) - Base::operator()(j,k) ) / s; } #elif 1 // Conversion method using derivation depending on max. denominator // @see http://en.wikipedia.org/wiki/Rotation_representation_%28mathematics%29#Rotation_matrix_.E2.86.94_quaternion // @deprecated // Determine max. denominator TYPE d[4] = { 1 + Base::operator()(0,0) - Base::operator()(1,1) - Base::operator()(2,2), 1 - Base::operator()(0,0) + Base::operator()(1,1) - Base::operator()(2,2), 1 - Base::operator()(0,0) - Base::operator()(1,1) + Base::operator()(2,2), 1 + Base::operator()(0,0) + Base::operator()(1,1) + Base::operator()(2,2) }; int maxidx = 0; for (int i = 1; i < 4; i++) { if (d[maxidx] < d[i]) maxidx = i; } // Compute quaternion starting with max. denominator if (maxidx == 0) { quat[0]=0.5*sqrt(1 + Base::operator()(0,0) - Base::operator()(1,1) - Base::operator()(2,2)); quat[3]=0.25*(Base::operator()(2,1)-Base::operator()(1,2))/quat[0]; quat[1]=0.25*(Base::operator()(0,1)+Base::operator()(1,0))/quat[0]; quat[2]=0.25*(Base::operator()(0,2)+Base::operator()(2,0))/quat[0]; } else if (maxidx == 1) { quat[1]=0.5*sqrt(1 - Base::operator()(0,0) + Base::operator()(1,1) - Base::operator()(2,2)); quat[3]=0.25*(Base::operator()(0,2)-Base::operator()(2,0))/quat[1]; quat[2]=0.25*(Base::operator()(1,2)+Base::operator()(2,1))/quat[1]; quat[0]=0.25*(Base::operator()(1,0)+Base::operator()(0,1))/quat[1]; } else if (maxidx == 2) { quat[2]=0.5*sqrt(1 - Base::operator()(0,0) - Base::operator()(1,1) + Base::operator()(2,2)); quat[3]=0.25*(Base::operator()(1,0)-Base::operator()(0,1))/quat[2]; quat[0]=0.25*(Base::operator()(2,0)+Base::operator()(0,2))/quat[2]; quat[1]=0.25*(Base::operator()(2,1)+Base::operator()(1,2))/quat[2]; } else { quat[3]=0.5*sqrt(1 + Base::operator()(0,0) + Base::operator()(1,1) + Base::operator()(2,2)); quat[0]=0.25*(Base::operator()(2,1)-Base::operator()(1,2))/quat[3]; quat[1]=0.25*(Base::operator()(0,2)-Base::operator()(2,0))/quat[3]; quat[2]=0.25*(Base::operator()(1,0)-Base::operator()(0,1))/quat[3]; } #elif 1 // More compact implementation of the conversion method from OpenSG // @deprecated TYPE trace = Base::operator()(0,0) + Base::operator()(1,1) + Base::operator()(2,2); if ( trace > 0 ) { TYPE s = 2.0 * sqrt(trace + 1.0); quat[3] = 0.25 * s; quat[0] = ( Base::operator()(2,1) - Base::operator()(1,2) ) / s; quat[1] = ( Base::operator()(0,2) - Base::operator()(2,0) ) / s; quat[2] = ( Base::operator()(1,0) - Base::operator()(0,1) ) / s; } else { if ( Base::operator()(0,0) > Base::operator()(1,1) && Base::operator()(0,0) > Base::operator()(2,2) ) { TYPE s = 2.0 * sqrt( 1.0 + Base::operator()(0,0) - Base::operator()(1,1) - Base::operator()(2,2)); quat[3] = (Base::operator()(2,1) - Base::operator()(1,2) ) / s; quat[0] = 0.25 * s; quat[1] = (Base::operator()(0,1) + Base::operator()(1,0) ) / s; quat[2] = (Base::operator()(0,2) + Base::operator()(2,0) ) / s; } else if (Base::operator()(1,1) > Base::operator()(2,2)) { TYPE s = 2.0 * sqrt( 1.0 + Base::operator()(1,1) - Base::operator()(0,0) - Base::operator()(2,2)); quat[3] = (Base::operator()(0,2) - Base::operator()(2,0) ) / s; quat[0] = (Base::operator()(0,1) + Base::operator()(1,0) ) / s; quat[1] = 0.25 * s; quat[2] = (Base::operator()(1,2) + Base::operator()(2,1) ) / s; } else { TYPE s = 2.0 * sqrt( 1.0 + Base::operator()(2,2) - Base::operator()(0,0) - Base::operator()(1,1) ); quat[3] = (Base::operator()(1,0) - Base::operator()(0,1) ) / s; quat[0] = (Base::operator()(0,2) + Base::operator()(2,0) ) / s; quat[1] = (Base::operator()(1,2) + Base::operator()(2,1) ) / s; quat[2] = 0.25 * s; } } #else // Old conversion method // @see http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm // @deprecated // @attention Computes wrong quaternion for rotation close to 180 degree! quat[3] = sqrt( max( 0.0, 1.0 + Base::operator()(0,0) + Base::operator()(1,1) + Base::operator()(2,2) ) ) / 2.0; quat[0] = sqrt( max( 0.0, 1.0 + Base::operator()(0,0) - Base::operator()(1,1) - Base::operator()(2,2) ) ) / 2.0; quat[1] = sqrt( max( 0.0, 1.0 - Base::operator()(0,0) + Base::operator()(1,1) - Base::operator()(2,2) ) ) / 2.0; quat[2] = sqrt( max( 0.0, 1.0 - Base::operator()(0,0) - Base::operator()(1,1) + Base::operator()(2,2) ) ) / 2.0; quat[0] = copysign( quat[0], Base::operator()(2,1) - Base::operator()(1,2) ) ; quat[1] = copysign( quat[1], Base::operator()(0,2) - Base::operator()(2,0) ) ; quat[2] = copysign( quat[2], Base::operator()(1,0) - Base::operator()(0,1) ) ; #endif // Normalize resulting quaternion quat.Normalize(); // Correct quaternion so that scalar part is non-negative if (quat[3] < 0) quat *= -1.0; } template void TRMatrixBase::GetRotationAxisAngle(Vec& Axis, TYPE& Angle) const { // nicked from OpenSG GetQuaternion().GetAxisAngle(Axis, Angle); } template typename TRMatrixBase::Vec TRMatrixBase::GetRotationAxis() const { Vec Axis; TYPE Angle; GetRotationAxisAngle(Axis, Angle); return Axis; } template TYPE TRMatrixBase::GetRotationAngle() const { Vec Axis; TYPE Angle; GetRotationAxisAngle(Axis, Angle); return Angle; } template int TRMatrixBase::GetRotationAnglesXYZ(TYPE& PhiX, TYPE& PhiY, TYPE& PhiZ) const { #if 1 if (Base::operator()(0,2) < 1) { if (Base::operator()(0,2) > -1) { PhiY = asin(Base::operator()(0,2)); PhiX = atan2(-Base::operator()(1,2), Base::operator()(2,2)); PhiZ = atan2(-Base::operator()(0,1), Base::operator()(0,0)); } else { // WARNING. Not a unique solution. PhiY = -FHALF_PI; PhiX = -atan2(Base::operator()(1,0), Base::operator()(1,1)); PhiZ = 0.f; // any angle works } } else { // WARNING. Not a unique solution. PhiY = FHALF_PI; PhiX = atan2(Base::operator()(1,0), Base::operator()(1,1)); PhiZ = 0.f; // any angle works } #else if ((Base::operator()(0,2) == 1.0)||(Base::operator()(0,2) == -1.0)) { ASSERT("Rotation for Y-axis is +-PI/2, can not decompose X-/Z-component!" == NULL); return -1; } PhiY = asin(Base::operator()(0,2)); if (ISINFORNAN(PhiY)) return -1; // changed this from atan to atan2 (koeser) PhiX = atan2(-Base::operator()(1,2),Base::operator()(2,2)); if (ISINFORNAN(PhiX)) return -2; PhiZ = atan2(-Base::operator()(0,1),Base::operator()(0,0)); if (ISINFORNAN(PhiZ)) return -3; #endif // #ifdef _CPC_DEBUG // TRMatrixBase R; // R.SetXYZ(PhiX, PhiY, PhiZ); // if (R == (*this)) // res = 0; // else // res = -1; // #endif return 0; } template int TRMatrixBase::GetRotationAnglesZXY(TYPE& PhiX, TYPE& PhiY, TYPE& PhiZ) const { if ((Base::operator()(0,2) == 1.0)||(Base::operator()(0,2) == -1.0)) { DEBUG("Rotation for Y-axis is +-PI/2, can not decompose X-/Z-component!"); return -1; } PhiZ = atan2(-Base::operator()(0,1),Base::operator()(1,1)); if (ISINFORNAN(PhiZ)) return -3; PhiX = asin(Base::operator()(2,1)); if (ISINFORNAN(PhiX)) return -2; PhiY = atan2(-Base::operator()(2,0),Base::operator()(2,2)); if (ISINFORNAN(PhiY)) return -1; // #ifdef _CPC_DEBUG // TRMatrixBase R; // R.SetZXY(PhiX, PhiY, PhiZ); // cout<<"Erg R: "< int TRMatrixBase::GetRotationAnglesYXZ(TYPE& PhiY, TYPE& PhiX, TYPE& PhiZ) const { if ((Base::operator()(0,2) == 1.0)||(Base::operator()(0,2) == -1.0)) { DEBUG("Rotation for Y-axis is +-PI/2, can not decompose X-/Z-component!"); return -1; } PhiZ = atan2(Base::operator()(0,2),Base::operator()(2,2)); if (ISINFORNAN(PhiZ)) return -3; PhiX = asin(-Base::operator()(1,2)); if (ISINFORNAN(PhiX)) return -2; PhiY = atan2(Base::operator()(1,0),Base::operator()(1,1)); if (ISINFORNAN(PhiY)) return -1; // #ifdef _CPC_DEBUG // TRMatrixBase R; // R.SetZXY(PhiX, PhiY, PhiZ); // cout<<"Erg R: "< int TRMatrixBase::GetRotationAnglesZYX(TYPE& PhiX, TYPE& PhiY, TYPE& PhiZ) const { if ((Base::operator()(0,2) == 1.0)||(Base::operator()(0,2) == -1.0)) { DEBUG("Rotation for Y-axis is +-PI/2, cannot decompose X-/Z-component!"); return -1; } PhiY = asin(-Base::operator()(2,0)); if (ISINFORNAN(PhiY)) return -1; // changed this from atan to atan2 (koeser) PhiX = atan2(Base::operator()(2,1),Base::operator()(2,2)); if (ISINFORNAN(PhiX)) return -2; PhiZ = atan2(Base::operator()(1,0),Base::operator()(0,0)); if (ISINFORNAN(PhiZ)) return -3; // #ifdef _CPC_DEBUG // TRMatrixBase R; // R.SetZYX(PhiX, PhiY, PhiZ); // if (R == (*this)) // res = 0; // else // res = -1; // #endif return 0; } template void TRMatrixBase::SetFromQuaternion(const Quat& qu) { // fast version, see below for old code: const TYPE nrm = 1.0/norm(qu); const TYPE q0 = qu[0]*nrm; const TYPE q1 = qu[1]*nrm; const TYPE q2 = qu[2]*nrm; const TYPE q3 = qu[3]*nrm; TYPE* pData = val; *pData++ = 1.0 - 2.0 * (q1 * q1 + q2 * q2); *pData++ = 2.0 * (q0 * q1 - q2 * q3); *pData++ = 2.0 * (q2 * q0 + q1 * q3); *pData++ = 2.0 * (q0 * q1 + q2 * q3); *pData++ = 1.0 - 2.0 * (q2 * q2 + q0 * q0); *pData++ = 2.0 * (q1 * q2 - q0 * q3); *pData++ = 2.0 * (q2 * q0 - q1 * q3); *pData++ = 2.0 * (q1 * q2 + q0 * q3); *pData++ = 1.0 - 2.0 * (q1 * q1 + q0 * q0); #ifdef WANT_TO_COMPARE_WITH_OLD_CODE Quat q = qu; // de-const q.Normalize(); // nicked from OpenSG TRMatrixBase RCompare(MatrixZero); RCompare[0][0] = 1.0 - 2.0 * (q[1] * q[1] + q[2] * q[2]); RCompare[1][0] = 2.0 * (q[0] * q[1] + q[2] * q[3]); RCompare[2][0] = 2.0 * (q[2] * q[0] - q[1] * q[3]); RCompare[0][1] = 2.0 * (q[0] * q[1] - q[2] * q[3]); RCompare[1][1] = 1.0 - 2.0 * (q[2] * q[2] + q[0] * q[0]); RCompare[2][1] = 2.0 * (q[1] * q[2] + q[0] * q[3]); RCompare[0][2] = 2.0 * (q[2] * q[0] + q[1] * q[3]); RCompare[1][2] = 2.0 * (q[1] * q[2] - q[0] * q[3]); RCompare[2][2] = 1.0 - 2.0 * (q[1] * q[1] + q[0] * q[0]); for (unsigned i=0; i<3; i++) { for (unsigned j=0; j<3; j++) { if (!equal(Base::operator()(i,j), RCompare[i][j], TYPE(1e-12))) { SLOG("Matrices are not same !"<<*this<<" "< void TRMatrixBase::SetFromOrthogonalBasis(const Vec& xxx, const Vec& yyy, const Vec& zzz) { SetFromRowVectors(normalized(xxx), normalized(yyy), normalized(zzz)); } template void TRMatrixBase::SetFromHV(const Vec& xxx, const Vec& yyy) { // normalize const Vec x0(normalized(xxx)); // compute base z in rhs: const Vec z0(normalized(cross(xxx, yyy))); // compute y base orthogonal in rhs const Vec y0(normalized(cross(z0, x0))); SetFromColumnVectors(x0, y0, z0); } template TRMatrixBase& TRMatrixBase::SetFromDir2Dir(const Vec& dir0, const Vec& dir1) { ASSERT(ISEQUAL(norm(dir0), TYPE(1))); ASSERT(ISEQUAL(norm(dir1), TYPE(1))); const TYPE cos01(CLAMP(dir1.dot(dir0), TYPE(-1), TYPE(1))); const TYPE sin01Sq(TYPE(1) - SQUARE(cos01)); if (sin01Sq > EPSILONTOLERANCE()) { const Vec v(cross(dir0, dir1)); const Mat V(CreateCrossProductMatrix3T(v)); *this = Mat::IDENTITY + V + (V*V)*((TYPE(1)-cos01)/sin01Sq); } else { *this = Mat::ZERO; if (cos01 > TYPE(0)) { Base::operator()(0,0) = TYPE(1); Base::operator()(1,1) = TYPE(1); Base::operator()(2,2) = TYPE(1); } else { Base::operator()(0,0) = TYPE(-1); Base::operator()(1,1) = TYPE(-1); Base::operator()(2,2) = TYPE(-1); } } return *this; } template void TRMatrixBase::SetFromDirUpGL(const Vec& viewDir, const Vec& viewUp) { ASSERT(ISEQUAL(norm(viewDir), TYPE(1))); ASSERT(ISEQUAL(norm(viewUp), TYPE(1))); const Vec right(normalized(cross(viewDir, viewUp))); const Vec up(normalized(cross(right, viewDir))); const Vec forward(viewDir * TYPE(-1)); // convert to right handed system SetFromColumnVectors(right, up, forward); } template void TRMatrixBase::SetFromDirUp(const Vec& viewDir, const Vec& viewUp) { ASSERT(ISEQUAL(norm(viewDir), TYPE(1))); ASSERT(ISEQUAL(norm(viewUp), TYPE(1))); const Vec right(normalized(cross(viewDir, viewUp))); const Vec up(normalized(cross(viewDir, right))); const Vec& forward(viewDir); SetFromColumnVectors(right, up, forward); } template void TRMatrixBase::LookAt(const Vec& from, const Vec& to, const Vec& up) { SetFromDirUp(normalized(to-from), up); } template inline void TRMatrixBase::SetRotationAxisAngle(const Vec& rot) { #if 0 // set rotation using Rodriguez formula *this = Mat::IDENTITY; const TYPE thetaSq = normSq(rot); if (thetaSq < TYPE(1e-12)) return; const TYPE theta = sqrt(thetaSq); // make cross product matrix of rot Mat J; J(0,0) = TYPE(0); J(0,1) = -rot.z; J(0,2) = rot.y; J(1,0) = rot.z; J(1,1) = TYPE(0); J(1,2) = -rot.x; J(2,0) = -rot.y; J(2,1) = rot.x; J(2,2) = TYPE(0); // compute R matrix const Mat J2 = J * J; const TYPE c1 = sin(theta)/theta; const TYPE c2 = (TYPE(1)-cos(theta))/thetaSq; for (int i = 0; i < 3; ++i) for (int j = 0; j < 3; ++j) (*this)(i,j) += c1*J(i,j) + c2*J2(i,j); #elif !defined(_USE_EIGEN) // set rotation using Rodriguez formula const TYPE x2 = rot.x*rot.x; const TYPE y2 = rot.y*rot.y; const TYPE z2 = rot.z*rot.z; const TYPE t2 = x2+y2+z2; if (t2 > TYPE(1e-12)) { // We want to be careful to only evaluate the square root if the // norm of the angle_axis vector is greater than zero. Otherwise // we get a division by zero. const TYPE t = sqrt(t2); const TYPE ct = /*t==0.0?0.5:*/(TYPE(1)-cos(t))/t2; const TYPE st = /*t==0.0?1.0:*/sin(t)/t; val[0] = TYPE(1) - (y2 + z2)*ct; val[1] = rot.x*rot.y*ct - rot.z*st; val[2] = rot.z*rot.x*ct + rot.y*st; val[3] = rot.x*rot.y*ct + rot.z*st; val[4] = TYPE(1) - (z2 + x2)*ct; val[5] = rot.y*rot.z*ct - rot.x*st; val[6] = rot.z*rot.x*ct - rot.y*st; val[7] = rot.y*rot.z*ct + rot.x*st; val[8] = TYPE(1) - (x2 + y2)*ct; } else { // At zero, we switch to using the first order Taylor expansion. val[0] = TYPE(1); val[1] =-rot.z; val[2] = rot.y; val[3] = rot.z; val[4] = TYPE(1); val[5] =-rot.x; val[6] =-rot.y; val[7] = rot.x; val[8] = TYPE(1); } #else *this = Eigen::SO3(rot).get_matrix(); #endif } template inline void TRMatrixBase::Apply(const Vec& delta) { const TRMatrixBase dR(delta); *this = dR * (*this); } template inline typename TRMatrixBase::Vec TRMatrixBase::GetRotationAxisAngle() const { #if 0 // get parametrized rotation using quaternions Vec angle_axis; TYPE angle; GetQuaternion().GetAxisAngle(angle_axis, angle); angle_axis.Normalize(); angle_axis *= angle; return angle_axis; #elif 0 // get parametrized rotation using Rodriguez formula Vec angle_axis; // x = k * 2 * sin(theta), where k is the axis of rotation. angle_axis[0] = (*this)(2, 1) - (*this)(1, 2); angle_axis[1] = (*this)(0, 2) - (*this)(2, 0); angle_axis[2] = (*this)(1, 0) - (*this)(0, 1); static const TYPE kZero(0); static const TYPE kOne(1); static const TYPE kTwo(2); // Since the right hand side may give numbers just above 1.0 or // below -1.0 leading to atan misbehaving, we threshold. const TYPE costheta(MINF(MAXF(((*this)(0, 0) + (*this)(1, 1) + (*this)(2, 2) - kOne)/kTwo, TYPE(-1.0)), kOne)); // sqrt is guaranteed to give non-negative results, so we only // threshold above. const TYPE sintheta(MINF(norm(angle_axis)/kTwo, kOne)); // Use the arctan2 to get the right sign on theta const TYPE theta(atan2(sintheta, costheta)); // Case 1: sin(theta) is large enough, so dividing by it is not a // problem. We do not use abs here, because while jets.h imports // std::abs into the namespace, here in this file, abs resolves to // the int version of the function, which returns zero always. // // We use a threshold much larger then the machine epsilon, because // if sin(theta) is small, not only do we risk overflow but even if // that does not occur, just dividing by a small number will result // in numerical garbage. So we play it safe. static const TYPE kThreshold(TYPE(1e-12)); if ((sintheta > kThreshold) || (sintheta < -kThreshold)) { angle_axis *= theta / (kTwo * sintheta); return angle_axis; } // Case 2: theta ~ 0, means sin(theta) ~ theta to a good // approximation. if (costheta > kZero) { angle_axis *= TYPE(0.5); return angle_axis; } // Case 3: theta ~ pi, this is the hard case. Since theta is large, // and sin(theta) is small. Dividing theta by sin(theta) will either // give an overflow or worse still numerically meaningless results. // Thus we use an alternate more complicated and expensive formula. #ifndef _USE_EIGEN // Since cos(theta) is negative, division by (1-cos(theta)) cannot // overflow. const TYPE inv_one_minus_costheta(kOne / (kOne - costheta)); // We now compute the absolute value of coordinates of the axis // vector using the diagonal entries of R. To resolve the sign of // these entries, we compare the sign of angle_axis[i]*sin(theta) // with the sign of sin(theta). If they are the same, then // angle_axis[i] should be positive, otherwise negative. for (int i = 0; i < 3; ++i) { angle_axis[i] = theta * sqrt(((*this)(i, i) - costheta) * inv_one_minus_costheta); if (((sintheta < kZero) && (angle_axis[i] > kZero)) || ((sintheta > kZero) && (angle_axis[i] < kZero))) angle_axis[i] = -angle_axis[i]; } #else Eigen::AngleAxis aa; aa.fromRotationMatrix(Base::EMatMap((TYPE*)this)); angle_axis[0] = aa.angle() * aa.axis()[0]; angle_axis[1] = aa.angle() * aa.axis()[1]; angle_axis[2] = aa.angle() * aa.axis()[2]; #endif return angle_axis; #else return reinterpret_cast*>(this)->ln(); #endif } template bool TRMatrixBase::Check(const TYPE eps, int verbose) const { // check determinant bool ok = equal(TYPE(cv::determinant(*this)), TYPE(1), eps); // check unit column vector length Vec cl[3]; for (unsigned i = 0; i < 3 && ok; i++) { cl[i] = Base::col(i); ok = equal(norm(cl[i]), TYPE(1), eps); } // check orthogonality if (ok) ok = equal(cl[0].dot(cl[1]), TYPE(0), eps); if (ok) ok = equal(cl[0].dot(cl[2]), TYPE(0), eps); if (ok) ok = equal(cl[1].dot(cl[2]), TYPE(0), eps); #if TD_VERBOSE != TD_VERBOSE_OFF // print the reason in case of failure if (!ok && VERBOSITY_LEVEL > verbose) { std::ostringstream res(" "); const TYPE det(cv::determinant(*this)); if (!equal(det, TYPE(1), eps)) { res << "det != 1 (det=" << std::setprecision(30) << det << ") "; } for (unsigned i = 0; i < 3; i++) { cl[i] = Base::col(i); if (!equal(norm(cl[i]), TYPE(1), eps)) { res << "col[" << i << "].NormL2() != 1) (= " << norm(cl[i]) << ") "; } } if (!equal(cl[0].dot(cl[1]), TYPE(0), eps)) { res << "col[0].ScalarProduct(c[1]) != 0 (= " << cl[0].dot(cl[1]) << ") "; } if (!equal(cl[0].dot(cl[2]), TYPE(0), eps)) { res << "col[0].ScalarProduct(c[2]) != 0 (= " << cl[0].dot(cl[2]) << ") "; } if (!equal(cl[1].dot(cl[2]), TYPE(0), eps)) { res << "col[1].ScalarProduct(c[2]) != 0 (= " << cl[1].dot(cl[2]) << ") "; } SLOG("Rotation matrix is invalid: " << res.str() << std::endl); } #endif return ok; } template void TRMatrixBase::EnforceOrthogonality() { #if 1 // use SVD decomposition to find the closest orthogonal matrix const cv::SVD svd(*this, cv::SVD::MODIFY_A|cv::SVD::FULL_UV); *this = svd.u*svd.vt; #elif 1 // replaces the 3 x 3 matrix with the nearest (least squares) orthogonal matrix; // a combination of Babylonian iteration and Berthold KP Horn's formula R = M (MT M)^-1/2 TYPE* r = this->val; TYPE m[9], ri[9], x[9]; memcpy(m, r, 9*sizeof(TYPE)); //By trial and error I found that 4 iterates always works unless your matrix is waaay off. Limit 10. for (unsigned iterate = 0; iterate != 10; iterate++) { //Invert r. Use inverse and not transpose since r is only approximately orthogonal (transpose will result in slow, oscillating convergence). InvertMatrix3x3(r, ri); //Get x = ri m + mt r. x[0] = m[6]*r[6]+ri[2]*m[6]+m[3]*r[3]+ri[1]*m[3]+m[0]*ri[0]+m[0]*r[0]; x[1] = m[6]*r[7]+ri[2]*m[7]+m[3]*r[4]+ri[1]*m[4]+m[0]*r[1]+ri[0]*m[1]; x[2] = m[6]*r[8]+ri[2]*m[8]+m[3]*r[5]+ri[1]*m[5]+m[0]*r[2]+ri[0]*m[2]; x[3] = r[6]*m[7]+ri[5]*m[6]+m[3]*ri[4]+r[3]*m[4]+m[0]*ri[3]+r[0]*m[1]; x[4] = m[7]*r[7]+ri[5]*m[7]+m[4]*ri[4]+m[4]*r[4]+m[1]*ri[3]+m[1]*r[1]; x[5] = m[7]*r[8]+ri[5]*m[8]+m[4]*r[5]+ri[4]*m[5]+m[2]*ri[3]+m[1]*r[2]; x[6] = m[6]*ri[8]+r[6]*m[8]+m[3]*ri[7]+m[0]*ri[6]+r[3]*m[5]+r[0]*m[2]; x[7] = m[7]*ri[8]+r[7]*m[8]+m[4]*ri[7]+m[1]*ri[6]+r[4]*m[5]+r[1]*m[2]; x[8] = m[8]*ri[8]+m[8]*r[8]+m[5]*ri[7]+m[2]*ri[6]+m[5]*r[5]+m[2]*r[2]; //Invert into ri. InvertMatrix3x3(x, ri); //Replace r with 2 m ri, where ri is as replaced above. r[0] = 2*(m[2]*ri[6]+m[1]*ri[3]+m[0]*ri[0]); r[1] = 2*(m[2]*ri[7]+m[1]*ri[4]+m[0]*ri[1]); r[2] = 2*(m[2]*ri[8]+m[1]*ri[5]+m[0]*ri[2]); r[3] = 2*(m[5]*ri[6]+ri[3]*m[4]+ri[0]*m[3]); r[4] = 2*(m[5]*ri[7]+m[4]*ri[4]+ri[1]*m[3]); r[5] = 2*(m[5]*ri[8]+m[4]*ri[5]+ri[2]*m[3]); r[6] = 2*(ri[6]*m[8]+ri[3]*m[7]+ri[0]*m[6]); r[7] = 2*(ri[7]*m[8]+ri[4]*m[7]+ri[1]*m[6]); r[8] = 2*(m[8]*ri[8]+ri[5]*m[7]+ri[2]*m[6]); //Check how off it is. x[0] = ABS(r[0]*r[0] + r[3]*r[3] + r[6]*r[6] - TYPE(1)); x[1] = ABS(r[1]*r[1] + r[4]*r[4] + r[7]*r[7] - TYPE(1)); x[2] = ABS(r[2]*r[2] + r[5]*r[5] + r[8]*r[8] - TYPE(1)); x[3] = ABS(r[0]*r[1] + r[3]*r[4] + r[6]*r[7]); x[4] = ABS(r[1]*r[2] + r[4]*r[5] + r[7]*r[8]); x[5] = ABS(r[2]*r[0] + r[5]*r[3] + r[8]*r[6]); //Max norm, by all means, really make it as accurate as possible. TYPE maxElement(-FLT_MAX); for (unsigned n=6; n != 0; ) if(x[--n] > maxElement) maxElement = x[n]; if (maxElement < TYPE(1e-15)) return; } #else // Matrix Orthogonalization // by Eric Raible from "Graphics Gems", Academic Press, 1990 // // Reorthogonalize matrix R - that is find an orthogonal matrix that is // "close" to R by computing an approximation to the orthogonal matrix // // T -1/2 // RC = R(R R) // T -1 // [RC is orthogonal because (RC) = (RC) ] // -1/2 // To compute C, we evaluate the Taylor expansion of F(x) = (I + x) // (where x = C - I) about x=0. // This gives C = I - (1/2)x + (3/8)x^2 - (5/16)x^3 + ... // By default, limit should be 8. const unsigned max_limit = 8; static const TYPE coef[10] = { 1, -1/2., 3/8., -5/16., 35/128., -63/256., 231/1024., -429/2048., 6435/32768., -12155/65536. }; Base& R(*this); const Base RtR(R.t()*R); const Base X(RtR - Matrix3x3::IDENTITY); /* RtR - I */ Base X_power(Matrix3x3::IDENTITY); /* X^0 */ Base Sum(Matrix3x3::IDENTITY); /* coef[0] * X^0 */ const unsigned limit(MINF(max_limit, (unsigned)10)); for (unsigned power=1; power inline bool IsRotationMatrix(const TMatrix& R) { ASSERT(sizeof(TMatrix) == sizeof(TRMatrixBase)); return ((const TRMatrixBase&)R).IsValid(); } // IsRotationMatrix template inline bool IsRotationMatrix(const Eigen::Matrix& R) { // the trace should be three and the determinant should be one return (ISEQUAL(R.determinant(), TYPE(1)) && ISEQUAL((R*R.transpose()).trace(), TYPE(3))); } // IsRotationMatrix /*----------------------------------------------------------------*/ // enforce matrix orthogonality template inline void EnsureRotationMatrix(TMatrix& R) { ASSERT(sizeof(TMatrix) == sizeof(TRMatrixBase)); ((TRMatrixBase&)R).EnforceOrthogonality(); } // EnsureRotationMatrix /*----------------------------------------------------------------*/ // compute the distance on SO(3) between the two given rotations // using log(R) as in: "Efficient and Robust Large-Scale Rotation Averaging", 2013 // same result as above, but returns directly the angle template inline TYPE ComputeAngleSO3(const TMatrix& I) { return TYPE(norm(((const TRMatrixBase&)I).GetRotationAxisAngle())); } // ComputeAngleSO3 template FORCEINLINE TYPE ComputeAngleSO3(const TMatrix& R1, const TMatrix& R2) { return ComputeAngleSO3(TMatrix(R1*R2.t())); } // ComputeAngleSO3 /*----------------------------------------------------------------*/ } // namespace SEACAVE