cgma
|
00001 // Class: CubitTransformMatrix 00002 // 00003 // Description: A 4-Dimensional Matrix. Essentially the same as 00004 // a generic CubitMatrix, except that it has some 00005 // extra 3D transformation functions. 00006 // 00007 // All transformations are pre-multiplications, 00008 // meaning that M*V will transform a point V 00009 // in the same order transformations are applied to M. 00010 // 00011 // Owner: Darryl Melander 00012 00013 #include "CubitTransformMatrix.hpp" 00014 #include "CubitMessage.hpp" 00015 00016 CubitTransformMatrix::CubitTransformMatrix() 00017 : CubitMatrix(4) 00018 { 00019 // Just creates a 4x4 matrix set to identity 00020 } 00021 00022 CubitTransformMatrix::CubitTransformMatrix(const CubitTransformMatrix& from) 00023 : CubitMatrix(from) 00024 { 00025 // Just copies it 00026 } 00027 00028 00029 CubitTransformMatrix::~CubitTransformMatrix() 00030 { 00031 // Just deletes it 00032 } 00033 00034 00035 00036 CubitTransformMatrix& CubitTransformMatrix::translate(const CubitVector& v) 00037 { 00038 return translate(v.x(), v.y(), v.z()); 00039 } 00040 00041 00042 CubitTransformMatrix& CubitTransformMatrix::translate (double x, double y, double z) 00043 { 00044 set (0, 3, get(0, 3) + x); 00045 set (1, 3, get(1, 3) + y); 00046 set (2, 3, get(2, 3) + z); 00047 00048 return *this; 00049 } 00050 00051 CubitTransformMatrix& CubitTransformMatrix::rotate(double degrees, 00052 const CubitVector& vector) 00053 { 00054 double angle, ct, st; 00055 // Make a copy of vector so we don't have to change it 00056 CubitVector axis = vector; 00057 00058 // Convert degrees to radians 00059 angle = -degrees * CUBIT_PI/180.0; 00060 00061 // Take sin and cos 00062 ct = cos(angle); 00063 st = sin(angle); 00064 00065 // Normalize the axis vector 00066 axis.normalize(); 00067 00068 // Make an identity matrix 00069 CubitTransformMatrix mat; 00070 00071 // Setup some calculations that occur repeatedly 00072 double one_minus_cos = 1.0 - ct; 00073 double dx_ct = axis.x() * one_minus_cos; 00074 double dy_ct = axis.y() * one_minus_cos; 00075 double dz_ct = axis.z() * one_minus_cos; 00076 double dx_st = axis.x() * st; 00077 double dy_st = axis.y() * st; 00078 double dz_st = axis.z() * st; 00079 00080 // Set the values in the matrix 00081 mat.set(0, 0, ct + axis.x() * dx_ct); 00082 mat.set(1, 0, -dz_st + axis.x() * dy_ct); 00083 mat.set(2, 0, dy_st + axis.x() * dz_ct); 00084 00085 mat.set(0, 1, dz_st + axis.y() * dx_ct); 00086 mat.set(1, 1, ct + axis.y() * dy_ct); 00087 mat.set(2, 1, -dx_st + axis.y() * dz_ct); 00088 00089 mat.set(0, 2, -dy_st + axis.z() * dx_ct); 00090 mat.set(1, 2, dx_st + axis.z() * dy_ct); 00091 mat.set(2, 2, ct + axis.z() * dz_ct); 00092 00093 // Premultiply the matrix 00094 *this = mat * *this; 00095 // Return 00096 return *this; 00097 } 00098 00099 CubitTransformMatrix& CubitTransformMatrix::rotate(double degrees, char axis) 00100 { 00101 if(axis != 'x' && axis != 'y' && axis != 'z') 00102 throw std::invalid_argument("Invalid Axis: must be X, Y, or Z"); 00103 //assert (axis == 'x' || axis == 'y' || axis == 'z'); 00104 00105 // Convert to Radians, Get the sine and cosine 00106 double angle = degrees * CUBIT_PI/180.; 00107 double s, c; 00108 s = sin(angle); 00109 c = cos(angle); 00110 00111 // Make an Identity matrix 00112 CubitTransformMatrix mat; 00113 // Place values in appropriate places 00114 switch (axis) 00115 { 00116 case 'x': 00117 mat.set(1, 1, c); 00118 mat.set(2, 2, c); 00119 mat.set(1, 2, -s); 00120 mat.set(2, 1, s); 00121 break; 00122 case 'y': 00123 mat.set(0, 0, c); 00124 mat.set(0, 2, s); 00125 mat.set(2, 0, -s); 00126 mat.set(2, 2, c); 00127 break; 00128 case 'z': 00129 mat.set(0, 0, c); 00130 mat.set(1, 0, s); 00131 mat.set(0, 1, -s); 00132 mat.set(1, 1, c); 00133 break; 00134 } 00135 00136 // Perform Pre-Multiplication 00137 *this = mat * *this; 00138 00139 return *this; 00140 } 00141 00142 CubitTransformMatrix& CubitTransformMatrix::reflect(const CubitVector& vector) 00143 { 00144 double a, b, c, d; 00145 CubitVector axis = vector; 00146 axis.normalize(); 00147 00148 a = axis.x(); 00149 b = axis.y(); 00150 c = axis.z(); 00151 00152 d = sqrt(b*b + c*c); 00153 00154 // Make an Identity matrix 00155 CubitTransformMatrix mat; 00156 if(d) 00157 { 00158 // Place values in appropriate places for negative rotate about x 00159 mat.set(1, 1, c/d); 00160 mat.set(1, 2, -b/d); 00161 mat.set(2, 1, b/d); 00162 mat.set(2, 2, c/d); 00163 00164 // Perform Pre-Multiplication 00165 *this = mat * *this; 00166 mat.set_to_identity(); 00167 } 00168 00169 // Place values in appropriate places for negative rotate about y 00170 mat.set(0, 0, d); 00171 mat.set(0, 2, -a); 00172 mat.set(2, 0, a); 00173 mat.set(2, 2, d); 00174 00175 00176 // Perform Pre-Multiplication 00177 *this = mat * *this; 00178 mat.set_to_identity(); 00179 00180 // Place values in appropriate places for reflect across z 00181 mat.set(2, 2, -1); 00182 00183 00184 // Perform Pre-Multiplication 00185 *this = mat * *this; 00186 mat.set_to_identity(); 00187 00188 // Place values in appropriate places for rotate about y 00189 mat.set(0, 0, d); 00190 mat.set(0, 2, a); 00191 mat.set(2, 0, -a); 00192 mat.set(2, 2, d); 00193 00194 00195 // Perform Pre-Multiplication 00196 *this = mat * *this; 00197 mat.set_to_identity(); 00198 00199 if(d) 00200 { 00201 // Place values in appropriate places for rotate about x 00202 mat.set(1, 1, c/d); 00203 mat.set(1, 2, b/d); 00204 mat.set(2, 1, -b/d); 00205 mat.set(2, 2, c/d); 00206 00207 // Perform Pre-Multiplication 00208 *this = mat * *this; 00209 mat.set_to_identity(); 00210 } 00211 00212 return *this; 00213 00214 } 00215 00216 CubitTransformMatrix& CubitTransformMatrix::rotate(double degrees, 00217 const CubitVector& axis_from, 00218 const CubitVector& axis_to) 00219 { 00220 // Translate so that axis_from is at origin 00221 translate (-axis_from); 00222 00223 // Rotate about specified axis 00224 rotate (degrees, axis_to - axis_from); 00225 00226 // Translate back 00227 translate (axis_from); 00228 00229 return *this; 00230 } 00231 00232 CubitTransformMatrix& CubitTransformMatrix::scale_about_origin (const CubitVector& scale) 00233 { 00234 return scale_about_origin(scale.x(), scale.y(), scale.z()); 00235 } 00236 00237 CubitTransformMatrix& CubitTransformMatrix::scale_about_origin (double x, double y, double z) 00238 { 00239 CubitTransformMatrix mat; 00240 mat.set(0, 0, x); 00241 mat.set(1, 1, y); 00242 mat.set(2, 2, z); 00243 00244 // Perform Pre-Multiplication 00245 *this = mat * *this; 00246 00247 return *this; 00248 } 00249 00250 CubitTransformMatrix& CubitTransformMatrix::scale_about_origin (double scale) 00251 { 00252 return scale_about_origin(scale, scale, scale); 00253 } 00254 00255 CubitTransformMatrix& CubitTransformMatrix::inverse() 00256 { 00257 CubitMatrix matrix = *this; 00258 matrix = matrix.inverse(); 00259 for( int ii = 0; ii < 4; ii++ ) 00260 { 00261 for( int jj = 0; jj < 4; jj++ ) 00262 { 00263 set( ii, jj, matrix.get(ii,jj) ); 00264 } 00265 } 00266 return *this; 00267 } 00268 00269 00270 // Post-multiplication of a point (M*V) 00271 CubitVector CubitTransformMatrix::operator* (const CubitVector& point) const 00272 { 00273 // Make a sub-matrix, multiply the point by it. 00274 CubitVector vec = (this->sub_matrix(3, 3))*point; 00275 // Handle the fourth column here 00276 vec.x(vec.x() + get(0, 3)); 00277 vec.y(vec.y() + get(1, 3)); 00278 vec.z(vec.z() + get(2, 3)); 00279 00280 return vec; 00281 } 00282 00283 00284 // point * matrix 00285 CUBIT_UTIL_EXPORT 00286 CubitVector operator* (const CubitVector& point, 00287 const CubitTransformMatrix& matrix) 00288 { 00289 // Make a 1x4 matrix, multiply matrix by it. 00290 CubitMatrix m1(1,4); 00291 m1.set(0, 0, point.x()); 00292 m1.set(0, 1, point.y()); 00293 m1.set(0, 2, point.z()); 00294 m1.set(0, 3, 1); 00295 00296 // Perform the multiplication 00297 m1 = m1 * matrix; 00298 // The result is a 1x4 00299 00300 // Put the results into a vector (dividing by w), and return 00301 double w = m1.get(0,3); 00302 return CubitVector(m1.get(0,0)/w, m1.get(0,1)/w, m1.get(0,2)/w); 00303 } 00304 00305 CubitTransformMatrix CubitTransformMatrix::operator*( 00306 const CubitTransformMatrix& matrix) const 00307 { 00308 CubitTransformMatrix rv; 00309 CubitMatrix temp(4); 00310 temp = CubitMatrix::operator*(matrix); 00311 for (int i = 0; i < 4; i++) 00312 for (int j = 0; j < 4; j++) 00313 rv.set(i, j, temp.get(i,j)); 00314 return rv; 00315 } 00316 00317 CubitMatrix CubitTransformMatrix::operator*(const CubitMatrix& matrix) const 00318 { 00319 return CubitMatrix::operator*(matrix); 00320 } 00321 00322 CubitTransformMatrix CubitTransformMatrix::operator*(double val) const 00323 { 00324 CubitTransformMatrix rv; 00325 for (int i = 0; i < 4; i++) 00326 for (int j = 0; j < 4; j++) 00327 rv.set(i, j, this->get(i,j)*val); 00328 return rv; 00329 } 00330 00331 void CubitTransformMatrix::print_me() const 00332 { 00333 PRINT_INFO("%8.4f %8.4f %8.4f %8.4f\n", 00334 get(0,0), get(0,1), get(0,2), get(0,3)); 00335 PRINT_INFO("%8.4f %8.4f %8.4f %8.4f\n", 00336 get(1,0), get(1,1), get(1,2), get(1,3)); 00337 PRINT_INFO("%8.4f %8.4f %8.4f %8.4f\n", 00338 get(2,0), get(2,1), get(2,2), get(2,3)); 00339 PRINT_INFO("%8.4f %8.4f %8.4f %8.4f\n", 00340 get(3,0), get(3,1), get(3,2), get(3,3)); 00341 } 00342 00344 CubitVector CubitTransformMatrix::origin() const 00345 { 00346 return CubitVector(this->get(0,3), this->get(1,3), this->get(2,3)); 00347 } 00348 00350 CubitVector CubitTransformMatrix::x_axis() const 00351 { 00352 CubitMatrix tmp1(4,1); 00353 tmp1.set(0,0,1); 00354 tmp1.set(1,0,0); 00355 tmp1.set(2,0,0); 00356 tmp1.set(3,0,0); 00357 CubitMatrix tmp2 = (*this) * tmp1; 00358 return CubitVector(tmp2.get(0,0), tmp2.get(1,0), tmp2.get(2,0)); 00359 } 00360 00362 CubitVector CubitTransformMatrix::y_axis() const 00363 { 00364 CubitMatrix tmp1(4,1); 00365 tmp1.set(0,0,0); 00366 tmp1.set(1,0,1); 00367 tmp1.set(2,0,0); 00368 tmp1.set(3,0,0); 00369 CubitMatrix tmp2 = (*this) * tmp1; 00370 return CubitVector(tmp2.get(0,0), tmp2.get(1,0), tmp2.get(2,0)); 00371 } 00372 00374 CubitVector CubitTransformMatrix::z_axis() const 00375 { 00376 CubitMatrix tmp1(4,1); 00377 tmp1.set(0,0,0); 00378 tmp1.set(1,0,0); 00379 tmp1.set(2,0,1); 00380 tmp1.set(3,0,0); 00381 CubitMatrix tmp2 = (*this) * tmp1; 00382 return CubitVector(tmp2.get(0,0), tmp2.get(1,0), tmp2.get(2,0)); 00383 } 00384 00385 00386 CubitTransformMatrix CubitTransformMatrix::construct_matrix(const CubitVector& origin, 00387 const CubitVector& x_axis, 00388 const CubitVector& y_axis) 00389 { 00390 CubitTransformMatrix mat; 00391 double angle = x_axis.interior_angle(CubitVector(1,0,0)); 00392 CubitVector axis = angle == 180 ? CubitVector(0,1,0) : CubitVector(1,0,0) * x_axis; 00393 mat.rotate(angle, axis); 00394 CubitVector y = mat * CubitVector(0,1,0); 00395 angle = y_axis.interior_angle(y); 00396 axis = angle == 180 ? CubitVector(0,0,1) : y * y_axis; 00397 mat.rotate(angle, axis); 00398 mat.translate(origin); 00399 return mat; 00400 } 00401 00402 void CubitTransformMatrix::get_rotation_axis_and_angle(CubitVector &rotation_axis, double &angle) 00403 { 00404 double cos_theta = (this->get(0,0) + this->get(1,1) + this->get(2,2) - 1) / 2; 00405 00406 double x = 0; 00407 double y = 0; 00408 double z = 0; 00409 00410 if (fabs(cos_theta - 1) < .0001) 00411 { 00412 // theta is 1 or almost 1 00413 angle = 0; 00414 x = 0; 00415 y = 0; 00416 z = 1; 00417 } 00418 else if (fabs(cos_theta + 1) > .0001) 00419 { 00420 // theta is NOT -1 or almost -1 00421 angle = acos(cos_theta); 00422 angle = (angle * 180.0) / CUBIT_PI; // convert to degrees 00423 double sin_theta = sqrt(1 - cos_theta * cos_theta); 00424 x = ( (this->get(2,1) - this->get(1,2)) / 2 ) / sin_theta; 00425 y = ( (this->get(0,2) - this->get(2,0)) / 2 ) / sin_theta; 00426 z = ( (this->get(1,0) - this->get(0,1)) / 2 ) / sin_theta; 00427 } 00428 else 00429 { 00430 angle = 180; 00431 if (this->get(0,0) >= this->get(1,1)) 00432 { 00433 00434 if (this->get(0,0) >= this->get(2,2)) 00435 { 00436 // 0,0 is maximal diagonal term 00437 x = sqrt(this->get(0,0) - this->get(1,1) - this->get(2,2) + 1) / 2; 00438 double half_inverse = 1 / (2 * x); 00439 y = half_inverse * this->get(0,1); 00440 z = half_inverse * this->get(0,2); 00441 } 00442 else 00443 { 00444 // 2,2 is maximal diagonal term 00445 z = sqrt(this->get(2,2) - this->get(0,0) - this->get(1,1) + 1) / 2; 00446 double half_inverse = 1 / (2 * z); 00447 x = half_inverse * this->get(0,2); 00448 y = half_inverse * this->get(1,2); 00449 } 00450 } 00451 else 00452 { 00453 if (this->get(1,1) >= this->get(2,2)) 00454 { 00455 // 1,1 is maximal diagonal term 00456 y = sqrt(this->get(1,1) - this->get(0,0) - this->get(2,2) + 1) / 2; 00457 double half_inverse = 1 / (2 * y); 00458 x = half_inverse * this->get(0,1); 00459 z = half_inverse * this->get(1,2); 00460 } 00461 else 00462 { 00463 // 2,2 is maximal diagonal term 00464 z = sqrt(this->get(2,2) - this->get(0,0) - this->get(1,1) + 1) / 2; 00465 double half_inverse = 1 / (2 * z); 00466 x = half_inverse * this->get(0,2); 00467 y = half_inverse * this->get(1,2); 00468 } 00469 } 00470 00471 } 00472 00473 rotation_axis.x(x); 00474 rotation_axis.y(y); 00475 rotation_axis.z(z); 00476 }