| 1 | /********************************************************************** | 
| 2 | matrix3x3.cpp - Handle 3D rotation matrix. | 
| 3 |  | 
| 4 | Copyright (C) 1998-2001 by OpenEye Scientific Software, Inc. | 
| 5 | Some portions Copyright (C) 2001-2005 by Geoffrey R. Hutchison | 
| 6 |  | 
| 7 | This file is part of the Open Babel project. | 
| 8 | For more information, see <http://openbabel.sourceforge.net/> | 
| 9 |  | 
| 10 | This program is free software; you can redistribute it and/or modify | 
| 11 | it under the terms of the GNU General Public License as published by | 
| 12 | the Free Software Foundation version 2 of the License. | 
| 13 |  | 
| 14 | This program is distributed in the hope that it will be useful, | 
| 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of | 
| 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
| 17 | GNU General Public License for more details. | 
| 18 | ***********************************************************************/ | 
| 19 |  | 
| 20 | #include <math.h> | 
| 21 |  | 
| 22 | #include "mol.hpp" | 
| 23 | #include "matrix3x3.hpp" | 
| 24 |  | 
| 25 | #ifndef true | 
| 26 | #define true 1 | 
| 27 | #endif | 
| 28 |  | 
| 29 | #ifndef false | 
| 30 | #define false 0 | 
| 31 | #endif | 
| 32 |  | 
| 33 | using namespace std; | 
| 34 |  | 
| 35 | namespace OpenBabel | 
| 36 | { | 
| 37 |  | 
| 38 | /** \class matrix3x3 | 
| 39 | \brief Represents a real 3x3 matrix. | 
| 40 |  | 
| 41 | Rotating points in space can be performed by a vector-matrix | 
| 42 | multiplication. The matrix3x3 class is designed as a helper to the | 
| 43 | vector3 class for rotating points in space. The rotation matrix may be | 
| 44 | initialised by passing in the array of doubleing point values, by | 
| 45 | passing euler angles, or a rotation vector and angle of rotation about | 
| 46 | that vector. Once set, the matrix3x3 class can be used to rotate | 
| 47 | vectors by the overloaded multiplication operator. The following | 
| 48 | demonstrates the usage of the matrix3x3 class: | 
| 49 |  | 
| 50 | \code | 
| 51 | matrix3x3 mat; | 
| 52 | mat.SetupRotMat(0.0,180.0,0.0); //rotate theta by 180 degrees | 
| 53 | vector3 v = VX; | 
| 54 | v *= mat; //apply the rotation | 
| 55 | \endcode | 
| 56 |  | 
| 57 | */ | 
| 58 |  | 
| 59 | /*! the axis of the rotation will be uniformly distributed on | 
| 60 | the unit sphere, the angle will be uniformly distributed in | 
| 61 | the interval 0..360 degrees. */ | 
| 62 | void matrix3x3::randomRotation(OBRandom &rnd) | 
| 63 | { | 
| 64 | double rotAngle; | 
| 65 | vector3 v1; | 
| 66 |  | 
| 67 | v1.randomUnitVector(&rnd); | 
| 68 | rotAngle = 360.0 * rnd.NextFloat(); | 
| 69 | this->RotAboutAxisByAngle(v1,rotAngle); | 
| 70 | } | 
| 71 |  | 
| 72 | void matrix3x3::SetupRotMat(double phi,double theta,double psi) | 
| 73 | { | 
| 74 | double p  = phi * DEG_TO_RAD; | 
| 75 | double h  = theta * DEG_TO_RAD; | 
| 76 | double b  = psi * DEG_TO_RAD; | 
| 77 |  | 
| 78 | double cx = cos(p); | 
| 79 | double sx = sin(p); | 
| 80 | double cy = cos(h); | 
| 81 | double sy = sin(h); | 
| 82 | double cz = cos(b); | 
| 83 | double sz = sin(b); | 
| 84 |  | 
| 85 | ele[0][0] = cy*cz; | 
| 86 | ele[0][1] = cy*sz; | 
| 87 | ele[0][2] = -sy; | 
| 88 |  | 
| 89 | ele[1][0] = sx*sy*cz-cx*sz; | 
| 90 | ele[1][1] = sx*sy*sz+cx*cz; | 
| 91 | ele[1][2] = sx*cy; | 
| 92 |  | 
| 93 | ele[2][0] = cx*sy*cz+sx*sz; | 
| 94 | ele[2][1] = cx*sy*sz-sx*cz; | 
| 95 | ele[2][2] = cx*cy; | 
| 96 | } | 
| 97 |  | 
| 98 | /*! Replaces *this with a matrix that represents reflection on | 
| 99 | the plane through 0 which is given by the normal vector norm. | 
| 100 |  | 
| 101 | \warning If the vector norm has length zero, this method will | 
| 102 | generate the 0-matrix. If the length of the axis is close to | 
| 103 | zero, but not == 0.0, this method may behave in unexpected | 
| 104 | ways and return almost random results; details may depend on | 
| 105 | your particular doubleing point implementation. The use of this | 
| 106 | method is therefore highly discouraged, unless you are certain | 
| 107 | that the length is in a reasonable range, away from 0.0 | 
| 108 | (Stefan Kebekus) | 
| 109 |  | 
| 110 | \deprecated This method will probably replaced by a safer | 
| 111 | algorithm in the future. | 
| 112 |  | 
| 113 | \todo Replace this method with a more fool-proof version. | 
| 114 |  | 
| 115 | @param norm specifies the normal to the plane | 
| 116 | */ | 
| 117 | void matrix3x3::PlaneReflection(const vector3 &norm) | 
| 118 | { | 
| 119 | //@@@ add a safety net | 
| 120 |  | 
| 121 | vector3 normtmp = norm; | 
| 122 | normtmp.normalize(); | 
| 123 |  | 
| 124 | SetColumn(0, vector3(1,0,0) - 2*normtmp.x()*normtmp); | 
| 125 | SetColumn(1, vector3(0,1,0) - 2*normtmp.y()*normtmp); | 
| 126 | SetColumn(2, vector3(0,0,1) - 2*normtmp.z()*normtmp); | 
| 127 | } | 
| 128 |  | 
| 129 | #define x vtmp.x() | 
| 130 | #define y vtmp.y() | 
| 131 | #define z vtmp.z() | 
| 132 |  | 
| 133 | /*! Replaces *this with a matrix that represents rotation about the | 
| 134 | axis by a an angle. | 
| 135 |  | 
| 136 | \warning If the vector axis has length zero, this method will | 
| 137 | generate the 0-matrix. If the length of the axis is close to | 
| 138 | zero, but not == 0.0, this method may behave in unexpected ways | 
| 139 | and return almost random results; details may depend on your | 
| 140 | particular doubleing point implementation. The use of this method | 
| 141 | is therefore highly discouraged, unless you are certain that the | 
| 142 | length is in a reasonable range, away from 0.0 (Stefan | 
| 143 | Kebekus) | 
| 144 |  | 
| 145 | \deprecated This method will probably replaced by a safer | 
| 146 | algorithm in the future. | 
| 147 |  | 
| 148 | \todo Replace this method with a more fool-proof version. | 
| 149 |  | 
| 150 | @param v specifies the axis of the rotation | 
| 151 | @param angle angle in degrees (0..360) | 
| 152 | */ | 
| 153 | void matrix3x3::RotAboutAxisByAngle(const vector3 &v,const double angle) | 
| 154 | { | 
| 155 | double theta = angle*DEG_TO_RAD; | 
| 156 | double s = sin(theta); | 
| 157 | double c = cos(theta); | 
| 158 | double t = 1 - c; | 
| 159 |  | 
| 160 | vector3 vtmp = v; | 
| 161 | vtmp.normalize(); | 
| 162 |  | 
| 163 | ele[0][0] = t*x*x + c; | 
| 164 | ele[0][1] = t*x*y + s*z; | 
| 165 | ele[0][2] = t*x*z - s*y; | 
| 166 |  | 
| 167 | ele[1][0] = t*x*y - s*z; | 
| 168 | ele[1][1] = t*y*y + c; | 
| 169 | ele[1][2] = t*y*z + s*x; | 
| 170 |  | 
| 171 | ele[2][0] = t*x*z + s*y; | 
| 172 | ele[2][1] = t*y*z - s*x; | 
| 173 | ele[2][2] = t*z*z + c; | 
| 174 | } | 
| 175 |  | 
| 176 | #undef x | 
| 177 | #undef y | 
| 178 | #undef z | 
| 179 |  | 
| 180 | void matrix3x3::SetColumn(int col, const vector3 &v) | 
| 181 | { | 
| 182 | if (col > 2) | 
| 183 | { | 
| 184 | obErrorLog.ThrowError(__FUNCTION__, | 
| 185 | "The method was called with col > 2.", obError); | 
| 186 | } | 
| 187 |  | 
| 188 | ele[0][col] = v.x(); | 
| 189 | ele[1][col] = v.y(); | 
| 190 | ele[2][col] = v.z(); | 
| 191 | } | 
| 192 |  | 
| 193 | void matrix3x3::SetRow(int row, const vector3 &v) | 
| 194 | { | 
| 195 | if (row > 2) | 
| 196 | { | 
| 197 | obErrorLog.ThrowError(__FUNCTION__, | 
| 198 | "The method was called with row > 2.", obError); | 
| 199 | } | 
| 200 |  | 
| 201 | ele[row][0] = v.x(); | 
| 202 | ele[row][1] = v.y(); | 
| 203 | ele[row][2] = v.z(); | 
| 204 | } | 
| 205 |  | 
| 206 | vector3 matrix3x3::GetColumn(unsigned int col) | 
| 207 | { | 
| 208 | if (col > 2) | 
| 209 | { | 
| 210 | obErrorLog.ThrowError(__FUNCTION__, | 
| 211 | "The method was called with col > 2.", obError); | 
| 212 | } | 
| 213 |  | 
| 214 | return vector3(ele[0][col], ele[1][col], ele[2][col]); | 
| 215 | } | 
| 216 |  | 
| 217 | vector3 matrix3x3::GetRow(unsigned int row) | 
| 218 | { | 
| 219 | if (row > 2) | 
| 220 | { | 
| 221 | obErrorLog.ThrowError(__FUNCTION__, | 
| 222 | "The method was called with row > 2.", obError); | 
| 223 | } | 
| 224 |  | 
| 225 | return vector3(ele[row][0], ele[row][1], ele[row][2]); | 
| 226 | } | 
| 227 |  | 
| 228 | /*! calculates the product m*v of the matrix m and the column | 
| 229 | vector represented by v | 
| 230 | */ | 
| 231 | vector3 operator *(const matrix3x3 &m,const vector3 &v) | 
| 232 | { | 
| 233 | vector3 vv; | 
| 234 |  | 
| 235 | vv._vx = v._vx*m.ele[0][0] + v._vy*m.ele[0][1] + v._vz*m.ele[0][2]; | 
| 236 | vv._vy = v._vx*m.ele[1][0] + v._vy*m.ele[1][1] + v._vz*m.ele[1][2]; | 
| 237 | vv._vz = v._vx*m.ele[2][0] + v._vy*m.ele[2][1] + v._vz*m.ele[2][2]; | 
| 238 |  | 
| 239 | return(vv); | 
| 240 | } | 
| 241 |  | 
| 242 | matrix3x3 operator *(const matrix3x3 &A,const matrix3x3 &B) | 
| 243 | { | 
| 244 | matrix3x3 result; | 
| 245 |  | 
| 246 | result.ele[0][0] = A.ele[0][0]*B.ele[0][0] + A.ele[0][1]*B.ele[1][0] + A.ele[0][2]*B.ele[2][0]; | 
| 247 | result.ele[0][1] = A.ele[0][0]*B.ele[0][1] + A.ele[0][1]*B.ele[1][1] + A.ele[0][2]*B.ele[2][1]; | 
| 248 | result.ele[0][2] = A.ele[0][0]*B.ele[0][2] + A.ele[0][1]*B.ele[1][2] + A.ele[0][2]*B.ele[2][2]; | 
| 249 |  | 
| 250 | result.ele[1][0] = A.ele[1][0]*B.ele[0][0] + A.ele[1][1]*B.ele[1][0] + A.ele[1][2]*B.ele[2][0]; | 
| 251 | result.ele[1][1] = A.ele[1][0]*B.ele[0][1] + A.ele[1][1]*B.ele[1][1] + A.ele[1][2]*B.ele[2][1]; | 
| 252 | result.ele[1][2] = A.ele[1][0]*B.ele[0][2] + A.ele[1][1]*B.ele[1][2] + A.ele[1][2]*B.ele[2][2]; | 
| 253 |  | 
| 254 | result.ele[2][0] = A.ele[2][0]*B.ele[0][0] + A.ele[2][1]*B.ele[1][0] + A.ele[2][2]*B.ele[2][0]; | 
| 255 | result.ele[2][1] = A.ele[2][0]*B.ele[0][1] + A.ele[2][1]*B.ele[1][1] + A.ele[2][2]*B.ele[2][1]; | 
| 256 | result.ele[2][2] = A.ele[2][0]*B.ele[0][2] + A.ele[2][1]*B.ele[1][2] + A.ele[2][2]*B.ele[2][2]; | 
| 257 |  | 
| 258 | return(result); | 
| 259 | } | 
| 260 |  | 
| 261 | /*! calculates the product m*(*this) of the matrix m and the | 
| 262 | column vector represented by *this | 
| 263 | */ | 
| 264 | vector3 &vector3::operator *= (const matrix3x3 &m) | 
| 265 | { | 
| 266 | vector3 vv; | 
| 267 |  | 
| 268 | vv.SetX(_vx*m.Get(0,0) + _vy*m.Get(0,1) + _vz*m.Get(0,2)); | 
| 269 | vv.SetY(_vx*m.Get(1,0) + _vy*m.Get(1,1) + _vz*m.Get(1,2)); | 
| 270 | vv.SetZ(_vx*m.Get(2,0) + _vy*m.Get(2,1) + _vz*m.Get(2,2)); | 
| 271 | _vx = vv.x(); | 
| 272 | _vy = vv.y(); | 
| 273 | _vz = vv.z(); | 
| 274 |  | 
| 275 | return(*this); | 
| 276 | } | 
| 277 |  | 
| 278 | /*! This method checks if the absolute value of the determinant is smaller than 1e-6. If | 
| 279 | so, nothing is done and an exception is thrown. Otherwise, the | 
| 280 | inverse matrix is calculated and returned. *this is not changed. | 
| 281 |  | 
| 282 | \warning If the determinant is close to zero, but not == 0.0, | 
| 283 | this method may behave in unexpected ways and return almost | 
| 284 | random results; details may depend on your particular doubleing | 
| 285 | point implementation. The use of this method is therefore highly | 
| 286 | discouraged, unless you are certain that the determinant is in a | 
| 287 | reasonable range, away from 0.0 (Stefan Kebekus) | 
| 288 | */ | 
| 289 | matrix3x3 matrix3x3::inverse(void) | 
| 290 | { | 
| 291 | double det = determinant(); | 
| 292 | if (fabs(det) <= 1e-6) | 
| 293 | { | 
| 294 | obErrorLog.ThrowError(__FUNCTION__, | 
| 295 | "The method was called on a matrix with |determinant| <= 1e-6.", obError); | 
| 296 | } | 
| 297 |  | 
| 298 | matrix3x3 inverse; | 
| 299 | inverse.ele[0][0] = ele[1][1]*ele[2][2] - ele[1][2]*ele[2][1]; | 
| 300 | inverse.ele[1][0] = ele[1][2]*ele[2][0] - ele[1][0]*ele[2][2]; | 
| 301 | inverse.ele[2][0] = ele[1][0]*ele[2][1] - ele[1][1]*ele[2][0]; | 
| 302 | inverse.ele[0][1] = ele[2][1]*ele[0][2] - ele[2][2]*ele[0][1]; | 
| 303 | inverse.ele[1][1] = ele[2][2]*ele[0][0] - ele[2][0]*ele[0][2]; | 
| 304 | inverse.ele[2][1] = ele[2][0]*ele[0][1] - ele[2][1]*ele[0][0]; | 
| 305 | inverse.ele[0][2] = ele[0][1]*ele[1][2] - ele[0][2]*ele[1][1]; | 
| 306 | inverse.ele[1][2] = ele[0][2]*ele[1][0] - ele[0][0]*ele[1][2]; | 
| 307 | inverse.ele[2][2] = ele[0][0]*ele[1][1] - ele[0][1]*ele[1][0]; | 
| 308 |  | 
| 309 | inverse /= det; | 
| 310 |  | 
| 311 | return(inverse); | 
| 312 | } | 
| 313 |  | 
| 314 | /* This method returns the transpose of a matrix. The original | 
| 315 | matrix remains unchanged. */ | 
| 316 | matrix3x3 matrix3x3::transpose(void) const | 
| 317 | { | 
| 318 | matrix3x3 transpose; | 
| 319 |  | 
| 320 | for(unsigned int i=0; i<3; i++) | 
| 321 | for(unsigned int j=0; j<3; j++) | 
| 322 | transpose.ele[i][j] = ele[j][i]; | 
| 323 |  | 
| 324 | return(transpose); | 
| 325 | } | 
| 326 |  | 
| 327 | double matrix3x3::determinant(void) const | 
| 328 | { | 
| 329 | double x,y,z; | 
| 330 |  | 
| 331 | x = ele[0][0] * (ele[1][1] * ele[2][2] - ele[1][2] * ele[2][1]); | 
| 332 | y = ele[0][1] * (ele[1][2] * ele[2][0] - ele[1][0] * ele[2][2]); | 
| 333 | z = ele[0][2] * (ele[1][0] * ele[2][1] - ele[1][1] * ele[2][0]); | 
| 334 |  | 
| 335 | return(x + y + z); | 
| 336 | } | 
| 337 |  | 
| 338 | /*! This method returns false if there are indices i,j such that | 
| 339 | fabs(*this[i][j]-*this[j][i]) > 1e-6. Otherwise, it returns | 
| 340 | true. */ | 
| 341 | bool matrix3x3::isSymmetric(void) const | 
| 342 | { | 
| 343 | if (fabs(ele[0][1] - ele[1][0]) > 1e-6) | 
| 344 | return false; | 
| 345 | if (fabs(ele[0][2] - ele[2][0]) > 1e-6) | 
| 346 | return false; | 
| 347 | if (fabs(ele[1][2] - ele[2][1]) > 1e-6) | 
| 348 | return false; | 
| 349 | return true; | 
| 350 | } | 
| 351 |  | 
| 352 | /*! This method returns false if there are indices i != j such | 
| 353 | that fabs(*this[i][j]) > 1e-6. Otherwise, it returns true. */ | 
| 354 | bool matrix3x3::isDiagonal(void) const | 
| 355 | { | 
| 356 | if (fabs(ele[0][1]) > 1e-6) | 
| 357 | return false; | 
| 358 | if (fabs(ele[0][2]) > 1e-6) | 
| 359 | return false; | 
| 360 | if (fabs(ele[1][2]) > 1e-6) | 
| 361 | return false; | 
| 362 |  | 
| 363 | if (fabs(ele[1][0]) > 1e-6) | 
| 364 | return false; | 
| 365 | if (fabs(ele[2][0]) > 1e-6) | 
| 366 | return false; | 
| 367 | if (fabs(ele[2][1]) > 1e-6) | 
| 368 | return false; | 
| 369 |  | 
| 370 | return true; | 
| 371 | } | 
| 372 |  | 
| 373 | /*! This method returns false if there are indices i != j such | 
| 374 | that fabs(*this[i][j]) > 1e-6, or if there is an index i such | 
| 375 | that fabs(*this[i][j]-1) > 1e-6. Otherwise, it returns | 
| 376 | true. */ | 
| 377 | bool matrix3x3::isUnitMatrix(void) const | 
| 378 | { | 
| 379 | if (!isDiagonal()) | 
| 380 | return false; | 
| 381 |  | 
| 382 | if (fabs(ele[0][0]-1) > 1e-6) | 
| 383 | return false; | 
| 384 | if (fabs(ele[1][1]-1) > 1e-6) | 
| 385 | return false; | 
| 386 | if (fabs(ele[2][2]-1) > 1e-6) | 
| 387 | return false; | 
| 388 |  | 
| 389 | return true; | 
| 390 | } | 
| 391 |  | 
| 392 | /*! This method employs the static method matrix3x3::jacobi(...) | 
| 393 | to find the eigenvalues and eigenvectors of a symmetric | 
| 394 | matrix. On entry it is checked if the matrix really is | 
| 395 | symmetric: if isSymmetric() returns 'false', an OBError is | 
| 396 | thrown. | 
| 397 |  | 
| 398 | \note The jacobi algorithm is should work great for all | 
| 399 | symmetric 3x3 matrices. If you need to find the eigenvectors | 
| 400 | of a non-symmetric matrix, you might want to resort to the | 
| 401 | sophisticated routines of LAPACK. | 
| 402 |  | 
| 403 | @param eigenvals a reference to a vector3 where the | 
| 404 | eigenvalues will be stored. The eigenvalues are ordered so | 
| 405 | that eigenvals[0] <= eigenvals[1] <= eigenvals[2]. | 
| 406 |  | 
| 407 | @return an orthogonal matrix whose ith column is an | 
| 408 | eigenvector for the eigenvalue eigenvals[i]. Here 'orthogonal' | 
| 409 | means that all eigenvectors have length one and are mutually | 
| 410 | orthogonal. The ith eigenvector can thus be conveniently | 
| 411 | accessed by the GetColumn() method, as in the following | 
| 412 | example. | 
| 413 | \code | 
| 414 | // Calculate eigenvectors and -values | 
| 415 | vector3 eigenvals; | 
| 416 | matrix3x3 eigenmatrix = somematrix.findEigenvectorsIfSymmetric(eigenvals); | 
| 417 |  | 
| 418 | // Print the 2nd eigenvector | 
| 419 | cout << eigenmatrix.GetColumn(1) << endl; | 
| 420 | \endcode | 
| 421 | With these conventions, a matrix is diagonalized in the following way: | 
| 422 | \code | 
| 423 | // Diagonalize the matrix | 
| 424 | matrix3x3 diagonalMatrix = eigenmatrix.inverse() * somematrix * eigenmatrix; | 
| 425 | \endcode | 
| 426 |  | 
| 427 | */ | 
| 428 | matrix3x3 matrix3x3::findEigenvectorsIfSymmetric(vector3 &eigenvals) | 
| 429 | { | 
| 430 | matrix3x3 result; | 
| 431 |  | 
| 432 | if (!isSymmetric()) | 
| 433 | { | 
| 434 | obErrorLog.ThrowError(__FUNCTION__, | 
| 435 | "The method was called on a matrix that was not symmetric, i.e. where isSymetric() == false.", obError); | 
| 436 | } | 
| 437 |  | 
| 438 | double d[3]; | 
| 439 | matrix3x3 copyOfThis = *this; | 
| 440 |  | 
| 441 | jacobi(3, copyOfThis.ele[0], d, result.ele[0]); | 
| 442 | eigenvals.Set(d); | 
| 443 |  | 
| 444 | return result; | 
| 445 | } | 
| 446 |  | 
| 447 | matrix3x3 &matrix3x3::operator/=(const double &c) | 
| 448 | { | 
| 449 | for (int row = 0;row < 3; row++) | 
| 450 | for (int col = 0;col < 3; col++) | 
| 451 | ele[row][col] /= c; | 
| 452 |  | 
| 453 | return(*this); | 
| 454 | } | 
| 455 |  | 
| 456 | #ifndef SQUARE | 
| 457 | #define SQUARE(x) ((x)*(x)) | 
| 458 | #endif | 
| 459 |  | 
| 460 | void matrix3x3::FillOrth(double Alpha,double Beta, double Gamma, | 
| 461 | double A, double B, double C) | 
| 462 | { | 
| 463 | double V; | 
| 464 |  | 
| 465 | Alpha *= DEG_TO_RAD; | 
| 466 | Beta  *= DEG_TO_RAD; | 
| 467 | Gamma *= DEG_TO_RAD; | 
| 468 |  | 
| 469 | // from the PDB specification: | 
| 470 | //  http://www.rcsb.org/pdb/docs/format/pdbguide2.2/part_75.html | 
| 471 |  | 
| 472 |  | 
| 473 | // since we'll ultimately divide by (a * b), we've factored those out here | 
| 474 | V = C * sqrt(1 - SQUARE(cos(Alpha)) - SQUARE(cos(Beta)) - SQUARE(cos(Gamma)) | 
| 475 | + 2 * cos(Alpha) * cos(Beta) * cos(Gamma)); | 
| 476 |  | 
| 477 | ele[0][0] = A; | 
| 478 | ele[0][1] = B*cos(Gamma); | 
| 479 | ele[0][2] = C*cos(Beta); | 
| 480 |  | 
| 481 | ele[1][0] = 0.0; | 
| 482 | ele[1][1] = B*sin(Gamma); | 
| 483 | ele[1][2] = C*(cos(Alpha)-cos(Beta)*cos(Gamma))/sin(Gamma); | 
| 484 |  | 
| 485 | ele[2][0] = 0.0; | 
| 486 | ele[2][1] = 0.0; | 
| 487 | ele[2][2] = V / (sin(Gamma)); // again, we factored out A * B when defining V | 
| 488 | } | 
| 489 |  | 
| 490 | ostream& operator<< ( ostream& co, const matrix3x3& m ) | 
| 491 |  | 
| 492 | { | 
| 493 | co << "[ " | 
| 494 | << m.ele[0][0] | 
| 495 | << ", " | 
| 496 | << m.ele[0][1] | 
| 497 | << ", " | 
| 498 | << m.ele[0][2] | 
| 499 | << " ]" << endl; | 
| 500 |  | 
| 501 | co << "[ " | 
| 502 | << m.ele[1][0] | 
| 503 | << ", " | 
| 504 | << m.ele[1][1] | 
| 505 | << ", " | 
| 506 | << m.ele[1][2] | 
| 507 | << " ]" << endl; | 
| 508 |  | 
| 509 | co << "[ " | 
| 510 | << m.ele[2][0] | 
| 511 | << ", " | 
| 512 | << m.ele[2][1] | 
| 513 | << ", " | 
| 514 | << m.ele[2][2] | 
| 515 | << " ]" << endl; | 
| 516 |  | 
| 517 | return co ; | 
| 518 | } | 
| 519 |  | 
| 520 | /*! This static function computes the eigenvalues and | 
| 521 | eigenvectors of a SYMMETRIC nxn matrix. This method is used | 
| 522 | internally by OpenBabel, but may be useful as a general | 
| 523 | eigenvalue finder. | 
| 524 |  | 
| 525 | The algorithm uses Jacobi transformations. It is described | 
| 526 | e.g. in Wilkinson, Reinsch "Handbook for automatic computation, | 
| 527 | Volume II: Linear Algebra", part II, contribution II/1. The | 
| 528 | implementation is also similar to the implementation in this | 
| 529 | book. This method is adequate to solve the eigenproblem for | 
| 530 | small matrices, of size perhaps up to 10x10. For bigger | 
| 531 | problems, you might want to resort to the sophisticated routines | 
| 532 | of LAPACK. | 
| 533 |  | 
| 534 | \note If you plan to find the eigenvalues of a symmetric 3x3 | 
| 535 | matrix, you will probably prefer to use the more convenient | 
| 536 | method findEigenvectorsIfSymmetric() | 
| 537 |  | 
| 538 | @param n the size of the matrix that should be diagonalized | 
| 539 |  | 
| 540 | @param a array of size n^2 which holds the symmetric matrix | 
| 541 | whose eigenvectors are to be computed. The convention is that | 
| 542 | the entry in row r and column c is addressed as a[n*r+c] where, | 
| 543 | of course, 0 <= r < n and 0 <= c < n. There is no check that the | 
| 544 | matrix is actually symmetric. If it is not, the behaviour of | 
| 545 | this function is undefined.  On return, the matrix is | 
| 546 | overwritten with junk. | 
| 547 |  | 
| 548 | @param d pointer to a field of at least n doubles which will be | 
| 549 | overwritten. On return of this function, the entries d[0]..d[n-1] | 
| 550 | will contain the eigenvalues of the matrix. | 
| 551 |  | 
| 552 | @param v an array of size n^2 where the eigenvectors will be | 
| 553 | stored. On return, the columns of this matrix will contain the | 
| 554 | eigenvectors. The eigenvectors are normalized and mutually | 
| 555 | orthogonal. | 
| 556 | */ | 
| 557 | void matrix3x3::jacobi(unsigned int n, double *a, double *d, double *v) | 
| 558 | { | 
| 559 | double onorm, dnorm; | 
| 560 | double b, dma, q, t, c, s; | 
| 561 | double  atemp, vtemp, dtemp; | 
| 562 | register int i, j, k, l; | 
| 563 | int nrot; | 
| 564 |  | 
| 565 | int MAX_SWEEPS=50; | 
| 566 |  | 
| 567 | // Set v to the identity matrix, set the vector d to contain the | 
| 568 | // diagonal elements of the matrix a | 
| 569 | for (j = 0; j < static_cast<int>(n); j++) | 
| 570 | { | 
| 571 | for (i = 0; i < static_cast<int>(n); i++) | 
| 572 | v[n*i+j] = 0.0; | 
| 573 | v[n*j+j] = 1.0; | 
| 574 | d[j] = a[n*j+j]; | 
| 575 | } | 
| 576 |  | 
| 577 | nrot = MAX_SWEEPS; | 
| 578 | for (l = 1; l <= nrot; l++) | 
| 579 | { | 
| 580 | // Set dnorm to be the maximum norm of the diagonal elements, set | 
| 581 | // onorm to the maximum norm of the off-diagonal elements | 
| 582 | dnorm = 0.0; | 
| 583 | onorm = 0.0; | 
| 584 | for (j = 0; j < static_cast<int>(n); j++) | 
| 585 | { | 
| 586 | dnorm += (double)fabs(d[j]); | 
| 587 | for (i = 0; i < j; i++) | 
| 588 | onorm += (double)fabs(a[n*i+j]); | 
| 589 | } | 
| 590 | // Normal end point of this algorithm. | 
| 591 | if((onorm/dnorm) <= 1.0e-12) | 
| 592 | goto Exit_now; | 
| 593 |  | 
| 594 | for (j = 1; j < static_cast<int>(n); j++) | 
| 595 | { | 
| 596 | for (i = 0; i <= j - 1; i++) | 
| 597 | { | 
| 598 |  | 
| 599 | b = a[n*i+j]; | 
| 600 | if(fabs(b) > 0.0) | 
| 601 | { | 
| 602 | dma = d[j] - d[i]; | 
| 603 | if((fabs(dma) + fabs(b)) <=  fabs(dma)) | 
| 604 | t = b / dma; | 
| 605 | else | 
| 606 | { | 
| 607 | q = 0.5 * dma / b; | 
| 608 | t = 1.0/((double)fabs(q) + (double)sqrt(1.0+q*q)); | 
| 609 | if (q < 0.0) | 
| 610 | t = -t; | 
| 611 | } | 
| 612 |  | 
| 613 | c = 1.0/(double)sqrt(t*t + 1.0); | 
| 614 | s = t * c; | 
| 615 | a[n*i+j] = 0.0; | 
| 616 |  | 
| 617 | for (k = 0; k <= i-1; k++) | 
| 618 | { | 
| 619 | atemp = c * a[n*k+i] - s * a[n*k+j]; | 
| 620 | a[n*k+j] = s * a[n*k+i] + c * a[n*k+j]; | 
| 621 | a[n*k+i] = atemp; | 
| 622 | } | 
| 623 |  | 
| 624 | for (k = i+1; k <= j-1; k++) | 
| 625 | { | 
| 626 | atemp = c * a[n*i+k] - s * a[n*k+j]; | 
| 627 | a[n*k+j] = s * a[n*i+k] + c * a[n*k+j]; | 
| 628 | a[n*i+k] = atemp; | 
| 629 | } | 
| 630 |  | 
| 631 | for (k = j+1; k < static_cast<int>(n); k++) | 
| 632 | { | 
| 633 | atemp = c * a[n*i+k] - s * a[n*j+k]; | 
| 634 | a[n*j+k] = s * a[n*i+k] + c * a[n*j+k]; | 
| 635 | a[n*i+k] = atemp; | 
| 636 | } | 
| 637 |  | 
| 638 | for (k = 0; k < static_cast<int>(n); k++) | 
| 639 | { | 
| 640 | vtemp = c * v[n*k+i] - s * v[n*k+j]; | 
| 641 | v[n*k+j] = s * v[n*k+i] + c * v[n*k+j]; | 
| 642 | v[n*k+i] = vtemp; | 
| 643 | } | 
| 644 |  | 
| 645 | dtemp = c*c*d[i] + s*s*d[j] - 2.0*c*s*b; | 
| 646 | d[j] = s*s*d[i] + c*c*d[j] +  2.0*c*s*b; | 
| 647 | d[i] = dtemp; | 
| 648 | } /* end if */ | 
| 649 | } /* end for i */ | 
| 650 | } /* end for j */ | 
| 651 | } /* end for l */ | 
| 652 |  | 
| 653 | Exit_now: | 
| 654 |  | 
| 655 | // Now sort the eigenvalues (and the eigenvectors) so that the | 
| 656 | // smallest eigenvalues come first. | 
| 657 | nrot = l; | 
| 658 |  | 
| 659 | for (j = 0; j < static_cast<int>(n)-1; j++) | 
| 660 | { | 
| 661 | k = j; | 
| 662 | dtemp = d[k]; | 
| 663 | for (i = j+1; i < static_cast<int>(n); i++) | 
| 664 | if(d[i] < dtemp) | 
| 665 | { | 
| 666 | k = i; | 
| 667 | dtemp = d[k]; | 
| 668 | } | 
| 669 |  | 
| 670 | if(k > j) | 
| 671 | { | 
| 672 | d[k] = d[j]; | 
| 673 | d[j] = dtemp; | 
| 674 | for (i = 0; i < static_cast<int>(n); i++) | 
| 675 | { | 
| 676 | dtemp = v[n*i+k]; | 
| 677 | v[n*i+k] = v[n*i+j]; | 
| 678 | v[n*i+j] = dtemp; | 
| 679 | } | 
| 680 | } | 
| 681 | } | 
| 682 | } | 
| 683 |  | 
| 684 | } // end namespace OpenBabel | 
| 685 |  | 
| 686 | //! \file matrix3x3.cpp | 
| 687 | //! \brief Handle 3D rotation matrix. | 
| 688 |  |