Mesh Oriented datABase
(version 5.4.1)
Array-based unstructured mesh datastructure
|
00001 /*========================================================================= 00002 00003 Module: $RCSfile: VerdictVector.cpp,v $ 00004 00005 Copyright (c) 2006 Sandia Corporation. 00006 All rights reserved. 00007 See Copyright.txt or http://www.kitware.com/Copyright.htm for details. 00008 00009 This software is distributed WITHOUT ANY WARRANTY; without even 00010 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 00011 PURPOSE. See the above copyright notice for more information. 00012 00013 =========================================================================*/ 00014 00015 /* 00016 * 00017 * VerdictVector.cpp contains implementation of Vector operations 00018 * 00019 * This file is part of VERDICT 00020 * 00021 */ 00022 00023 #define VERDICT_EXPORTS 00024 00025 #include "moab/verdict.h" 00026 #include <cmath> 00027 #include "VerdictVector.hpp" 00028 #include <cfloat> 00029 00030 #if defined( __BORLANDC__ ) 00031 #pragma warn - 8004 /* "assigned a value that is never used" */ 00032 #endif 00033 00034 const double TWO_VERDICT_PI = 2.0 * VERDICT_PI; 00035 00036 VerdictVector& VerdictVector::length( const double new_length ) 00037 { 00038 double len = this->length(); 00039 xVal *= new_length / len; 00040 yVal *= new_length / len; 00041 zVal *= new_length / len; 00042 return *this; 00043 } 00044 00045 double VerdictVector::distance_between( const VerdictVector& test_vector ) 00046 { 00047 double xv = xVal - test_vector.x(); 00048 double yv = yVal - test_vector.y(); 00049 double zv = zVal - test_vector.z(); 00050 00051 return ( sqrt( xv * xv + yv * yv + zv * zv ) ); 00052 } 00053 00054 /* 00055 void VerdictVector::print_me() 00056 { 00057 PRINT_INFO("X: %f\n",xVal); 00058 PRINT_INFO("Y: %f\n",yVal); 00059 PRINT_INFO("Z: %f\n",zVal); 00060 00061 } 00062 */ 00063 00064 double VerdictVector::interior_angle( const VerdictVector& otherVector ) 00065 { 00066 double cosAngle = 0., angleRad = 0., len1, len2 = 0.; 00067 00068 if( ( ( len1 = this->length() ) > 0 ) && ( ( len2 = otherVector.length() ) > 0 ) ) 00069 cosAngle = ( *this % otherVector ) / ( len1 * len2 ); 00070 else 00071 { 00072 assert( len1 > 0 ); 00073 assert( len2 > 0 ); 00074 } 00075 00076 if( ( cosAngle > 1.0 ) && ( cosAngle < 1.0001 ) ) 00077 { 00078 cosAngle = 1.0; 00079 angleRad = acos( cosAngle ); 00080 } 00081 else if( cosAngle < -1.0 && cosAngle > -1.0001 ) 00082 { 00083 cosAngle = -1.0; 00084 angleRad = acos( cosAngle ); 00085 } 00086 else if( cosAngle >= -1.0 && cosAngle <= 1.0 ) 00087 angleRad = acos( cosAngle ); 00088 else 00089 { 00090 assert( cosAngle < 1.0001 && cosAngle > -1.0001 ); 00091 } 00092 00093 return ( ( angleRad * 180. ) / VERDICT_PI ); 00094 } 00095 00096 // Interpolate between two vectors. 00097 // Returns (1-param)*v1 + param*v2 00098 VerdictVector interpolate( const double param, const VerdictVector& v1, const VerdictVector& v2 ) 00099 { 00100 VerdictVector temp = ( 1.0 - param ) * v1; 00101 temp += param * v2; 00102 return temp; 00103 } 00104 00105 void VerdictVector::xy_to_rtheta() 00106 { 00107 // careful about overwriting 00108 double r_ = length(); 00109 double theta_ = atan2( y(), x() ); 00110 if( theta_ < 0.0 ) theta_ += TWO_VERDICT_PI; 00111 00112 r( r_ ); 00113 theta( theta_ ); 00114 } 00115 00116 void VerdictVector::rtheta_to_xy() 00117 { 00118 // careful about overwriting 00119 double x_ = r() * cos( theta() ); 00120 double y_ = r() * sin( theta() ); 00121 00122 x( x_ ); 00123 y( y_ ); 00124 } 00125 00126 void VerdictVector::rotate( double angle, double ) 00127 { 00128 xy_to_rtheta(); 00129 theta() += angle; 00130 rtheta_to_xy(); 00131 } 00132 00133 void VerdictVector::blow_out( double gamma, double rmin ) 00134 { 00135 // if gamma == 1, then 00136 // map on a circle : r'^2 = sqrt( 1 - (1-r)^2 ) 00137 // if gamma ==0, then map back to itself 00138 // in between, linearly interpolate 00139 xy_to_rtheta(); 00140 // r() = sqrt( (2. - r()) * r() ) * gamma + r() * (1-gamma); 00141 assert( gamma > 0.0 ); 00142 // the following limits should really be roundoff-based 00143 if( r() > rmin * 1.001 && r() < 1.001 ) 00144 { 00145 r() = rmin + pow( r(), gamma ) * ( 1.0 - rmin ); 00146 } 00147 rtheta_to_xy(); 00148 } 00149 00150 void VerdictVector::reflect_about_xaxis( double, double ) 00151 { 00152 yVal = -yVal; 00153 } 00154 00155 void VerdictVector::scale_angle( double gamma, double ) 00156 { 00157 const double r_factor = 0.3; 00158 const double theta_factor = 0.6; 00159 00160 xy_to_rtheta(); 00161 00162 // if neary 2pi, treat as zero 00163 // some near zero stuff strays due to roundoff 00164 if( theta() > TWO_VERDICT_PI - 0.02 ) theta() = 0; 00165 // the above screws up on big sheets - need to overhaul at the sheet level 00166 00167 if( gamma < 1 ) 00168 { 00169 // squeeze together points of short radius so that 00170 // long chords won't cross them 00171 theta() += ( VERDICT_PI - theta() ) * ( 1 - gamma ) * theta_factor * ( 1 - r() ); 00172 00173 // push away from center of circle, again so long chords won't cross 00174 r( ( r_factor + r() ) / ( 1 + r_factor ) ); 00175 00176 // scale angle by gamma 00177 theta() *= gamma; 00178 } 00179 else 00180 { 00181 // scale angle by gamma, making sure points nearly 2pi are treated as zero 00182 double new_theta = theta() * gamma; 00183 if( new_theta < 2.5 * VERDICT_PI || r() < 0.2 ) theta( new_theta ); 00184 } 00185 rtheta_to_xy(); 00186 } 00187 00188 double VerdictVector::vector_angle_quick( const VerdictVector& vec1, const VerdictVector& vec2 ) 00189 { 00190 //- compute the angle between two vectors in the plane defined by this vector 00191 // build yAxis and xAxis such that xAxis is the projection of 00192 // vec1 onto the normal plane of this vector 00193 00194 // NOTE: vec1 and vec2 are Vectors from the vertex of the angle along 00195 // the two sides of the angle. 00196 // The angle returned is the right-handed angle around this vector 00197 // from vec1 to vec2. 00198 00199 // NOTE: vector_angle_quick gives exactly the same answer as vector_angle below 00200 // providing this vector is normalized. It does so with two fewer 00201 // cross-product evaluations and two fewer vector normalizations. 00202 // This can be a substantial time savings if the function is called 00203 // a significant number of times (e.g Hexer) ... (jrh 11/28/94) 00204 // NOTE: vector_angle() is much more robust. Do not use vector_angle_quick() 00205 // unless you are very sure of the safety of your input vectors. 00206 00207 VerdictVector ry = ( *this ) * vec1; 00208 VerdictVector rx = ry * ( *this ); 00209 00210 double xv = vec2 % rx; 00211 double yv = vec2 % ry; 00212 00213 double angle; 00214 assert( xv != 0.0 || yv != 0.0 ); 00215 00216 angle = atan2( yv, xv ); 00217 00218 if( angle < 0.0 ) 00219 { 00220 angle += TWO_VERDICT_PI; 00221 } 00222 return angle; 00223 } 00224 00225 VerdictVector vectorRotate( const double angle, const VerdictVector& normalAxis, const VerdictVector& referenceAxis ) 00226 { 00227 // A new coordinate system is created with the xy plane corresponding 00228 // to the plane normal to the normal axis, and the x axis corresponding to 00229 // the projection of the reference axis onto the normal plane. The normal 00230 // plane is the tangent plane at the root point. A unit vector is 00231 // constructed along the local x axis and then rotated by the given 00232 // ccw angle to form the new point. The new point, then is a unit 00233 // distance from the global origin in the tangent plane. 00234 00235 double x, y; 00236 00237 // project a unit distance from root along reference axis 00238 00239 VerdictVector yAxis = normalAxis * referenceAxis; 00240 VerdictVector xAxis = yAxis * normalAxis; 00241 yAxis.normalize(); 00242 xAxis.normalize(); 00243 00244 x = cos( angle ); 00245 y = sin( angle ); 00246 00247 xAxis *= x; 00248 yAxis *= y; 00249 return VerdictVector( xAxis + yAxis ); 00250 } 00251 00252 double VerdictVector::vector_angle( const VerdictVector& vector1, const VerdictVector& vector2 ) const 00253 { 00254 // This routine does not assume that any of the input vectors are of unit 00255 // length. This routine does not normalize the input vectors. 00256 // Special cases: 00257 // If the normal vector is zero length: 00258 // If a new one can be computed from vectors 1 & 2: 00259 // the normal is replaced with the vector cross product 00260 // else the two vectors are colinear and zero or 2PI is returned. 00261 // If the normal is colinear with either (or both) vectors 00262 // a new one is computed with the cross products 00263 // (and checked again). 00264 00265 // Check for zero length normal vector 00266 VerdictVector normal = *this; 00267 double normal_lensq = normal.length_squared(); 00268 double len_tol = 0.0000001; 00269 if( normal_lensq <= len_tol ) 00270 { 00271 // null normal - make it the normal to the plane defined by vector1 00272 // and vector2. If still null, the vectors are colinear so check 00273 // for zero or 180 angle. 00274 normal = vector1 * vector2; 00275 normal_lensq = normal.length_squared(); 00276 if( normal_lensq <= len_tol ) 00277 { 00278 double cosine = vector1 % vector2; 00279 if( cosine > 0.0 ) 00280 return 0.0; 00281 else 00282 return VERDICT_PI; 00283 } 00284 } 00285 00286 // Trap for normal vector colinear to one of the other vectors. If so, 00287 // use a normal defined by the two vectors. 00288 double dot_tol = 0.985; 00289 double dot = vector1 % normal; 00290 if( dot * dot >= vector1.length_squared() * normal_lensq * dot_tol ) 00291 { 00292 normal = vector1 * vector2; 00293 normal_lensq = normal.length_squared(); 00294 00295 // Still problems if all three vectors were colinear 00296 if( normal_lensq <= len_tol ) 00297 { 00298 double cosine = vector1 % vector2; 00299 if( cosine >= 0.0 ) 00300 return 0.0; 00301 else 00302 return VERDICT_PI; 00303 } 00304 } 00305 else 00306 { 00307 // The normal and vector1 are not colinear, now check for vector2 00308 dot = vector2 % normal; 00309 if( dot * dot >= vector2.length_squared() * normal_lensq * dot_tol ) 00310 { 00311 normal = vector1 * vector2; 00312 } 00313 } 00314 00315 // Assume a plane such that the normal vector is the plane's normal. 00316 // Create yAxis perpendicular to both the normal and vector1. yAxis is 00317 // now in the plane. Create xAxis as the perpendicular to both yAxis and 00318 // the normal. xAxis is in the plane and is the projection of vector1 00319 // into the plane. 00320 00321 normal.normalize(); 00322 VerdictVector yAxis = normal; 00323 yAxis *= vector1; 00324 double yv = vector2 % yAxis; 00325 // yAxis memory slot will now be used for xAxis 00326 yAxis *= normal; 00327 double xv = vector2 % yAxis; 00328 00329 // assert(x != 0.0 || y != 0.0); 00330 if( xv == 0.0 && yv == 0.0 ) 00331 { 00332 return 0.0; 00333 } 00334 double angle = atan2( yv, xv ); 00335 00336 if( angle < 0.0 ) 00337 { 00338 angle += TWO_VERDICT_PI; 00339 } 00340 return angle; 00341 } 00342 00343 bool VerdictVector::within_tolerance( const VerdictVector& vectorPtr2, double tolerance ) const 00344 { 00345 if( ( fabs( this->x() - vectorPtr2.x() ) < tolerance ) && ( fabs( this->y() - vectorPtr2.y() ) < tolerance ) && 00346 ( fabs( this->z() - vectorPtr2.z() ) < tolerance ) ) 00347 { 00348 return true; 00349 } 00350 00351 return false; 00352 } 00353 00354 void VerdictVector::orthogonal_vectors( VerdictVector& vector2, VerdictVector& vector3 ) 00355 { 00356 double xv[3]; 00357 unsigned short i = 0; 00358 unsigned short imin = 0; 00359 double rmin = 1.0E20; 00360 unsigned short iperm1[3]; 00361 unsigned short iperm2[3]; 00362 unsigned short cont_flag = 1; 00363 double vec1[3], vec2[3]; 00364 double rmag; 00365 00366 // Copy the input vector and normalize it 00367 VerdictVector vector1 = *this; 00368 vector1.normalize(); 00369 00370 // Initialize perm flags 00371 iperm1[0] = 1; 00372 iperm1[1] = 2; 00373 iperm1[2] = 0; 00374 iperm2[0] = 2; 00375 iperm2[1] = 0; 00376 iperm2[2] = 1; 00377 00378 // Get into the array format we can work with 00379 vector1.get_xyz( vec1 ); 00380 00381 while( i < 3 && cont_flag ) 00382 { 00383 if( fabs( vec1[i] ) < 1e-6 ) 00384 { 00385 vec2[i] = 1.0; 00386 vec2[iperm1[i]] = 0.0; 00387 vec2[iperm2[i]] = 0.0; 00388 cont_flag = 0; 00389 } 00390 00391 if( fabs( vec1[i] ) < rmin ) 00392 { 00393 imin = i; 00394 rmin = fabs( vec1[i] ); 00395 } 00396 ++i; 00397 } 00398 00399 if( cont_flag ) 00400 { 00401 xv[imin] = 1.0; 00402 xv[iperm1[imin]] = 0.0; 00403 xv[iperm2[imin]] = 0.0; 00404 00405 // Determine cross product 00406 vec2[0] = vec1[1] * xv[2] - vec1[2] * xv[1]; 00407 vec2[1] = vec1[2] * xv[0] - vec1[0] * xv[2]; 00408 vec2[2] = vec1[0] * xv[1] - vec1[1] * xv[0]; 00409 00410 // Unitize 00411 rmag = sqrt( vec2[0] * vec2[0] + vec2[1] * vec2[1] + vec2[2] * vec2[2] ); 00412 vec2[0] /= rmag; 00413 vec2[1] /= rmag; 00414 vec2[2] /= rmag; 00415 } 00416 00417 // Copy 1st orthogonal vector into VerdictVector vector2 00418 vector2.set( vec2 ); 00419 00420 // Cross vectors to determine last orthogonal vector 00421 vector3 = vector1 * vector2; 00422 } 00423 00424 //- Find next point from this point using a direction and distance 00425 void VerdictVector::next_point( const VerdictVector& direction, double distance, VerdictVector& out_point ) 00426 { 00427 VerdictVector my_direction = direction; 00428 my_direction.normalize(); 00429 00430 // Determine next point in space 00431 out_point.x( xVal + ( distance * my_direction.x() ) ); 00432 out_point.y( yVal + ( distance * my_direction.y() ) ); 00433 out_point.z( zVal + ( distance * my_direction.z() ) ); 00434 00435 return; 00436 } 00437 00438 VerdictVector::VerdictVector( const double xyz[3] ) : xVal( xyz[0] ), yVal( xyz[1] ), zVal( xyz[2] ) {}