00001 
00002 
00003 
00004 
00005 
00006 
00007 #include <stdlib.h>
00008 #include <stdio.h>
00009 #include <setjmp.h>
00010 #include <math.h>
00011 #include <windows.h>
00012 
00013 #include "struct.h"
00014 #include "dstruct.h"
00015 
00016 #include "chull.h"
00017 
00018 jmp_buf jmp_buf_here;
00019 
00020 
00021 typedef BOOL    bool;
00022 
00023 typedef struct tVertexStructure tsVertex;
00024 typedef tsVertex *tVertex;
00025 
00026 typedef struct tEdgeStructure tsEdge;
00027 typedef tsEdge *tEdge;
00028 
00029 typedef struct tFaceStructure tsFace;
00030 typedef tsFace *tFace;
00031 
00032 struct tVertexStructure {
00033    int      v[3];
00034    int      vnum;
00035    int      OFXid;           
00036    tEdge    duplicate;       
00037    bool     onhull;                    
00038    bool     mark;            
00039    tVertex  next, prev;
00040 };
00041 
00042 struct tEdgeStructure {
00043    tFace    adjface[2];
00044    tVertex  endpts[2];
00045    tFace    newface;            
00046    bool     HULL_DELETE;                
00047    tEdge    next, prev;
00048 };
00049 
00050 struct tFaceStructure {
00051    tEdge    edge[3];
00052    tVertex  vertex[3];
00053    bool     visible;            
00054    tFace    next, prev;
00055 };
00056 
00057 
00058 
00059 
00060 
00061 
00062 
00063 
00064 
00065 #define SWAP(t,x,y)     { t = x; x = y; y = t; }
00066 
00067 
00068 #define NEW(p,type)     if ((p=(type *) malloc (sizeof(type))) == NULL) {\
00069                                 longjmp(jmp_buf_here,-1);\
00070                         }
00071 
00072 #define FREE(p)         if (p) { free ((char *) p); p = NULL; }
00073 
00074 
00075 #define HULL_ADD( head, p )  if ( head )  { \
00076                                 p->next = head; \
00077                                 p->prev = head->prev; \
00078                                 head->prev = p; \
00079                                 p->prev->next = p; \
00080                         } \
00081                         else { \
00082                                 head = p; \
00083                                 head->next = head->prev = p; \
00084                         }
00085 
00086 #define HULL_DELETE( head, p ) if ( head )  { \
00087                                 if ( head == head->next ) \
00088                                         head = NULL;  \
00089                                 else if ( p == head ) \
00090                                         head = head->next; \
00091                                 p->next->prev = p->prev;  \
00092                                 p->prev->next = p->next;  \
00093                                 FREE( p ); \
00094                         } 
00095 
00097 
00098 #define DESELECTED 0
00099 #define SELECTED   1
00100 #define MINUNIT       2048L     // was 1024 trying larger to prevent error
00101 #define MAXUNIT   33554432L
00102 
00103 #ifndef min
00104 #define min(a,b)  ( ((a) < (b)) ? (a) : (b) )
00105 #endif
00106 #ifndef max
00107 #define max(a,b)  ( ((a) > (b)) ? (a) : (b) )
00108 #endif
00109 
00110 BOOL    DoubleTriangle( void );
00111 void    ConstructHull( void );
00112 tVertex MakeNullVertex( void );
00113 void    DeleteTemporaryStructures(void);
00114 
00115 
00116 #define X   0
00117 #define Y   1
00118 #define Z   2
00119 
00120 
00121 tVertex vertices = NULL;
00122 tEdge edges      = NULL;
00123 tFace faces      = NULL;
00124 
00125 static HWND      hParent;
00126 static HINSTANCE hThisInstance;
00127 
00128 #if __WATCOMC__
00129 int APIENTRY LibMain(HANDLE hDLL, DWORD dwReason, LPVOID lpReserved){
00130 #else
00131 BOOL WINAPI DllMain(HANDLE hDLL, DWORD dwReason, LPVOID lpReserved){
00132 #endif
00133   switch (dwReason) {
00134     case DLL_PROCESS_ATTACH: {
00135       hThisInstance=hDLL;
00136       if(hDLL == NULL)MessageBox ( GetFocus()," NULL instance",NULL, MB_OK);
00137       break;
00138     }
00139     case DLL_PROCESS_DETACH:
00140       break;
00141   }
00142   return TRUE;
00143 }
00144 
00145 BOOL _Xmodeler
00146  (HWND parent_window,HWND info_window,X__STRUCTURE *lpevi){
00147  char text_string[255],name_string[255];
00148  lpEVI=lpevi;
00149  hParent=parent_window;
00150  LoadString(hThisInstance,IDX_STRING2,text_string,256);
00151  LoadString(hThisInstance,IDX_STRING1,name_string,256);
00152  if(MessageBox(hParent,text_string,name_string,MB_OKCANCEL)
00153                == IDCANCEL)return FALSE;
00154  else{
00155    HCURSOR hSave;
00156    vertex *vp;
00157    long i,maxdim;
00158    long xmin=MAXUNIT,xmax=-MAXUNIT,ymin=MAXUNIT,ymax=-MAXUNIT,zmin=MAXUNIT,zmax=-MAXUNIT;
00159    double scale;
00160    if(NvertSelect < 4 || NvertSelect > 32000)return FALSE;
00161    hSave=SetCursor(LoadCursor(NULL,IDC_WAIT));
00162    vp=MainVp; for(i=0;i<Nvert;i++){
00163      if(vp->status == SELECTED){
00164        if(vp->xyz[0] < xmin)xmin=vp->xyz[0];
00165        if(vp->xyz[1] < ymin)ymin=vp->xyz[1];
00166        if(vp->xyz[2] < zmin)zmin=vp->xyz[2];
00167        if(vp->xyz[0] > xmax)xmax=vp->xyz[0];
00168        if(vp->xyz[1] > ymax)ymax=vp->xyz[1];
00169        if(vp->xyz[2] > zmax)zmax=vp->xyz[2];
00170            }
00171      vp++;
00172    }
00173    maxdim=max(xmax-xmin,ymax-ymin);
00174    maxdim=max(maxdim,zmax-zmin);
00175    if(xmax-xmin > MINUNIT &&
00176       ymax-ymin > MINUNIT && 
00177           zmax-zmin > MINUNIT){ 
00178      tVertex v;
00179      tEdge   e;
00180      tFace   f;
00181      int vnum=0;
00182      scale=(double)MINUNIT/(double)maxdim;        
00183      vp=MainVp; for(i=0;i<Nvert;i++){
00184        if(vp->status == SELECTED){
00185          v=MakeNullVertex();
00186          v->v[X] = (long)((double)vp->xyz[0]*scale);
00187          v->v[Y] = (long)((double)vp->xyz[1]*scale);
00188          v->v[Z] = (long)((double)vp->xyz[2]*scale);
00189          v->vnum=vnum++;
00190        }
00191        vp++;
00192      }
00193      
00194      
00195      
00196      
00197      if(setjmp(jmp_buf_here) != 0){
00198        DeleteTemporaryStructures();
00199        SetCursor(hSave);
00200        LoadString(hThisInstance,IDX_STRING3,text_string,256);
00201        MessageBox(hParent,text_string,name_string,MB_OKCANCEL);
00202        return FALSE;
00203      }
00204      if(DoubleTriangle()){
00205        long nf=0,ne=0,nv=0;
00206              ConstructHull();
00207        
00208        f=faces; if(f != NULL)do {
00209          nf++;
00210          f = f->next;
00211        } while(f != faces);
00212        e = edges;if(e != NULL)do {
00213          ne++;
00214                e = e->next;
00215        } while (e != edges);
00216        v = vertices; if(v != NULL)do {
00217          nv++;
00218          v = v->next;
00219        } while (v != vertices);
00220        
00221        
00222        
00223        
00224        
00225        if(!UpdateVertexHeap(Nvert+nv))goto CANTDO;
00226        if(!UpdateEdgeHeap(Nedge+ne))goto CANTDO;
00227        if(!UpdateFaceHeap(Nface+ne))goto CANTDO;
00228        v = vertices; if(v != NULL)do {
00229          CreateVertex();
00230          vp=(MainVp+Nvert-1);
00231          vp->xyz[0]=(long)((double)(v->v[X])/scale);
00232          vp->xyz[1]=(long)((double)(v->v[Y])/scale);
00233          vp->xyz[2]=(long)((double)(v->v[Z])/scale);
00234          vp->status=DESELECTED;
00235          v->OFXid = Nvert-1;
00236          NvertDeselect++; NvertSelect--;
00237          nv++;
00238          v = v->next;
00239        } while (v != vertices);
00240 
00241        e = edges;
00242        if(e != NULL)do {
00243          CreateEdge(e->endpts[0]->OFXid,e->endpts[1]->OFXid);
00244          e = e->next;
00245        } while (e != edges);
00246        f = faces;
00247        if(f != NULL)do {
00248          CreateFace(f->vertex[0]->OFXid,f->vertex[1]->OFXid,f->vertex[2]->OFXid);
00249          f = f->next;
00250        } while(f != faces);
00251        CANTDO:;
00252      }
00253      DeleteTemporaryStructures();
00254      SetCursor(hSave);
00255      LoadString(hThisInstance,IDX_STRING3,text_string,256);
00256      MessageBox(hParent,text_string,name_string,MB_OKCANCEL);
00257    }
00258    else{
00259      LoadString(hThisInstance,IDX_STRING4,text_string,256);
00260      MessageBox(hParent,text_string,name_string,MB_OKCANCEL);
00261      SetCursor(hSave);
00262    }
00263  }
00264  return TRUE;
00265 }
00266 
00267 void DeleteTemporaryStructures(void){
00268  tEdge   e,te;
00269  tFace   f,tf;
00270  tVertex v,tv;
00271  f=faces;
00272  if(f != NULL)do {
00273    tf = f;
00274    f = f->next;
00275          FREE(tf)
00276  } while(f != faces);
00277  e = edges;
00278  if(e != NULL)do {
00279          te = e;
00280          e = e->next;
00281          FREE(te)
00282  } while (e != edges);
00283  v = vertices;
00284  if(v != NULL)do {
00285          tv = v; 
00286          v = v->next;
00287          FREE(tv)
00288  } while (v != vertices);
00289  return;
00290 }
00291 
00292 
00293 
00294 
00295 
00296 
00297 
00298 
00299 
00300 
00301 
00302 
00303 
00304 
00305 
00306 
00307 
00308 
00309 
00310 
00311 
00312 
00313 
00314 
00315 
00316 
00317 
00318 
00319 #define ONHULL          TRUE
00320 #define REMOVED         TRUE
00321 #define VISIBLE         TRUE
00322 #define PROCESSED       TRUE
00323 #define SAFE            1000000         
00324 
00325 
00326 void    ReadVertices( void );
00327 void    Print( void );
00328 void    SubVec( int a[3], int b[3], int c[3]);
00329 bool    HULL_ADDOne( tVertex p );
00330 int     VolumeSign(tFace f, tVertex p);
00331 int     Volumei( tFace f, tVertex p );
00332 tFace   MakeConeFace( tEdge e, tVertex p );
00333 void    MakeCcw( tFace f, tEdge e, tVertex p );
00334 tEdge   MakeNullEdge( void );
00335 tFace   MakeNullFace( void );
00336 tFace   MakeFace( tVertex v0, tVertex v1, tVertex v2, tFace f );
00337 void    CleanUp( void );
00338 void    CleanEdges( void );
00339 void    CleanFaces( void );
00340 void    CleanVertices( void );
00341 bool    Collinear( tVertex a, tVertex b, tVertex c );
00342 
00343 
00344 
00345 
00346 tVertex MakeNullVertex( void ){
00347    tVertex  v;
00348    NEW( v, tsVertex );
00349    v->duplicate = NULL;
00350    v->onhull = !ONHULL;
00351    v->mark = !PROCESSED;
00352    HULL_ADD( vertices, v );
00353 
00354    return v;
00355 }
00356 
00357 
00358 
00359 
00360 void    SubVec( int a[3], int b[3], int c[3]){
00361    int  i;
00362 
00363    for( i=0; i < 2; i++ )
00364       c[i] = a[i] - b[i];
00365 
00366 }
00367 
00368 
00369 
00370 
00371 
00372 
00373 
00374 
00375 
00376 
00377 BOOL    DoubleTriangle( void ){
00378    tVertex  v0, v1, v2, v3, t;
00379    tFace    f0, f1 = NULL;
00380    tEdge    e0, e1, e2, s;
00381    int      vol;
00382         
00383    
00384    v0 = vertices;
00385    while ( Collinear( v0, v0->next, v0->next->next ) )
00386       if ( ( v0 = v0->next ) == vertices )
00387           return FALSE;
00388    v1 = v0->next;
00389    v2 = v1->next;
00390         
00391    
00392    v0->mark = PROCESSED;
00393    v1->mark = PROCESSED;
00394    v2->mark = PROCESSED;
00395    
00396    
00397    f0 = MakeFace( v0, v1, v2, f1 );
00398    f1 = MakeFace( v2, v1, v0, f0 );
00399 
00400    
00401    f0->edge[0]->adjface[1] = f1;
00402    f0->edge[1]->adjface[1] = f1;
00403    f0->edge[2]->adjface[1] = f1;
00404    f1->edge[0]->adjface[1] = f0;
00405    f1->edge[1]->adjface[1] = f0;
00406    f1->edge[2]->adjface[1] = f0;
00407         
00408    
00409    v3 = v2->next;
00410    vol = VolumeSign( f0, v3 );
00411    while ( !vol )   {
00412       if ( ( v3 = v3->next ) == v0 ) 
00413                   return FALSE;
00414       vol = VolumeSign( f0, v3 );
00415    }
00416         
00417    
00418    vertices = v3;
00419    return TRUE; 
00420 }
00421 
00422   
00423 
00424 
00425 
00426 
00427 void    ConstructHull( void ){
00428    tVertex  v, vnext;
00429    int      vol;
00430    bool     changed;
00431    v = vertices;
00432    do {
00433       vnext = v->next;
00434       if ( !v->mark ) {
00435          v->mark = PROCESSED;
00436          changed = HULL_ADDOne( v );
00437              CleanUp();
00438       }
00439       v = vnext;
00440    } while ( v != vertices );
00441 }
00442 
00443 
00444 
00445 
00446 
00447 
00448 
00449 
00450 bool    HULL_ADDOne( tVertex p )
00451 {
00452    tFace  f; 
00453    tEdge  e, temp;
00454    int    vol;
00455    bool   vis = FALSE;
00456 
00457    
00458    f = faces;
00459    do {
00460       vol = VolumeSign( f, p );
00461       if ( vol < 0 ) {
00462          f->visible = VISIBLE;  
00463          vis = TRUE;                      
00464       }
00465       f = f->next;
00466    } while ( f != faces );
00467 
00468    
00469    if ( !vis ) {
00470       p->onhull = !ONHULL;  
00471       return FALSE; 
00472    }
00473 
00474    
00475 
00476    e = edges;
00477    do {
00478       temp = e->next;
00479       if ( e->adjface[0]->visible && e->adjface[1]->visible )
00480          
00481          e->HULL_DELETE = REMOVED;
00482       else if ( e->adjface[0]->visible || e->adjface[1]->visible ) 
00483          
00484          e->newface = MakeConeFace( e, p );
00485       e = temp;
00486    } while ( e != edges );
00487    return TRUE;
00488 }
00489 
00490 
00491 
00492 
00493 
00494 
00495 
00496 
00497 int  VolumeSign( tFace f, tVertex p ){
00498    double  vol;
00499    int     voli;
00500    double  ax, ay, az, bx, by, bz, cx, cy, cz;
00501 
00502    ax = f->vertex[0]->v[X] - p->v[X];
00503    ay = f->vertex[0]->v[Y] - p->v[Y];
00504    az = f->vertex[0]->v[Z] - p->v[Z];
00505    bx = f->vertex[1]->v[X] - p->v[X];
00506    by = f->vertex[1]->v[Y] - p->v[Y];
00507    bz = f->vertex[1]->v[Z] - p->v[Z];
00508    cx = f->vertex[2]->v[X] - p->v[X];
00509    cy = f->vertex[2]->v[Y] - p->v[Y];
00510    cz = f->vertex[2]->v[Z] - p->v[Z];
00511 
00512    vol =   ax * (by*cz - bz*cy)
00513          + ay * (bz*cx - bx*cz)
00514          + az * (bx*cy - by*cx);
00515 
00516    
00517    if      ( vol >  0.5 )  return  1;
00518    else if ( vol < -0.5 )  return -1;
00519    else                    return  0;
00520 }
00521 
00522 
00523 
00524 int  Volumei( tFace f, tVertex p ){
00525    int  vol;
00526    int  ax, ay, az, bx, by, bz, cx, cy, cz, dx, dy, dz;
00527 
00528    ax = f->vertex[0]->v[X] - p->v[X];
00529    ay = f->vertex[0]->v[Y] - p->v[Y];
00530    az = f->vertex[0]->v[Z] - p->v[Z];
00531    bx = f->vertex[1]->v[X] - p->v[X];
00532    by = f->vertex[1]->v[Y] - p->v[Y];
00533    bz = f->vertex[1]->v[Z] - p->v[Z];
00534    cx = f->vertex[2]->v[X] - p->v[X];
00535    cy = f->vertex[2]->v[Y] - p->v[Y];
00536    cz = f->vertex[2]->v[Z] - p->v[Z];
00537 
00538    vol =  (ax * (by*cz - bz*cy)
00539          + ay * (bz*cx - bx*cz)
00540          + az * (bx*cy - by*cx));
00541 
00542    return vol;
00543 }
00544 
00545 
00546 
00547 
00548 
00549 
00550 tFace   MakeConeFace( tEdge e, tVertex p )
00551 {
00552    tEdge  new_edge[2];
00553    tFace  new_face;
00554    int    i, j;
00555 
00556    
00557    for ( i=0; i < 2; ++i ) 
00558       
00559       if ( !( new_edge[i] = e->endpts[i]->duplicate) ) {
00560          
00561          new_edge[i] = MakeNullEdge();
00562          new_edge[i]->endpts[0] = e->endpts[i];
00563          new_edge[i]->endpts[1] = p;
00564          e->endpts[i]->duplicate = new_edge[i];
00565       }
00566 
00567    
00568    new_face = MakeNullFace();   
00569    new_face->edge[0] = e;
00570    new_face->edge[1] = new_edge[0];
00571    new_face->edge[2] = new_edge[1];
00572    MakeCcw( new_face, e, p ); 
00573         
00574    
00575    for ( i=0; i < 2; ++i )
00576       for ( j=0; j < 2; ++j )  
00577          
00578          if ( !new_edge[i]->adjface[j] ) {
00579             new_edge[i]->adjface[j] = new_face;
00580             break;
00581          }
00582         
00583    return new_face;
00584 }
00585 
00586 
00587 
00588 
00589 
00590 
00591 
00592 
00593 
00594 
00595 
00596 
00597 void    MakeCcw( tFace f, tEdge e, tVertex p )
00598 {
00599    tFace  fv;   
00600    int    i;    
00601    tEdge  s;    
00602       
00603    if  ( e->adjface[0]->visible )      
00604         fv = e->adjface[0];
00605    else fv = e->adjface[1];
00606        
00607    
00608  
00609    for ( i=0; fv->vertex[i] != e->endpts[0]; ++i )
00610       ;
00611    
00612    if ( fv->vertex[ (i+1) % 3 ] != e->endpts[1] ) {
00613       f->vertex[0] = e->endpts[1];  
00614       f->vertex[1] = e->endpts[0];    
00615    }
00616    else {                               
00617       f->vertex[0] = e->endpts[0];   
00618       f->vertex[1] = e->endpts[1];      
00619       SWAP( s, f->edge[1], f->edge[2] );
00620    }
00621    
00622 
00623 
00624    
00625    f->vertex[2] = p;
00626 }
00627  
00628 
00629 
00630 
00631 
00632 tEdge   MakeNullEdge( void )
00633 {
00634    tEdge  e;
00635 
00636    NEW( e, tsEdge );
00637    e->adjface[0] = e->adjface[1] = e->newface = NULL;
00638    e->endpts[0] = e->endpts[1] = NULL;
00639    e->HULL_DELETE = !REMOVED;
00640    HULL_ADD( edges, e );
00641    return e;
00642 }
00643 
00644 
00645 
00646 
00647 
00648 
00649 tFace   MakeNullFace( void ){
00650    tFace  f;
00651    int    i;
00652 
00653    NEW( f, tsFace);
00654    for ( i=0; i < 3; ++i ) {
00655       f->edge[i] = NULL;
00656       f->vertex[i] = NULL;
00657    }
00658    f->visible = !VISIBLE;
00659    HULL_ADD( faces, f );
00660    return f;
00661 }
00662 
00663 
00664 
00665 
00666 
00667 tFace   MakeFace( tVertex v0, tVertex v1, tVertex v2, tFace fold ){
00668    tFace  f;
00669    tEdge  e0, e1, e2;
00670 
00671    
00672    if( !fold ) {
00673      e0 = MakeNullEdge();
00674      e1 = MakeNullEdge();
00675      e2 = MakeNullEdge();
00676    }
00677    else { 
00678      e0 = fold->edge[2];
00679      e1 = fold->edge[1];
00680      e2 = fold->edge[0];
00681    }
00682    e0->endpts[0] = v0;              e0->endpts[1] = v1;
00683    e1->endpts[0] = v1;              e1->endpts[1] = v2;
00684    e2->endpts[0] = v2;              e2->endpts[1] = v0;
00685         
00686    
00687    f = MakeNullFace();
00688    f->edge[0]   = e0;  f->edge[1]   = e1; f->edge[2]   = e2;
00689    f->vertex[0] = v0;  f->vertex[1] = v1; f->vertex[2] = v2;
00690         
00691    
00692    e0->adjface[0] = e1->adjface[0] = e2->adjface[0] = f;
00693         
00694    return f;
00695 }
00696 
00697 
00698 
00699 
00700 
00701 
00702 void    CleanUp( void ){
00703    CleanEdges();
00704    CleanFaces();
00705    CleanVertices();
00706 }
00707 
00708 
00709 
00710 
00711 
00712 
00713 void    CleanEdges( void ){
00714    tEdge  e;    
00715    tEdge  t;    
00716                 
00717    
00718    
00719    e = edges;
00720    do {
00721       if ( e->newface ) { 
00722          if ( e->adjface[0]->visible )
00723             e->adjface[0] = e->newface; 
00724          else   e->adjface[1] = e->newface;
00725          e->newface = NULL;
00726       }
00727       e = e->next;
00728    } while ( e != edges );
00729 
00730    
00731    while ( edges && edges->HULL_DELETE ) { 
00732       e = edges;
00733       HULL_DELETE( edges, e );
00734    }
00735    e = edges->next;
00736    do {
00737       if ( e->HULL_DELETE ) {
00738          t = e;
00739          e = e->next;
00740          HULL_DELETE( edges, t );
00741       }
00742       else e = e->next;
00743    } while ( e != edges );
00744 }
00745 
00746 
00747 
00748 
00749 void    CleanFaces( void ){
00750    tFace  f;    
00751    tFace  t;    
00752    while ( faces && faces->visible ) { 
00753       f = faces;
00754       HULL_DELETE( faces, f );
00755    }
00756    f = faces->next;
00757    do {
00758       if ( f->visible ) {
00759          t = f;
00760          f = f->next;
00761          HULL_DELETE( faces, t );
00762       }
00763       else f = f->next;
00764    } while ( f != faces );
00765 }
00766 
00767 
00768 
00769 
00770 
00771 
00772 void    CleanVertices( void ){
00773    tEdge    e;
00774    tVertex  v, t;
00775         
00776    
00777    e = edges;
00778    do {
00779       e->endpts[0]->onhull = e->endpts[1]->onhull = ONHULL;
00780       e = e->next;
00781    } while (e != edges);
00782         
00783    
00784 
00785    while ( vertices && vertices->mark && !vertices->onhull ) { 
00786       v = vertices;
00787       HULL_DELETE( vertices, v );
00788    }
00789    v = vertices->next;
00790    do {
00791       if ( v->mark && !v->onhull ) {    
00792          t = v; 
00793          v = v->next;
00794          HULL_DELETE( vertices, t )
00795       }
00796       else v = v->next;
00797    } while ( v != vertices );
00798         
00799    
00800    v = vertices;
00801    do {
00802       v->duplicate = NULL; 
00803       v->onhull = !ONHULL; 
00804       v = v->next;
00805    } while ( v != vertices );
00806 }
00807 
00808 
00809 
00810 
00811 
00812 bool    Collinear( tVertex a, tVertex b, tVertex c )
00813 {
00814    return 
00815          ( c->v[Z] - a->v[Z] ) * ( b->v[Y] - a->v[Y] ) -
00816          ( b->v[Z] - a->v[Z] ) * ( c->v[Y] - a->v[Y] ) == 0
00817       && ( b->v[Z] - a->v[Z] ) * ( c->v[X] - a->v[X] ) -
00818          ( b->v[X] - a->v[X] ) * ( c->v[Z] - a->v[Z] ) == 0
00819       && ( b->v[X] - a->v[X] ) * ( c->v[Y] - a->v[Y] ) -
00820          ( b->v[Y] - a->v[Y] ) * ( c->v[X] - a->v[X] ) == 0  ;
00821 }
00822