cgma
BoundaryConstrainTool.cpp
Go to the documentation of this file.
00001 //-Class:       BoundaryConstrainTool
00002 //-Description: recover edges from a triangle mesh.  Currently 2D meshes only
00003 //-Owner:       Steve Owen
00004 //-Checked by:
00005 
00006 
00007 #include "GeometryDefines.h"
00008 #include "CubitVector.hpp"
00009 #include "BoundaryConstrainTool.hpp"
00010 
00011 #define ON_LINE_TOL 1.0e-7
00012 #ifndef SQR
00013 #define SQR(x) ((x) * (x))
00014 #endif
00015 
00016 //-----------------------------------------------------------------------------
00017 // Function:    BoundaryConstrainTool
00018 // Type:        Public
00019 // Description: constructor
00020 // Author:      sjowen
00021 // Altered by:  jitken(07/08/02)
00022 // Date:        2/17/02
00023 //-----------------------------------------------------------------------------
00024 template <class SURF, class TRI, class EDGE, class NODE, class TRICHILD> 
00025 BoundaryConstrainTool<SURF,TRI,EDGE,NODE,TRICHILD>::BoundaryConstrainTool(SURF *ref_face_ptr)
00026 {
00027   refFacePtr = ref_face_ptr;
00028 }
00029 
00030 
00031 //-----------------------------------------------------------------------------
00032 // Function:    BoundaryConstrainTool
00033 // Type:        Public
00034 // Description: constructor
00035 // Author:      sjowen
00036 // Altered by:  jitken(07/08/02)
00037 // Date:        2/17/02
00038 //-----------------------------------------------------------------------------
00039 template <class SURF, class TRI, class EDGE, class NODE, class TRICHILD> 
00040 BoundaryConstrainTool<SURF,TRI,EDGE,NODE,TRICHILD>::BoundaryConstrainTool()
00041 {
00042   refFacePtr = NULL;
00043 }
00044 
00045 //-----------------------------------------------------------------------------
00046 // Function:    ~BoundaryConstrainTool
00047 // Type:        Public
00048 // Description: desctructor
00049 // Author:      sjowen
00050 // Date:        2/17/02
00051 //-----------------------------------------------------------------------------
00052 template <class SURF, class TRI, class EDGE, class NODE, class TRICHILD> 
00053 BoundaryConstrainTool<SURF,TRI,EDGE,NODE,TRICHILD>::~BoundaryConstrainTool()
00054 {
00055 }
00056 
00057 //-----------------------------------------------------------------------------
00058 // Function:    ~BoundaryConstrainTool
00059 // Type:        Public
00060 // Description: desctructor
00061 // Author:      sjowen
00062 // Date:        2/17/02
00063 //-----------------------------------------------------------------------------
00064 
00065 //-----------------------------------------------------------------------------
00066 // Function:    recover_edge
00067 // Type:        Public
00068 // Description: flip edges in the triangulation to recover the edge between
00069 //              n0_ptr and n1_ptr. Assumes 2D (x-y-0)
00070 // Author:      sjowen
00071 // Altered by:  jitken(07/08/02)
00072 // Date:        2/17/02
00073 //-----------------------------------------------------------------------------
00074 template <class SURF, class TRI, class EDGE, class NODE, class TRICHILD> 
00075 CubitStatus BoundaryConstrainTool<SURF,TRI,EDGE,NODE,TRICHILD>::recover_edge(
00076   NODE *n0_ptr,
00077   NODE *n1_ptr,
00078   EDGE *&recovered_edge_ptr,
00079   DLIList <TRI *> *facet_list)
00080 {
00081   assert(n0_ptr && n1_ptr);
00082   CubitStatus rv = CUBIT_SUCCESS;
00083   
00084   //Update the private Variable
00085   facetList = facet_list;
00086 
00087   // TRIvial case: edge is already in the triangulation.  Return
00088   // now with the edge
00089   
00090   recovered_edge_ptr = n0_ptr->shared_edge(n1_ptr);
00091 
00092   if ( recovered_edge_ptr != NULL ) 
00093     return CUBIT_SUCCESS;
00094   
00095  
00096   // Define the vector from node0 to node1.  This is the direction
00097   // of the edge we want to recover
00098   
00099   
00100   CubitVector edge_vec = n1_ptr->coordinates() - n0_ptr->coordinates();
00101   double length = edge_vec.normalize( );
00102   if (length == 0.0)
00103     return CUBIT_FAILURE;
00104   
00105   zeroTol = length * CUBIT_RESABS;
00106   
00107   // Initialize the edge cross queue
00108   
00109   edgeCrossQueue.clean_out();
00110   
00111   // Fill in the Queue with edges that cross the edge_vec
00112   
00113   rv = get_crossing_edges( edge_vec, n0_ptr, n1_ptr );
00114   if (rv != CUBIT_SUCCESS)
00115     return rv;
00116   
00117   // Process each edge in the queue and swap edges on faces
00118   // adjacent to the edges crossing the recover vector
00119     
00120   EDGE *edge_ptr;
00121   SwapStatus swap_stat;
00122   while (edgeCrossQueue.size() > 0)
00123   {
00124     edge_ptr = edgeCrossQueue.remove();
00125     swap_stat = swap_edge( edge_ptr );
00126     switch (swap_stat) 
00127     {
00128       
00129     case SWAP_FAILURE:
00130       return CUBIT_FAILURE;
00131       
00132     case SWAP_INVALID:
00133       
00134       // If flip was not valid, then put it back at the end of
00135       // the queue to be processed later
00136       
00137       edgeCrossQueue.append( edge_ptr );
00138       break;
00139       
00140     case SWAP_SUCCESS:
00141       
00142       // Check if the edge just created by the flip also
00143       // crosses the recover vector.  If it does, then
00144       // add these at the end of the queue to be processed later
00145       
00146       if (edgeCrossQueue.size() > 0)
00147       {
00148         rv = edge_intersected( edge_vec, edge_ptr, n0_ptr, n1_ptr );
00149         if (rv != CUBIT_SUCCESS)
00150           return rv;
00151       }
00152     }
00153   }
00154   
00155   // Retrieve the recovered edge to pass back
00156   
00157   recovered_edge_ptr = n0_ptr->shared_edge(n1_ptr);
00158   if ( recovered_edge_ptr == NULL ) 
00159     rv = CUBIT_FAILURE;
00160   
00161   return rv;
00162 }
00163 
00164 
00165 
00166 //-----------------------------------------------------------------------------
00167 // Function:    get_crossing_edges
00168 // Type:        Private
00169 // Description: generate the initial list of all edges that cross the edge_vec
00170 //              between n0 and n1  
00171 // Author:      sjowen
00172 // Date:        2/17/02
00173 //-----------------------------------------------------------------------------
00174 template <class SURF, class TRI, class EDGE, class NODE, class TRICHILD> 
00175 CubitStatus BoundaryConstrainTool<SURF,TRI,EDGE,NODE,TRICHILD>::get_crossing_edges( 
00176   CubitVector &edge_vec, 
00177   NODE *n0_ptr, 
00178   NODE *n1_ptr )
00179 {
00180   CubitStatus status = CUBIT_FAILURE;
00181   IntersectionType isect_type = AT_BEGIN;
00182   IntersectionType last_isect_type = NO_ISECT;
00183   NODE *node_ptr;
00184   EDGE *edge_ptr;
00185   TRI *tri_ptr;
00186   CubitBoolean done = CUBIT_FALSE;
00187   
00188   do 
00189   {
00190     switch(isect_type) 
00191     {
00192     case AT_BEGIN:
00193       last_isect_type = AT_BEGIN;
00194       isect_type = intersect_from_node( edge_vec, n0_ptr, 
00195                                         tri_ptr, edge_ptr, node_ptr );
00196       break;
00197     case AT_EDGE:
00198       last_isect_type = AT_EDGE;
00199       edgeCrossQueue.append( edge_ptr );
00200       isect_type = intersect_from_edge( edge_vec, n0_ptr, n1_ptr,
00201                                         tri_ptr, edge_ptr, node_ptr );
00202       break;
00203     case AT_MID:
00204       status = node_at_mid( edge_vec, tri_ptr, node_ptr );
00205       if (status != CUBIT_SUCCESS) {
00206         return status;
00207       }
00208       isect_type = last_isect_type;
00209       break;
00210     case AT_END:
00211       done = CUBIT_TRUE;
00212       status = CUBIT_SUCCESS;
00213       break;
00214     case NO_ISECT:
00215       done = 1;
00216       return CUBIT_FAILURE;
00217     }
00218   } while (!done);
00219 
00220   return status;
00221 }
00222 
00223 //-----------------------------------------------------------------------------
00224 // Function:    intersect_from_node
00225 // Type:        Private
00226 // Description: determine next intersection assuming start from n0
00227 // Author:      sjowen
00228 // Date:        2/17/02
00229 //-----------------------------------------------------------------------------
00230 template <class SURF, class TRI, class EDGE, class NODE, class TRICHILD> 
00231 IntersectionType BoundaryConstrainTool<SURF,TRI,EDGE,NODE,TRICHILD>::intersect_from_node(
00232   CubitVector &edge_vec, 
00233   NODE *n0_ptr, 
00234   TRI *&tri_ptr, 
00235   EDGE *&edge_ptr, 
00236   NODE *&node_ptr ) 
00237 {
00238   IntersectionType isect_type = NO_ISECT;
00239 
00240   // get all tris adjacent to n0_ptr
00241   
00242   DLIList<TRI *> tri_list;
00243   n0_ptr->tris( tri_list );
00244   CubitBoolean found = CUBIT_FALSE;
00245   
00246   int itri;
00247   for (itri=0; itri<tri_list.size() && !found; itri++) 
00248   {
00249     tri_ptr = tri_list.get_and_step();
00250       
00251     // If the dot product of edges radiating from the node
00252     // are both greater than zero, then it is a candidate 
00253       
00254     NODE *n1_ptr = tri_ptr->next_node( n0_ptr );
00255     assert( n1_ptr != NULL );
00256     edge_ptr = n0_ptr->shared_edge( n1_ptr );
00257     assert( edge_ptr != NULL );
00258 
00259     // define the normal vector to the edge
00260 
00261     CubitVector vec = n1_ptr->coordinates() - n0_ptr->coordinates();
00262     vec.z( vec.x() );
00263     vec.x( -vec.y() );
00264     vec.y( vec.z() );
00265     vec.z( 0.0 );
00266     double length = sqrt(SQR(vec.x()) + SQR(vec.y()));
00267     if (length == 0.0)
00268     {
00269       node_ptr = NULL;
00270       edge_ptr = NULL;
00271       tri_ptr = NULL;
00272       return NO_ISECT;
00273     }
00274     vec.x( vec.x() / length );
00275     vec.y( vec.y() / length );
00276 
00277     // dot with the recover vector
00278 
00279     double dot = edge_vec.x() * vec.x() + edge_vec.y() * vec.y();
00280          
00281     if (dot > -ON_LINE_TOL) {
00282 
00283       // Dot product within tolerance of zero means the vector
00284       // follows along edge of face and passes through a node
00285 
00286       if (fabs(dot) < ON_LINE_TOL) {
00287     vec = n1_ptr->coordinates() - n0_ptr->coordinates();
00288     double direction = edge_vec.x() * vec.x() + edge_vec.y() * vec.y();
00289     //if vector n1-n0 is in the same direction as edge_vec
00290     if(direction > 0){
00291       vec = n1_ptr->coordinates() - n0_ptr->coordinates();
00292       node_ptr = n1_ptr;
00293       isect_type = AT_MID;
00294       return isect_type;
00295     }
00296       }
00297 
00298       // do the same check on the other edge
00299       
00300       NODE *n2_ptr = tri_ptr->next_node( n1_ptr );
00301       assert( n2_ptr != NULL );
00302       edge_ptr = n0_ptr->shared_edge( n2_ptr );
00303       assert( edge_ptr != NULL );
00304       vec = n0_ptr->coordinates() - n2_ptr->coordinates();
00305       vec.z( vec.x() );
00306       vec.x( -vec.y() );
00307       vec.y( vec.z() );
00308       vec.z( 0.0 );
00309       length = sqrt(SQR(vec.x()) + SQR(vec.y()));
00310       if (length == 0.0)
00311       {
00312         node_ptr = NULL;
00313         edge_ptr = NULL;
00314         tri_ptr = NULL;
00315         return NO_ISECT;
00316       }
00317       vec.x( vec.x() / length );
00318       vec.y( vec.y() / length );
00319       dot = edge_vec.x() * vec.x() + edge_vec.y() * vec.y();
00320       if (fabs(dot) < ON_LINE_TOL) {
00321     node_ptr = n2_ptr;
00322     isect_type = AT_MID;
00323     return isect_type;
00324       }
00325       
00326       if (dot > -ON_LINE_TOL){
00327     node_ptr = NULL;
00328     edge_ptr = n1_ptr->shared_edge( n2_ptr );
00329     isect_type = AT_EDGE;
00330     return isect_type;
00331       }
00332     }
00333   }
00334   
00335   if (!found)
00336   {
00337     isect_type = NO_ISECT;
00338     node_ptr = NULL;
00339     edge_ptr = NULL;
00340     tri_ptr = NULL;
00341   }
00342 
00343   return isect_type;
00344 }
00345 
00346 //-----------------------------------------------------------------------------
00347 // Function:    intersect_from_edge
00348 // Type:        Private
00349 // Description: determine next intersection assuming start from edge_ptr
00350 // Author:      sjowen
00351 // Date:        2/17/02
00352 //-----------------------------------------------------------------------------
00353 template <class SURF, class TRI, class EDGE, class NODE, class TRICHILD> 
00354 IntersectionType BoundaryConstrainTool<SURF,TRI,EDGE,NODE,TRICHILD>::intersect_from_edge( 
00355   CubitVector &edge_vec, 
00356   NODE *n0_ptr, 
00357   NODE *n1_ptr,
00358   TRI * &tri_ptr, 
00359   EDGE *&edge_ptr, 
00360   NODE *&node_ptr )
00361 {
00362   IntersectionType isect_type = NO_ISECT;
00363   assert( edge_ptr != NULL && tri_ptr != NULL);
00364 
00365   // Get the adjacent face to the edge - there must be exactly two faces
00366   // next to this edge otherwise we've left the triangulation
00367   
00368   DLIList<TRI *> adjtris;
00369   edge_ptr->tris( adjtris );
00370   if (adjtris.size() != 2) 
00371     return NO_ISECT;
00372   TRI * nexttri_ptr = adjtris.get_and_step();
00373   if (nexttri_ptr == tri_ptr)
00374   {
00375     nexttri_ptr = adjtris.get();
00376     assert(nexttri_ptr != NULL && nexttri_ptr != tri_ptr);
00377   }
00378   tri_ptr = nexttri_ptr;
00379   
00380   // Check if we've arrived at the end (Does the this triangle contain n1)
00381   
00382   int ii;
00383   NODE *n_ptr[3];
00384   nexttri_ptr->tri_nodes( n_ptr[0], n_ptr[1], n_ptr[2] );
00385   CubitBoolean found = CUBIT_FALSE;
00386   for (ii=0; ii<3 && !found; ii++) {
00387     if (n_ptr[ii] == n1_ptr) {
00388       found = CUBIT_TRUE;
00389       edge_ptr = NULL;
00390       node_ptr = n1_ptr;
00391       tri_ptr = nexttri_ptr;
00392       isect_type = AT_END;
00393     }
00394   }
00395   
00396   // Determine which edge (or node) the vector intersects
00397   
00398   if (!found) {
00399     NODE *tn0_ptr, *tn1_ptr, *tn2_ptr;
00400     tn0_ptr = edge_ptr->start_node();
00401     tn1_ptr = edge_ptr->end_node();
00402     tn2_ptr = tri_ptr->next_node(tn1_ptr);
00403     //Checking for CCW order. Correct it if it is wrong
00404     if(tn2_ptr == tn0_ptr){
00405       tn0_ptr = tn1_ptr;
00406       tn1_ptr = tn2_ptr;
00407       tn2_ptr = tri_ptr->next_node(tn1_ptr);
00408     }
00409     
00410     // Determine vector from n0 to tn2
00411     
00412     CubitVector vec( tn2_ptr->coordinates().x() - n0_ptr->coordinates().x(),
00413                      tn2_ptr->coordinates().y() - n0_ptr->coordinates().y(),
00414                      0.0 );
00415     double len = sqrt(SQR(vec.x()) + SQR(vec.y()));
00416     if (len == 0.0)
00417       return NO_ISECT;
00418     vec.x( vec.x()/len );
00419     vec.y( vec.y()/len );
00420     
00421     // Compute the dot product of the normal to vec (above)
00422     // with the edgevec. 
00423     // One of the following will result:
00424     // Dot > 0:  edge defined by tn2 - tn0 is intersected
00425     // Dot < 0:  edge defined by tn1 - tn2 is intersected
00426     // Dot = 0:  node tn2 is intersected  */
00427 
00428     vec.z( vec.x() );
00429     vec.x( -vec.y() );
00430     vec.y( vec.z() );
00431     vec.z( 0.0 );
00432 
00433     double dot = vec.x() * edge_vec.x() + vec.y() * edge_vec.y();
00434 
00435     if (fabs(dot) < ON_LINE_TOL) {
00436       isect_type = AT_MID;
00437       edge_ptr = NULL;
00438       node_ptr = tn2_ptr;
00439     }
00440     else if (dot > 0) {
00441       isect_type = AT_EDGE;
00442       edge_ptr = tn0_ptr->shared_edge( tn2_ptr );
00443       node_ptr = NULL;
00444     }
00445     else {
00446       isect_type = AT_EDGE;
00447       edge_ptr = tn1_ptr->shared_edge( tn2_ptr );
00448       node_ptr = NULL;
00449     }
00450   }
00451   
00452   return isect_type;
00453 }
00454 
00455 //-----------------------------------------------------------------------------
00456 // Function:    node_at_mid
00457 // Type:        Private
00458 // Description: the vector hit a node on its way to the n1
00459 // Author:      sjowen
00460 // Date:        2/17/02
00461 //-----------------------------------------------------------------------------
00462 template <class SURF, class TRI, class EDGE, class NODE, class TRICHILD> 
00463 CubitStatus BoundaryConstrainTool<SURF,TRI,EDGE,NODE,TRICHILD>::node_at_mid( 
00464   CubitVector &edge_vec, 
00465   TRI * tri_ptr, 
00466   NODE *node_ptr )
00467 {
00468   // don't know what to do here yet
00469   return CUBIT_FAILURE;
00470 }
00471 
00472 //-----------------------------------------------------------------------------
00473 // Function:    swap_edge
00474 // Type:        Private
00475 // Description: attempt to swap a single edge.  Check for valid swap 
00476 //              before doing so
00477 // Author:      sjowen
00478 // Date:        2/17/02
00479 //-----------------------------------------------------------------------------
00480 template <class SURF, class TRI, class EDGE, class NODE, class TRICHILD> 
00481 SwapStatus BoundaryConstrainTool<SURF,TRI,EDGE,NODE,TRICHILD>::swap_edge(  
00482   EDGE *&edge_ptr )
00483 {
00484 
00485   DLIList<TRI *>adjtris;
00486   edge_ptr->tris(refFacePtr, adjtris);
00487   if(adjtris.size() != 2)
00488     return SWAP_FAILURE;
00489 
00490   // check the potential new triangles
00491 
00492   TRI * tri0 = adjtris.get_and_step();
00493   TRI * tri1 = adjtris.get();
00494   NODE *n0, *n1, *n2, *n3;
00495   n0 = edge_ptr->start_node();
00496   n1 = edge_ptr->end_node();
00497   n2 = tri0 ->next_node(n1);
00498   //if direction is not in CCW order, correct it.
00499   if(n2 == n0){
00500     n0 = n1;
00501     n1 = n2;
00502     n2 = tri0->next_node(n1);
00503   }
00504   n3 = tri1->next_node(n0);
00505 
00506   CubitVector e1( n1->coordinates().x() - n3->coordinates().x(),
00507                   n1->coordinates().y() - n3->coordinates().y(),
00508                   0.0 );
00509   double len = sqrt(SQR(e1.x()) + SQR(e1.y()));
00510   if (len == 0.0)
00511     return SWAP_FAILURE;
00512   e1.x( e1.x()/len ); e1.y( e1.y()/len );
00513   CubitVector diag( n2->coordinates().x() - n3->coordinates().x(),
00514                     n2->coordinates().y() - n3->coordinates().y(),
00515                     0.0 );
00516   len = sqrt(SQR(diag.x()) + SQR(diag.y()));
00517   if (len == 0.0)
00518     return SWAP_FAILURE;
00519   diag.x( diag.x()/len ); diag.y( diag.y()/len );
00520 
00521   CubitVector e2( n0->coordinates().x() - n3->coordinates().x(),
00522                   n0->coordinates().y() - n3->coordinates().y(),
00523                   0.0 );
00524   len = sqrt(SQR(e2.x()) + SQR(e2.y()));
00525   if (len == 0.0)
00526     return SWAP_FAILURE;
00527   e2.x( e2.x()/len ); e2.y( e2.y()/len );
00528 
00529   // return now if we would create invalid triangles
00530 
00531   double cross = e1.x() * diag.y() - e1.y() * diag.x();
00532   if (cross < ON_LINE_TOL)
00533     return SWAP_INVALID;
00534 
00535   cross = diag.x() * e2.y() - diag.y() * e2.x();
00536   if (cross < ON_LINE_TOL)
00537     return SWAP_INVALID;
00538 
00539   // Do the swap
00540   // Delete the old triangles
00541 
00542   facetList->move_to(tri0);
00543   facetList->extract();
00544   facetList->move_to(tri1);
00545   facetList->extract();
00546   delete tri0;
00547   delete tri1;
00548   delete edge_ptr;
00549 
00550   // Create the new triangles
00551 
00552   tri0 = (TRI *) new TRICHILD( n1, n2, n3, refFacePtr);
00553   tri1 = (TRI *) new TRICHILD( n0, n3, n2, refFacePtr);
00554   facetList->append(tri0);
00555   facetList->append(tri1);
00556   edge_ptr = n2->shared_edge( n3 );
00557 
00558   return SWAP_SUCCESS;
00559 }
00560 
00561 //-----------------------------------------------------------------------------
00562 // Function:    edge_intersected
00563 // Type:        Private
00564 // Description: check to see if the edge we just generated crosses the
00565 //              edge_vec.  If so, the add it to the queue 
00566 // Author:      sjowen
00567 // Date:        2/17/02
00568 //-----------------------------------------------------------------------------
00569 template <class SURF, class TRI, class EDGE, class NODE, class TRICHILD> 
00570 CubitStatus BoundaryConstrainTool<SURF,TRI,EDGE,NODE,TRICHILD>::edge_intersected( 
00571    CubitVector &edge_vec, 
00572    EDGE *edge_ptr, 
00573    NODE *n0_ptr, 
00574    NODE *n1_ptr )
00575 {
00576 
00577   // if the end nodes match the end nodes of the edge we are checking, then
00578   // we are OK - no intersection
00579 
00580   NODE *en0_ptr = edge_ptr->start_node();
00581   NODE *en1_ptr = edge_ptr->end_node();
00582   if (en0_ptr == n0_ptr || en1_ptr == n0_ptr ||
00583       en0_ptr == n1_ptr || en1_ptr == n1_ptr)
00584     return CUBIT_SUCCESS;
00585 
00586   CubitVector vec1( en0_ptr->coordinates().x() - n0_ptr->coordinates().x(),
00587                     en0_ptr->coordinates().y() - n0_ptr->coordinates().y(),
00588                     0.0 );
00589   double len = sqrt( SQR( vec1.x() ) + SQR( vec1.y() ) );
00590   if (len == 0.0)
00591     return CUBIT_FAILURE;
00592   vec1.x( vec1.x() / len );  vec1.y( vec1.y() / len );
00593 
00594   CubitVector vec2( en1_ptr->coordinates().x() - n0_ptr->coordinates().x(),
00595                     en1_ptr->coordinates().y() - n0_ptr->coordinates().y(),
00596                     0.0 );
00597   len = sqrt( SQR( vec2.x() ) + SQR( vec2.y() ) );
00598   if (len == 0.0)
00599     return CUBIT_FAILURE;
00600   vec2.x( vec2.x() / len );  vec2.y( vec2.y() / len );
00601 
00602   double cross1 = edge_vec.x() * vec1.y() - edge_vec.y() * vec1.x();
00603   double cross2 = edge_vec.x() * vec2.y() - edge_vec.y() * vec2.x();
00604 
00605   // if the en0 and en1 are both on opposite sides of edge_vec then
00606   // we know that it intersects somewhere
00607 
00608   if (cross1 * cross2 <= 0.0)
00609   {
00610     edgeCrossQueue.append( edge_ptr );
00611   }
00612 
00613   return CUBIT_SUCCESS;
00614 }
00615 // EOF
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines