cgma
|
00001 //- Class: CubitPlane 00002 //- Description: This file defines the CubitPlane class. 00003 //- Owner: Greg Sjaardema 00004 //- Checked by: 00005 00006 00007 #include <cstdio> 00008 #include <cstdlib> 00009 #include <math.h> 00010 #include "CubitPlane.hpp" 00011 #include "CubitMatrix.hpp" 00012 #include "CubitVector.hpp" 00013 #include "DLIList.hpp" 00014 #include "CubitMessage.hpp" 00015 #include "CubitDefines.h" 00016 #include "GeometryDefines.h" 00017 00018 CubitPlane::CubitPlane(const double A, const double B, 00019 const double C, const double D) : normal_(A, B, C) 00020 { 00021 double length = normal_.length(); 00022 d_ = D / length; 00023 normal_ /= length; 00024 } 00025 00026 CubitPlane::CubitPlane() : normal_(0.0, 0.0, 0.0) 00027 { 00028 d_ = 0.0; 00029 } 00030 00031 CubitPlane::CubitPlane(const CubitVector &Normal, const double D) : 00032 normal_(Normal) 00033 { 00034 // make sure that Normal is not (0,0,0) causing a division by 0 00035 if(normal_.length()==0) 00036 throw std::invalid_argument ("Normal Length must not be 0"); 00037 //assert(!normal_.length()==0); 00038 00039 double length = normal_.length(); 00040 d_ = D / length; 00041 normal_ /= length; 00042 } 00043 00044 CubitPlane::CubitPlane(const CubitVector &Normal, const CubitVector &point) : 00045 normal_(Normal) 00046 { 00047 // plane_D = -(plane_normal.x()*X + plane_normal.y()*Y + plane_normal.z()*Z) 00048 normal_.normalize(); 00049 d_ = -1.0 * (normal_.x()*point.x() + normal_.y()*point.y() + 00050 normal_.z()*point.z()); 00051 } 00052 00053 void CubitPlane::set(const CubitVector &Normal, const CubitVector &point) 00054 { 00055 normal_ = Normal; 00056 normal_.normalize(); 00057 d_ = -1.0 * (normal_.x()*point.x() + normal_.y()*point.y() + 00058 normal_.z()*point.z()); 00059 } 00060 00061 CubitPlane::CubitPlane(DLIList<CubitVector> &positions) 00062 { 00063 // Newells method for determining approximate plane equation. 00064 // Plane equation is of the form: 00065 // 0 = plane_D +plane_normal.x()*X + plane_normal.y()*Y + plane_normal.z()*Z 00066 if(positions.size() < 3) 00067 throw std::invalid_argument("Must have 3 or more Points"); 00068 CubitVector vector_diff; 00069 CubitVector vector_sum; 00070 CubitVector ref_point = CubitVector (0.0, 0.0, 0.0); 00071 CubitVector tmp_vector; 00072 normal_ = CubitVector(0.0, 0.0, 0.0); 00073 CubitVector vector1, vector2; 00074 00075 for (int i=positions.size(); i > 0; i--) 00076 { 00077 vector1 = positions.get_and_step(); 00078 vector2 = positions.get(); 00079 vector_diff = (vector2) - (vector1); 00080 ref_point += (vector1); 00081 vector_sum = (vector1) + (vector2); 00082 00083 tmp_vector.set(vector_diff.y() * vector_sum.z(), 00084 vector_diff.z() * vector_sum.x(), 00085 vector_diff.x() * vector_sum.y()); 00086 normal_ += tmp_vector; 00087 } 00088 double divisor = positions.size() * normal_.length(); 00089 // if (divisor < .000000001) 00090 // { 00091 // PRINT_ERROR("Colinear basis vector in CubitPlane constructor"); 00092 // } 00093 d_ = (ref_point % normal_) / divisor; 00094 normal_.normalize(); 00095 normal_ *= -1.0; 00096 } 00097 00098 bool CubitPlane::fit_points(const std::vector<CubitVector> &positions) 00099 { 00100 //- use the method of linear regression 00101 // 00102 // by creating an Ax=b system where: 00103 // 00104 // ___ ___ 00105 // | x[i]*x[i], x[i]*y[i], x[i] | 00106 // A = sum_i | x[i]*y[i], y[i]*y[i], y[i] | 00107 // | x[i], y[i], n | 00108 // --- --- 00109 // ___ ___ 00110 // | x[i]*z[i] | 00111 // b = sum_i | y[i]*z[i] | 00112 // | z[i]} | 00113 // --- --- 00114 // But this assumes that z is a linear function of x and y (i.e. z component 00115 // of plane normal != 0.). We check the rank of A to catch this case. We 00116 // handle this case, by transposing the x or y coordinates for the z, 00117 // computing the normal, then transposing back (see itry below). 00118 00119 for ( int itry=0; itry < 3; itry++ ) 00120 { 00121 CubitMatrix A(3,3); 00122 std::vector<double> b(3); 00123 A.set(2,2,positions.size()); 00124 for ( size_t ipt=0; ipt<positions.size(); ipt++ ) 00125 { 00126 double x=0., y=0., z=0.; 00127 if ( itry == 0 ) 00128 { 00129 x = positions[ipt].x(); 00130 y = positions[ipt].y(); 00131 z = positions[ipt].z(); 00132 } 00133 else if ( itry == 1 ) 00134 { 00135 x = positions[ipt].x(); 00136 z = positions[ipt].y(); 00137 y = positions[ipt].z(); 00138 } 00139 else if ( itry == 2 ) 00140 { 00141 z = positions[ipt].x(); 00142 y = positions[ipt].y(); 00143 x = positions[ipt].z(); 00144 } 00145 double sum_xx = x*x + A.get(0,0); 00146 double sum_yy = y*y + A.get(1,1); 00147 double sum_xy = x*y + A.get(1,0); 00148 double sum_x = x + A.get(2,0); 00149 double sum_y = y + A.get(2,1); 00150 A.set(0,0,sum_xx); 00151 A.set(1,1,sum_yy); 00152 A.set(1,0,sum_xy); A.set(0,1,sum_xy); 00153 A.set(2,0,sum_x); A.set(0,2,sum_x); 00154 A.set(1,2,sum_y); A.set(2,1,sum_y); 00155 b[0] += x*z; 00156 b[1] += y*z; 00157 b[2] += z; 00158 } 00159 00160 // Make sure we have full rank. If not, it means: 00161 // if itry==0, plane is parallel to z-axis. 00162 // if itry==1, plane is parallel to z-axis & y-axis 00163 // if itry==2, points are colinear 00164 int rank = A.rank(); 00165 if ( rank == 3 ) 00166 { 00167 std::vector<double> coef(3); 00168 A.solveNxN( b, coef ); 00169 if ( itry == 0 ) 00170 normal_.set(coef[0], coef[1], -1); 00171 else if ( itry == 1 ) 00172 normal_.set(coef[0], -1, coef[1]); 00173 else if ( itry == 2 ) 00174 normal_.set(-1, coef[1], coef[0]); 00175 normal_.normalize(); 00176 d_ = 0; 00177 for ( size_t ipt=0; ipt<positions.size(); ipt++ ) 00178 { 00179 d_ += normal_.x()*positions[ipt].x() + 00180 normal_.y()*positions[ipt].y() + 00181 normal_.z()*positions[ipt].z(); 00182 } 00183 d_ /= positions.size(); 00184 return true; 00185 } 00186 } 00187 d_ = 0.0; 00188 normal_.set(0.,0.,0.); 00189 return false; 00190 } 00191 00192 CubitPlane::CubitPlane(const CubitPlane& copy_from) 00193 { 00194 normal_ = copy_from.normal_; 00195 d_ = copy_from.d_; 00196 } 00197 00198 int CubitPlane::mk_plane_with_points( const CubitVector& V0, 00199 const CubitVector& V1, 00200 const CubitVector& V2 ) 00201 { 00202 // First check to make sure that the points are not collinear 00203 00204 // Vector going from Vertex 0 to Vertex 1 00205 CubitVector vector01 = V1 - V0; 00206 vector01.normalize(); 00207 00208 // Vector going from Vertex 0 to Vertex 2 00209 CubitVector vector02 = V2 - V0; 00210 vector02.normalize(); 00211 00212 // If the 3 points are collinear, then the cross product of these two 00213 // vectors will yield a null vector (one whose length is zero). 00214 normal_ = vector01 * vector02; 00215 00216 if(normal_.length() <= CUBIT_RESABS) 00217 { 00218 PRINT_ERROR("Points are collinear.\n" 00219 " Cannot create a CubitPlane object.\n"); 00220 d_ = 0.0; 00221 return CUBIT_FAILURE; 00222 } 00223 00224 normal_.normalize(); 00225 00226 double D0 = -(normal_ % V0); 00227 double D1 = -(normal_ % V1); 00228 double D2 = -(normal_ % V2); 00229 d_ = (D0 + D1 + D2) / 3.0; 00230 00231 return CUBIT_SUCCESS; 00232 } 00233 00234 CubitVector CubitPlane::point_on_plane() const 00235 { 00236 if ( fabs( normal_.x() ) > CUBIT_RESABS ) 00237 return CubitVector(-d_ / normal_.x(), 0, 0); 00238 else if ( fabs( normal_.y() ) > CUBIT_RESABS ) 00239 return CubitVector(0, -d_ / normal_.y(), 0); 00240 else if ( fabs( normal_.z() ) > CUBIT_RESABS ) 00241 return CubitVector(0, 0, -d_ / normal_.z()); 00242 // If A B and C are all zero, the plane is invalid, 00243 // Just return <0,0,0> 00244 return CubitVector(0,0,0); 00245 } 00246 00247 //- Plane assignment operator. 00248 CubitPlane& CubitPlane::operator=(const CubitPlane &plane) 00249 { 00250 if (this != &plane) 00251 { 00252 normal_ = plane.normal_; 00253 d_ = plane.d_; 00254 } 00255 return *this; 00256 } 00257 00258 double CubitPlane::distance(const CubitVector &vector) const 00259 { 00260 return normal_ % vector + d_; 00261 } 00262 00263 CubitVector CubitPlane::intersect(const CubitVector &base, 00264 const CubitVector &direction) const 00265 { 00266 double t = -(normal_ % base + d_) / (normal_ % direction); 00267 return (base + direction * t); 00268 } 00269 00270 int CubitPlane::intersect(const CubitPlane &plane_2, 00271 CubitVector &origin, CubitVector &vector) const 00272 { 00273 // Code from Graphics Gems III, Georgiades 00274 // Calculate the line of intersection between two planes. 00275 // Initialize the unit direction vector of the line of intersection in 00276 // xdir. 00277 // Pick the point on the line of intersection on the coordinate plane most 00278 // normal to xdir. 00279 // Return TRUE if successful, FALSE otherwise (indicating that the planes 00280 // don't intersect). The order in which the planes are given determines the 00281 // choice of direction of xdir. 00282 // 00283 // int GetXLine(vect4 *pl1, vect4 *plane_2, vect3 *vector, vect3 *xpt) 00284 double invdet; // inverse of 2x2 matrix determinant 00285 vector = normal() * plane_2.normal(); 00286 CubitVector dir2(vector.x()*vector.x(), 00287 vector.y()*vector.y(), 00288 vector.z()*vector.z()); 00289 CubitVector plane1n = normal(); 00290 CubitVector plane2n = plane_2.normal(); 00291 00292 if (dir2.z() > dir2.y() && dir2.z() > dir2.x() && dir2.z() > CUBIT_RESABS) 00293 { 00294 // then get a point on the XY plane 00295 invdet = 1.0 / vector.z(); 00296 00297 //solve < pl1.x * origin.x + pl1.y * origin.y = - pl1.w > 00298 // < plane2n.x * origin.x + plane2n.y * origin.y = - plane2n.w > 00299 origin.set(plane1n.y() * plane_2.coefficient() - 00300 plane2n.y() * coefficient(), 00301 plane2n.x() * coefficient() - 00302 plane1n.x() * plane_2.coefficient(), 00303 0.0); 00304 } 00305 else if (dir2.y() > dir2.x() && dir2.y() > CUBIT_RESABS) 00306 { 00307 // then get a point on the XZ plane 00308 invdet = -1.0 / vector.y(); 00309 00310 // solve < pl1.x * origin.x + pl1.z * origin.z = -pl1.w > 00311 // < plane2n.x * origin.x + plane2n.z * origin.z = -plane2n.w > 00312 origin.set(plane1n.z() * plane_2.coefficient() - 00313 plane2n.z() * coefficient(), 00314 0.0, 00315 plane2n.x() * coefficient() - 00316 plane1n.x() * plane_2.coefficient()); 00317 } 00318 else if (dir2.x() > CUBIT_RESABS) 00319 { 00320 // then get a point on the YZ plane 00321 invdet = 1.0 / vector.x(); 00322 00323 // solve < pl1.y * origin.y + pl1.z * origin.z = - pl1.w > 00324 // < plane2n.y * origin.y + plane2n.z * origin.z = - plane2n.w > 00325 origin.set(0.0, 00326 plane1n.z() * plane_2.coefficient() - 00327 plane2n.z() * coefficient(), 00328 plane2n.y() * coefficient() - 00329 plane1n.y() * plane_2.coefficient()); 00330 } 00331 else // vector is zero, then no point of intersection exists 00332 return CUBIT_FALSE; 00333 00334 origin *= invdet; 00335 invdet = 1.0 / (float)sqrt(dir2.x() + dir2.y() + dir2.z()); 00336 vector *= invdet; 00337 return CUBIT_TRUE; 00338 } 00339 00340 CubitVector CubitPlane::project( const CubitVector& point ) const 00341 { 00342 return point - distance(point) * normal(); 00343 }