Molmodel
|
00001 /* -------------------------------------------------------------------------- * 00002 * SimTK Core: SimTK Molmodel * 00003 * -------------------------------------------------------------------------- * 00004 * This is part of the SimTK Core biosimulation toolkit originating from * 00005 * Simbios, the NIH National Center for Physics-Based Simulation of * 00006 * Biological Structures at Stanford, funded under the NIH Roadmap for * 00007 * Medical Research, grant U54 GM072970. See https://simtk.org. * 00008 * * 00009 * Portions copyright (c) 2008 Stanford University and the Authors. * 00010 * Authors: Christopher Bruns * 00011 * Contributors: * 00012 * * 00013 * Permission is hereby granted, free of charge, to any person obtaining a * 00014 * copy of this software and associated documentation files (the "Software"), * 00015 * to deal in the Software without restriction, including without limitation * 00016 * the rights to use, copy, modify, merge, publish, distribute, sublicense, * 00017 * and/or sell copies of the Software, and to permit persons to whom the * 00018 * Software is furnished to do so, subject to the following conditions: * 00019 * * 00020 * The above copyright notice and this permission notice shall be included in * 00021 * all copies or substantial portions of the Software. * 00022 * * 00023 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * 00024 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * 00025 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * 00026 * THE AUTHORS, CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 00027 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * 00028 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE * 00029 * USE OR OTHER DEALINGS IN THE SOFTWARE. * 00030 * -------------------------------------------------------------------------- */ 00031 00032 00038 #ifndef MOLMODEL_SUPERPOSE_H_ 00039 #define MOLMODEL_SUPERPOSE_H_ 00040 00041 #include "SimTKmath.h" 00042 #include <vector> 00043 00044 namespace SimTK { 00045 00047 class Vec3Pair 00048 { 00049 public: 00050 Vec3Pair(const Vec3& s, const Vec3& t, Real w = 1.0) 00051 : source(s), target(t), weight(w) {} 00052 00053 const Vec3& getSource() const {return source;} 00054 const Vec3& getTarget() const {return target;} 00055 Real getWeight() const {return weight;} 00056 00057 private: 00058 Vec3 source; 00059 Vec3 target; 00060 Real weight; 00061 }; 00062 00063 class TransformAndResidual { 00064 public: 00065 TransformAndResidual(const Transform& t, Real r) 00066 : transform(t), residual(r) {} 00067 00068 Transform transform; 00069 Real residual; 00070 }; 00071 00072 class SimTK_MOLMODEL_EXPORT Kabsch78 { 00073 public: 00074 typedef std::vector<Vec3Pair> VectorSet ; 00075 00083 static TransformAndResidual superpose(const VectorSet& vectors) 00084 { 00085 // a) Remove any translation between the two given 00086 // vector sets x(n) and y(n), and determine 00087 // E0 = 1/2 SUM[ w(n)*(x(n)^2 + y(n)^2) ] 00088 // and R, r(ij) = SUM(n)[ w(n)*y(ni)*x(nj) ] 00089 00090 // 1) Identify center of mass of each set of vectors 00091 Real totalMass = 0.0; 00092 Vec3 sourceCentroid(0.0, 0.0, 0.0); 00093 Vec3 targetCentroid(0.0, 0.0, 0.0); 00094 VectorSet::const_iterator vI; 00095 for (vI = vectors.begin(); vI != vectors.end(); ++vI) 00096 { 00097 totalMass += vI->getWeight(); 00098 sourceCentroid += vI->getWeight() * vI->getSource(); 00099 targetCentroid += vI->getWeight() * vI->getTarget(); 00100 } 00101 if (totalMass != 0.0) { 00102 sourceCentroid /= totalMass; 00103 targetCentroid /= totalMass; 00104 } 00105 00106 // Form R matrix from Kabsch paper 00107 Mat<3,3> R(0.0); 00108 Real E0 = 0.0; // Initial residual, see Kabsch 00109 for (vI = vectors.begin(); vI != vectors.end(); ++vI) 00110 { 00111 Vec3 x = vI->getSource() - sourceCentroid; 00112 Vec3 y = vI->getTarget() - targetCentroid; 00113 E0 += 0.5 * vI->getWeight() * ( dot(x, x) + dot(y, y) ); 00114 for (int i = 0; i < 3; ++i) 00115 for (int j = 0; j < 3; ++j) 00116 R[i][j] += vI->getWeight() * y[i] * x[j]; 00117 } 00118 00119 // b) Form ~RR, determine eigenvalues mu(k) and the 00120 // mutually orthogonal eigenvectors a(k) and 00121 // sort so that mu1 >= mu2 >= mu3. 00122 // Set 00123 // a3 == a1 cross a2 00124 // to be sure to have a right handed system 00125 Vector_<std::complex<double> > mu0; // eigenvalues, complex, unsorted 00126 Matrix_<std::complex<double> > a0; // eigenvectors, complex, unsorted 00127 Eigen eigen( Matrix( R.transpose() * R ) ); 00128 eigen.getAllEigenValuesAndVectors(mu0, a0); 00129 00130 // use only real component of results 00131 Vec3 a1[3]; // eigenvectors, real, unsorted 00132 Real mu1[3]; // eigenvalues, real, unsorted 00133 for (int i = 0; i < 3; ++i) { 00134 mu1[i] = mu0[i].real(); 00135 for (int j = 0; j < 3; ++j) 00136 // Note swapping of indices: It appears that the columns of a0 are eigenvectors 00137 a1[j][i] = a0[i][j].real(); 00138 } 00139 00140 // sort indices of eigenvalues, from largest eigenvalue to smallest 00141 int indMax(0); 00142 int indMin(0); 00143 for (int i = 0; i < 3; ++i) { 00144 if ( mu1[i] > mu1[indMax] ) { 00145 indMax = i; 00146 } 00147 if ( mu1[i] <= mu1[indMin] ) { 00148 indMin = i; 00149 } 00150 } 00151 assert (indMin != indMax); 00152 int indMid = 3 - indMax - indMin; // too clever... 00153 assert(indMid >= 0); 00154 assert(indMid <= 2); 00155 assert(indMid != indMax); 00156 assert(indMid != indMin); 00157 int indSort[3] = {indMax, indMid, indMin}; 00158 00159 Real mu[3]; // eigenvalues, sorted 00160 Vec3 a[3]; // eigenvectors, sorted 00161 for (int i = 0; i < 3; ++i) { 00162 mu[i] = mu1[indSort[i]]; 00163 a[i] = Vec3(UnitVec3(a1[indSort[i]])); 00164 } 00165 00166 a[2] = cross(a[0], a[1]); // force right handed system 00167 00168 // c) Determine Ra(k) (k = 1,2,3), normalize the first 00169 // two vectors to obtain b1, b2, and set b3 == b1 cross b2. 00170 // This will also take care of the case mu2 > mu3 = 0. 00171 Real sigma[] = {1.0, 1.0, 1.0}; 00172 Vec3 b[3]; 00173 b[0] = Vec3(UnitVec3(R * a[0])); 00174 b[1] = Vec3(UnitVec3(R * a[1])); 00175 b[2] = cross(b[0], b[1]); 00176 00177 if ( dot(b[2], R * a[2]) < 0 ) 00178 sigma[2] = -1.0; 00179 00180 // d) Form U according to eq. 7: 00181 // u(ij) = SUM(k)[ b(ki)a(kj) ] 00182 // where b(k) = R*a(k)/(sigma(k)sqrt(mu(k))) 00183 Mat<3,3> U(0.0); // initialize 2-d array 00184 for (int i = 0; i < 3; ++i) 00185 for (int j = 0; j < 3; ++j) 00186 for (int k = 0; k < 3; ++k) 00187 U[i][j] += b[k][i] * a[k][j]; 00188 00189 // Compute residual error 00190 Real residualError = E0 - sigma[0] * std::sqrt(mu[0]) - sigma[1] * std::sqrt(mu[1]) - sigma[2] * std::sqrt(std::abs(mu[2])); 00191 // E = 1/2 SUM(over n)[w(n)*(Ux(n) - y(n))^2] 00192 // variance would be sqrt( 1/SUM(w(n)) * SUM(w(n)*(Ux(n) - y(n))^2) ) 00193 Real variance = 0.0; 00194 if (totalMass > 0) 00195 variance = std::sqrt(2 * residualError / totalMass); 00196 00197 Rotation rotation(U); 00198 00199 // No rotation for single point overlay 00200 if (vectors.size() < 2) 00201 rotation = Rotation(); 00202 00203 Transform transform1(-sourceCentroid); 00204 Transform transform2(rotation); 00205 Transform transform3(targetCentroid); 00206 00207 return TransformAndResidual(transform3 * transform2 * transform1, variance); 00208 } 00209 }; 00210 00211 } // namespace SimTK 00212 00213 #endif // MOLMODEL_SUPERPOSE_H_