JOINS.C

Go to the documentation of this file.
00001 /* --
00002 OpenFX version 2.0 - Modelling, Animation and Rendering Package
00003 Copyright (C) 2000 - 2007 OpenFX Development Team
00004 /*  JOINS.C  */
00005 
00006 /***************************************************************************\
00007 
00008  Join a series of curves together. The algorithm works by joining groups of
00009  two. Find the first two curves and join them. Then get another curve and
00010  join that one to the closest of the first two. Repeat this process.
00011  The curves do not have to have the same number of vertices.
00012 
00013  For each group of two curves repeat the following.
00014 
00015 1) Make a list of all vertices in path 1 and path 2 (called va1 and va2)
00016    numbers being Npath1 and Npath2.  (paths must be closed)
00017 
00018 2) Find vertices in va1 and va2 that are closest together, these are vf1
00019    and vf2.
00020 
00021 3) Re-make the list of vertices in the two paths so that vf1 and vf2 are
00022    the first vertices in the list. (this is the step that requires the
00023    paths to be closed).
00024 
00025 4) We require that path 1 has the fewest number of vertices so if path 2 has
00026    fewer vertices then swap the paths.
00027 
00028 *  We are now basically ready to go - but we need a direction that is the
00029 *  same for paths one and two.
00030 
00031 5) Compare the spacing between the second vertices in the curves
00032    (call it x)  with the spacing between the second vertex in curve 1
00033    and the last vertex in curve 2 (call it y).
00034    If x > y then the curves are going in the wrong the direction and so one
00035    must be reversed. Reverse curve two.
00036 
00037    ---2---1---0---n......   curve 2     The right way round
00038               |
00039               |
00040     ----1-----0----m....    curve 1
00041 
00042    ------ n---0---1--2...   curve 2     The wrong way round
00043               |
00044               |
00045     ----1-----0----m....    curve 1
00046 
00047 
00048    In the second case the distance  1 - 1 is > the distance  1- n. Getting the
00049    curves the right way round allows us to follow both curves in assending
00050    vertex order and basically join them up vertex 1 to 1, 2 to 2 etc.
00051    There will be a little bit of messing because curve 2 may contain more
00052    vertices than curve 1 but thats relatively easy to sort out. (as we shall
00053    see).
00054 
00055 6) Now proceed in two passes round path 1.
00056    First;
00057 
00058    For each vertex in path 1 find the closest vertex in path 2 that does NOT
00059    have a vertex further along the curve already connected to a
00060    vertex in path 1. (Remember - path 2 may have more vertices than path 1).
00061    Add an edge for each of these connections.
00062 
00063    While doing this record for each vertex in path 1 the identity of the
00064    vertex in path 2 to which it is attached.
00065 
00066    before ...
00067 
00068    path 2     --- 0 ---- 1 ---- 2  ---- 3 ---- 4 -----
00069 
00070 
00071    path 1     --- 0 ----------- 1 ------ 2 - 3 -------
00072 
00073    after ...
00074 
00075    path 2     --- 0 ---- 1 ---- 2  ---- 3 ---- 4 -----
00076                   !      !             !!
00077                   !       !           !  !
00078                   !        !          !  !
00079                   !         !         !   !
00080    path 1     --- 0 -------- 1 ------ 2 - 3 -------
00081 
00082 
00083    After this step every vertex in path 1 will be connected to a vertex in
00084    path 2. There may still be some vertices in path 2 not connected and
00085    faces need to be created but we have a basic framework that gives
00086    a reasonable geometry and has no overlapping edges.
00087 
00088    In the example above the vertices in path 1 will record that they are
00089    attached,
00090              0 attached to 0 in curve 2.
00091              1             1
00092              2             3
00093              3             3
00094              etc.
00095 
00096 7) Second - Go around path 1 again.
00097    (Most of this steps complexity is in creating the appropriate faces).
00098    Two markers "vactive" and "vcurrent" identity the vertices from the
00099    path lists so that edges and faces can be created.
00100    (There is a bit of fixing up to to at the ends of the curves)
00101 
00102    For each vertex in curve 1 do the following.
00103 
00104    Set k0 equal to the identity of vertex in curve 2 to which it is
00105    attached.
00106    If we are not at the end of curve 1 set k1 equal to the vertex
00107    in curve 2 that the next vertex in curve 1 is attached to,
00108    otherwise set k1 to point to beyond the end of curve2
00109 
00110    Now; if k1 = k0 then the next vertex in curve 1 is connected to
00111         the same vertex (in curve 2) and only a face need be added.
00112 
00113         eg   ----- n -----   Path 2
00114                   ! !
00115                  !   !     <--- Add one face
00116                 !     !
00117             --- m --- l ----  Path 1
00118 
00119     Otherwise;
00120          if k1 = k0+1  then we have to add two faces and one edge
00121 
00122        this;
00123     Path 2  ------- n ------ n+1 ------          ------- n ---- n+1 ----
00124                     !         !                          !    .  !
00125                     !         !         becomes          !   .   !
00126                     !         !                          ! .     !
00127     Path 1  ------- j ------ j+1  ------         --------j ---- j+1 ---
00128 
00129 
00130          if k1 > k0+1  we have to add a variable number of edges and
00131          faces that is -;    for(j=k0+1;j<k1;j++)
00132 
00133             For example before this step may have something like
00134 
00135             ---- 3 ---- 4 ---- 5 ----- 6 ------
00136                   .                   .
00137                    .                 .
00138                     .               .
00139                      .             .
00140               ------- 2 --------- 3 ---------
00141 
00142 
00143            with edges  3 -> 2 and 3 -> 6 created in step 6.
00144            Faces and edge are added to build up the triangular mesh.
00145 
00146             ---- 3 ---- 4 ---- 5 ----- 6 ------ Path 2
00147                   .    .     . .      .
00148                    .   .   .    .    .
00149                     .  . .       .  .
00150                      . .           .
00151               ------- 2 ---------- 3 ---------  Path 1
00152 
00153            The order doesn't really matter, it could be edges,
00154                2 -> 4   2 -> 5   2 -> 6
00155            or  2 -> 4   2 -> 5   3 -> 5  (*)
00156            or  2 -> 4   3 -> 4   3 -> 5
00157 
00158            In my implementation I attach vertex 2 (path 1) to all the
00159            vertices in curve 2 until one of them gets closer to vertex 3
00160            then I attach the rest to vertex 3.
00161            This results in the pattern labelled (*) above.
00162 
00163 8) Tidy up the ends where the end of the curves meet back on themselves.
00164 
00165 \***************************************************************************/
00166 
00167 #include <stdlib.h>
00168 #include <stdio.h>
00169 #include <math.h>
00170 #include <windows.h>
00171 
00172 #include "struct.h"
00173 #include "dstruct.h"
00174 
00175 #include "joins.h"
00176 
00177 #define NO               0
00178 #define YES              1
00179 #define FAIL            -1
00180 #define OK               1
00181 #define DESELECTED       0
00182 #define SELECTED         1
00183 
00184 static HWND      hParent;
00185 static HINSTANCE hThisInstance;
00186 
00187 static void JoinCurves(void);
00188 
00189 #if __WATCOMC__
00190 int APIENTRY LibMain(HANDLE hDLL, DWORD dwReason, LPVOID lpReserved){
00191 #else
00192 BOOL WINAPI DllMain(HANDLE hDLL, DWORD dwReason, LPVOID lpReserved){
00193 #endif
00194   switch (dwReason) {
00195     case DLL_PROCESS_ATTACH: {
00196       hThisInstance=hDLL;
00197       if(hDLL == NULL)MessageBox( GetFocus()," NULL instance",NULL, MB_OK);
00198       break;
00199     }
00200     case DLL_PROCESS_DETACH:
00201       break;
00202   }
00203   return TRUE;
00204 }
00205 
00206 #if __SC__
00207 #pragma startaddress(DllMain)
00208 #endif
00209 
00210 static BOOL CALLBACK JoinCurvesDlgProc(HWND hwnd, UINT msg,
00211                              WPARAM wparam, LPARAM lparam){
00212  switch( msg ) {
00213    case WM_INITDIALOG:
00214      CentreDlgOnS(hwnd);
00215      return (TRUE);
00216    case WM_COMMAND:
00217       switch(LOWORD(wparam)){
00218         case IDCANCEL:
00219           EndDialog(hwnd,FALSE);
00220           return(TRUE);
00221         case IDOK:
00222           EndDialog(hwnd,TRUE);
00223           return(TRUE);
00224         default:
00225           break;
00226       }
00227       break;
00228     default: break;
00229  }
00230  return(FALSE);
00231 }
00232 
00233 BOOL _Xmodeler
00234  (HWND parent_window,HWND info_window,X__STRUCTURE *lpevi){
00235  HCURSOR hSave;
00236  lpEVI=lpevi;
00237  hParent=parent_window;
00238  if(DialogBox(hThisInstance,MAKEINTRESOURCE(DLG_JOIN),hParent,
00239              (DLGPROC)JoinCurvesDlgProc) == FALSE)return FALSE;
00240  hSave=SetCursor(LoadCursor(NULL,IDC_WAIT));
00241  JoinCurves();
00242  SetCursor(hSave);
00243  return TRUE;
00244 }
00245 
00246 static double dspacing(long Va1, long Va2){
00247  vertex *va1,*va2;
00248  double d;
00249  va1=(MainVp+Va1); va2=(MainVp+Va2);
00250  d=(double)(va1->xyz[0] - va2->xyz[0]) *
00251    (double)(va1->xyz[0] - va2->xyz[0]) +
00252    (double)(va1->xyz[1] - va2->xyz[1]) *
00253    (double)(va1->xyz[1] - va2->xyz[1]) +
00254    (double)(va1->xyz[2] - va2->xyz[2]) *
00255    (double)(va1->xyz[2] - va2->xyz[2]);
00256  return sqrt(d);
00257 }
00258 
00259 static long get_next_closest(long vf1){
00260  vertex *vp;
00261  long i,vf;
00262  double dmin,d;
00263  dmin=1.e30;
00264  vf = -1;
00265  if((vp=MainVp) != NULL)for(i=0;i<Nvert;i++){
00266    if(vp->status == SELECTED){
00267      d=(double)(vp->xyz[0]-(MainVp+vf1)->xyz[0])*
00268        (double)(vp->xyz[0]-(MainVp+vf1)->xyz[0])
00269       +(double)(vp->xyz[1]-(MainVp+vf1)->xyz[1])*
00270        (double)(vp->xyz[1]-(MainVp+vf1)->xyz[1])
00271       +(double)(vp->xyz[2]-(MainVp+vf1)->xyz[2])*
00272        (double)(vp->xyz[2]-(MainVp+vf1)->xyz[2]);
00273      if(d < dmin){
00274        dmin=d;
00275        vf=i;
00276      }
00277    }
00278    vp++;
00279  }
00280  return vf;
00281 }
00282 
00283 static void JoinCurves(void){
00284  BOOL bSwapped;
00285  vertex *Vp;
00286  long vp,vf1,vf2,*va1,*va2,*tempva,tempv,vactive,vnext,vcurrent;
00287  double d,dmin=1e32;
00288  long Npath1,Npath2,i,j,k0,k1,k2,flag,lc;
00289  if(NvertSelect<6){MessageBeep(MB_OK); return; }
00290  if(NvertSelect > 32000){ MessageBeep(MB_OK); return; }
00291  vf1=vf2=-1;
00292  if((vf1=get_closest_vertex()) < 0){
00293    MessageBeep(MB_OK); goto GETOUT;
00294  }
00295  lc=0;
00296  /* */
00297  /* Loop over pairs of curves - curves are identified by making them  */
00298  /* selected/deselected                                               */
00299  /* */
00300  MAINLOOP:
00301  vp=0; while(vp < Nvert){ Vp=(MainVp+vp); Vp->id=0;  vp++; }
00302  va1=va2=NULL;
00303  if((va1=(long *)X__Malloc(NvertSelect*sizeof(long))) == NULL){
00304    MessageBeep(MB_OK);
00305    goto GETOUT;
00306  }
00307  Npath1=0; va1[0]=vf1; (MainVp+va1[0])->id=1;
00308  (MainVp+vf1)->status=DESELECTED; NvertSelect--; NvertDeselect++;
00309  vp=0; while(vp < Nvert){
00310    Vp=(MainVp+vp);
00311    if(Vp->status == SELECTED && Vp->id == 0 && Connected(va1[Npath1],vp)){
00312      Npath1++;
00313      va1[Npath1]=vp;
00314      Vp->id=1;   /* flag so not used again also indicates in path */
00315      Vp->status=DESELECTED;
00316      NvertSelect--; NvertDeselect++;
00317      vp=0;
00318    }
00319    else vp++;
00320  }
00321  if((vf2=get_next_closest(vf1)) < 0){
00322    MessageBeep(MB_OK);
00323    X__Free(va1);
00324    goto GETOUT;
00325  }
00326  if((va2=(long *)X__Malloc(NvertSelect*sizeof(long))) == NULL){
00327    MessageBeep(MB_OK);
00328    X__Free(va1);
00329    goto GETOUT;
00330  }
00331  Npath2=0; va2[0]=vf2; (MainVp+va2[0])->id=2;
00332  (MainVp+vf2)->status=DESELECTED; NvertSelect--; NvertDeselect++;
00333  vp=0; while(vp < Nvert){
00334    Vp=(MainVp+vp);
00335    if(Vp->status == SELECTED && Vp->id == 0 && Connected(va2[Npath2],vp)){
00336      Npath2++;
00337      va2[Npath2]=vp;
00338      Vp->id=2;
00339      Vp->status=DESELECTED;
00340      NvertSelect--; NvertDeselect++;
00341      vp=0;
00342    }
00343    else vp++;
00344  }
00345  Npath1++; Npath2++;
00346  /* */
00347  /* At this point two curves to be joined have been identified and  */
00348  /* lists have been make of the vertices in each curve              */
00349  /* */
00350  if(Npath1 < 3 || Npath2 < 3){
00351    MessageBeep(MB_OK);
00352    X__Free(va1);
00353    X__Free(va2);
00354    goto GETOUT;
00355  }
00356  /* */
00357  /* Find the vertices in curve 1 and 2 that is closest together      */
00358  /* */
00359  for(i=0;i<Npath1;i++)
00360    for(j=0;j<Npath2;j++){
00361      if((d=dspacing(va1[i],va2[j])) < dmin){
00362        dmin=d; vf1=va1[i]; vf2=va2[j];
00363      }
00364  }
00365  /* */
00366  /*  Re-make the curves so that vf1 and vf2 are at the start of the list */
00367  /* */
00368  Npath1=0; va1[0]=vf1; (MainVp+va1[0])->id=0;
00369  vp=0; while(vp < Nvert){
00370    Vp=(MainVp+vp);
00371    if(Vp->id == 1 && Connected(va1[Npath1],vp)){
00372      Npath1++;
00373      va1[Npath1]=vp;
00374      Vp->id=0;
00375      vp=0;
00376    }
00377    else vp++;
00378  }
00379  Npath2=0; va2[0]=vf2; (MainVp+va2[0])->id=0;
00380  vp=0; while(vp < Nvert){
00381    Vp=(MainVp+vp);
00382    if(Vp->id == 2 && Connected(va2[Npath2],vp)){
00383      Npath2++;
00384      va2[Npath2]=vp;
00385      Vp->id=0;
00386      vp=0;
00387    }
00388    else vp++;
00389  }
00390  /* */
00391  /* Curves re-made */
00392  /* */
00393  if(Npath1 == 0 || Npath2 == 0){
00394    MessageBox(NULL,"Fatal Fail",NULL,MB_OK);
00395    goto GETOUT;
00396  }
00397  /* */
00398  /* Make curve 1 the curve with the fewest number of vertices */
00399  /* */
00400  Npath1++; Npath2++;
00401  if(Npath2 < Npath1){
00402    tempv=vf1; vf1=vf2; vf2=tempv;
00403    tempva=va1; va1=va2; va2=tempva;
00404    i=Npath1; Npath1=Npath2; Npath2=i;
00405    bSwapped=TRUE;
00406  }
00407  else bSwapped=FALSE;
00408  /* *\
00409  If necessary reverse the order of vertices along curve 2
00410  \* */
00411  if(dspacing(va1[1],va2[1]) > dspacing(va1[1],va2[Npath2-1])){
00412    for(i=1;i<=(Npath2-1)/2;i++){
00413      tempv=va2[i];
00414      va2[i]=va2[Npath2-i];
00415      va2[Npath2-i]=tempv;
00416    }
00417  }
00418  if(!UpdateEdgeHeap(Nedge+2*(Npath1+Npath2+1)))goto UPDATE_HEAP;
00419  if(!UpdateFaceHeap(Nface+2*(Npath1+Npath2+4)))goto UPDATE_HEAP;
00420  k0=1;
00421  /* *\
00422  Join each vertex in curve 1 to nearest vertex in curve 2 (Step 6 above)
00423  \* */
00424  CreateEdge(va1[0],va2[0]);
00425  (MainVp+va1[0])->id=0;
00426  for(i=1;i<Npath1;i++){
00427    dmin=1e32; k1= -1;
00428    if(k0 < Npath2)for(j=k0;j<Npath2;j++){
00429      if((d=dspacing(va1[i],va2[j])) < dmin){dmin=d; k1=j;}
00430    }
00431    if(k1 >= 0){
00432      k0=k1;
00433      (MainVp+va1[i])->id=k0;
00434      CreateEdge(va1[i],va2[k0]);
00435    }
00436  }
00437  /* *\
00438  Step 7
00439  \* */
00440  for(i=0;i<Npath1;i++){
00441    flag=0;
00442    vactive=va1[i];
00443    k0=(long)((MainVp+va1[i])->id);
00444    vcurrent=va2[k0];
00445    if(i < Npath1-1){
00446      vnext=va1[i+1];
00447      k1=(long)((MainVp+va1[i+1])->id);
00448      k2=k1;
00449    }
00450    else{
00451      vnext=va1[0]; k1=Npath2; k2=0;
00452    }
00453    if(k1 > k0){           /* must add at least 1 edge and 2 faces  */
00454      if(k1 > k0+1){       /* must add at least 2 edges and 3 faces */
00455        for(j=k0+1;j<k1;j++){
00456          vcurrent=va2[j];
00457          if(flag == 0){
00458            CreateEdge(vcurrent,vactive);
00459            CreateFace(vactive,vcurrent,va2[j-1]);
00460            if(dspacing(vcurrent,va2[k0]) > dspacing(vcurrent,va2[k2])){
00461              CreateEdge(vcurrent,vnext);
00462              CreateFace(vactive,vcurrent,vnext);
00463              flag=1;
00464            }
00465          }
00466          else{
00467            CreateEdge(vcurrent,vnext);
00468            CreateFace(va2[j-1],vnext,vcurrent);
00469          }
00470        }
00471      }
00472      if(flag == 0){
00473        CreateEdge(vcurrent,vnext);
00474        CreateFace(vactive,vnext,vcurrent);
00475      }
00476      CreateFace(vcurrent,vnext,va2[k2]);
00477    }
00478    else{  /* no edges required only one face - edge make in step 6 */
00479      CreateFace(vactive,vcurrent,vnext);
00480    }
00481  }
00482  /* *\
00483  End of step 7 - now get the next two curves in the sequence, curve 1
00484  will become the current curve 2
00485  \* */
00486  if(NvertSelect > 3){ /* move onto next curve - select the old curve */
00487    if(bSwapped){
00488      for(i=0;i<Npath1;i++){
00489        (MainVp+va1[i])->status = SELECTED;
00490        NvertSelect++; NvertDeselect--;
00491      }
00492    }
00493    else{
00494      vf1=vf2;
00495      for(i=0;i<Npath2;i++){
00496        (MainVp+va2[i])->status = SELECTED;
00497        NvertSelect++; NvertDeselect--;
00498      }
00499    }
00500    if(va1 != NULL)X__Free(va1); va1=NULL;
00501    if(va2 != NULL)X__Free(va2); va2=NULL;
00502    lc++;
00503    goto MAINLOOP;
00504  }
00505  UPDATE_HEAP:
00506  UpdateFaceHeap(Nface);
00507  UpdateEdgeHeap(Nedge);
00508  if(va1 != NULL)X__Free(va1);
00509  if(va2 != NULL)X__Free(va2);
00510  GETOUT:
00511  return;
00512 }

Generated on Sun Apr 27 14:20:11 2014 for OpenFX by  doxygen 1.5.6