Skip to content

AFNI/NIfTI Server

Sections
Personal tools
You are here: Home » AFNI » Documentation

Doxygen Source Code Documentation


Main Page   Alphabetical List   Data Structures   File List   Data Fields   Globals   Search  

SUMA_GeomComp.c

Go to the documentation of this file.
00001 #include "SUMA_suma.h"
00002 
00003 #undef STAND_ALONE
00004 
00005 #if defined SUMA_SurfSmooth_STAND_ALONE
00006 #define STAND_ALONE
00007 #elif defined SUMA_getPatch_STANDALONE
00008 #define STAND_ALONE 
00009 #elif defined SUMA_SurfQual_STANDALONE
00010 #define STAND_ALONE
00011 #endif
00012 
00013 #ifdef STAND_ALONE
00014 /* these global variables must be declared even if they will not be used by this main */
00015 SUMA_SurfaceViewer *SUMAg_cSV = NULL; /*!< Global pointer to current Surface Viewer structure*/
00016 SUMA_SurfaceViewer *SUMAg_SVv = NULL; /*!< Global pointer to the vector containing the various Surface Viewer Structures 
00017                                     SUMAg_SVv contains SUMA_MAX_SURF_VIEWERS structures */
00018 int SUMAg_N_SVv = 0; /*!< Number of SVs realized by X */
00019 SUMA_DO *SUMAg_DOv = NULL;   /*!< Global pointer to Displayable Object structure vector*/
00020 int SUMAg_N_DOv = 0; /*!< Number of DOs stored in DOv */
00021 SUMA_CommonFields *SUMAg_CF = NULL; /*!< Global pointer to structure containing info common to all viewers */
00022 #else
00023 extern SUMA_CommonFields *SUMAg_CF;
00024 extern SUMA_DO *SUMAg_DOv;
00025 extern SUMA_SurfaceViewer *SUMAg_SVv;
00026 extern int SUMAg_N_SVv; 
00027 extern int SUMAg_N_DOv;  
00028 #endif
00029 
00030 /*!
00031 
00032    \brief A function to calculate the geodesic distance of nodes connected to node n
00033           See labbook NIH-3 pp 138 and on for notes on algorithm 
00034    \param n (int) index of center node
00035    \param SO (SUMA_SurfaceObject *) structure containing surface object
00036    \param off (float *) a vector such that off[i] = the geodesic distance of node i
00037                         to node n. The vector should be initialized to -1.0
00038    \param lim (float) maximum geodesic distance to travel
00039    
00040    - This function is too slow. See SUMA_getoffsets2
00041    \sa SUMA_getoffsets2
00042 */
00043 #define DBG 1
00044 #define DoCheck 1
00045 
00046 #if 0 /* now set in default arguments */
00047 int SUMA_GEOMCOMP_NI_MODE = NI_BINARY_MODE;
00048 #endif
00049 
00050 /*!
00051    \brief function to subdivide triangles to meet a maxarea criterion
00052    Divisions are done by adding a node at the centroid of the triangle 
00053    to be subdivided. Bad idea, for very large triangles, such as produced
00054    by convex hull, you could end up with nodes that have hundreds of neighbors...
00055 */
00056 int SUMA_Subdivide_Mesh(float **NodeListp, int *N_Nodep, int **FaceSetListp, int *N_FaceSetp, float maxarea)
00057 {
00058    static char FuncName[]={"SUMA_Subdivide_Mesh"};
00059    int in, it, N_NodeAlloc, N_FaceSetAlloc, N_Node, N_FaceSet, it3, in0, in1, in2, inc3, inc, itn, itn3;
00060    float c[3];
00061    float *NodeList = NULL, a, *n1, *n2, *n0;
00062    int *FaceSetList = NULL;
00063    SUMA_SurfaceObject SObuf, *SO=NULL;
00064    SUMA_Boolean LocalHead = YUP;
00065    
00066    SUMA_ENTRY;
00067    
00068    SO = &SObuf;
00069    
00070    N_NodeAlloc = N_Node = *N_Nodep;
00071    N_FaceSetAlloc = N_FaceSet = *N_FaceSetp;
00072    NodeList = *NodeListp;
00073    FaceSetList = *FaceSetListp;
00074    SO->NodeList = NodeList; SO->FaceSetList = FaceSetList;
00075    if (!NodeList || !FaceSetList) { SUMA_SL_Err("NULL input"); SUMA_RETURN(NOPE); }
00076    
00077    it = 0; /* triangle index */
00078    while (it < N_FaceSet) {
00079       it3 = 3*it; 
00080       in0 = FaceSetList[it3]; in1 = FaceSetList[it3+1]; in2 = FaceSetList[it3+2]; /* node indices */
00081       n0 = &(NodeList[3*in0]); n1 = &(NodeList[3*in1]); n2 = &(NodeList[3*in2]);   /* node coordinates */
00082       SUMA_TRI_AREA(n0, n1, n2, a); /* area of triangle */
00083       if (a > maxarea) {
00084          if (N_NodeAlloc <= N_Node) { /* need to realloc ?*/
00085             N_NodeAlloc += 20000;
00086             NodeList = (float *)SUMA_realloc(NodeList, N_NodeAlloc * 3 * sizeof(float));
00087             /* you always add 2 triangles per new node here */
00088             N_FaceSetAlloc += 40000;
00089             FaceSetList = (int *)SUMA_realloc(FaceSetList, N_FaceSetAlloc * 3 * sizeof(int));
00090             if (!NodeList || !FaceSetList) { SUMA_SL_Crit("Failed to realloc"); SUMA_RETURN(NOPE); }
00091             SO->NodeList = NodeList; SO->FaceSetList = FaceSetList;
00092          }
00093          SUMA_FACE_CENTROID(SO, it, c); /* c is the centroid of triangle it */
00094          inc = N_Node; inc3 = inc*3;  ++N_Node; /* index of new centroid node */
00095          NodeList[inc3] = c[0]; NodeList[inc3+1] = c[1]; NodeList[inc3+2] = c[2];   /* add new centroid to bottom of list */
00096          FaceSetList[it3+2] = inc; /* old triangle is now 1st new triangle in0 in1 inc */
00097          itn = N_FaceSet; itn3 = 3 * itn; ++N_FaceSet; /* index of new second triangle */ 
00098          FaceSetList[itn3] = inc; FaceSetList[itn3+1] = in1; FaceSetList[itn3+2] = in2;
00099          itn = N_FaceSet; itn3 = 3 * itn; ++N_FaceSet; /* index of new third triangle */ 
00100          FaceSetList[itn3] = inc; FaceSetList[itn3+1] = in2; FaceSetList[itn3+2] = in0; 
00101       } else {
00102          ++it;
00103       }
00104    }
00105    
00106    /* reallocate */
00107    FaceSetList = (int *)SUMA_realloc(FaceSetList, N_FaceSet * 3 * sizeof(int));
00108    NodeList = (float *)SUMA_realloc(NodeList, N_Node * 3 * sizeof(float));
00109    
00110    *NodeListp = NodeList;
00111    *FaceSetListp = FaceSetList;
00112    *N_FaceSetp = N_FaceSet;
00113    *N_Nodep = N_Node;
00114    
00115    SUMA_RETURN(YUP);
00116 }
00117 
00118 
00119 /*!
00120    \brief Function to allocate and initialize a SUMA_VTI *  structure
00121    \param N_TriIndex (int): Number of triangles whose intersections will be sought
00122    \param **TriIndex (int **): Pointer to vector containing indices of triangles 
00123                                whose intersections will be sought. This vector will
00124                                essentially be stored in vti if you supply it and its
00125                                pointer is set to NULL so that you can't free it afterwards.
00126                                If this parameter is NULL, then an empty vti->TriIndex is 
00127                                created.
00128    \return vti (SUMA_VTI *): Initialized structure containing allocated
00129                               TriIndex, N_IntersectedVoxels, IntersectedVoxels vectors.
00130    - Free with SUMA_FreeVTI
00131 */
00132 SUMA_VTI *SUMA_CreateVTI(int N_TriIndex, int *TriIndex)
00133 {
00134    static char FuncName[]={"SUMA_CreateVTI"};
00135    SUMA_VTI *vti = NULL;
00136    
00137    SUMA_ENTRY;
00138    if (!N_TriIndex) {
00139       SUMA_SL_Err("Nothing to do !");
00140       SUMA_RETURN(vti);
00141    }
00142    
00143    vti = (SUMA_VTI *)SUMA_malloc(sizeof(SUMA_VTI));
00144    vti->N_TriIndex = N_TriIndex;
00145    if (TriIndex ) {
00146       vti->TriIndex = TriIndex;
00147    }else {
00148       /* create empty copy */
00149       vti->TriIndex = (int *)SUMA_calloc(N_TriIndex, sizeof(int));
00150       if (!vti->TriIndex) {
00151          SUMA_SL_Crit("Failed to allocate for vti->TriIndex");
00152          SUMA_RETURN(NULL);
00153       }
00154    }
00155    vti->N_IntersectedVoxels = (int *)SUMA_calloc(N_TriIndex, sizeof(int));
00156    vti->IntersectedVoxels = (int **)SUMA_calloc(N_TriIndex, sizeof(int*));
00157    if (!vti->N_IntersectedVoxels || !vti->IntersectedVoxels) {
00158          SUMA_SL_Crit("Failed to allocate for vti's innerds");
00159          SUMA_RETURN(NULL);
00160    }
00161 
00162    SUMA_RETURN(vti);
00163 }
00164 
00165 SUMA_VTI * SUMA_FreeVTI(SUMA_VTI *vti)
00166 {
00167    static char FuncName[]={"SUMA_FreeVTI"};
00168    int i;
00169    
00170    SUMA_ENTRY;
00171    
00172    if (!vti) SUMA_RETURN(NULL);
00173    if (vti->TriIndex) SUMA_free(vti->TriIndex);
00174    if (vti->IntersectedVoxels) {
00175       for (i=0; i<vti->N_TriIndex; ++i) {
00176          if (vti->IntersectedVoxels[i]) free(vti->IntersectedVoxels[i]);
00177       }
00178       SUMA_free(vti->IntersectedVoxels);
00179    }
00180    if (vti->N_IntersectedVoxels) SUMA_free(vti->N_IntersectedVoxels);    
00181    SUMA_free(vti);
00182      
00183    SUMA_RETURN(NULL);
00184 }     
00185 /*!
00186    \brief Function to return a set of voxels that are intersected by
00187    a triangle. 
00188    
00189    \param SO (SUMA_SurfaceObject *)
00190    \param VolPar (SUMA_VOLPAR *)
00191    \param NodeIJKlist (float *) the equivalent of SO->NodeList only in i,j,k indices into VolPar's grid.
00192                            In the future, might want to allow for this param to be NULL and have it
00193                            be generated internally from SO->NodeList and VolPar.
00194    \param vti (SUMA_VTI *) properly initialized Voxel Triangle Intersection structure
00195    \return vti (SUMA_VTI *) filled up VTI structure.
00196    - Closely based on section in function SUMA_SurfGridIntersect
00197    If you find bugs here, fix them there too. 
00198 */
00199 SUMA_VTI *SUMA_GetVoxelsIntersectingTriangle(   SUMA_SurfaceObject *SO, SUMA_VOLPAR *VolPar, float *NodeIJKlist,
00200                                                 SUMA_VTI *vti )
00201 {
00202    static char FuncName[]={"SUMA_GetVoxelsIntersectingTriangle"};
00203    int ti, nx, ny, nz, nxy, nxyz, N_inbox, n1, n2, n3, nt, nt3, nijk, nf;
00204    int N_alloc, N_realloc, en, *voxelsijk=NULL, N_voxels1d = 0, *voxels1d = NULL;
00205    int *TriIndex=NULL, N_TriIndex;
00206    float dxyz[3];
00207    float tol_dist = 0; /* A way to fatten up the shell a little bit. Set to 0 if no fat is needed */
00208    float *p1, *p2, *p3, min_v[3], max_v[3], p[3], dist;
00209    FILE *fp=NULL;
00210    SUMA_Boolean LocalHead = YUP;
00211    
00212    SUMA_ENTRY;
00213       
00214    if (SO->FaceSetDim != 3 || SO->NodeDim != 3) {
00215       SUMA_SL_Err("SO->FaceSetDim != 3 || SO->NodeDim != 3"); 
00216       SUMA_RETURN(NULL);
00217    }
00218    if (!vti) {
00219       SUMA_SL_Err("vti must be non NULL");
00220       SUMA_RETURN(NULL);
00221    }
00222    if (vti->N_TriIndex <= 0) {
00223       SUMA_SL_Err("vti must be initialized");
00224       SUMA_RETURN(NULL);
00225    }
00226    
00227    TriIndex = vti->TriIndex;
00228    N_TriIndex = vti->N_TriIndex;
00229    
00230    nx = VolPar->nx; ny = VolPar->ny; nz = VolPar->nz; nxy = nx * ny; nxyz = nx * ny * nz;
00231    
00232    if (LocalHead) {
00233       fp = fopen("SUMA_GetVoxelsIntersectingTriangle.1D","w");
00234       if (fp) fprintf(fp, "# Voxels from %s that intersect the triangles \n", VolPar->filecode);
00235    }   
00236    /* cycle through all triangles and find voxels that intersect them */
00237    N_alloc = 2000; /* expected maximum number of voxels in triangle's bounding box */
00238    N_realloc = 0;
00239    voxelsijk = (int *)SUMA_malloc(sizeof(int)*N_alloc*3);
00240    if (!voxelsijk) { SUMA_SL_Crit("Failed to Allocate!"); SUMA_RETURN(NULL);  }   
00241    dxyz[0] = VolPar->dx; dxyz[1] = VolPar->dy; dxyz[2] = VolPar->dz;
00242    for (ti=0; ti<N_TriIndex; ++ti) {
00243       if (LocalHead) fprintf(SUMA_STDERR,"%s: Now processing %dth triangle\n", FuncName, ti);
00244       nf = TriIndex[ti];
00245       if (LocalHead) fprintf(SUMA_STDERR,"\t\tindexed %d, \n", nf);
00246       n1 = SO->FaceSetList[SO->FaceSetDim*nf]; n2 = SO->FaceSetList[SO->FaceSetDim*nf+1]; n3 = SO->FaceSetList[SO->FaceSetDim*nf+2];
00247       if (LocalHead) fprintf(SUMA_STDERR,"\t\tmade up of  nodes %d, %d. %d . \n", n1, n2, n3);
00248       /* find the bounding box of the triangle */
00249       p1 = &(NodeIJKlist[3*n1]); p2 = &(NodeIJKlist[3*n2]); p3 = &(NodeIJKlist[3*n3]); 
00250       SUMA_TRIANGLE_BOUNDING_BOX(p1, p2, p3, min_v, max_v);
00251       
00252       /* quick check of preallocate size of voxelsijk */
00253       en =((int)(max_v[0] - min_v[0] + 5) * (int)(max_v[1] - min_v[1] + 5) * (int)(max_v[2] - min_v[2] + 5)); 
00254       if ( en > N_alloc) {
00255          ++N_realloc; if (N_realloc > 5) { SUMA_SL_Warn("Reallocating, increase limit to improve speed.\nEither triangles too large or grid too small"); }
00256          N_alloc = 2*en;
00257          voxelsijk = (int *)SUMA_realloc(voxelsijk, 3*N_alloc*sizeof(int));
00258          if (!voxelsijk) { SUMA_SL_Crit("Failed to Allocate!"); SUMA_RETURN(NULL); }
00259       } 
00260       /* find the list of voxels inhabiting this box */
00261       N_inbox = 0;
00262       if (!SUMA_VoxelsInBox(voxelsijk, &N_inbox, min_v, max_v)) {
00263          SUMA_SL_Err("Unexpected error!"); SUMA_RETURN(NULL); 
00264       }
00265       if (!N_inbox) { SUMA_SL_Err("Unexpected error, no voxels in box!"); SUMA_RETURN(NULL);  }
00266       if (N_inbox >= N_alloc) { SUMA_SL_Err("Allocation trouble!"); SUMA_RETURN(NULL);  }
00267       if (LocalHead) fprintf(SUMA_STDERR,"\t\t%d nodes in box\n", N_inbox);
00268       
00269       /* allocate for 1D indices of voxels intersecting the triangle */
00270       if (voxels1d) {
00271          SUMA_SL_Err("NULL pointer expected here");
00272          SUMA_RETURN(NULL);
00273       }
00274       if (LocalHead) fprintf(SUMA_STDERR,"\t\tShit man, %d nodes in box\n", N_inbox);
00275       voxels1d = (int *)malloc(N_inbox * sizeof(int)); /* Too many SUMA_mallocs keep it simple here, this function is called many many times */
00276       if (LocalHead) fprintf(SUMA_STDERR,"\t\tWTF man, %d nodes in box\n", N_inbox);
00277       N_voxels1d=0;
00278       if (!voxels1d) {
00279          SUMA_SL_Crit("Failed to allocate voxels1d");
00280          SUMA_RETURN(NULL);
00281       }
00282       /* mark these voxels as inside the business */
00283       if (LocalHead) fprintf(SUMA_STDERR,"%s:\t\tabout to process %d voxels\n", FuncName, N_inbox);
00284       for (nt=0; nt < N_inbox; ++nt) {
00285          nt3 = 3*nt;
00286          if (voxelsijk[nt3] < nx &&  voxelsijk[nt3+1] < ny &&  voxelsijk[nt3+2] < nz) {
00287             nijk = SUMA_3D_2_1D_index(voxelsijk[nt3], voxelsijk[nt3+1], voxelsijk[nt3+2], nx , nxy);  
00288             { 
00289                /* what side of the plane is this voxel on ? */
00290                p[0] = (float)voxelsijk[nt3]; p[1] = (float)voxelsijk[nt3+1]; p[2] = (float)voxelsijk[nt3+2]; 
00291                SUMA_DIST_FROM_PLANE(p1, p2, p3, p, dist);
00292                /* Does voxel contain any of the nodes ? */
00293                if (tol_dist && SUMA_ABS(dist) < tol_dist) dist = tol_dist; /* Fatten the representation a little bit 
00294                                                              There are holes in the mask created using
00295                                                              the condition below alone. I am not sure
00296                                                              why that is the case but whatever gap there
00297                                                              is in one plane, results from a thick line in
00298                                                              the other. Don't know if that is a bug yet
00299                                                              or an effect of discretization. At any rate
00300                                                              it should not affect what I plan to do with
00301                                                              this. Could the bug be in 
00302                                                              SUMA_isVoxelIntersect_Triangle?*/
00303                if (!(SUMA_IS_STRICT_NEG(VolPar->Hand * dist))) { /* voxel is outside (along normal) */
00304                   /* does this triangle actually intersect this voxel ?*/
00305                   if (SUMA_isVoxelIntersect_Triangle (p, dxyz, p1, p2, p3)) {
00306                      /* looks good, store it */
00307                      if (LocalHead) fprintf(SUMA_STDERR,"nt %d, N_voxels1d %d\n", nt, N_voxels1d);
00308                      voxels1d[N_voxels1d] = nijk; ++N_voxels1d;
00309                      if (fp) fprintf(fp, "%d %d %d\n", voxelsijk[nt3], voxelsijk[nt3+1], voxelsijk[nt3+2]);
00310                   } 
00311                }
00312                
00313             }
00314          }
00315       }
00316       /* store the results */
00317       vti->IntersectedVoxels[ti] = voxels1d; 
00318       vti->N_IntersectedVoxels[ti] = N_voxels1d;
00319       voxels1d = NULL; N_voxels1d = 0; 
00320    }
00321    
00322    if (LocalHead) {
00323       if (fp) fclose(fp); fp = NULL;
00324    } 
00325    SUMA_RETURN(vti);
00326 }
00327 /*!
00328    \brief Function to detect surface self intersection
00329    returns -1 in case of error,
00330             0 in case of no intersection
00331             1 in case of intersection
00332 */ 
00333 int SUMA_isSelfIntersect(SUMA_SurfaceObject *SO, int StopAt)
00334 {
00335    static char FuncName[]={"SUMA_isSelfIntersect"};
00336    float *NodePos = NULL, *p1=NULL, *p2=NULL, *p3 = NULL, p[3], *ep1=NULL, *ep2=NULL;
00337    int hit = 0, k, t1, t2, it, it3, n1, n2, n3;
00338    SUMA_MT_INTERSECT_TRIANGLE *MTI = NULL;
00339    SUMA_Boolean LocalHead = NOPE;
00340    
00341    SUMA_ENTRY;
00342    
00343    if (!SO->EL) {
00344       SUMA_SL_Err("NULL SO->EL");
00345       SUMA_RETURN(-1);
00346    }
00347    
00348    if (StopAt < 1) StopAt = 1;
00349    
00350    hit = 0; k = 0;
00351    while (k < SO->EL->N_EL) {
00352          t1 = SO->EL->ELps[k][1]; t2 = SO->EL->ELps[SUMA_MIN_PAIR(k+1, SO->EL->N_EL-1)][1];
00353          ep1 = &(SO->NodeList[3*SO->EL->EL[k][0]]); ep2 = &(SO->NodeList[3*SO->EL->EL[k][1]]);
00354          /* find out if segment intersects */
00355          MTI = SUMA_MT_intersect_triangle(ep1, ep2, SO->NodeList, SO->N_Node, SO->FaceSetList, SO->N_FaceSet, MTI); 
00356          for (it=0; it<SO->N_FaceSet; ++it) {
00357             if (MTI->isHit[it] && it != t1 && it != t2) {
00358                /* ray hit triangle, is intersection inside segment ? */
00359                /* SUMA_LH("Checking hit..."); */
00360                it3 = SO->FaceSetDim*it;
00361                n1 = SO->FaceSetList[it3]; n2 = SO->FaceSetList[it3+1]; n3 = SO->FaceSetList[it3+2];
00362                p1 = &(SO->NodeList[SO->NodeDim*n1]); p2 = &(SO->NodeList[SO->NodeDim*n2]); p3 = &(SO->NodeList[SO->NodeDim*n3]);   
00363                SUMA_FROM_BARYCENTRIC(MTI->u[it], MTI->v[it], p1, p2, p3, p);
00364                if (p[0] > ep1[0] && p[0] < ep2[0]) {
00365                   if (p[1] > ep1[1] && p[1] < ep2[1]) {
00366                      if (p[2] > ep1[2] && p[2] < ep2[2]) {
00367                         /* point in segment, self intersection detected. */
00368                         if (LocalHead) fprintf(SUMA_STDERR,"%s: Triangle %d (%d, %d, %d) was hit by segment formed by nodes [%d, %d]\n", 
00369                            FuncName, it, n1, n2, n3, SO->EL->EL[k][0], SO->EL->EL[k][1]);
00370                            ++ hit;
00371                         break;
00372                      }
00373                   }
00374                }
00375             }
00376          }
00377          if (hit >= StopAt) break;
00378          /* skip duplicate edges */
00379          if (SO->EL->ELps[k][2] > 0) {
00380                k += SO->EL->ELps[k][2];
00381          } else ++k;
00382    }
00383    
00384    if (MTI) MTI = SUMA_Free_MT_intersect_triangle(MTI); 
00385    
00386    if (LocalHead) {
00387       if (!hit) {
00388          SUMA_LH("Surface does not self intersect.");
00389       } else {
00390          SUMA_LH("Surface self intersects.");
00391       }
00392    }
00393    SUMA_RETURN(hit);
00394 }
00395 
00396 /*!
00397    \brief find the neighbors to a voxel.
00398    \param ijk (int) a voxel's 1D index 
00399    \param ni, nj, nk (int) number of voxels in each of the three directions
00400    \param ntype (SUMA_VOX_NEIGHB_TYPES) neighborhood type
00401                   SUMA_VOX_NEIGHB_FACE a maximum total of 6 neighbors
00402                   SUMA_VOX_NEIGHB_EDGE a maximum total of 6 + 12 neighbors
00403                   SUMA_VOX_NEIGHB_CORNER a maximum total of 6 + 12 + 8 neighbors
00404    \param nl (int *) vector to contain the 1D indices of neighboring voxels. Voxels 
00405                      outside the volume boundaries are not considered. You should make sure nl
00406                      can hold a total of 26 values.
00407    \param N_n (int) number of neighbors.
00408 */               
00409 int SUMA_VoxelNeighbors (int ijk, int ni, int nj, int nk, SUMA_VOX_NEIGHB_TYPES ntype, int *nl)
00410 {
00411    static char FuncName[]={"SUMA_VoxelNeighbors"};
00412    int i, j, k;
00413    int nij, N_n;
00414    
00415    SUMA_ENTRY;
00416    
00417    N_n = 0; nij = ni * nj;
00418    
00419    /* change ijk to 3D */
00420    SUMA_1D_2_3D_index(ijk, i, j, k, ni, nij);
00421    
00422    if (i >= ni || i < 0) { SUMA_SL_Err("Voxel out of bounds along i direction"); SUMA_RETURN(N_n); }
00423    if (j >= nj || j < 0) { SUMA_SL_Err("Voxel out of bounds along j direction"); SUMA_RETURN(N_n); }
00424    if (k >= nk || k < 0) { SUMA_SL_Err("Voxel out of bounds along k direction"); SUMA_RETURN(N_n); }
00425   
00426    /* start with the face neighbors */
00427    if (i-1 >= 0) { nl[N_n] = SUMA_3D_2_1D_index(i-1, j, k, ni, nij); ++N_n; }
00428    if (j-1 >= 0) { nl[N_n] = SUMA_3D_2_1D_index(i, j-1, k, ni, nij); ++N_n; } 
00429    if (k-1 >= 0) { nl[N_n] = SUMA_3D_2_1D_index(i, j, k-1, ni, nij); ++N_n; } 
00430    if (i+1 < ni) { nl[N_n] = SUMA_3D_2_1D_index(i+1, j, k, ni, nij); ++N_n; } 
00431    if (j+1 < nj) { nl[N_n] = SUMA_3D_2_1D_index(i, j+1, k, ni, nij); ++N_n; } 
00432    if (k+1 < nk) { nl[N_n] = SUMA_3D_2_1D_index(i, j, k+1, ni, nij); ++N_n; } 
00433    
00434    if ( ntype < SUMA_VOX_NEIGHB_EDGE) { SUMA_RETURN(N_n); }
00435    
00436    /* add edge neighbors */
00437    if (i-1 >= 0 && j-1 >= 0) { nl[N_n] = SUMA_3D_2_1D_index(i-1, j-1, k, ni, nij); ++N_n; }
00438    if (i-1 >= 0 && j+1 < nj) { nl[N_n] = SUMA_3D_2_1D_index(i-1, j+1, k, ni, nij); ++N_n; }
00439    if (i-1 >= 0 && k-1 >= 0) { nl[N_n] = SUMA_3D_2_1D_index(i-1, j, k-1, ni, nij); ++N_n; }
00440    if (i-1 >= 0 && k+1 < nk) { nl[N_n] = SUMA_3D_2_1D_index(i-1, j, k+1, ni, nij); ++N_n; }
00441    if (j-1 >= 0 && k-1 >= 0) { nl[N_n] = SUMA_3D_2_1D_index(i, j-1, k-1, ni, nij); ++N_n; } 
00442    if (j-1 >= 0 && k+1 < nk) { nl[N_n] = SUMA_3D_2_1D_index(i, j-1, k+1, ni, nij); ++N_n; } 
00443    if (i+1 < ni && j-1 >= 0) { nl[N_n] = SUMA_3D_2_1D_index(i+1, j-1, k, ni, nij); ++N_n; }
00444    if (i+1 < ni && j+1 < nj) { nl[N_n] = SUMA_3D_2_1D_index(i+1, j+1, k, ni, nij); ++N_n; }
00445    if (i+1 < ni && k-1 >= 0) { nl[N_n] = SUMA_3D_2_1D_index(i+1, j, k-1, ni, nij); ++N_n; }
00446    if (i+1 < ni && k+1 < nk) { nl[N_n] = SUMA_3D_2_1D_index(i+1, j, k+1, ni, nij); ++N_n; }
00447    if (j+1 < nj && k-1 >= 0) { nl[N_n] = SUMA_3D_2_1D_index(i, j+1, k-1, ni, nij); ++N_n; } 
00448    if (j+1 < nj && k+1 < nk) { nl[N_n] = SUMA_3D_2_1D_index(i, j+1, k+1, ni, nij); ++N_n; } 
00449    
00450    if ( ntype < SUMA_VOX_NEIGHB_CORNER) { SUMA_RETURN(N_n); }
00451    
00452    /* add corner neighbors */
00453    if (i-1 >= 0 && j-1 >= 0 && k-1 >= 0) { nl[N_n] = SUMA_3D_2_1D_index(i-1, j-1, k-1, ni, nij); ++N_n; }
00454    if (i-1 >= 0 && j-1 >= 0 && k+1 < nk) { nl[N_n] = SUMA_3D_2_1D_index(i-1, j-1, k+1, ni, nij); ++N_n; }
00455    if (i-1 >= 0 && j+1 < nj && k-1 >= 0) { nl[N_n] = SUMA_3D_2_1D_index(i-1, j+1, k-1, ni, nij); ++N_n; }
00456    if (i-1 >= 0 && j+1 < nj && k+1 < nk) { nl[N_n] = SUMA_3D_2_1D_index(i-1, j+1, k+1, ni, nij); ++N_n; }
00457    if (i+1 < ni && j-1 >= 0 && k-1 >= 0) { nl[N_n] = SUMA_3D_2_1D_index(i+1, j-1, k-1, ni, nij); ++N_n; }
00458    if (i+1 < ni && j-1 >= 0 && k+1 < nk) { nl[N_n] = SUMA_3D_2_1D_index(i+1, j-1, k+1, ni, nij); ++N_n; }
00459    if (i+1 < ni && j+1 < nj && k-1 >= 0) { nl[N_n] = SUMA_3D_2_1D_index(i+1, j+1, k-1, ni, nij); ++N_n; }
00460    if (i+1 < ni && j+1 < nj && k+1 < nk) { nl[N_n] = SUMA_3D_2_1D_index(i+1, j+1, k+1, ni, nij); ++N_n; }
00461 
00462    
00463    SUMA_RETURN(N_n);
00464 }
00465 
00466 /*!
00467    \brief Function to fill the volume enclose in a mask
00468    \param ijkmask (byte *) mask (nvox x 1), typically the result of the intersection of a closed surface with a volume
00469    \param ijkseed (int) 1D index of seed voxel. Must be inside the mask and not a part of it.
00470    \param ni (int) number of voxels in the i direction
00471    \param nj (int) number of voxels in the j direction
00472    \param nk (int) number of voxels in the k direction
00473    \param N_in (int *) to contain the number of voxels inside the mask
00474    \parm usethisisin (byte *)store results in this mask vector rather than allocate for a new one.
00475    \param fillhole (int) fills small holes, intended to correct for volume masks created from surfaces with minor intersections
00476    \return isin (byte *) a nvox x 1 vector containing:
00477       0: for voxels outside mask
00478       1: for voxels inside mask
00479        
00480 */
00481 byte *SUMA_FillToVoxelMask(byte *ijkmask, int ijkseed, int ni, int nj, int nk, int *N_in, byte *usethisisin) 
00482 {
00483    static char FuncName[]={"SUMA_FillToVoxelMask"};
00484    byte *isin = NULL, *visited=NULL;
00485    DList*candlist=NULL;
00486    DListElmt *dothiselm=NULL;
00487    int dothisvoxel, itmp;
00488    int nl[50], N_n, in ,neighb, nijk, i, j, k, nij;
00489    SUMA_Boolean LocalHead = NOPE;
00490    
00491    SUMA_ENTRY;
00492    
00493    *N_in = 0;
00494    
00495    if (!ijkmask) {
00496       SUMA_SL_Err("Nothing to do");
00497       SUMA_RETURN(NULL);
00498    }
00499    if (ijkmask[ijkseed]) {
00500       SUMA_SL_Err("Seed is on mask. Bad business.");
00501       SUMA_RETURN(NULL);
00502    }
00503    
00504    nij = ni * nj;
00505    nijk = ni * nj * nk;
00506    
00507    if (LocalHead) {
00508       SUMA_1D_2_3D_index (ijkseed, i, j, k, ni, nij);
00509       fprintf(SUMA_STDERR,"%s:\nSeed is %d %d %d\n", FuncName, i, j, k); 
00510    }
00511    candlist = (DList*)SUMA_malloc(sizeof(DList));
00512    visited = (byte *)SUMA_calloc(nijk, sizeof(byte));
00513    if (!visited || !candlist) {
00514       SUMA_SL_Crit("Failed to allocate for visited or candlist");  
00515       SUMA_RETURN(NULL);  
00516    }
00517    
00518    if (usethisisin) isin = usethisisin;
00519    else {
00520       isin = (byte *)SUMA_calloc(nijk, sizeof(byte));
00521       if (!isin) {
00522          SUMA_SL_Crit("Failed to allocate");
00523          SUMA_RETURN(NULL);
00524       }
00525    }
00526    
00527    dothisvoxel = ijkseed;
00528    dlist_init(candlist, NULL);
00529    
00530    
00531    isin[dothisvoxel] = 1; ++(*N_in); /* Add voxel to cluster */
00532    visited[dothisvoxel] = 1;  
00533    dlist_ins_next(candlist, dlist_tail(candlist), (void *)dothisvoxel); /* Add voxel as next candidate*/
00534    
00535    while (dlist_size(candlist)) {
00536       /* find neighbors in its vicinity */
00537       dothiselm = dlist_head(candlist); dothisvoxel = (int) dothiselm->data;
00538       N_n = SUMA_VoxelNeighbors (dothisvoxel, ni, nj, nk, SUMA_VOX_NEIGHB_FACE, nl);
00539       /* remove node from candidate list */
00540       dlist_remove(candlist, dothiselm, (void*)&itmp);
00541       /* search to see if any are to be assigned */
00542       for (in=0; in<N_n; ++in) { 
00543          neighb = nl[in];
00544          if (!ijkmask[neighb]) {
00545             isin[neighb] = 1; ++(*N_in); /* Add voxel to cluster */
00546             /* mark it as a candidate if it has not been visited as a candidate before */
00547             if (!visited[neighb]) {
00548                dlist_ins_next(candlist, dlist_tail(candlist), (void *)neighb);
00549                visited[neighb] = 1;   
00550             }
00551          }
00552       }
00553    }
00554    
00555    if (visited) SUMA_free(visited); visited = NULL;
00556    if (candlist) { dlist_destroy(candlist); SUMA_free(candlist); candlist  = NULL; }
00557 
00558    
00559    SUMA_RETURN(isin);
00560 }
00561 
00562 /*!
00563    \brief find voxels whose centers are inside the box with corners c1 and c2
00564    c1, c2 are in voxel index coordinates. c1 is the minimum coordinates point.
00565    c2 is the maximum coordinates point.
00566 */
00567 SUMA_Boolean SUMA_VoxelsInBox(int *voxelsijk, int *N_in, float *c1, float *c2)
00568 {
00569    static char FuncName[]={"SUMA_VoxelsInBox"};
00570    int n3, i, j, k;
00571    
00572    SUMA_ENTRY;
00573    
00574    if (!voxelsijk) { 
00575       SUMA_SL_Err("NULL voxelsijk");
00576       SUMA_RETURN(NOPE); 
00577    }
00578    
00579    *N_in = 0;
00580    
00581    #if 0
00582    for (k = SUMA_ROUND(c1[2]); k <= SUMA_ROUND(c2[2]); ++k) {
00583       for (j = SUMA_ROUND(c1[1]); j <= SUMA_ROUND(c2[1]); ++j) {
00584          for (i = SUMA_ROUND(c1[0]); i <= SUMA_ROUND(c2[0]); ++i) {
00585             n3 = 3*(*N_in);
00586             voxelsijk[n3] = i; voxelsijk[n3+1] = j; voxelsijk[n3+2] = k; 
00587             ++(*N_in); 
00588          }
00589       }
00590    }
00591    #else
00592    for (k = (int)(c1[2]); k <= SUMA_CEIL(c2[2]); ++k) {
00593       for (j = (int)(c1[1]); j <= SUMA_CEIL(c2[1]); ++j) {
00594          for (i = (int)(c1[0]); i <= SUMA_CEIL(c2[0]); ++i) {
00595             n3 = 3*(*N_in);
00596             voxelsijk[n3] = i; voxelsijk[n3+1] = j; voxelsijk[n3+2] = k; 
00597             ++(*N_in); 
00598          }
00599       }
00600    }
00601    #endif     
00602    SUMA_RETURN(YUP); 
00603 }
00604 
00605 
00606 /*!
00607    \brief Applies an affine transform the coordinates in NodeList
00608    
00609    \param NodeList (float *) a vector of node XYZ triplets. (N_Node x 3 long)
00610    \param N_Node (int) number of nodes in NodeList
00611    \param M (float **) the affine transform matrix. 
00612                      Minimum size is 3 rows x 4 columns. 
00613                      The top left 3x3 is mat
00614                      The right most column is the shift vector vec (3 elements)
00615    \param center (float *) If center is not null then
00616                      XYZnew = mat * (vec - center) + vec + center
00617                      else  XYZnew = mat * (vec ) + vec 
00618    \return ans (SUMA_Boolean ) 1 OK, 0 not OK
00619    
00620    - COORDINATES IN NodeList are REPLACED with transformed ones.
00621                         
00622 */                     
00623 SUMA_Boolean SUMA_ApplyAffine (float *NodeList, int N_Node, float M[][4], float *center)
00624 {
00625    static char FuncName[] = {"SUMA_ApplyAffine"};
00626    float **XYZo, **Mr, **XYZn, D[3];
00627    int i, i3, idbg = 0;
00628    SUMA_Boolean LocalHead = NOPE;
00629    
00630    SUMA_ENTRY;
00631    
00632    if (!NodeList || N_Node <=0) { 
00633       SUMA_SL_Err("Bad Entries.\n");
00634       SUMA_RETURN(NOPE);
00635    }
00636    
00637    Mr = (float **)SUMA_allocate2D(3, 3, sizeof(float));
00638    XYZn = (float **)SUMA_allocate2D(3, 1, sizeof(float));
00639    XYZo = (float **)SUMA_allocate2D(3, 1, sizeof(float));
00640    
00641    SUMA_LH("Forming Mr");
00642    Mr[0][0] = M[0][0]; Mr[0][1] = M[0][1]; Mr[0][2] = M[0][2]; 
00643    Mr[1][0] = M[1][0]; Mr[1][1] = M[1][1]; Mr[1][2] = M[1][2]; 
00644    Mr[2][0] = M[2][0]; Mr[2][1] = M[2][1]; Mr[2][2] = M[2][2];
00645    D[0] = M[0][3]; D[1] = M[1][3]; D[2] = M[2][3];
00646    
00647    SUMA_LH("Transforming");
00648    if (LocalHead ) {
00649       i3 = 3*idbg;
00650       fprintf (SUMA_STDERR,"In: %f %f %f\n", NodeList[i3], NodeList[i3+1], NodeList[i3+2]);
00651    }
00652    for (i=0; i< N_Node; ++i) {
00653       i3 = 3 * i;
00654       if (!center) {
00655          XYZo[0][0] = NodeList[i3]; XYZo[1][0] = NodeList[i3+1]; XYZo[2][0] = NodeList[i3+2];
00656       } else {
00657          XYZo[0][0] = NodeList[i3] - center[0]; XYZo[1][0] = NodeList[i3+1] - center[1]; XYZo[2][0] = NodeList[i3+2] - center[2];
00658       }   
00659 
00660       SUMA_MULT_MAT(Mr, XYZo, XYZn, 3, 3, 1, float,float,float);
00661       
00662       if (!center) { 
00663          NodeList[i3] = XYZn[0][0]+D[0]; NodeList[i3+1] = XYZn[1][0]+D[1]; NodeList[i3+2] = XYZn[2][0]+D[2]; 
00664       } else {
00665          NodeList[i3] = XYZn[0][0]+D[0] + center[0]; NodeList[i3+1] = XYZn[1][0]+D[1]+ center[1]; NodeList[i3+2] = XYZn[2][0]+D[2]+ center[2]; 
00666       }
00667       
00668    }
00669    if (LocalHead ) {
00670       i3 = 3*idbg;
00671       fprintf (SUMA_STDERR,"Out: %f %f %f\n", NodeList[i3], NodeList[i3+1], NodeList[i3+2]);
00672    }
00673    SUMA_LH("Done");
00674    
00675    SUMA_free2D((char**)Mr, 3);
00676    SUMA_free2D((char**)XYZn, 3);
00677    SUMA_free2D((char**)XYZo, 3);
00678    
00679    SUMA_RETURN(YUP);
00680 }
00681 
00682 SUMA_Boolean SUMA_getoffsets (int n, SUMA_SurfaceObject *SO, float *Off, float lim) 
00683 {
00684    static char FuncName[]={"SUMA_getoffsets"};
00685    int i, ni, iseg;
00686    float Off_tmp;
00687    SUMA_Boolean Visit = NOPE;
00688    static SUMA_Boolean LocalHead = NOPE;
00689    
00690    SUMA_ENTRY;
00691    
00692    #if DoCheck
00693    if (!SO->FN || !SO->EL) {
00694       SUMA_SL_Err("SO->FN &/| SO->EL are NULL.\n");
00695       SUMA_RETURN(NOPE);
00696    }
00697    #endif
00698    
00699    #if DBG
00700    if (LocalHead) fprintf(SUMA_STDERR,"%s: Working node %d, %d neighbs. lim = %f\n", 
00701                                     FuncName, n, SO->FN->N_Neighb[n], lim);
00702    #endif
00703    
00704    for (i=0; i < SO->FN->N_Neighb[n]; ++i) {
00705       ni = SO->FN->FirstNeighb[n][i]; /* for notational sanity */
00706       iseg = SUMA_FindEdge (SO->EL, n, SO->FN->FirstNeighb[n][i]);
00707       #if DoCheck
00708       if (iseg < 0) {
00709          SUMA_SL_Err("Failed to find segment");
00710          SUMA_RETURN(NOPE);
00711       }
00712       #endif
00713       
00714       Off_tmp = Off[n] + SO->EL->Le[iseg];   /* that is the distance from n (original n) to ni along
00715                                                 that particular path */
00716                                              
00717       Visit = NOPE;
00718       if (Off[ni] < 0 || Off_tmp < Off[ni]) { /* Distance improvement, visit/revist that node */
00719          if (Off_tmp < lim) { /* only record if less than lim */
00720             Visit = YUP;
00721             Off[ni] = Off_tmp;
00722          } 
00723       } 
00724       
00725       #if DBG
00726       if (LocalHead) fprintf(SUMA_STDERR,"%s: %d --> %d. Visit %d, Current %f, Old %f\n", 
00727          FuncName, n, ni, Visit, Off_tmp, Off[ni]);
00728       #endif
00729       
00730       #if 0
00731          { int jnk; fprintf(SUMA_STDOUT,"Pausing ..."); jnk = getchar(); fprintf(SUMA_STDOUT,"\n"); }
00732       #endif
00733 
00734       if (Visit) { /* a new node has been reached with an offset less than limit, go down that road */
00735          if (!SUMA_getoffsets (ni, SO, Off, lim))  {
00736             SUMA_SL_Err("Failed in SUMA_getoffsets");
00737             SUMA_RETURN (NOPE);
00738          }
00739       }
00740    }
00741 
00742    SUMA_RETURN(YUP);
00743 }
00744 
00745 /*!
00746    \brief Allocate and initialize SUMA_GET_OFFSET_STRUCT* struct 
00747    OffS = SUMA_Initialize_getoffsets (N_Node);
00748    
00749    \param N_Node(int) number of nodes forming mesh 
00750    \return OffS (SUMA_GET_OFFSET_STRUCT *) allocate structure
00751            with initialized fields for zeroth order layer
00752    
00753    \sa SUMA_AddNodeToLayer
00754    \sa SUMA_Free_getoffsets
00755    \sa SUMA_Initialize_getoffsets
00756 */           
00757    
00758 SUMA_GET_OFFSET_STRUCT *SUMA_Initialize_getoffsets (int N_Node)
00759 {
00760    static char FuncName[]={"SUMA_Initialize_getoffsets"};
00761    int i;
00762    SUMA_GET_OFFSET_STRUCT *OffS = NULL;
00763    
00764    SUMA_ENTRY;
00765    
00766    if (N_Node <= 0) {
00767       SUMA_SL_Err("Bad values for N_Node");
00768       SUMA_RETURN (OffS);
00769    }
00770    
00771    OffS = (SUMA_GET_OFFSET_STRUCT *)SUMA_malloc(sizeof(SUMA_GET_OFFSET_STRUCT));
00772    if (!OffS) {
00773       SUMA_SL_Err("Failed to allocate for OffS");
00774       SUMA_RETURN (OffS);
00775    }
00776    
00777    OffS->OffVect = (float *) SUMA_malloc(N_Node * sizeof(float));
00778    OffS->LayerVect = (int *) SUMA_malloc(N_Node * sizeof(int));
00779    OffS->N_Nodes = N_Node;
00780    
00781    if (!OffS->LayerVect || !OffS->OffVect) {
00782       SUMA_SL_Err("Failed to allocate for OffS->LayerVect &/| OffS->OffVect");
00783       SUMA_free(OffS);
00784       SUMA_RETURN (OffS);
00785    }
00786    
00787    /* initialize vectors */
00788    for (i=0; i< N_Node; ++i) {
00789       OffS->OffVect[i] = 0.0;
00790       OffS->LayerVect[i] = -1;
00791    }
00792    
00793    /* add a zeroth layer for node n */
00794    OffS->N_layers = 1;
00795    OffS->layers = (SUMA_NODE_NEIGHB_LAYER *) SUMA_malloc(OffS->N_layers * sizeof(SUMA_NODE_NEIGHB_LAYER));
00796    OffS->layers[0].N_AllocNodesInLayer = 1;
00797    OffS->layers[0].NodesInLayer = (int *) SUMA_malloc(OffS->layers[0].N_AllocNodesInLayer * sizeof(int));
00798    OffS->layers[0].N_NodesInLayer = 0;   
00799    
00800    SUMA_RETURN (OffS);
00801    
00802 }
00803 
00804 /*!
00805    \brief Add node n to neighboring layer LayInd in OffS
00806    ans = SUMA_AddNodeToLayer (n, LayInd, OffS);
00807    
00808    \param n (int)
00809    \param LayInd (int)
00810    \param OffS (SUMA_GET_OFFSET_STRUCT *)
00811    \return YUP/NOPE (good/bad)
00812    
00813    - allocation is automatically taken care of
00814    
00815    \sa SUMA_Free_getoffsets
00816    \sa SUMA_Initialize_getoffsets
00817 */
00818 SUMA_Boolean SUMA_AddNodeToLayer (int n, int LayInd, SUMA_GET_OFFSET_STRUCT *OffS)
00819 {
00820    static char FuncName[]={"SUMA_AddNodeToLayer"};
00821    static SUMA_Boolean LocalHead = NOPE;
00822    
00823    /* is this a new layer */
00824    if (LayInd > OffS->N_layers) { /* error */
00825       SUMA_SL_Err("LayInd > OffS->N_layers. This should not be!");
00826       SUMA_RETURN(NOPE);
00827    } else if (LayInd == OffS->N_layers) { /* need a new one */
00828       SUMA_LH("Adding layer");
00829       OffS->N_layers += 1;
00830       OffS->layers = (SUMA_NODE_NEIGHB_LAYER *) SUMA_realloc(OffS->layers, OffS->N_layers*sizeof(SUMA_NODE_NEIGHB_LAYER));
00831       OffS->layers[LayInd].N_AllocNodesInLayer = 200;
00832       OffS->layers[LayInd].NodesInLayer = (int *) SUMA_malloc(OffS->layers[LayInd].N_AllocNodesInLayer * sizeof(int));
00833       OffS->layers[LayInd].N_NodesInLayer = 0;
00834    }
00835    
00836    OffS->layers[LayInd].N_NodesInLayer += 1;
00837    /* do we need to reallocate for NodesInLayer ? */
00838    if (OffS->layers[LayInd].N_NodesInLayer ==  OffS->layers[LayInd].N_AllocNodesInLayer) { /* need more space */
00839       SUMA_LH("reallocating neighbors");
00840       OffS->layers[LayInd].N_AllocNodesInLayer += 200;
00841       OffS->layers[LayInd].NodesInLayer = (int *) SUMA_realloc (OffS->layers[LayInd].NodesInLayer, OffS->layers[LayInd].N_AllocNodesInLayer * sizeof(int));
00842    }
00843    
00844    OffS->layers[LayInd].NodesInLayer[OffS->layers[LayInd].N_NodesInLayer - 1] = n;
00845    
00846    SUMA_RETURN(YUP); 
00847 }
00848 
00849 /*!
00850    \brief free memory associated with SUMA_GET_OFFSET_STRUCT * struct
00851    
00852    \param OffS (SUMA_GET_OFFSET_STRUCT *) Offset strcture
00853    \return NULL
00854    
00855    \sa SUMA_Recycle_getoffsets
00856    \sa SUMA_Initialize_getoffsets
00857 */
00858 SUMA_GET_OFFSET_STRUCT * SUMA_Free_getoffsets (SUMA_GET_OFFSET_STRUCT *OffS) 
00859 {
00860    static char FuncName[]={"SUMA_Free_getoffsets"};
00861    int i = 0;
00862    static SUMA_Boolean LocalHead = NOPE;
00863    
00864    SUMA_ENTRY;
00865    
00866    if (!OffS) SUMA_RETURN(NULL);
00867    
00868    if (OffS->layers) {
00869       for (i=0; i< OffS->N_layers; ++i) if (OffS->layers[i].NodesInLayer) SUMA_free(OffS->layers[i].NodesInLayer);
00870       SUMA_free(OffS->layers);
00871    }
00872    
00873    if (OffS->OffVect) SUMA_free(OffS->OffVect);
00874    if (OffS->LayerVect) SUMA_free(OffS->LayerVect);
00875    SUMA_free(OffS); OffS = NULL;
00876    
00877    SUMA_RETURN(NULL);
00878 }
00879 
00880 /*!
00881    \brief reset the SUMA_GET_OFFSET_STRUCT after it has been used by a node
00882    \param OffS (SUMA_GET_OFFSET_STRUCT *) Offset structure that has node neighbor
00883                                           info and detail to be cleared
00884    \return (YUP/NOPE) success/failure 
00885    
00886    - No memory is freed here
00887    - The used node layer indices are reset to -1
00888    - The number of nodes in each layer are reset to 0
00889    
00890    \sa SUMA_Free_getoffsets to free this structure once and for all
00891    \sa SUMA_Initialize_getoffsets
00892 */
00893 SUMA_Boolean SUMA_Recycle_getoffsets (SUMA_GET_OFFSET_STRUCT *OffS)
00894 {
00895    static char FuncName[]={"SUMA_Recycle_getoffsets"};
00896    int i, j;
00897    static SUMA_Boolean LocalHead = NOPE;
00898    
00899    for (i=0; i < OffS->N_layers; ++i) {
00900       /* reset the layer index of used nodes in LayerVect */
00901       for (j=0; j < OffS->layers[i].N_NodesInLayer; ++j) {
00902          OffS->LayerVect[OffS->layers[i].NodesInLayer[j]] = -1;
00903       }
00904       /* reset number of nodes in each layer */
00905       OffS->layers[i].N_NodesInLayer = 0;
00906    }
00907    
00908    SUMA_RETURN(YUP);
00909 }
00910 
00911 /*!
00912    \brief calculates the length of the segments defined
00913    by a node and its first-order neighbors. The resulting
00914    matrix very closely resembles SO->FN->FirstNeighb
00915    DistFirstNeighb = SUMA_CalcNeighbDist (SO);
00916    
00917    \param SO (SUMA_SurfaceObject *) with FN field required
00918    \return DistFirstNeighb (float **) DistFirstNeighb[i][j] contains the 
00919                                       length of the segment formed by nodes
00920                                       SO->FN->NodeId[i] and SO->FN->FirstNeighb[i][j]
00921                                       
00922    This function was created to try and speed up SUMA_getoffsets2 but it proved
00923    useless.
00924    Sample code showing two ways of getting segment length:
00925    #if 1
00926          // calculate segment distances(a necessary horror) 
00927          // this made no difference in speed  
00928          DistFirstNeighb = SUMA_CalcNeighbDist (SO);
00929          if (!DistFirstNeighb) { 
00930             SUMA_SL_Crit("Failed to allocate for DistFirstNeighb\n");
00931             exit(1);
00932          }
00933          { int n1, n2, iseg;
00934             n1 = 5; n2 = SO->FN->FirstNeighb[n1][2];
00935             iseg = SUMA_FindEdge(SO->EL, n1, n2);
00936             fprintf(SUMA_STDERR, "%s: Distance between nodes %d and %d:\n"
00937                                  "from DistFirstNeighb = %f\n"
00938                                  "from SO->EL->Le = %f\n", FuncName, n1, n2,
00939                                  DistFirstNeighb[n1][2], SO->EL->Le[iseg]);
00940             exit(1);
00941          } 
00942    #endif
00943 */   
00944    
00945 float ** SUMA_CalcNeighbDist (SUMA_SurfaceObject *SO) 
00946 {
00947    static char FuncName[]={"SUMA_CalcNeighbDist"};
00948    float **DistFirstNeighb=NULL, *a, *b;
00949    int i, j;
00950    static SUMA_Boolean LocalHead = NOPE;
00951    
00952    SUMA_ENTRY;
00953    
00954    if (!SO) { SUMA_RETURN(NULL); }
00955    if (!SO->FN) { SUMA_RETURN(NULL); }
00956    
00957    DistFirstNeighb = (float **)SUMA_allocate2D(SO->FN->N_Node, SO->FN->N_Neighb_max, sizeof(float));
00958    if (!DistFirstNeighb) {
00959       SUMA_SL_Crit("Failed to allocate for DistFirstNeighb");
00960       SUMA_RETURN(NULL);
00961    }
00962    for (i=0; i < SO->FN->N_Node; ++i) {
00963       a = &(SO->NodeList[3*SO->FN->NodeId[i]]);
00964       for (j=0; j < SO->FN->N_Neighb[i]; ++j) {
00965          b = &(SO->NodeList[3*SO->FN->FirstNeighb[i][j]]);
00966          SUMA_SEG_LENGTH(a, b, DistFirstNeighb[i][j]);
00967          if (SO->FN->NodeId[i] == 5 && SO->FN->FirstNeighb[i][j] == 133092) {
00968             fprintf (SUMA_STDERR, "%f %f %f\n%f %f %f\n%f\n", 
00969                SO->NodeList[3*SO->FN->NodeId[i]], SO->NodeList[3*SO->FN->NodeId[i]+1], SO->NodeList[3*SO->FN->NodeId[i]+2],
00970                SO->NodeList[3*SO->FN->FirstNeighb[i][j]], SO->NodeList[3*SO->FN->FirstNeighb[i][j]+1], 
00971                SO->NodeList[3*SO->FN->FirstNeighb[i][j]+2], DistFirstNeighb[i][j]);
00972          }
00973       }
00974    }
00975    
00976    SUMA_RETURN (DistFirstNeighb);
00977 }
00978 
00979 /*!
00980    \brief A function to calculate the geodesic distance of nodes connected to node n
00981            SUMA_getoffsets was the first incarnation but it was too slow.
00982     ans = SUMA_getoffsets2 (n, SO, lim, OffS, CoverThisNode, N_CoverThisNode) 
00983    
00984    \param n (int) index of center node
00985    \param SO (SUMA_SurfaceObject *) structure containing surface object
00986    \param lim (float) maximum geodesic distance to travel 
00987                      (ignored when CoverThisNode is used)
00988    \param OffS (SUMA_GET_OFFSET_STRUCT *) initialized structure to contain
00989           the nodes that neighbor n within lim mm 
00990           or until all the nodes in CoverThisNode are used up
00991    \param CoverThisNode (int *) SO->N_Node mask vector such that
00992                                  if CoverThisNode[i] then node i
00993                                  has to be reached (supersedes lim)
00994                                  NULL if you don't want to use it.
00995    \param N_CoverThisNode (int) number of nodes to cover (where CoverThisNode = 1).
00996    \return ans (SUMA_Boolean) YUP = GOOD, NOPE = BAD
00997    
00998    \sa SUMA_AddNodeToLayer
00999    \sa SUMA_Free_getoffsets
01000    \sa SUMA_Initialize_getoffsets
01001    \sa SUMA_getoffsets_ll
01002 
01003 
01004 
01005 The following code was used to test different methods for calculating the segment length,
01006 none (except for Seg = constant) proved to be faster, probably because of memory access time.
01007 One of the options required the use of DistFirstNeighb which is calculated by function
01008 SUMA_CalcNeighbDist. It mirrors SO->EL->FirstNeighb  
01009 
01010 static int SEG_METHOD;
01011 switch (SEG_METHOD) {
01012    case CALC_SEG: 
01013       // this is the slow part, too many redundant computations. 
01014       //cuts computation time by a factor > 3 if Seg was set to a constant
01015       //However, attempts at accessing pre-calculated segment lengths
01016       //proved to be slower. 
01017       SUMA_SEG_LENGTH (a, b, Seg); 
01018       break;
01019    case FIND_EDGE_MACRO:
01020       // this one's even slower, calculations have been made once but
01021       //function calls are costly (7.53 min)
01022       iseg = -1;
01023       if (n_k < n_jne) {SUMA_FIND_EDGE (SO->EL, n_k, n_jne, iseg);}
01024       else {SUMA_FIND_EDGE (SO->EL, n_jne, n_k, iseg);}
01025       if (iseg < 0) { 
01026          SUMA_SL_Err("Segment not found.\nSetting Seg = 10000.0");
01027          Seg = 10000.0; 
01028       } else Seg = SO->EL->Le[iseg];
01029       break;
01030    case FIND_EDGE:
01031       //this one's even slower, calculations have been made once but
01032       //function calls are costly
01033       iseg = SUMA_FindEdge (SO->EL, n_k, n_jne); 
01034       Seg = SO->EL->Le[iseg];
01035       break;
01036 
01037    case DIST_FIRST_NEIGHB:
01038       // consumes memory but might be faster than previous 2 (5.22 min)
01039       Seg = DistFirstNeighb[n_jne][k];
01040       break;
01041    case CONST:
01042       // 1.7 min 
01043       Seg = 1.0;
01044       break;
01045    default:
01046       SUMA_SL_Err("Bad option");
01047       break;
01048 }                    
01049 */
01050 SUMA_Boolean SUMA_getoffsets2 (int n, SUMA_SurfaceObject *SO, float lim, SUMA_GET_OFFSET_STRUCT *OffS, int *CoverThisNode, int N_CoverThisNode) 
01051 {
01052    static char FuncName[]={"SUMA_getoffsets2"};
01053    int LayInd, il, n_il, n_jne, k, n_prec = -1, n_k, jne, iseg=0;
01054    float Off_tmp, Seg, *a, *b, minSeg, SegPres; /*! *** SegPres added Jul 08 04, ZSS bug before ... */
01055    SUMA_Boolean Visit = NOPE;
01056    SUMA_Boolean AllDone = NOPE;
01057    static SUMA_Boolean LocalHead = NOPE;
01058    
01059    SUMA_ENTRY;
01060    
01061    if (!OffS) {
01062       SUMA_SL_Err("NULL OffS");
01063       SUMA_RETURN(NOPE);
01064    }
01065    
01066    /* setup 0th layer */
01067    OffS->OffVect[n] = 0.0;   /* n is at a distance 0.0 from itself */
01068    OffS->LayerVect[n] = 0;   /* n is on the zeroth layer */
01069    OffS->layers[0].N_NodesInLayer = 1;
01070    OffS->layers[0].NodesInLayer[0] = n;
01071    if (CoverThisNode) { 
01072       if (CoverThisNode[n]) {
01073          CoverThisNode[n] = 0; --N_CoverThisNode;
01074       }
01075    }
01076    LayInd = 1;  /* index of next layer to build */
01077    AllDone = NOPE;
01078    while (!AllDone) {
01079       
01080       AllDone = YUP; /* assume that this would be the last layer */
01081       for (il=0; il < OffS->layers[LayInd - 1].N_NodesInLayer; ++il) { /* go over all nodes in previous layer */
01082          n_il =  OffS->layers[LayInd - 1].NodesInLayer[il]; /* node from previous layer */
01083          for (jne=0; jne < SO->FN->N_Neighb[n_il]; ++jne) { /* go over all the neighbours of node n_il */
01084             n_jne = SO->FN->FirstNeighb[n_il][jne];        /* node that is an immediate neighbor to n_il */
01085             if (OffS->LayerVect[n_jne] < 0) { /* node is not assigned to a layer yet */
01086                OffS->LayerVect[n_jne] =  LayInd;    /* assign new layer index to node */
01087                OffS->OffVect[n_jne] = 0.0;          /* reset its distance from node n */
01088                SUMA_AddNodeToLayer (n_jne, LayInd, OffS);   /* add the node to the nodes in the layer */
01089                minSeg = 100000.0;
01090                n_prec = -1; 
01091                Seg = 0.0;
01092                SegPres = 0.0;
01093                for (k=0; k < SO->FN->N_Neighb[n_jne]; ++k) { /* calculate shortest distance of node to any precursor */  
01094                   n_k = SO->FN->FirstNeighb[n_jne][k];
01095                   if (OffS->LayerVect[n_k] == LayInd - 1) { /* this neighbor is a part of the previous layer, good */
01096                      if (n_prec < 0) n_prec = SO->FN->FirstNeighb[n_jne][0];
01097                      a = &(SO->NodeList[3*n_k]); b = &(SO->NodeList[3*n_jne]);
01098                      /* this is the slow part, too many redundant computations. 
01099                         Computation time is cut by a factor > 2 if Seg was set to a constant
01100                         However, attempts at accessing pre-calculated segment lengths
01101                         proved to be slower. See Comments in function help*/
01102                      SUMA_SEG_LENGTH_SQ (a, b, Seg);                    
01103                      if (OffS->OffVect[n_prec] + Seg < minSeg) {
01104                         minSeg = Seg + OffS->OffVect[n_prec];
01105                         SegPres = Seg;
01106                         n_prec = n_k;
01107                      }
01108                   }
01109                }/* for k */
01110                
01111                if (n_prec < 0) { /* bad news */
01112                   SUMA_SL_Crit("No precursor found for node.");
01113                   OffS = SUMA_Free_getoffsets (OffS);
01114                   SUMA_RETURN(NOPE);
01115                } else {
01116                   OffS->OffVect[n_jne] = OffS->OffVect[n_prec] + sqrt(SegPres); SegPres = 0.0;
01117                   if (!CoverThisNode) {
01118                      if (OffS->OffVect[n_jne] < lim) { /* must go at least one more layer */
01119                         AllDone = NOPE;
01120                      }
01121                   } else {
01122                      if (CoverThisNode[n_jne]) {
01123                         CoverThisNode[n_jne] = 0; --N_CoverThisNode;
01124                      }
01125                      if (N_CoverThisNode > 0) {
01126                         AllDone = NOPE;
01127                      }
01128                   }
01129                }
01130             } /* node not already in layer */
01131             
01132          } /* for jne */
01133       
01134       } /* for il */    
01135       ++LayInd;
01136    } /* while AllDone */
01137    
01138    SUMA_RETURN(YUP);
01139 }
01140 
01141 void SUMA_Free_Offset_ll_Datum(void *data)
01142 {
01143    static char FuncName[]={"SUMA_Free_Offset_ll_Datum"};
01144    SUMA_OFFSET_LL_DATUM *dt;
01145    
01146    SUMA_ENTRY;
01147    
01148    if (data) {
01149       dt = (SUMA_OFFSET_LL_DATUM *)data; 
01150       SUMA_free(dt);
01151    }
01152    
01153    SUMA_RETURNe;
01154 }   
01155 
01156 SUMA_OFFSET_LL_DATUM *SUMA_New_Offset_ll_Datum(int n, int layer)
01157 {
01158    static char FuncName[]={"SUMA_New_Offset_ll_Datum"};
01159    SUMA_OFFSET_LL_DATUM * datum = NULL;
01160    
01161    SUMA_ENTRY;
01162    
01163    datum = (SUMA_OFFSET_LL_DATUM *)SUMA_malloc(sizeof(SUMA_OFFSET_LL_DATUM));
01164    datum->ni = n;
01165    datum->layer = layer;
01166    datum->off = -1.0;
01167    
01168    SUMA_RETURN(datum);
01169 }
01170 #define SUMA_BEGINNING_OF_LAYER(list, LayInd, Elm) {   \
01171    SUMA_OFFSET_LL_DATUM * m_dat = NULL; \
01172    DListElmt *m_Elm = NULL;   \
01173    do {  \
01174      if (m_Elm) m_Elm = m_Elm->next;   \
01175      else m_Elm =  dlist_head(list); \
01176      m_dat = (SUMA_OFFSET_LL_DATUM *)m_Elm->data; \
01177    } while(m_dat->layer != LayInd && m_Elm != dlist_tail(list));   \
01178    if (m_dat->layer != LayInd) Elm = NULL;  \
01179    else Elm = m_Elm; \
01180 } 
01181 #define SUMA_FIND_ELMENT_FOR_NODE(list, n_jne, Elm){  \
01182    SUMA_OFFSET_LL_DATUM * m_dat = NULL; \
01183    DListElmt *m_Elm = NULL;   \
01184    do {  \
01185      if (m_Elm) m_Elm = m_Elm->next;   \
01186      else m_Elm =  dlist_head(list); \
01187      m_dat = (SUMA_OFFSET_LL_DATUM *)m_Elm->data; \
01188    } while(m_dat->ni != n_jne && m_Elm != dlist_tail(list));   \
01189    if (m_dat->ni != n_jne) Elm = NULL;  \
01190    else Elm = m_Elm; \
01191 }
01192 DList * SUMA_getoffsets_ll (int n, SUMA_SurfaceObject *SO, float lim, int *CoverThisNode, int N_CoverThisNode) 
01193 {
01194    static char FuncName[]={"SUMA_getoffsets_ll"};
01195    int LayInd, il, n_il, n_jne, k, n_prec = -1, n_k, jne, iseg=0;
01196    float Off_tmp, Seg, *a, *b, minSeg, SegPres; /*! *** SegPres added Jul 08 04, ZSS bug before ... */
01197    SUMA_Boolean Visit = NOPE;
01198    SUMA_Boolean AllDone = NOPE;
01199    SUMA_OFFSET_LL_DATUM * n_dat = NULL, *dat = NULL, *dat_nk = NULL, *dat_prec = NULL, *dat_ne=NULL;
01200    DList *list = NULL;
01201    DListElmt *elm = NULL, *elm_prec = NULL, *elm_ne=NULL, *elm_nk=NULL;
01202    static SUMA_Boolean LocalHead = NOPE;
01203    
01204    SUMA_ENTRY;
01205    
01206    
01207    /* create the list */
01208    SUMA_LH("Initializing list ...");
01209    list = (DList *)SUMA_malloc(sizeof(DList));
01210    dlist_init(list, SUMA_Free_Offset_ll_Datum);
01211    
01212    /* setup 0th layer */
01213    SUMA_LH("New OffsetDatum");
01214    n_dat = SUMA_New_Offset_ll_Datum(n, 0);
01215    n_dat->off = 0.0;   /* n is at a distance 0.0 from itself */
01216    dlist_ins_next(list, dlist_tail(list), (void*)n_dat);
01217    
01218    if (CoverThisNode) { 
01219       if (CoverThisNode[n]) {
01220          CoverThisNode[n] = 0; --N_CoverThisNode;
01221       }
01222    }
01223    LayInd = 1;  /* index of next layer to build */
01224    AllDone = NOPE;
01225    while (!AllDone) {
01226       AllDone = YUP; /* assume that this would be the last layer */
01227       elm = NULL;
01228          do {
01229             if (!elm) { SUMA_BEGINNING_OF_LAYER(list, (LayInd-1), elm); }
01230             else elm = elm->next;
01231             if (!elm) {
01232                SUMA_SL_Err("Could not find beginning of layer!");
01233                SUMA_RETURN(NULL);
01234             }
01235             dat = (SUMA_OFFSET_LL_DATUM *)elm->data;
01236             if (dat->layer == LayInd -1) {
01237                n_il = dat->ni;
01238                for (jne=0; jne < SO->FN->N_Neighb[n_il]; ++jne) { /* go over all the neighbours of node n_il */
01239                   n_jne = SO->FN->FirstNeighb[n_il][jne];        /* node that is an immediate neighbor to n_il */
01240                   SUMA_FIND_ELMENT_FOR_NODE(list, n_jne, elm_ne);
01241                   if (!elm_ne) { /* node not in any layer */
01242                      dat_ne = SUMA_New_Offset_ll_Datum(n_jne, LayInd); /* create an element for it */
01243                      dat_ne->off = 0.0;
01244                      dlist_ins_next(list, dlist_tail(list), (void*)dat_ne);
01245                      minSeg = 100000.0;
01246                      n_prec = -1; 
01247                      Seg = 0.0;
01248                      SegPres = 0.0;
01249                      for (k=0; k < SO->FN->N_Neighb[n_jne]; ++k) { /* calculate shortest distance of node to any precursor */  
01250                         n_k = SO->FN->FirstNeighb[n_jne][k];
01251                         SUMA_FIND_ELMENT_FOR_NODE(list, n_k, elm_nk); 
01252                         if (n_prec < 0 && elm_nk) { 
01253                            n_prec = n_k; elm_prec = elm_nk; 
01254                            dat_prec = (SUMA_OFFSET_LL_DATUM *)elm_prec->data;
01255                         }
01256                         if (elm_nk) {
01257                            dat_nk = (SUMA_OFFSET_LL_DATUM *)elm_nk->data;
01258                            if (dat_nk->layer == LayInd - 1) { /* this neighbor is a part of the previous layer, good */
01259                               a = &(SO->NodeList[3*n_k]); b = &(SO->NodeList[3*n_jne]);
01260                               /* this is the slow part, too many redundant computations. 
01261                                  Computation time is cut by a factor > 2 if Seg was set to a constant
01262                                  However, attempts at accessing pre-calculated segment lengths
01263                                  proved to be slower. See Comments in function help*/
01264                               SUMA_SEG_LENGTH_SQ (a, b, Seg);                    
01265                               if (dat_prec->off + Seg < minSeg) {
01266                                  minSeg = Seg + dat_prec->off;
01267                                  SegPres = Seg;
01268                                  n_prec = n_k;
01269                                  elm_prec = elm_nk;
01270                                  dat_prec = dat_nk;
01271                               }
01272                            }
01273                         } /* if elm_nk */
01274                      }/* for k */
01275                      if (n_prec < 0) { /* bad news */
01276                         SUMA_SL_Crit("No precursor found for node.");
01277                         SUMA_RETURN(NULL);
01278                      } else {
01279                         dat_ne->off = dat_prec->off + sqrt(SegPres); SegPres = 0.0;
01280                         if (!CoverThisNode) {
01281                            if (dat_ne->off < lim) { /* must go at least one more layer */
01282                               AllDone = NOPE;
01283                            }
01284                         } else {
01285                            if (CoverThisNode[n_jne]) {
01286                               CoverThisNode[n_jne] = 0; --N_CoverThisNode;
01287                            }
01288                            if (N_CoverThisNode > 0) {
01289                               AllDone = NOPE;
01290                            }
01291                         }
01292                      }
01293                   } /* if elm_ne */
01294                } /* for jne */
01295             } /* dat->layer == LayInd */
01296          }  while (dat->layer == (LayInd-1) && elm != dlist_tail(list));
01297       
01298       ++LayInd;
01299    } /* while AllDone */
01300    
01301    SUMA_RETURN(list);
01302 }
01303 
01304 typedef struct {
01305    SUMA_SurfaceObject *SO;
01306    SUMA_SurfaceObject *SOref;
01307    SUMA_COMM_STRUCT *cs;
01308    double Vref;
01309    double Rref;
01310    double V;
01311    double R;
01312    float *tmpList;
01313 } SUMA_VolDiffDataStruct; /*!< a special struct for the functions to equate the volume of two surfaces */
01314 typedef struct {
01315    SUMA_SurfaceObject *SO;
01316    SUMA_SurfaceObject *SOref;
01317    SUMA_COMM_STRUCT *cs;
01318    double Aref;
01319    double Rref;
01320    double A;
01321    double R;
01322    float *tmpList;
01323 } SUMA_AreaDiffDataStruct; /*!< a special struct for the functions to equate the area of two surfaces */
01324 
01325 /*!
01326    \brief Changes the coordinates of SO's nodes so that the new average radius of the surface
01327    is equal to r
01328    
01329    This function is an integral part of the function for equating the areas of 2 surfaces.
01330    Nodes are stretched by a fraction equal to:
01331       (Rref - r) / Rref * Un where Un is the distance of the node from the center of the surface
01332       Rref is the reference radius, r is the desired radius
01333    \param SO (SUMA_SurfaceObject *) Surface object, obviously
01334    \param r (double) (see above)
01335    \param Rref (double) (see above)
01336    \pram tmpList (float *) a pre-allocated vector to contain the new coordinates of the surface 
01337    \return A (double) the area of the new surface (post streching)
01338    \sa SUMA_AreaDiff
01339 */
01340 double SUMA_NewAreaAtRadius(SUMA_SurfaceObject *SO, double r, double Rref, float *tmpList)
01341 {
01342    static char FuncName[]={"SUMA_NewAreaAtRadius"};
01343    double Dr, A=0.0,  Un, U[3], Dn, P2[2][3], c[3];
01344    float *fp;
01345    int i;
01346    SUMA_Boolean LocalHead = NOPE;
01347 
01348    SUMA_ENTRY;
01349 
01350    /* calculate Dr and normalize by the radius of SOref */
01351    Dr = ( Rref - r ) / Rref;
01352 
01353    /* Now loop over all the nodes in SO and add the deal */
01354    for (i=0; i<SO->N_Node; ++i) {
01355       /* change node coordinate of each node by Dr, along radial direction  */
01356       fp = &(SO->NodeList[3*i]); SUMA_UNIT_VEC(SO->Center, fp, U, Un);
01357       Dn = Dr*Un + Un;
01358       if (Un) {
01359          SUMA_COPY_VEC(SO->Center, c, 3, float, double);
01360          SUMA_POINT_AT_DISTANCE_NORM(U, c, Dn, P2);
01361          tmpList[3*i] = (float)P2[0][0]; tmpList[3*i+1] = (float)P2[0][1]; tmpList[3*i+2] = (float)P2[0][2];
01362       } else {
01363          SUMA_SL_Err("Identical points!\n"
01364                      "No coordinates modified");
01365          SUMA_RETURN(0);
01366       }
01367    }
01368 
01369 
01370    /* calculate the new Area */
01371    fp = SO->NodeList;/* save NodeList */
01372    SO->NodeList = tmpList; /* use new coordinates */
01373    A = fabs((double)SUMA_Mesh_Area(SO, NULL, -1));
01374    SO->NodeList = fp; fp = NULL;   /* make NodeList point to the original data */
01375 
01376    SUMA_RETURN(A);
01377 } 
01378 
01379 /*!
01380    \brief Changes the coordinates of SO's nodes so that the new average radius of the surface
01381    is equal to r
01382    
01383    This function is an integral part of the function for equating the volumes of 2 surfaces.
01384    Nodes are stretched by a fraction equal to:
01385       (Rref - r) / Rref * Un where Un is the distance of the node from the center of the surface
01386       Rref is the reference radius, r is the desired radius
01387    \param SO (SUMA_SurfaceObject *) Surface object, obviously
01388    \param r (double) (see above)
01389    \param Rref (double) (see above)
01390    \pram tmpList (float *) a pre-allocated vector to contain the new coordinates of the surface 
01391    \return V (double) the volume of the new surface (post streching)
01392    \sa SUMA_VolDiff
01393 */
01394 double SUMA_NewVolumeAtRadius(SUMA_SurfaceObject *SO, double r, double Rref, float *tmpList)
01395 {
01396    static char FuncName[]={"SUMA_NewVolumeAtRadius"};
01397    double Dr, V=0.0,  Un, U[3], Dn, P2[2][3], c[3];
01398    float *fp;
01399    int i;
01400    SUMA_Boolean LocalHead = NOPE;
01401 
01402    SUMA_ENTRY;
01403 
01404    /* calculate Dr and normalize by the radius of SOref */
01405    Dr = ( Rref - r ) / Rref;
01406 
01407    /* Now loop over all the nodes in SO and add the deal */
01408    for (i=0; i<SO->N_Node; ++i) {
01409       /* change node coordinate of each node by Dr, along radial direction  */
01410       fp = &(SO->NodeList[3*i]); SUMA_UNIT_VEC(SO->Center, fp, U, Un);
01411       Dn = Dr*Un + Un;
01412       if (Un) {
01413          SUMA_COPY_VEC(SO->Center, c, 3, float, double);
01414          SUMA_POINT_AT_DISTANCE_NORM(U, c, Dn, P2);
01415          tmpList[3*i] = (float)P2[0][0]; tmpList[3*i+1] = (float)P2[0][1]; tmpList[3*i+2] = (float)P2[0][2];
01416       } else {
01417          SUMA_SL_Err("Identical points!\n"
01418                      "No coordinates modified");
01419          SUMA_RETURN(0);
01420       }
01421    }
01422 
01423 
01424    /* calculate the new volume */
01425    fp = SO->NodeList;/* save NodeList */
01426    SO->NodeList = tmpList; /* use new coordinates */
01427    V = fabs((double)SUMA_Mesh_Volume(SO, NULL, -1));
01428    SO->NodeList = fp; fp = NULL;   /* make NodeList point to the original data */
01429 
01430    SUMA_RETURN(V);
01431 } 
01432 
01433 double SUMA_AreaDiff(double r, void *fvdata)
01434 {
01435    static char FuncName[]={"SUMA_AreaDiff"};
01436    double da, *fp, Dr, A;
01437    static int ncall=0;
01438    int i;
01439    static double Rref = 0.0, Aref = 0.0;
01440    SUMA_SurfaceObject *SO, *SOref;
01441    SUMA_COMM_STRUCT *cs=NULL;
01442    SUMA_AreaDiffDataStruct *fdata = (SUMA_AreaDiffDataStruct*)fvdata ;
01443    SUMA_Boolean LocalHead = NOPE;
01444 
01445    SUMA_ENTRY;
01446    
01447    if (!fdata) {
01448       SUMA_LH("Reset");
01449       Rref = 0.0; Aref = 0.0;
01450       ncall = 0;
01451       SUMA_RETURN(0.0);
01452    }
01453    
01454    SO = fdata->SO;
01455    SOref = fdata->SOref;
01456    cs = fdata->cs;
01457    
01458    if (!ncall) {
01459       SUMA_LH("Initializing, calculating Aref and Rref");
01460       Aref = fdata->Aref;
01461       Rref = fdata->Rref;
01462       if (LocalHead) { fprintf(SUMA_STDERR,"%s: Reference volume = %f, radius = %f \n", FuncName, Aref, Rref); }
01463       if (cs->Send) { /* send the first monster (it's SOref "in SUMA" that's being modified on the fly) */
01464          if (!SUMA_SendToSuma (SOref, cs, (void *)SO->NodeList, SUMA_NODE_XYZ, 1)) {
01465          SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCommunication halted.");
01466          }
01467       }
01468    }
01469    
01470    A = SUMA_NewAreaAtRadius(SO, r, Rref, fdata->tmpList);
01471    da = Aref - A; /* the area difference */
01472    if (LocalHead) {
01473       fprintf(SUMA_STDERR,"%s: Call %d, A = %f, Aref = %f, da = %f\n", FuncName,ncall, A, Aref, da);
01474    }
01475       
01476    /* need an update ? */
01477    if (cs->Send) { /* send the update (it's SOref "in SUMA" that's being modified on the fly) */
01478       if (!SUMA_SendToSuma (SOref, cs, (void *)fdata->tmpList, SUMA_NODE_XYZ, 1)) {
01479       SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCommunication halted.");
01480       }
01481    }
01482 
01483    ++ncall;
01484    
01485    SUMA_RETURN(da);
01486 }
01487 
01488 double SUMA_VolDiff(double r, void *fvdata)
01489 {
01490    static char FuncName[]={"SUMA_VolDiff"};
01491    double dv, *fp, Dr, V;
01492    static int ncall=0;
01493    int i;
01494    static double Rref = 0.0, Vref = 0.0;
01495    SUMA_SurfaceObject *SO, *SOref;
01496    SUMA_COMM_STRUCT *cs=NULL;
01497    SUMA_VolDiffDataStruct *fdata = (SUMA_VolDiffDataStruct*)fvdata ;
01498    SUMA_Boolean LocalHead = NOPE;
01499 
01500    SUMA_ENTRY;
01501    
01502    if (!fdata) {
01503       SUMA_LH("Reset");
01504       Rref = 0.0; Vref = 0.0;
01505       ncall = 0;
01506       SUMA_RETURN(0.0);
01507    }
01508    
01509    SO = fdata->SO;
01510    SOref = fdata->SOref;
01511    cs = fdata->cs;
01512    
01513    if (!ncall) {
01514       SUMA_LH("Initializing, calculating Vref and Rref");
01515       Vref = fdata->Vref;
01516       Rref = fdata->Rref;
01517       if (LocalHead) { fprintf(SUMA_STDERR,"%s: Reference volume = %f, radius = %f \n", FuncName, Vref, Rref); }
01518       if (cs->Send) { /* send the first monster (it's SOref "in SUMA" that's being modified on the fly) */
01519          if (!SUMA_SendToSuma (SOref, cs, (void *)SO->NodeList, SUMA_NODE_XYZ, 1)) {
01520          SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCommunication halted.");
01521          }
01522       }
01523    }
01524    
01525    V = SUMA_NewVolumeAtRadius(SO, r, Rref, fdata->tmpList);
01526    dv = Vref - V; /* the volume difference */
01527       
01528    /* need an update ? */
01529    if (cs->Send) { /* send the update (it's SOref "in SUMA" that's being modified on the fly) */
01530       if (!SUMA_SendToSuma (SOref, cs, (void *)fdata->tmpList, SUMA_NODE_XYZ, 1)) {
01531       SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCommunication halted.");
01532       }
01533    }
01534 
01535    ++ncall;
01536    
01537    SUMA_RETURN(dv);
01538 }
01539  
01540 /*! \brief Binary Zero Search, a function to find the zero of a function 
01541    \param a (double) 1st point, f(a) < 0
01542    \param b (double) 2nd point, f(b) > 0 (actually all you need is that f(a)*f(b) is < 0
01543    \param *f (double )(double x, void *data) function to find the zero point (at the right x)
01544    \param fdata(void *)a pointer to the data that accompanies x as input to f
01545    \param Nitermax (int) the maximum number of iterations
01546    \param tol(double) the tolerance for convergence. Stop when ( |f(x)| < tol ) 
01547 */
01548 double SUMA_BinaryZeroSearch(double a, double b, double(*f)(double x, void *data), void *fdata, int Nitermax, double tol) {
01549    static char FuncName[]={"SUMA_BinaryZeroSearch"};
01550    int Niter;
01551    double x, fx;
01552    SUMA_Boolean done;
01553    SUMA_Boolean LocalHead = NOPE;
01554 
01555    SUMA_ENTRY;
01556    
01557    if (Nitermax < 0) Nitermax = 1000;
01558 
01559    x = 0.0;
01560    Niter = 0;
01561    done = NOPE;
01562    while(!done && Niter < Nitermax) {
01563       x = (a+b)/2.0;
01564       fx = (*f)(x, fdata);
01565       if (LocalHead) fprintf(SUMA_STDERR,"%s: %d\ta=%.4f\tb=%.4f\tx=%.4f\tfx=%.4f\n", 
01566                                           FuncName, Niter, a, b, x, fx);
01567       if (fx < 0) a = x;
01568       else b = x;
01569       if (fabs(fx) < tol) done = YUP;
01570       ++Niter;
01571    }
01572    
01573    if (!done) {
01574       SUMA_SL_Warn(  "Reached iteration limit\n"
01575                      "without converging.\n");
01576    }
01577    
01578    SUMA_RETURN(x);
01579 }
01580 
01581 
01582 /*
01583 #define FROM_THIS_NODE 0
01584 #define TO_THIS_NODE 10
01585 */
01586 /*!
01587    \brief a function to find two values a and b such that
01588    DA(a) is < 0 and DA(b) is > 0
01589    These two starting points are used for the optimization function
01590    SUMA_BinaryZeroSearch
01591 */
01592 SUMA_Boolean SUMA_GetAreaDiffRange(SUMA_AreaDiffDataStruct *fdata, double *ap, double *bp)
01593 {
01594    static char FuncName[]={"SUMA_GetAreaDiffRange"};
01595    double a = 0.0, b = 0.0, nat=0, nbt=0, An, Bn;
01596    SUMA_Boolean LocalHead = NOPE;
01597 
01598    SUMA_ENTRY;
01599 
01600    /* decide on segment range */
01601    fdata->Aref = fabs((double)SUMA_Mesh_Area(fdata->SOref, NULL, -1));
01602    SUMA_SO_RADIUS(fdata->SOref, fdata->Rref);
01603    fdata->A = fabs((double)SUMA_Mesh_Area(fdata->SO, NULL, -1));
01604    SUMA_SO_RADIUS(fdata->SO, fdata->R);
01605 
01606    /* a very simple range setting. might very well fail at times */
01607    if (fdata->Aref - fdata->A < 0) { 
01608       a = fdata->R; /* choose 'a' such that Aref - A is < 0, Choose b later*/
01609       An = fdata->A;
01610       b = fdata->Rref;
01611       do {
01612          SUMA_LH("Looking for b");
01613          b *= 1.3;  /* choose A that Aref - A is > 0 */
01614          Bn = SUMA_NewAreaAtRadius(fdata->SO, b, fdata->Rref, fdata->tmpList);
01615          ++nbt;
01616       } while ( fdata->Aref - Bn < 0 && nbt < 200);
01617    }else{
01618       b = fdata->R; /* choose 'b' such that Aref - A is > 0, Choose a later*/
01619       Bn = fdata->A;
01620       a = fdata->Rref;
01621       do {
01622          SUMA_LH("Looking for a");
01623          a *= 0.7;
01624          An = SUMA_NewAreaAtRadius(fdata->SO, a, fdata->Rref, fdata->tmpList);
01625          ++nat;
01626       } while ( fdata->Aref -  An> 0 && nat < 200);
01627    }
01628 
01629    *ap = a; *bp = b;
01630 
01631    if (nat >= 200 || nbt >= 200) {
01632       SUMA_SL_Err("Failed to find segment.");
01633       SUMA_RETURN(NOPE);
01634    }
01635 
01636    if (LocalHead) {
01637       fprintf (SUMA_STDERR,"%s:\nChosen range is [%f %f] with Areas [%f %f], reference Area is %f\n", 
01638             FuncName, a, b, An, Bn, fdata->Aref);
01639    }
01640    
01641    SUMA_RETURN(YUP); 
01642 }
01643 /*!
01644    \brief a function to find two values a and b such that
01645    DV(a) is < 0 and DV(b) is > 0
01646    These two starting points are used for the optimization function
01647    SUMA_BinaryZeroSearch
01648 */
01649 SUMA_Boolean SUMA_GetVolDiffRange(SUMA_VolDiffDataStruct *fdata, double *ap, double *bp)
01650 {
01651    static char FuncName[]={"SUMA_GetVolDiffRange"};
01652    double a = 0.0, b = 0.0, nat=0, nbt=0;
01653    SUMA_Boolean LocalHead = NOPE;
01654 
01655    SUMA_ENTRY;
01656 
01657    /* decide on segment range */
01658    fdata->Vref = fabs((double)SUMA_Mesh_Volume(fdata->SOref, NULL, -1));
01659    SUMA_SO_RADIUS(fdata->SOref, fdata->Rref);
01660    fdata->V = fabs((double)SUMA_Mesh_Volume(fdata->SO, NULL, -1));
01661    SUMA_SO_RADIUS(fdata->SO, fdata->R);
01662 
01663    /* a very simple range setting. might very well fail at times */
01664    if (fdata->Vref - fdata->V < 0) { 
01665       a = fdata->R; /* choose 'a' such that Vref - V is < 0, Choose b later*/
01666       b = fdata->Rref;
01667       do {
01668          SUMA_LH("Looking for b");
01669          b *= 1.3; ++nbt; /* choose b that Vref - V is > 0 */
01670       } while ( fdata->Vref - SUMA_NewVolumeAtRadius(fdata->SO, b, fdata->Rref, fdata->tmpList) < 0 && nbt < 200);
01671    }else{
01672       b = fdata->R; /* choose 'b' such that Vref - V is > 0, Choose a later*/
01673       a = fdata->Rref;
01674       do {
01675          SUMA_LH("Looking for a");
01676          a *= 0.7; ++nat;
01677       } while ( fdata->Vref - SUMA_NewVolumeAtRadius(fdata->SO, a, fdata->Rref, fdata->tmpList) > 0 && nat < 200);
01678    }
01679 
01680    *ap = a; *bp = b;
01681 
01682    if (nat >= 200 || nbt >= 200) {
01683       SUMA_SL_Err("Failed to find segment.");
01684       SUMA_RETURN(NOPE);
01685    }
01686 
01687    SUMA_RETURN(YUP); 
01688 }
01689 
01690 /*!
01691    \brief inflates or deflates a surface to make the area of one surface (SO) equal to the area of another (SOref)
01692    \param SO: The surface to modify. SO's NodeList pointer is reallocated in the function!
01693    \param SOref: The reference surface
01694    \param tol (float): The acceptable difference between the two areas
01695    \param cs (SUMA_COMM_STRUCT *): The suma communication structure
01696    
01697    - This function does not update the normals and other coordinate related properties for SO.
01698    \sa SUMA_RECOMPUTE_NORMALS 
01699 */
01700 SUMA_Boolean SUMA_EquateSurfaceAreas(SUMA_SurfaceObject *SO, SUMA_SurfaceObject *SOref, float tol, SUMA_COMM_STRUCT *cs)
01701 {
01702    static char FuncName[]={"SUMA_EquateSurfaceAreas"};
01703    int iter, i, iter_max, ndiv;
01704    double a, b, d;
01705    SUMA_AreaDiffDataStruct fdata;
01706    SUMA_Boolean LocalHead = NOPE;
01707    
01708    SUMA_ENTRY;
01709 
01710    if (!SO || !SOref) { SUMA_SL_Err("NULL surfaces"); SUMA_RETURN(NOPE); }
01711    if (SO->N_Node != SOref->N_Node || SO->N_FaceSet != SOref->N_FaceSet) { SUMA_SL_Err("Surfaces not isotopic"); SUMA_RETURN(NOPE); }
01712    
01713    if (LocalHead) {
01714       fprintf(SUMA_STDERR, "%s:\n"
01715                            " SO    Center: %f, %f, %f\n"
01716                            " SOref Center: %f, %f, %f\n"
01717                            , FuncName, 
01718                            SO->Center[0], SO->Center[1], SO->Center[2],
01719                            SOref->Center[0], SOref->Center[1], SOref->Center[2]);  
01720    }
01721      
01722    /* fill up fdata */
01723    fdata.SO = SO; fdata.SOref = SOref; fdata.cs = cs;
01724    fdata.tmpList = (float *)SUMA_malloc(SOref->NodeDim * SOref->N_Node * sizeof(float));
01725    if (!fdata.tmpList) {
01726       SUMA_SL_Err("Failed to allocate");
01727       SUMA_RETURN(0);
01728    }
01729 
01730    if (!SUMA_GetAreaDiffRange(&fdata, &a, &b)) {
01731       SUMA_SL_Err("Failed to get range");
01732       SUMA_RETURN(NOPE);
01733    }
01734    
01735    if (LocalHead) {
01736       fprintf(SUMA_STDERR,"%s:\na = %f\tb=%f\n", FuncName, a, b);
01737    }      
01738    SUMA_BinaryZeroSearch(a, b, SUMA_AreaDiff, &fdata, 500, tol);  
01739    
01740    /* now make the new node list be SO's thingy*/
01741    SUMA_free(SO->NodeList); SO->NodeList = fdata.tmpList; fdata.tmpList = NULL;
01742        
01743    SUMA_RETURN(YUP);
01744 }
01745 
01746 /*!
01747    \brief inflates or deflates a surface to make the volume of one surface (SO) equal to the volume of another (SOref)
01748    \param SO: The surface to modify
01749    \param SOref: The reference surface
01750    \param tol (float): The acceptable difference between the two volumes
01751    \param cs (SUMA_COMM_STRUCT *): The suma communication structure
01752    
01753    - This function does not update the normals and other coordinate related properties for SO.
01754    \sa SUMA_RECOMPUTE_NORMALS 
01755 */
01756 SUMA_Boolean SUMA_EquateSurfaceVolumes(SUMA_SurfaceObject *SO, SUMA_SurfaceObject *SOref, float tol, SUMA_COMM_STRUCT *cs)
01757 {
01758    static char FuncName[]={"SUMA_EquateSurfaceVolumes"};
01759    int iter, i, iter_max, ndiv;
01760    double a, b, d;
01761    SUMA_VolDiffDataStruct fdata;
01762    SUMA_Boolean LocalHead = NOPE;
01763    
01764    SUMA_ENTRY;
01765 
01766    if (!SO || !SOref) { SUMA_SL_Err("NULL surfaces"); SUMA_RETURN(NOPE); }
01767    if (SO->N_Node != SOref->N_Node || SO->N_FaceSet != SOref->N_FaceSet) { SUMA_SL_Err("Surfaces not isotopic"); SUMA_RETURN(NOPE); }
01768    
01769    if (LocalHead) {
01770       fprintf(SUMA_STDERR, "%s:\n"
01771                            " SO    Center: %f, %f, %f\n"
01772                            " SOref Center: %f, %f, %f\n"
01773                            , FuncName, 
01774                            SO->Center[0], SO->Center[1], SO->Center[2],
01775                            SOref->Center[0], SOref->Center[1], SOref->Center[2]);  
01776    }
01777      
01778    /* fill up fdata */
01779    fdata.SO = SO; fdata.SOref = SOref; fdata.cs = cs;
01780    fdata.tmpList = (float *)SUMA_malloc(SOref->NodeDim * SOref->N_Node * sizeof(float));
01781    if (!fdata.tmpList) {
01782       SUMA_SL_Err("Failed to allocate");
01783       SUMA_RETURN(0);
01784    }
01785 
01786    if (!SUMA_GetVolDiffRange(&fdata, &a, &b)) {
01787       SUMA_SL_Err("Failed to get range");
01788       SUMA_RETURN(NOPE);
01789    }
01790    
01791    if (LocalHead) {
01792       fprintf(SUMA_STDERR,"%s:\na = %f\tb=%f\n", FuncName, a, b);
01793    }      
01794    SUMA_BinaryZeroSearch(a, b, SUMA_VolDiff, &fdata, 500, tol);  
01795    
01796    /* now make the new node list be SO's thingy*/
01797    SUMA_free(SO->NodeList); SO->NodeList = fdata.tmpList; fdata.tmpList = NULL;
01798        
01799    SUMA_RETURN(YUP);
01800 }
01801 
01802 /*!
01803    \brief stretch each node along the center--node direction such that the new distance is = radius
01804    \param SO The surface to be modified.
01805           Adjust node coordinates of SO so that
01806           Node i on SO is repositioned such 
01807           that |c i| = radius
01808           c is the centers of SO .
01809    \param SOref reference SurfaceObject, used to communicate with SUMA 
01810    \param radius , you know what.
01811    \param cs the famed communication structure
01812 */
01813 /*
01814 #define FROM_THIS_NODE 0
01815 #define TO_THIS_NODE 10
01816 */
01817 SUMA_Boolean SUMA_ProjectSurfaceToSphere(SUMA_SurfaceObject *SO, SUMA_SurfaceObject *SOref ,float radius, SUMA_COMM_STRUCT *cs)
01818 {
01819    static char FuncName[]={"SUMA_ProjectSurfaceToSphere"};
01820    int i=0, j=0, cnt = 0, istrt, istp;
01821    struct timeval start_time, start_time_all;
01822    float etime_GetOffset, etime_GetOffset_all, ave_dist= 0.0, dj = 0.0, ave_dist_ref= 0.0, *a=NULL;
01823    float P2[2][3], U[3], Un;
01824    SUMA_Boolean LocalHead = NOPE;
01825    
01826    SUMA_ENTRY;
01827 
01828    if (!SO || !SOref) { SUMA_SL_Err("NULL surface"); SUMA_RETURN(NOPE); }
01829    
01830    if (LocalHead) {
01831       fprintf(SUMA_STDERR, "%s:\n"
01832                            " SO    Center: %f, %f, %f\n"
01833                            " radius = %f\n", FuncName, 
01834                            SO->Center[0], SO->Center[1], SO->Center[2],
01835                            radius);  
01836    }
01837       
01838    #ifdef FROM_THIS_NODE
01839    istrt = FROM_THIS_NODE;
01840    istp = TO_THIS_NODE+1;
01841    #else
01842    istrt = 0;
01843    istp = SO->N_Node;
01844    #endif
01845    ave_dist_ref =  radius;
01846    for (i =istrt ; i<istp; ++i) {
01847       if (i == 0) {
01848          SUMA_etime(&start_time,0);
01849       }
01850       /* move node i to the reference average location
01851       Do not travel along normals, you should travel along
01852       radial direction Center-->node*/
01853       a = &(SO->NodeList[3*i]); SUMA_UNIT_VEC(SO->Center, a, U, Un);
01854       if (Un) {
01855          SUMA_POINT_AT_DISTANCE_NORM(U, SO->Center, ave_dist_ref, P2);
01856          SO->NodeList[3*i] = P2[0][0]; SO->NodeList[3*i+1] = P2[0][1]; SO->NodeList[3*i+2] = P2[0][2];
01857       } else {
01858             SUMA_SL_Err("Identical points!\n"
01859                         "No coordinates modified");
01860       }
01861       
01862       if (LocalHead) {
01863          if (! (i%999)) {
01864             a = &(SO->NodeList[3*i]);
01865             SUMA_SEG_LENGTH(a, SO->Center, dj);
01866             fprintf(SUMA_STDERR, "%s:\n"
01867                            "node i=%d, avg_dist_ref = %f\ncnt = %d\n"
01868                            "Check on P2: New dist =%f ?=? %f\n", 
01869                            FuncName, i, ave_dist_ref, cnt, dj, ave_dist_ref);
01870             etime_GetOffset = SUMA_etime(&start_time,1);
01871             fprintf(SUMA_STDERR, "%s: Search to %f mm took %f seconds for %d nodes.\n"
01872                   "Projected completion time: %f minutes\n",
01873                   FuncName, radius, etime_GetOffset, i+1,
01874                   etime_GetOffset * SO->N_Node / 60.0 / (i+1));
01875          }
01876       }
01877       if (! (i%99) && cs) {
01878          if (cs->Send) { /* send the first monster (it's SOref  "in SUMA" that's being modified on the fly*/
01879             if (!SUMA_SendToSuma (SOref, cs, (void *)SO->NodeList, SUMA_NODE_XYZ, 1)) {
01880             SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCommunication halted.");
01881             }
01882          }
01883       }
01884       
01885       #ifdef FROM_THIS_NODE
01886       {
01887          FILE *fid=NULL;
01888          char *outname=NULL, tmp[20];
01889          int ii;
01890          if (cs->Send) { /* send the first monster (it's SOref that's being modified on the fly*/
01891             if (!SUMA_SendToSuma (SOref, cs, (void *)SO->NodeList, SUMA_NODE_XYZ, 1)) {
01892             SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCommunication halted.");
01893             }
01894          }
01895          sprintf(tmp,"offset_n%d", FROM_THIS_NODE);
01896          outname = SUMA_Extension("", ".1D", YUP);
01897          outname = SUMA_append_replace_string(outname, "offset.1D", "", 1);
01898          fid = fopen(outname, "w"); free(outname); outname = NULL;
01899          if (!fid) {
01900             SUMA_SL_Err("Could not open file for writing.\nCheck file permissions, disk space.\n");
01901          } else {
01902             fprintf (fid,"#Column 1 = Node index\n"
01903                          "#column 2 = Neighborhood layer\n"
01904                          "#Column 3 = Distance from node %d\n", 99);
01905             for (ii=0; ii<SO->N_Node; ++ii) {
01906                if (OffS->LayerVect[ii] >= 0) {
01907                   fprintf(fid,"%d\t%d\t%f\n", ii, OffS->LayerVect[ii], OffS->OffVect[ii]);
01908                }
01909             }
01910             fclose(fid);
01911          }
01912          { int jnk; fprintf(SUMA_STDOUT,"Pausing, next node is %d...", i+1); jnk = getchar(); fprintf(SUMA_STDOUT,"\n"); }
01913       }
01914       #endif
01915              
01916       
01917    }   
01918    
01919    
01920    SUMA_RETURN(YUP);
01921 }
01922 
01923 /*!
01924    \brief make the size of 2 surfaces match see help -match_size option in SurfSmooth
01925    \param SO The surface to be modified.
01926           Adjust node coordinates of SO so that
01927           it matches the original size.
01928           Node i on SO is repositioned such 
01929           that |c i| = 1/N sum(|cr j|) where
01930           c and cr are the centers of SO and SOref, respectively.
01931           N is the number of nodes that are within max_off along
01932           the surface (geodesic) from node i.
01933           j is one of the nodes neighboring i.
01934    \param SOref The surface to be matched
01935    \param max_off geodesic neighborhood to search around i
01936    \param cs the famed communication structure
01937 */
01938 /*
01939 #define FROM_THIS_NODE 0
01940 #define TO_THIS_NODE 10
01941 */
01942 SUMA_Boolean SUMA_EquateSurfaceSize(SUMA_SurfaceObject *SO, SUMA_SurfaceObject *SOref, float max_off, SUMA_COMM_STRUCT *cs)
01943 {
01944    static char FuncName[]={"SUMA_EquateSurfaceSize"};
01945    int i=0, j=0, cnt = 0, istrt, istp;
01946    struct timeval start_time, start_time_all;
01947    float etime_GetOffset, etime_GetOffset_all, ave_dist= 0.0, dj = 0.0, ave_dist_ref= 0.0, *a=NULL;
01948    float P2[2][3], U[3], Un;
01949    SUMA_GET_OFFSET_STRUCT *OffS = NULL;
01950    SUMA_Boolean LocalHead = NOPE;
01951    
01952    SUMA_ENTRY;
01953 
01954    if (!SO || !SOref) { SUMA_SL_Err("NULL surfaces"); SUMA_RETURN(NOPE); }
01955    if (SO->N_Node != SOref->N_Node || SO->N_FaceSet != SOref->N_FaceSet) { SUMA_SL_Err("Surfaces not isotopic"); SUMA_RETURN(NOPE); }
01956    
01957    if (LocalHead) {
01958       fprintf(SUMA_STDERR, "%s:\n"
01959                            " SO    Center: %f, %f, %f\n"
01960                            " SOref Center: %f, %f, %f\n"
01961                            " max_off = %f\n", FuncName, 
01962                            SO->Center[0], SO->Center[1], SO->Center[2],
01963                            SOref->Center[0], SOref->Center[1], SOref->Center[2],
01964                            max_off);  
01965    }
01966       
01967    OffS = SUMA_Initialize_getoffsets (SOref->N_Node);
01968    #ifdef FROM_THIS_NODE
01969    istrt = FROM_THIS_NODE;
01970    istp = TO_THIS_NODE+1;
01971    #else
01972    istrt = 0;
01973    istp = SOref->N_Node;
01974    #endif
01975    for (i =istrt ; i<istp; ++i) {
01976       if (i == 0) {
01977          SUMA_etime(&start_time,0);
01978       }
01979       SUMA_getoffsets2 (i, SOref, max_off, OffS, NULL, 0);
01980       /* find average distance between nodes within offset and center of surface */
01981       a = &(SOref->NodeList[3*i]); SUMA_SEG_LENGTH(a, SOref->Center, ave_dist_ref); 
01982       cnt = 1;
01983       #ifdef FROM_THIS_NODE
01984             fprintf(SUMA_STDERR, "%s: Considering the following %d neighbors to:\n"
01985                                  "i=%d; [%f, %f, %f]; d[%d] = %f\n", FuncName, OffS->N_Nodes,
01986                                     i, SOref->NodeList[3*i], SOref->NodeList[3*i+1], SOref->NodeList[3*i+2], 
01987                                     cnt, ave_dist_ref);
01988       #endif
01989       for (j=0; j<OffS->N_Nodes; ++j)
01990       {
01991          
01992          if (i!=j && OffS->LayerVect[j] >= 0 && OffS->OffVect[j] <= max_off)
01993          {
01994             
01995             a = &(SOref->NodeList[3*j]); SUMA_SEG_LENGTH(a, SOref->Center, dj);
01996             ave_dist_ref += dj;
01997             ++cnt;
01998             #ifdef FROM_THIS_NODE
01999                fprintf(SUMA_STDERR, ""
02000                                  "j=%d; [%f, %f, %f]; d[%d] = %f\n",
02001                                     j, SOref->NodeList[3*j], SOref->NodeList[3*j+1], SOref->NodeList[3*j+2], 
02002                                     cnt, dj);
02003             #endif
02004          }
02005       }
02006       ave_dist_ref /=  (float)cnt;
02007       /* move node i to the reference average location
02008       Do not travel along normals, you should travel along
02009       radial direction Center-->node*/
02010       a = &(SO->NodeList[3*i]); SUMA_UNIT_VEC(SO->Center, a, U, Un);
02011       if (Un) {
02012          SUMA_POINT_AT_DISTANCE_NORM(U, SO->Center, ave_dist_ref, P2);
02013          SO->NodeList[3*i] = P2[0][0]; SO->NodeList[3*i+1] = P2[0][1]; SO->NodeList[3*i+2] = P2[0][2];
02014       } else {
02015             SUMA_SL_Err("Identical points!\n"
02016                         "No coordinates modified");
02017       }
02018       
02019       if (LocalHead) {
02020          if (! (i%999)) {
02021             a = &(SO->NodeList[3*i]);
02022             SUMA_SEG_LENGTH(a, SOref->Center, dj);
02023             fprintf(SUMA_STDERR, "%s:\n"
02024                            "node i=%d, avg_dist_ref = %f\ncnt = %d\n"
02025                            "Check on P2: New dist =%f ?=? %f\n", 
02026                            FuncName, i, ave_dist_ref, cnt, dj, ave_dist_ref);
02027             etime_GetOffset = SUMA_etime(&start_time,1);
02028             fprintf(SUMA_STDERR, "%s: Search to %f mm took %f seconds for %d nodes.\n"
02029                   "Projected completion time: %f minutes\n",
02030                   FuncName, max_off, etime_GetOffset, i+1,
02031                   etime_GetOffset * SO->N_Node / 60.0 / (i+1));
02032          }
02033       }
02034       if (! (i%99) && cs) {
02035          if (cs->Send) { /* send the first monster (it's SOref  "in SUMA" that's being modified on the fly*/
02036             if (!SUMA_SendToSuma (SOref, cs, (void *)SO->NodeList, SUMA_NODE_XYZ, 1)) {
02037             SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCommunication halted.");
02038             }
02039          }
02040       }
02041       
02042       #ifdef FROM_THIS_NODE
02043       {
02044          FILE *fid=NULL;
02045          char *outname=NULL, tmp[20];
02046          int ii;
02047          if (cs->Send) { /* send the first monster (it's SOref that's being modified on the fly*/
02048             if (!SUMA_SendToSuma (SOref, cs, (void *)SO->NodeList, SUMA_NODE_XYZ, 1)) {
02049             SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCommunication halted.");
02050             }
02051          }
02052          sprintf(tmp,"offset_n%d", FROM_THIS_NODE);
02053          outname = SUMA_Extension("", ".1D", YUP);
02054          outname = SUMA_append_replace_string(outname, "offset.1D", "", 1);
02055          fid = fopen(outname, "w"); free(outname); outname = NULL;
02056          if (!fid) {
02057             SUMA_SL_Err("Could not open file for writing.\nCheck file permissions, disk space.\n");
02058          } else {
02059             fprintf (fid,"#Column 1 = Node index\n"
02060                          "#column 2 = Neighborhood layer\n"
02061                          "#Column 3 = Distance from node %d\n", 99);
02062             for (ii=0; ii<SO->N_Node; ++ii) {
02063                if (OffS->LayerVect[ii] >= 0) {
02064                   fprintf(fid,"%d\t%d\t%f\n", ii, OffS->LayerVect[ii], OffS->OffVect[ii]);
02065                }
02066             }
02067             fclose(fid);
02068          }
02069          { int jnk; fprintf(SUMA_STDOUT,"Pausing, next node is %d...", i+1); jnk = getchar(); fprintf(SUMA_STDOUT,"\n"); }
02070       }
02071       #endif
02072              
02073       /* recycle offsets structure */
02074       SUMA_Recycle_getoffsets (OffS);
02075       
02076    }   
02077    
02078    /* offsets are to be freed */
02079    SUMA_Free_getoffsets(OffS); OffS = NULL;
02080    
02081    SUMA_RETURN(YUP);
02082 }
02083 
02084 /*!
02085    \brief calculate the interpolation weights required to smooth data on the surface
02086    using M.K. Chung et al. Neuroimage 03
02087    
02088    \param SO (SUMA_SurfaceObject *) Surface object with valid SO->NodeList, SO->FaceSetList and SO->FN
02089    \return wgt (float **) 2D matrix of the same size as SO->FirstNeighb that contains the
02090                            weights to be applied to a node's neighbors in interpolation
02091                            Free the result with SUMA_free2D ((char **)wgt, SO->N_Node);
02092                            The weights are computed using the Finite Element method.
02093    \sa SUMA_Chung_Smooth
02094 */
02095 float ** SUMA_Chung_Smooth_Weights (SUMA_SurfaceObject *SO)
02096 {
02097    static char FuncName[]={"SUMA_Chung_Smooth_Weights"};
02098    float **wgt=NULL, *coord_nbr=NULL, *cotan=NULL, *tfp=NULL;
02099    float dv[3], p[3], q[3];
02100    float area, area_p, area_q, dot_p, dot_q;
02101    int i, j, k, n, j3p1, j3m1, n3, j3=0, nj, nj3, i_dbg;
02102    SUMA_Boolean LocalHead = NOPE;
02103    
02104    SUMA_ENTRY;
02105 
02106    if (!SO) {
02107       SUMA_SL_Err("Null SO");
02108       SUMA_RETURN(NULL);
02109    }
02110    if (!SO->FN) {
02111       SUMA_SL_Err("Null SO->FN");
02112       SUMA_RETURN(NULL);
02113    }
02114    
02115    i_dbg = -1; /* index of debugging node, set to -1 for no debugging */
02116    /* in the demo mesh that Moo Chung gave me, 
02117    Node 17 has the following neighbors in ccw order:
02118    231 230 250 261 239 236 - 231 230 250 261 239 236 
02119    Set i_dbg = 17 and turn on LocalHead to get a confirmation of this
02120    by this function*/
02121    /* implement the non-parametric weight estimation method */
02122    wgt = (float **)SUMA_allocate2D(SO->N_Node, SO->FN->N_Neighb_max, sizeof(float));  /* vector of node weights */
02123    coord_nbr = (float *)SUMA_malloc((SO->FN->N_Neighb_max + 2) * sizeof(float) * 3); /* vector of neighboring node coordinates */ 
02124    cotan = (float *)SUMA_malloc(SO->FN->N_Neighb_max * sizeof(float)); 
02125    if (!wgt || !coord_nbr || !cotan) {
02126       SUMA_SL_Crit("Failed to allocate for wgt &/|coord_nbr &/|cotan");
02127       SUMA_RETURN(NULL);
02128    }
02129    
02130    for (n=0; n < SO->N_Node; ++n) {
02131       n3 = 3 * n;
02132       /* translate the coordinates of the neighboring nodes to make n be the origin */
02133       for (j=0; j<SO->FN->N_Neighb[n]; ++j) {
02134          j3 = 3 * (j+1);
02135          nj = SO->FN->FirstNeighb[n][j]; nj3 = 3 * nj;
02136          coord_nbr[j3] = SO->NodeList[nj3] - SO->NodeList[n3];
02137          coord_nbr[j3+1] = SO->NodeList[nj3+1] - SO->NodeList[n3+1];
02138          coord_nbr[j3+2] = SO->NodeList[nj3+2] - SO->NodeList[n3+2];
02139       }  /* for j */
02140       /* padd with last neighbor at the very beginning and 1st neighbor at the end 
02141          in matlab: coord_nbr = [coord_nbr(:,last_neighb) coord_nbr coord_nbr(:,first_neighb)];
02142       */   
02143       for (k=0; k < 3; ++k) coord_nbr[k] = coord_nbr[j3+k];  
02144       j3 = 3 * ( SO->FN->N_Neighb[n] + 1);
02145       for (k=0; k < 3; ++k) coord_nbr[j3+k] = coord_nbr[3+k];
02146       if (LocalHead && n == i_dbg) { SUMA_disp_vect (coord_nbr, 3 * (SO->FN->N_Neighb[n] + 2)) ;  }
02147       
02148       
02149       area = 0.0;
02150       for (j=1; j<=SO->FN->N_Neighb[n]; ++j) { 
02151          j3 = 3 * j; j3p1 = 3 * (j+1); j3m1 = 3 * (j-1);
02152          for (k=0; k < 3; ++k) dv[k] = coord_nbr[j3p1+k] - coord_nbr[j3+k]; 
02153          tfp = &(coord_nbr[j3p1]);
02154          dot_p = SUMA_MT_DOT (tfp, dv);
02155          SUMA_MT_CROSS(p, tfp, dv);
02156          for (k=0; k < 3; ++k) dv[k] = coord_nbr[j3m1+k] - coord_nbr[j3+k]; 
02157          tfp = &(coord_nbr[j3m1]);
02158          dot_q = SUMA_MT_DOT (tfp, dv);
02159          SUMA_MT_CROSS(q, tfp, dv);
02160          
02161          SUMA_NORM(area_p, p); 
02162          SUMA_NORM(area_q, q); 
02163          
02164          cotan[j-1] = dot_p/area_p + dot_q/area_q;
02165          area += area_p/2.0;
02166          if (LocalHead && n == i_dbg) {
02167             fprintf (SUMA_STDERR,"[%d->%d] area_p, area_q = %f, %f\n",
02168                                   n, SO->FN->FirstNeighb[n][j-1],
02169                                   area_p / 2.0,  area_q / 2.0);
02170          }
02171       }
02172       
02173       for (j=0; j<SO->FN->N_Neighb[n]; ++j) {
02174          wgt[n][j] = cotan[j]/area;
02175       }
02176       if (LocalHead && n == i_dbg) {
02177          fprintf (SUMA_STDERR,"%s: Weight Results for neighbors of %d (matlab node %d):\n",
02178                               FuncName, n, n+1);
02179          SUMA_disp_vect (wgt[n], SO->FN->N_Neighb[n]);
02180       }
02181    }  /* for n */
02182 
02183    /* free local variables */
02184    if (coord_nbr) SUMA_free(coord_nbr); coord_nbr = NULL;
02185    if (cotan) SUMA_free(cotan); cotan = NULL;
02186 
02187    SUMA_RETURN(wgt);
02188 }
02189 
02190 /*!
02191    \brief Show the transfer function (f(k)) for the Taubin 
02192    smoothing algorithm for a combination of scaling factors
02193    and number of iterations 
02194    
02195    \param l (float)  postive scaling factor
02196    \param m (float)  negative scaling factor
02197                     (for avoiding the shrinkage of surfaces)
02198             The band-pass frequency (kbp) is 1/m + 1/l
02199             f(kbp) = 1
02200    \param N_iter (int) number of iterations
02201    \param Out (FILE *) pointer to output file. If NULL,
02202                   output is to stdout
02203    \return ans (SUMA_Boolean) YUP, no problems
02204                               NOPE, yes problems
02205    
02206    The output is of the form:
02207    k f(k)
02208    
02209    where k is the normalized frequency and 
02210    f(k) is the value of the transfer function at k
02211    
02212    \sa figure 4 in Geometric Signal Processing on 
02213    Polygonal Meshes (Taubin G, Eurographics 2000)
02214    \sa SUMA_Taubin_Smooth
02215    \sa SUMA_Taubin_Smooth_Coef
02216 */   
02217 SUMA_Boolean  SUMA_Taubin_Smooth_TransferFunc (float l, float m, int N, FILE *Out)
02218 {
02219    static char FuncName[]={"SUMA_Taubin_Smooth_TransferFunc"};
02220    FILE *Outp = NULL;
02221    int i, imax = 100;
02222    float fk, k;
02223    SUMA_Boolean LocalHead = NOPE;
02224    
02225    SUMA_ENTRY;
02226 
02227    if (N % 2) {
02228       SUMA_SL_Err("N_iter must be even");
02229       SUMA_RETURN(NOPE);
02230    }
02231    
02232    if (!Out) Outp = stdout;
02233    else Outp = Out;
02234    
02235    k = 0.0;
02236    for (i=0; i< imax; ++i) {
02237       fk = pow( ( ( 1-m*k ) * ( 1-l*k ) ) , N / 2 );
02238       fprintf (Outp,"%f %f\n", k, fk);
02239       k += (float)i/(float)imax;
02240    }
02241    
02242    
02243    SUMA_RETURN(YUP);
02244 }
02245 /*!
02246    \brief Calculates Mu(m) and Lambda(l) smoothing coefficients
02247    based on Taubin's smoothing algorithm in Geometric Signal 
02248    Processing on Polygonal Meshes (Eurographics 2000)
02249    
02250    \param k (float) the pass-band frequency (typically 0.1)
02251    \param *l (float) what will be the postive scaling factor
02252    \param *m (float) what will be the negative scaling factor
02253                      (for avoiding the shrinkage of surfaces)
02254    \return ans (SUMA_Boolean) YUP, good solution found
02255                               NOPE, solution could not be found        
02256             
02257    k, l and m are related by the following equations:
02258    
02259    k = 1/l + 1/m                 (eq 8)
02260    0 = 1 - 3(l+m) + 5lm          (eq 9)
02261    
02262    the solutions must verify the following:
02263    l > 0
02264    m < -l < 0
02265    
02266    \sa SUMA_Taubin_Smooth_TransferFunc 
02267    \sa SUMA_Taubin_Smooth
02268     
02269 */ 
02270 SUMA_Boolean SUMA_Taubin_Smooth_Coef (float k, float *l, float *m)
02271 {
02272    static char FuncName[]={"SUMA_Taubin_Smooth_Coef"};
02273    int i;
02274    float ls[2], delta;
02275    SUMA_Boolean Done = NOPE;
02276    SUMA_Boolean LocalHead = NOPE;
02277    
02278    SUMA_ENTRY;
02279    
02280    if (k < 0) { SUMA_SL_Err("k < 0"); SUMA_RETURN(NOPE); }
02281    
02282    /* l1 and l2 are solutions of the quadratic equation:
02283       (5 - 3 k) l^2 + k l - 1 = 0 */
02284    delta = ( k * k - 12.0 * k + 20 );
02285    if (delta < 0) { SUMA_SL_Err("Delta is < 0 for specified k"); SUMA_RETURN(NOPE); }
02286    
02287    ls[0] = ( -k + sqrt(delta) ) / ( 10 - 6 * k );
02288    ls[1] = ( -k - sqrt(delta) ) / ( 10 - 6 * k );
02289    if (ls[0] < 0 && ls[1] < 0) { SUMA_SL_Err("No positive solution for l"); SUMA_RETURN(NOPE); }
02290    
02291    if (ls[1] > ls[0]) { /* swap them */
02292       *l = ls[0]; ls[0] = ls[1]; ls[1] = *l;
02293    }
02294    
02295    Done = NOPE;
02296    i = 0;
02297    while (!Done && i < 2) {
02298       /* calculate mu */
02299       *l = ls[i]; 
02300       *m = *l / ( k * *l - 1.0 );
02301       if (*m < 0) Done = YUP;
02302       ++i;
02303    }
02304    
02305    if (!Done) { SUMA_SL_Err("No good solutions found."); SUMA_RETURN(NOPE); }
02306    
02307    if ( ! ( ( *m < -1.0 * *l ) && ( -1.0 * *l < 0 ) ) ) {
02308       SUMA_SL_Err("Solution did not meet m < -l < 0"); SUMA_RETURN(NOPE);
02309    }
02310    
02311    SUMA_RETURN(YUP);
02312 }
02313 
02314 /*!
02315    \brief performs smoothing based on Taubin's smoothing 
02316    algorithm in Geometric Signal Processing on Polygonal 
02317    Meshes (Eurographics 2000)
02318    
02319    fout =  SUMA_Taubin_Smooth (SUMA_SurfaceObject *SO, float **wgt, 
02320                             float lambda, float mu, float *fin, 
02321                             int N_iter, int vpn,  d_order,
02322                             float *fout_user, SUMA_COMM_STRUCT *cs);
02323                             
02324    \param SO (SUMA_SurfaceObject *SO) The surface, with NodeList, FaceSetList
02325                                        and FN fields present
02326    \param wgt (float **) interpolation weights for each node. 
02327                          The dimentions of wgt are equal to those of 
02328                          SO->FN->FirstNeighb
02329                          These weights may need to be re-evaluated for
02330                          each iteration
02331                          For equal weights (1/SO->FN->N_FirstNeighb[n]), 
02332                          just pass NULL         
02333    \param lambda (float) postive scaling factor
02334    \param mu (float) negative scaling factor 
02335    \param fin (float *) vector containing node data. The length of this vector
02336                         is vpn x SO->N_Node , where vpn is the number of values
02337                         per node. 
02338    \param N_iter (int)  number of iterations (same weights are used in each iteration)
02339    \param vpn (int) number of values per node in fin
02340    \param d_order (SUMA_INDEXING_ORDER) Indicates how multiple values per node are stored in fin
02341                         SUMA_ROW_MAJOR: The data in fin is stored in *** Row Major *** order.
02342                         The ith value (start at 0) for node n is at index fin[vpn*n+i]
02343                         SUMA_COLUMN_MAJOR: The data in fin is stored in *** Column Major *** order.
02344                         The ith (start at 0) value for node n is at index fin[n+SO->N_Node*i]; 
02345                         etc...
02346    \param fout_user (float *) a pointer to the vector where the smoothed version of fin will reside
02347                         You can pass NULL and the function will do the allocation for you and return 
02348                         the pointer to the smoothed data in fout.
02349                         If you already have space allocated for the result, then pass the pointer
02350                         in fout_user and save on allocation time and space. In that case, fout
02351                         is equal to fout_user.
02352                         Either way, you are responsible for freeing memory pointed to by fout.
02353                         DO NOT PASS fout_user = fin 
02354    \param cs (SUMA_COMM_STRUCT *) See SUMA_Chung_Smooth
02355    \param nmask (byte *) NULL == filter all nodes 
02356                          else filter node n if nmask[n]
02357    \return fout (float *) A pointer to the smoothed data (vpn * SO->N_Node values). 
02358                         You will have to free the memory allocated for fout yourself.
02359                         
02360     
02361    
02362 */
02363 float * SUMA_Taubin_Smooth (SUMA_SurfaceObject *SO, float **wgt, 
02364                             float lambda, float mu, float *fin_orig, 
02365                             int N_iter, int vpn, SUMA_INDEXING_ORDER d_order,
02366                             float *fout_final_user, SUMA_COMM_STRUCT *cs, 
02367                             byte *nmask)
02368 {
02369    static char FuncName[]={"SUMA_Taubin_Smooth"};
02370    float *fout_final=NULL, *fbuf=NULL, *fin=NULL, *fout=NULL, *fin_next=NULL, *ftmp=NULL;
02371    float fp, dfp, fpj;
02372    int i, n , k, j, niter, vnk, n_offset, DoThis; 
02373    SUMA_Boolean LocalHead = NOPE;
02374    
02375    SUMA_ENTRY;
02376 
02377    if (N_iter % 2) {
02378       SUMA_SL_Err("N_iter must be an even number\n");
02379       SUMA_RETURN(NULL);
02380    }
02381    
02382    if (!SO || !fin_orig) {
02383       SUMA_SL_Err("NULL SO or fin_orig\n");
02384       SUMA_RETURN(NULL);
02385    }
02386    if (!SO->FN) {
02387       SUMA_SL_Err("NULL SO->FN\n");
02388       SUMA_RETURN(NULL);
02389    }
02390    
02391    if (vpn < 1) {
02392       SUMA_SL_Err("vpn < 1\n");
02393       SUMA_RETURN(NULL);
02394    }  
02395    
02396    if (fout_final_user == fin_orig) {
02397       SUMA_SL_Err("fout_final_user == fin_orig");
02398       SUMA_RETURN(NULL);
02399    }
02400    
02401    if (!fout_final_user) { /* allocate for output */
02402       fout_final = (float *)SUMA_calloc(SO->N_Node * vpn, sizeof(float));
02403       if (!fout_final) {
02404          SUMA_SL_Crit("Failed to allocate for fout_final\n");
02405          SUMA_RETURN(NULL);
02406       }
02407    }else {
02408       fout_final = fout_final_user; /* pre-allocated */
02409    }
02410    
02411    if (d_order == SUMA_COLUMN_MAJOR) {
02412          SUMA_SL_Warn("SUMA_COLUMN_MAJOR has not been thoroughly tested.");
02413    }
02414    
02415    if (cs->Send) {
02416       if(vpn != 3) {
02417          SUMA_SL_Warn("It does not look like you are smoothing coordinates!\nCommunication halted.");
02418          cs->Send = NOPE;
02419       }
02420       if (d_order == SUMA_COLUMN_MAJOR) {
02421          SUMA_SL_Warn("Talking with SUMA_COLUMN_MAJOR has not been tested.");
02422       }
02423    }
02424    
02425    /* allocate for buffer */
02426    fbuf = (float *)SUMA_calloc(SO->N_Node * vpn, sizeof(float));
02427    if (!fbuf) {
02428       SUMA_SL_Crit("Failed to allocate for fbuf\n");
02429       SUMA_RETURN(NULL);
02430    }
02431    
02432    if (LocalHead) {
02433       fprintf (SUMA_STDERR,"%s: Mu = %f, Lambda = %f\nShould have M(%f)< -L(%f) < 0\nN_iter=%d\n", 
02434          FuncName, mu, lambda, mu, -lambda, N_iter);
02435    }
02436    
02437    if (cs->Send) { /* send the first monster */
02438       if (!SUMA_SendToSuma (SO, cs, (void *)fin_orig, SUMA_NODE_XYZ, 1)) {
02439          SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCommunication halted.");
02440       }
02441    }
02442    
02443    fin_next = fin_orig;
02444    switch (d_order) {
02445       case SUMA_COLUMN_MAJOR:
02446          for (niter=0; niter < N_iter; ++niter) {
02447             if ( niter % 2 ) { /* odd */
02448                fin = fin_next; /* input from previous output buffer */
02449                fout = fout_final; /* results go into final vector */
02450                fin_next = fout_final; /* in the next iteration, the input is from fout_final */
02451             } else { /* even */
02452                /* input data is in fin_new */
02453                fin = fin_next;
02454                fout = fbuf; /* results go into buffer */
02455                fin_next = fbuf; /* in the next iteration, the input is from the buffer */
02456             }
02457             for (k=0; k < vpn; ++k) {
02458                n_offset = k * SO->N_Node;  /* offset of kth node value in fin */
02459                for (n=0; n < SO->N_Node; ++n) {
02460                   DoThis = 1;
02461                   if (nmask && !nmask[n]) DoThis = 0;
02462                   vnk = n+n_offset; 
02463                   if (DoThis) {
02464                      fp = fin[vnk]; /* kth value at node n */
02465                      dfp = 0.0;
02466                      for (j=0; j < SO->FN->N_Neighb[n]; ++j) { /* calculating the laplacian */
02467                         fpj = fin[SO->FN->FirstNeighb[n][j]+n_offset]; /* value at jth neighbor of n */
02468                         if (wgt) dfp += wgt[n][j] * (fpj - fp); 
02469                         else dfp += (fpj - fp); /* will apply equal weight later */
02470                      }/* for j*/
02471                      if (niter%2) { /* odd */
02472                         if (wgt) fout[vnk] = fin[vnk] + mu * dfp;
02473                         else fout[vnk] = fin[vnk] + mu * dfp / (float)SO->FN->N_Neighb[n];   /* apply equal weight factor here */
02474                      }else{ /* even */
02475                        if (wgt) fout[vnk] = fin[vnk] + lambda * dfp;
02476                        else fout[vnk] = fin[vnk] + lambda * dfp / (float)SO->FN->N_Neighb[n];  /* apply equal weight factor here */
02477                      }
02478                   } else {
02479                      fout[vnk] = fin[vnk];
02480                   }      
02481                }/* for n */   
02482             }/* for k */
02483             if (cs->Send) {
02484                /* SUMA_SendToSuma does not deal with such COLUMN_MAJOR order.
02485                Must flip things here, boooooring */
02486                if (!niter) { /* allocate for buffer */
02487                   ftmp = (float *) SUMA_malloc(3*SO->N_Node*sizeof(float));
02488                   if (!ftmp) { SUMA_SL_Err("Failed to allocate. Communication Off.\n"); cs->Send = NOPE; }
02489                }
02490                if (ftmp) {
02491                   for (i=0; i<SO->N_Node; ++i) { ftmp[3*i] = fout[i]; ftmp[3*i+1] = fout[i+SO->N_Node];  ftmp[3*i+2] = fout[i+2*SO->N_Node];}
02492                   if (!SUMA_SendToSuma (SO, cs, (void *)ftmp, SUMA_NODE_XYZ, 1)) {
02493                      SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCommunication halted.");
02494                   }
02495                }
02496                if (niter == N_iter -1) { /* free the buffer */
02497                   if (ftmp) { SUMA_free(ftmp); ftmp = NULL; }
02498                }
02499             }
02500          }/* for niter */
02501          break;
02502       case SUMA_ROW_MAJOR:
02503          for (niter=0; niter < N_iter; ++niter) {
02504             if ( niter % 2 ) { /* odd */
02505                fin = fin_next; /* input from previous output buffer */
02506                fout = fout_final; /* results go into final vector */
02507                fin_next = fout_final; /* in the next iteration, the input is from fout_final */
02508             } else { /* even */
02509                /* input data is in fin_new */
02510                fin = fin_next;
02511                fout = fbuf; /* results go into buffer */
02512                fin_next = fbuf; /* in the next iteration, the input is from the buffer */
02513             }
02514             for (n=0; n < SO->N_Node; ++n) {
02515                DoThis = 1;
02516                if (nmask && !nmask[n]) DoThis = 0;
02517                vnk = n * vpn; /* index of 1st value at node n */
02518                for (k=0; k < vpn; ++k) {
02519                   if (DoThis) {
02520                      fp = fin[vnk]; /* kth value at node n */
02521                      dfp = 0.0;
02522                      for (j=0; j < SO->FN->N_Neighb[n]; ++j) { /* calculating the laplacian */
02523                         fpj = fin[SO->FN->FirstNeighb[n][j]*vpn+k]; /* value at jth neighbor of n */
02524                         if (wgt) dfp += wgt[n][j] * (fpj - fp); 
02525                         else dfp += (fpj - fp); /* will apply equal weight later */
02526                      }/* for j*/
02527                      if (niter%2) { /* odd */
02528                         if (wgt) fout[vnk] = fin[vnk] + mu * dfp;
02529                         else fout[vnk] = fin[vnk] + mu * dfp / (float)SO->FN->N_Neighb[n];   /* apply equal weight factor here */
02530                      }else{ /* even */
02531                        if (wgt) fout[vnk] = fin[vnk] + lambda * dfp;
02532                        else fout[vnk] = fin[vnk] + lambda * dfp / (float)SO->FN->N_Neighb[n];  /* apply equal weight factor here */
02533                      }
02534                   } else {
02535                      fout[vnk] = fin[vnk];
02536                   } 
02537                   ++vnk; /* index of next value at node n */
02538                } /* for k */
02539             }/* for n */
02540             if (cs->Send) {
02541                if (!SUMA_SendToSuma (SO, cs, (void *)fout, SUMA_NODE_XYZ, 1)) {
02542                   SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCommunication halted.");
02543                }
02544             }
02545          }/* for niter */
02546          break;
02547       default:
02548          SUMA_SL_Err("Bad Major, very bad.\n");
02549          SUMA_RETURN(NULL);
02550          break;
02551    }
02552    
02553    if (fbuf) SUMA_free(fbuf); fbuf = NULL;
02554       
02555    SUMA_RETURN(fout);
02556 }
02557 
02558 /*!
02559    \brief a function to turn the often cumbersome SUMA_GET_OFFSET_STRUCT
02560    to a more friendly SUMA_OFFSET_STRUCT
02561 */
02562 
02563 SUMA_Boolean SUMA_GetOffset2Offset (SUMA_GET_OFFSET_STRUCT *GOS, SUMA_OFFSET_STRUCT *OS) 
02564 {
02565    static char FuncName[]={"SUMA_GetOffset2Offset"};
02566    int il, jl, noffs;
02567    SUMA_Boolean LocalHead = NOPE;
02568    
02569    SUMA_ENTRY;
02570    
02571    if (!GOS || !OS) {
02572       SUMA_SL_Err("NULL input"); SUMA_RETURN(NOPE);
02573    }
02574    
02575    OS->N_Neighb = 0; 
02576    for (il=1; il<GOS->N_layers; ++il) {
02577       OS->N_Neighb += GOS->layers[il].N_NodesInLayer;
02578    }
02579    OS->Neighb_ind = (int *)SUMA_malloc(OS->N_Neighb * sizeof(int));
02580    OS->Neighb_dist = (float *)SUMA_malloc(OS->N_Neighb * sizeof(float));
02581    if (!OS->Neighb_ind || !OS->Neighb_dist) {
02582       SUMA_SL_Crit("Failed to allocate.");
02583       SUMA_RETURN(NOPE);
02584    }
02585    
02586    noffs = 0;
02587    for (il=1; il<GOS->N_layers; ++il) {
02588       for (jl=0; jl<GOS->layers[il].N_NodesInLayer; ++jl) {
02589          OS->Neighb_ind[noffs] = GOS->layers[il].NodesInLayer[jl]; 
02590          OS->Neighb_dist[noffs] = GOS->OffVect[OS->Neighb_ind[noffs]];
02591          ++noffs;
02592       }
02593    }
02594    
02595    SUMA_RETURN(YUP);
02596 }
02597 
02598 char * SUMA_ShowOffset_Info (SUMA_GET_OFFSET_STRUCT *OffS, int detail)
02599 {
02600    static char FuncName[]={"SUMA_ShowOffset_Info"};
02601    SUMA_STRING *SS = NULL;
02602    int ii, *ltmp=NULL, *imap = NULL;
02603    char *s=NULL;   
02604 
02605    SUMA_ENTRY;
02606 
02607    SS = SUMA_StringAppend (NULL, NULL);
02608 
02609    if (!OffS) {
02610       SS = SUMA_StringAppend (SS,"#NULL offset structure.\n");
02611    } else {
02612       SS = SUMA_StringAppend_va (SS,"#Node Offsets (graph distance) from node %d\n", OffS->layers[0].NodesInLayer[0]);
02613       SS = SUMA_StringAppend_va (SS,"#Column 0 = Node index\n"
02614                                     "#column 1 = Neighborhood layer\n"
02615                                     "#Column 2 = Distance from node %d\n", OffS->layers[0].NodesInLayer[0]);
02616       ltmp = (int *)SUMA_malloc(OffS->N_Nodes*sizeof(int)); /* make a copy to avoid disturbinh OffS's contents*/
02617       if (!ltmp) {
02618          SUMA_SL_Crit("Failed to allocate for ltmp");
02619          SUMA_RETURN(NULL);
02620       }
02621       for (ii=0; ii<OffS->N_Nodes; ++ii) ltmp[ii] = OffS->LayerVect[ii]; 
02622       imap = SUMA_z_dqsort(ltmp,OffS->N_Nodes); 
02623       for (ii=0; ii<OffS->N_Nodes; ++ii) {
02624          if (OffS->LayerVect[imap[ii]] >= 0) {
02625             SS = SUMA_StringAppend_va (SS,"%6d\t%6d\t%f\n", imap[ii], OffS->LayerVect[imap[ii]], OffS->OffVect[imap[ii]]);
02626          }
02627       }
02628    }
02629    if (ltmp) SUMA_free(ltmp); ltmp = NULL;
02630    if (imap) SUMA_free(imap); imap = NULL;
02631    SUMA_SS2S(SS,s);
02632 
02633    SUMA_RETURN(s);
02634 }
02635 
02636 char * SUMA_ShowOffset_ll_Info (DList *list, int detail)
02637 {
02638    static char FuncName[]={"SUMA_ShowOffset_ll_Info"};
02639    SUMA_STRING *SS = NULL;
02640    DListElmt *elm = NULL;
02641    SUMA_OFFSET_LL_DATUM *dat=NULL;
02642    int ii;
02643    char *s=NULL;   
02644 
02645    SUMA_ENTRY;
02646 
02647    SS = SUMA_StringAppend (NULL, NULL);
02648 
02649    if (!list) {
02650       SS = SUMA_StringAppend (SS,"#NULL offset list.\n");
02651    } else {
02652       do {
02653          if (!elm) elm = dlist_head(list); 
02654          else elm = elm->next;
02655          dat = (SUMA_OFFSET_LL_DATUM *)elm->data;
02656          if (elm == dlist_head(list)) {
02657             SS = SUMA_StringAppend_va (SS,"#Node Offsets (graph distance) from node %d\n", dat->ni);
02658             SS = SUMA_StringAppend_va (SS,"#Column 0 = Node index\n"
02659                                        "#column 1 = Neighborhood layer\n"
02660                                        "#Column 2 = Distance from node %d\n", dat->ni);
02661          }
02662          SS = SUMA_StringAppend_va (SS,"%6d\t%6d\t%f\n", dat->ni, dat->layer, dat->off);
02663       } while (elm != dlist_tail(list));
02664    }
02665    SUMA_SS2S(SS,s);
02666 
02667    SUMA_RETURN(s);
02668 }
02669 
02670 /*!
02671    \brief creates a vector of node neighbors structures such that:
02672    OffS = SUMA_FormNeighbOffset ( SUMA_SurfaceObject *SO, float OffsetLim);
02673    
02674    \param OffS (SUMA_OFFSET_STRUCT *) SO->Node x 1 vector of structures 
02675          OffS[i] is a structure containing node neighbors of node i
02676          OffS[i].N_Neighb (int) number of neighbors of node i
02677          OffS[i].Neighb_ind (int *) OffS[i].N_Neighb x 1 vector containing 
02678                                     nodes neighboring node i
02679          OffS[i].Neighb_dist (float *) OffS[i].N_Neighb x 1 vector containing 
02680                                     node distances from node i. 
02681                                     These are the shortes distances ON THE GRAPH.
02682                                     The distances might be larger than OffsetLim
02683                                     because the child function SUMA_getoffsets2
02684                                     does so.
02685    \param OffsetLim (float) maximal inclusive neighbor distance. In reality, some 
02686                            nodes may be farther apart. But all nodes closer than 
02687                            OffsetLim to each node will be included
02688    - NOTE: This function will chew up a lot of memory, real quick.
02689             An approximate equation for the size needed for OffS:
02690                (mean_N_Neighb_per_Node * 8 + 12) * SO->N_Node Bytes
02691                With OffsetLim = 10 and a FreeSurfer surface of 150'000 nodes,
02692                the average number of neighbors per node is ~800. 
02693                Which means 962MB for the returned structure.
02694          When memory usage is this high, you should consider using SUMA_getoffsets2
02695          repeatedly for each node, as is done in this function.
02696          
02697 */
02698  
02699 SUMA_OFFSET_STRUCT *SUMA_FormNeighbOffset ( SUMA_SurfaceObject *SO, float OffsetLim)
02700 {
02701    static char FuncName[]={"SUMA_FormNeighbOffset"};
02702    int i, ii, il, jl, noffs, ShowNode = 4;
02703    SUMA_GET_OFFSET_STRUCT *OffS = NULL;
02704    struct  timeval start_time;
02705    float etime_GetOffset, mean_N_Neighb, dist, dist_norm;   
02706    SUMA_OFFSET_STRUCT *OffS_out=NULL;
02707    SUMA_Boolean LocalHead = NOPE;
02708    
02709    SUMA_ENTRY;
02710    if (!SO) { SUMA_SL_Err("NULL SO"); SUMA_RETURN(NULL); }
02711    if (!SO->FN) {
02712       SUMA_SL_Err("NULL SO->FN");
02713       SUMA_RETURN(NULL);
02714    }
02715    OffS_out = (SUMA_OFFSET_STRUCT *)SUMA_malloc(SO->N_Node * sizeof(SUMA_OFFSET_STRUCT));
02716    
02717    SUMA_etime(&start_time,0);
02718    
02719    OffS = SUMA_Initialize_getoffsets (SO->N_Node);
02720    mean_N_Neighb = 0;
02721    dist_norm = 1.1 * OffsetLim;
02722    for (i=0; i < SO->N_Node; ++i) {
02723       SUMA_getoffsets2 (i, SO, OffsetLim, OffS, NULL, 0);
02724       /* Now store all the relevant info in OffS in OffS_out[i] */
02725       OffS_out[i].N_Neighb = 0; 
02726       for (il=1; il<OffS->N_layers; ++il) {
02727          OffS_out[i].N_Neighb += OffS->layers[il].N_NodesInLayer;
02728       }
02729       OffS_out[i].Neighb_ind = (int *)SUMA_malloc(OffS_out[i].N_Neighb * sizeof(int));
02730       OffS_out[i].Neighb_dist = (float *)SUMA_malloc(OffS_out[i].N_Neighb * sizeof(float));
02731       mean_N_Neighb += OffS_out[i].N_Neighb;
02732       noffs = 0;
02733       for (il=1; il<OffS->N_layers; ++il) {
02734          for (jl=0; jl<OffS->layers[il].N_NodesInLayer; ++jl) {
02735             OffS_out[i].Neighb_ind[noffs] = OffS->layers[il].NodesInLayer[jl]; 
02736             #if 1
02737             /* don't play fancy with the weights here */
02738             OffS_out[i].Neighb_dist[noffs] = OffS->OffVect[OffS_out[i].Neighb_ind[noffs]];
02739             #else
02740             dist = OffS->OffVect[OffS_out[i].Neighb_ind[noffs]];
02741             if (dist > OffsetLim) OffS_out[i].Neighb_dist[noffs] = 0;
02742             else OffS_out[i].Neighb_dist[noffs] = (dist ); 
02743             #endif
02744             ++noffs;
02745          }
02746       }
02747       
02748       /* Show me the offsets for one node*/
02749       if (0) {
02750          if (i == ShowNode) {
02751             FILE *fid=NULL;
02752             char *outname=NULL;
02753             outname = SUMA_Extension("SomethingOffset", ".1D", YUP);
02754             outname = SUMA_append_replace_string(outname, "offset.1D", "", 1);
02755             fid = fopen(outname, "w"); free(outname); outname = NULL;
02756             if (!fid) {
02757                SUMA_SL_Err("Could not open file for writing.\nCheck file permissions, disk space.\n");
02758             } else {
02759                fprintf (fid,"#Column 1 = Node index\n"
02760                             "#column 2 = Neighborhood layer\n"
02761                             "#Column 3 = Distance from node %d\n", ShowNode);
02762                for (ii=0; ii<SO->N_Node; ++ii) {
02763                   if (OffS->LayerVect[ii] >= 0) {
02764                      fprintf(fid,"%d\t%d\t%f\n", ii, OffS->LayerVect[ii], OffS->OffVect[ii]);
02765                   }
02766                }
02767                fclose(fid);
02768             }
02769          }
02770       }
02771                
02772       if (i == 99) {
02773          etime_GetOffset = SUMA_etime(&start_time,1);
02774          fprintf(SUMA_STDERR, "%s: Search to %f mm took %f seconds for %d nodes.\n"
02775                               "Projected completion time: %f minutes\n"
02776                               "Projected memory need for structure %f MB", 
02777                               FuncName, OffsetLim, etime_GetOffset, i+1, 
02778                               etime_GetOffset * SO->N_Node / 60.0 / (i+1),
02779                               (mean_N_Neighb / (i+1) * 8 + 12)* SO->N_Node/1000000.0);
02780       }
02781       SUMA_Recycle_getoffsets (OffS);
02782    }
02783    SUMA_Free_getoffsets(OffS); OffS = NULL;
02784    
02785    etime_GetOffset = SUMA_etime(&start_time,1);
02786    fprintf(SUMA_STDERR, "%s: Search to %f mm took %f seconds for %d nodes.\n"
02787                         "Mean number of neighbors per node: %f\n",
02788                         FuncName, OffsetLim, etime_GetOffset, SO->N_Node, mean_N_Neighb / SO->N_Node);
02789    
02790    SUMA_RETURN(OffS_out);           
02791 } 
02792 
02793 /*!
02794    \brief frees what is created by SUMA_FormNeighbOffset
02795    
02796    \sa SUMA_FormNeighbOffset
02797 */
02798 SUMA_OFFSET_STRUCT * SUMA_free_NeighbOffset (SUMA_SurfaceObject *SO, SUMA_OFFSET_STRUCT *OffS_out)
02799 {
02800    static char FuncName[]={"SUMA_free_NeighbOffset"};
02801    int i;
02802    SUMA_ENTRY;
02803 
02804    if (!SO) {
02805       SUMA_S_Err("NULL SO!");
02806       SUMA_RETURN(NULL);
02807    }
02808    if (!OffS_out) SUMA_RETURN(NULL);
02809    for (i=0; i < SO->N_Node; ++i) {
02810       OffS_out[i].N_Neighb = 0;
02811       if (OffS_out[i].Neighb_dist) SUMA_free(OffS_out[i].Neighb_dist); OffS_out[i].Neighb_dist = NULL;
02812       if (OffS_out[i].Neighb_ind) SUMA_free(OffS_out[i].Neighb_ind); OffS_out[i].Neighb_ind = NULL;
02813    }
02814    SUMA_free(OffS_out); 
02815    SUMA_RETURN(NULL);
02816 }
02817 
02818 /*!
02819    \brief A filtering function that is based on brute force estimates of node neighbor distance
02820    matrix. 
02821    It is not finished because it makes no use of the neighbor distances to properly weigh the interpolation.
02822    It ends up being too slow because of the high memory load for computing OffS_out
02823 */
02824 float *SUMA_Offset_GeomSmooth( SUMA_SurfaceObject *SO, int N_iter, float OffsetLim, float *fin_orig, 
02825                               int vpn, SUMA_INDEXING_ORDER d_order, float *fout_final_user,
02826                               SUMA_COMM_STRUCT *cs)
02827 {
02828    
02829    static char FuncName[]= {"SUMA_Offset_GeomSmooth"};
02830    float *fout_final=NULL, *fbuf=NULL, *fin_next=NULL, *fin=NULL, *fout=NULL;
02831    int niter=0, i, il,jl, j, ii,  noffs;
02832    struct  timeval start_time;
02833    float etime_GetOffset, weight_tot;   
02834    SUMA_OFFSET_STRUCT *OffS_out=NULL;
02835    SUMA_Boolean LocalHead = NOPE;
02836    
02837    SUMA_ENTRY;
02838    if (!SO) { SUMA_SL_Err("NULL SO"); SUMA_RETURN(NULL); }
02839    if (!SO->FN) {
02840       SUMA_SL_Err("NULL SO->FN");
02841       SUMA_RETURN(NULL);
02842    }   
02843    if (N_iter % 2) {
02844       SUMA_SL_Err("N_iter must be an even number\n");
02845       SUMA_RETURN(NULL);
02846    }
02847    if (vpn < 1) {
02848       SUMA_SL_Err("vpn < 1\n");
02849       SUMA_RETURN(NULL);
02850    }  
02851    
02852    if (fout_final_user == fin_orig) {
02853       SUMA_SL_Err("fout_final_user == fin_orig");
02854       SUMA_RETURN(NULL);
02855    }
02856    
02857    if (!fout_final_user) { /* allocate for output */
02858       fout_final = (float *)SUMA_calloc(SO->N_Node * vpn, sizeof(float));
02859       if (!fout_final) {
02860          SUMA_SL_Crit("Failed to allocate for fout_final\n");
02861          SUMA_RETURN(NULL);
02862       }
02863    }else {
02864       fout_final = fout_final_user; /* pre-allocated */
02865    }
02866    
02867    /* allocate for buffer */
02868    fbuf = (float *)SUMA_calloc(SO->N_Node * vpn, sizeof(float));
02869    if (!fbuf) {
02870       SUMA_SL_Crit("Failed to allocate for fbuf\n");
02871       SUMA_RETURN(NULL);
02872    }
02873    
02874    
02875    if (cs->Send) { /* send the first monster */
02876       if (!SUMA_SendToSuma (SO, cs, (void *)fin_orig, SUMA_NODE_XYZ, 1)) {
02877          SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCommunication halted.");
02878       }
02879    }
02880    SUMA_LH("Calculating OffS_out ...");
02881    OffS_out = SUMA_FormNeighbOffset (SO, OffsetLim);
02882    fin_next = fin_orig;
02883    switch (d_order) {
02884       case SUMA_ROW_MAJOR:
02885          for (niter=0; niter < N_iter; ++niter) {
02886             if ( niter % 2 ) { /* odd */
02887                fin = fin_next; /* input from previous output buffer */
02888                fout = fout_final; /* results go into final vector */
02889                fin_next = fout_final; /* in the next iteration, the input is from fout_final */
02890             } else { /* even */
02891                /* input data is in fin_new */
02892                fin = fin_next;
02893                fout = fbuf; /* results go into buffer */
02894                fin_next = fbuf; /* in the next iteration, the input is from the buffer */
02895             }
02896             
02897             SUMA_etime(&start_time,0);
02898             
02899             for (i=0; i < SO->N_Node; ++i) {
02900                for (j=0; j < vpn; ++j) {
02901                   /* do the averaging using OffS_out NO ATTENTION IS GIVEN TO PROPER WEIGHING YET!*/
02902                   fout[i*vpn+j] = fin[i*vpn+j];
02903                   for (il=0; il<OffS_out[i].N_Neighb; ++il) {
02904                      fout[i*vpn+j] += fin[OffS_out[i].Neighb_ind[il]*vpn+j];
02905                   }
02906                   fout[i*vpn+j] /= (OffS_out[i].N_Neighb+1);  
02907                }
02908             }
02909             
02910             etime_GetOffset = SUMA_etime(&start_time,1);
02911             fprintf(SUMA_STDERR, "%s: Smoothing at dist %f took %f seconds for %d nodes.\n",
02912                            FuncName, OffsetLim, etime_GetOffset, SO->N_Node);
02913             
02914             if (cs->Send) {
02915                if (!SUMA_SendToSuma (SO, cs, (void *)fout, SUMA_NODE_XYZ, 1)) {
02916                   SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCommunication halted.");
02917                }
02918             }
02919          }
02920          break;
02921       case SUMA_COLUMN_MAJOR:
02922          SUMA_SL_Err("Column Major not implemented");
02923          SUMA_RETURN(NULL);
02924          break;
02925       default:
02926          SUMA_SL_Err("Bad Major, very bad.\n");
02927          SUMA_RETURN(NULL);
02928          break;
02929    }
02930    
02931    if (fbuf) SUMA_free(fbuf); fbuf = NULL;
02932 
02933    /* Have to free OffS_out */
02934    OffS_out = SUMA_free_NeighbOffset (SO, OffS_out);
02935       
02936    SUMA_RETURN(fout);    
02937 }
02938 
02939 float *SUMA_NN_GeomSmooth( SUMA_SurfaceObject *SO, int N_iter, float *fin_orig, 
02940                            int vpn, SUMA_INDEXING_ORDER d_order, float *fout_final_user,
02941                            SUMA_COMM_STRUCT *cs)
02942 {
02943    static char FuncName[]= {"SUMA_NN_GeomSmooth"};
02944    float *fout_final=NULL, *fbuf=NULL, *fin_next=NULL, *fin=NULL, *fout=NULL;
02945    int niter=0;
02946 
02947    SUMA_ENTRY;
02948    if (!SO) { SUMA_SL_Err("NULL SO"); SUMA_RETURN(NULL); }
02949    if (!SO->FN) {
02950       SUMA_SL_Err("NULL SO->FN");
02951       SUMA_RETURN(NULL);
02952    }   
02953    if (N_iter % 2) {
02954       SUMA_SL_Err("N_iter must be an even number\n");
02955       SUMA_RETURN(NULL);
02956    }
02957    if (vpn < 1) {
02958       SUMA_SL_Err("vpn < 1\n");
02959       SUMA_RETURN(NULL);
02960    }  
02961    
02962    if (fout_final_user == fin_orig) {
02963       SUMA_SL_Err("fout_final_user == fin_orig");
02964       SUMA_RETURN(NULL);
02965    }
02966    
02967    if (!fout_final_user) { /* allocate for output */
02968       fout_final = (float *)SUMA_calloc(SO->N_Node * vpn, sizeof(float));
02969       if (!fout_final) {
02970          SUMA_SL_Crit("Failed to allocate for fout_final\n");
02971          SUMA_RETURN(NULL);
02972       }
02973    }else {
02974       fout_final = fout_final_user; /* pre-allocated */
02975    }
02976    
02977    /* allocate for buffer */
02978    fbuf = (float *)SUMA_calloc(SO->N_Node * vpn, sizeof(float));
02979    if (!fbuf) {
02980       SUMA_SL_Crit("Failed to allocate for fbuf\n");
02981       SUMA_RETURN(NULL);
02982    }
02983    
02984    
02985    if (cs->Send) { /* send the first monster */
02986       if (!SUMA_SendToSuma (SO, cs, (void *)fin_orig, SUMA_NODE_XYZ, 1)) {
02987          SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCommunication halted.");
02988       }
02989    }
02990    
02991    fin_next = fin_orig;
02992    switch (d_order) {
02993       case SUMA_ROW_MAJOR:
02994          for (niter=0; niter < N_iter; ++niter) {
02995             if ( niter % 2 ) { /* odd */
02996                fin = fin_next; /* input from previous output buffer */
02997                fout = fout_final; /* results go into final vector */
02998                fin_next = fout_final; /* in the next iteration, the input is from fout_final */
02999             } else { /* even */
03000                /* input data is in fin_new */
03001                fin = fin_next;
03002                fout = fbuf; /* results go into buffer */
03003                fin_next = fbuf; /* in the next iteration, the input is from the buffer */
03004             }
03005             fout = SUMA_SmoothAttr_Neighb ( fin, vpn*SO->N_Node, 
03006                                                  fout, SO->FN, vpn);
03007             if (cs->Send) {
03008                if (!SUMA_SendToSuma (SO, cs, (void *)fout, SUMA_NODE_XYZ, 1)) {
03009                   SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCommunication halted.");
03010                }
03011             }
03012          }
03013          break;
03014       case SUMA_COLUMN_MAJOR:
03015          SUMA_SL_Err("Column Major not implemented");
03016          SUMA_RETURN(NULL);
03017          break;
03018       default:
03019          SUMA_SL_Err("Bad Major, very bad.\n");
03020          SUMA_RETURN(NULL);
03021          break;
03022    }
03023    
03024    if (fbuf) SUMA_free(fbuf); fbuf = NULL;
03025 
03026    SUMA_RETURN(fout);    
03027 }
03028         
03029 
03030 /*!
03031    \brief Filter data defined on the surface using M.K. Chung et al.'s method (Neuroimage 03)
03032    dm_smooth = SUMA_Chung_Smooth (SO, wgt, N_iter, FWHM, fin, vpn, d_order, fout_user, SUMA_COMM_STRUCT *cs);
03033    
03034    \param SO (SUMA_SurfaceObject *) Surface object with valid 
03035                                     SO->NodeList, SO->FaceSetList and SO->FN 
03036    \param wgt (float **) interpolation weights for each node. 
03037                            These weights are obtained from SUMA_Chung_Smooth_Weights 
03038    \param N_iter (int) number of smoothing iterations (must be even, > 1) 
03039    \param FWHM (float) Full Width at Half Maximum of equivalent Gaussian filter
03040    \param fin (float *) vector containing node data. The length of this vector
03041                         is vpn x SO->N_Node , where vpn is the number of values
03042                         per node. 
03043    \param vpn (int) the numberof values per node in fin 
03044    \param d_order (SUMA_INDEXING_ORDER) Indicates how multiple values per node are stored in fin
03045                         SUMA_ROW_MAJOR: The data in fin is stored in *** Row Major *** order.
03046                         The ith value (start at 0) for node n is at index fin[vpn*n+i]
03047                         SUMA_COLUMN_MAJOR: The data in fin is stored in *** Column Major *** order.
03048                         The ith (start at 0) value for node n is at index fin[n+SO->N_Node*i]; 
03049                         etc...
03050    \param fout_user (float *) a pointer to the vector where the smoothed version of fin will reside
03051                         You can pass NULL and the function will do the allocation for you and return 
03052                         the pointer to the smoothed data in dm_smooth.
03053                         If you already have space allocated for the result, then pass the pointer
03054                         in fout_user and save on allocation time and space. In that case, dm_smooth
03055                         is equal to fout_user.
03056                         Either way, you are responsible for freeing memory pointed to by dm_smooth
03057                         DO NOT PASS fout_user = fin 
03058    \param cs (SUMA_COMM_STRUCT *) A pointer to the structure containing info for taking to SUMA
03059                            
03060    \return dm_smooth (float *) A pointer to the smoothed data (vpn * SO->N_Node values). 
03061                         You will have to free the memory allocated for dm_smooth yourself.
03062                         
03063    \sa SUMA_Chung_Smooth_Weights
03064                         
03065 */
03066 
03067 
03068 float * SUMA_Chung_Smooth (SUMA_SurfaceObject *SO, float **wgt, 
03069                            int N_iter, float FWHM, float *fin_orig, 
03070                            int vpn, SUMA_INDEXING_ORDER d_order, float *fout_final_user,
03071                            SUMA_COMM_STRUCT *cs)
03072 {
03073    static char FuncName[]={"SUMA_Chung_Smooth"};
03074    float *fout_final = NULL, *fbuf=NULL, *fin=NULL, *fout=NULL, *fin_next = NULL;
03075    float delta_time, fp, dfp, fpj, minfn=0.0, maxfn=0.0;
03076    int n , k, j, niter, vnk, os;
03077    SUMA_Boolean LocalHead = NOPE;
03078    
03079    SUMA_ENTRY;
03080 
03081    if (N_iter % 2) {
03082       SUMA_SL_Err("N_iter must be an even number\n");
03083       SUMA_RETURN(NULL);
03084    }
03085    
03086    if (!SO || !wgt || !fin_orig) {
03087       SUMA_SL_Err("NULL SO or wgt or fin_orig\n");
03088       SUMA_RETURN(NULL);
03089    }
03090    if (!SO->FN) {
03091       SUMA_SL_Err("NULL SO->FN\n");
03092       SUMA_RETURN(NULL);
03093    }
03094    
03095    if (vpn < 1) {
03096       SUMA_SL_Err("vpn < 1\n");
03097       SUMA_RETURN(NULL);
03098    }  
03099    
03100    if (fout_final_user == fin_orig) {
03101       SUMA_SL_Err("fout_final_user == fin_orig");
03102       SUMA_RETURN(NULL);
03103    }
03104    
03105    if (!fout_final_user) { /* allocate for output */
03106       fout_final = (float *)SUMA_calloc(SO->N_Node * vpn, sizeof(float));
03107       if (!fout_final) {
03108          SUMA_SL_Crit("Failed to allocate for fout_final\n");
03109          SUMA_RETURN(NULL);
03110       }
03111    }else {
03112       fout_final = fout_final_user; /* pre-allocated */
03113    }
03114    
03115    /* allocate for buffer */
03116    fbuf = (float *)SUMA_calloc(SO->N_Node * vpn, sizeof(float));
03117    if (!fbuf) {
03118       SUMA_SL_Crit("Failed to allocate for fbuf\n");
03119       SUMA_RETURN(NULL);
03120    }
03121    
03122    
03123    if (cs->Send) { /* send the first monster */
03124       if (!SUMA_SendToSuma (SO, cs, (void *)fin_orig, SUMA_NODE_RGBAb, 1)) {
03125          SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCommunication halted.");
03126       }
03127    }
03128    
03129    fin_next = fin_orig;
03130    delta_time= (FWHM * FWHM)/(16*N_iter*log(2));
03131    switch (d_order) {
03132       case SUMA_COLUMN_MAJOR:
03133          for (niter=0; niter < N_iter; ++niter) {
03134             if ( niter % 2 ) { /* odd */
03135                fin = fin_next; /* input from previous output buffer */
03136                fout = fout_final; /* results go into final vector */
03137                fin_next = fout_final; /* in the next iteration, the input is from fout_final */
03138             } else { /* even */
03139                /* input data is in fin_new */
03140                fin = fin_next;
03141                fout = fbuf; /* results go into buffer */
03142                fin_next = fbuf; /* in the next iteration, the input is from the buffer */
03143             }
03144             for (k=0; k < vpn; ++k) {
03145                os = SO->N_Node*k;   /* node value indexing offset */
03146                for (n=0; n < SO->N_Node; ++n) {
03147                   vnk = n+os; /* index of kth value at node n */
03148                   fp = fin[vnk]; /* kth value at node n */
03149                   dfp = 0.0;
03150                   if (SO->FN->N_Neighb[n]) minfn = maxfn = fin[SO->FN->FirstNeighb[n][0]+os];
03151                   for (j=0; j < SO->FN->N_Neighb[n]; ++j) {
03152                      fpj = fin[SO->FN->FirstNeighb[n][j]+os]; /* value at jth neighbor of n */
03153                      if (fpj < minfn) minfn = fpj;
03154                      if (fpj > maxfn) maxfn = fpj;
03155                      dfp += wgt[n][j] * (fpj - fp); 
03156                   }/* for j*/
03157                   fout[vnk] = fin[vnk] + delta_time * dfp;
03158                   if (fout[vnk] < minfn) fout[vnk] = minfn;
03159                   if (fout[vnk] > maxfn) fout[vnk] = maxfn;
03160                }/* for n */ 
03161                  
03162                if (cs->Send) {
03163                   if (!SUMA_SendToSuma (SO, cs, (void *)fout, SUMA_NODE_RGBAb, 1)) {
03164                      SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCommunication halted.");
03165                   }
03166                }
03167             } /* for k */
03168          }/* for niter */
03169          break;
03170       case SUMA_ROW_MAJOR:
03171          SUMA_SL_Err("Row Major not implemented");
03172          SUMA_RETURN(NULL);
03173          break;
03174       default:
03175          SUMA_SL_Err("Bad Major, very bad.\n");
03176          SUMA_RETURN(NULL);
03177          break;
03178    }
03179    
03180    if (fbuf) SUMA_free(fbuf); fbuf = NULL;
03181                
03182    SUMA_RETURN(fout);
03183 }
03184 
03185 #ifdef SUMA_SurfSmooth_STAND_ALONE
03186 void usage_SUMA_SurfSmooth ()
03187    {
03188       static char FuncName[]={"usage_SUMA_SurfSmooth"};
03189       char * s = NULL, *st = NULL;
03190       s = SUMA_help_basics();
03191       st = SUMA_help_talk();
03192       printf ("\nUsage:  SurfSmooth <-spec SpecFile> <-surf_A insurf> <-met method> \n"
03193               "\n"
03194               "   Some methods require additional options detailed below.\n"
03195               "   I recommend using the -talk_suma option to watch the \n"
03196               "   progression of the smoothing in real-time in suma.\n"
03197               "\n"
03198               "   Method specific options:\n"
03199               "      LB_FEM: <-input inData.1D> <-fwhm f>\n"
03200               "              This method is used to filter data\n"
03201               "              on the surface.\n"
03202               "      LM: [-kpb k] [-lm l m] [-surf_out surfname]\n"
03203               "          This method is used to filter the surface's\n"
03204               "          geometry (node coordinates).\n"
03205               "      NN_geom: smooth by averaging coordinates of \n"
03206               "               nearest neighbors.\n"
03207               "               This method causes shrinkage of surface\n"
03208               "               and is meant for test purposes only.\n"
03209               "\n"
03210               "   Common options:\n"
03211               "      [-Niter N] [-output out.1D] [-h/-help] \n"
03212               "      [-add_index] [-ni_text|-ni_binary] [-talk_suma]\n\n"
03213               "\n"
03214               "   Detailed usage:\n"
03215               "      -spec SpecFile: Name of specfile containing surface of interest.\n"
03216               "                      If the surface does not have a spec file, use the \n"
03217               "                      program quickspec to create one.\n"
03218               "      -surf_A insurf: Name of surface of interest. \n"
03219               "                      NOTE: i_TYPE inSurf option is now obsolete.\n"
03220               "      -met method: name of smoothing method to use. Choose from:\n"
03221               "                 LB_FEM: The method by Chung et al. 03.\n"
03222               "                         This method is used for filtering \n"
03223               "                         data on the surface not for smoothing the\n"
03224               "                         surface's geometry per se. See References below.\n"
03225               "                 LM: The smoothing method proposed by G. Taubin 2000\n"
03226               "                     This method is used for smoothing\n"
03227               "                     a surface's geometry. See References below.\n"
03228               "                 NN_geom: A simple nearest neighbor coordinate smoothing.\n"
03229               "                          This interpolation method causes surface shrinkage\n"
03230               "                          that might need to be corrected with the -match_*\n"
03231               "                          options below. \n" 
03232               "\n"
03233               "   Options for LB_FEM:\n"
03234               "      -input inData.1D: file containing data (in 1D format)\n"
03235               "                        Each column in inData.1D is processed separately.\n"
03236               "                        The number of rows must equal the number of\n"
03237               "                        nodes in the surface. You can select certain\n"
03238               "                        columns using the [] notation adopted by AFNI's\n"
03239               "                        programs.\n"
03240               "      -fwhm f: Full Width at Half Maximum in surface coordinate units (usuallly mm)\n"
03241               "               of an equivalent Gaussian filter had the surface been flat.\n"
03242               "               With curved surfaces, the equation used to estimate FWHM is \n"
03243               "               an approximation. \n"
03244               "               Blurring on the surface depends on the geodesic instead \n"
03245               "               of the Euclidean disntaces. See Ref #1 for more details \n"
03246               "               on this parameter.\n"
03247               "\n"
03248               "   Options for LM:\n"
03249               "      -kpb k: Band pass frequency (default is 0.1).\n"
03250               "              values should be in the range 0 < k < 10\n"
03251               "              -lm and -kpb options are mutually exclusive.\n"
03252               "      -lm l m: Lambda and Mu parameters. Sample values are:\n"
03253               "               0.6307 and -.6732\n"
03254               "      NOTE: -lm and -kpb options are mutually exclusive.\n"
03255               "      -surf_out surfname: Writes the surface with smoothed coordinates\n"
03256               "                          to disk. For SureFit and 1D formats, only the\n"
03257               "                          coord file is written out.\n"
03258               "      NOTE: -surf_out and -output are mutually exclusive.\n"  
03259               "\n"
03260               "   Options for NN_geom:\n"
03261               "      -match_size r: Adjust node coordinates of smoothed surface to \n"
03262               "                   approximates the original's size.\n"
03263               "                   Node i on the filtered surface is repositioned such \n"
03264               "                   that |c i| = 1/N sum(|cr j|) where\n"
03265               "                   c and cr are the centers of the smoothed and original\n"
03266               "                   surfaces, respectively.\n"
03267               "                   N is the number of nodes that are within r [surface \n"
03268               "                   coordinate units] along the surface (geodesic) from node i.\n"
03269               "                   j is one of the nodes neighboring i.\n"
03270               "      -match_vol tol: Adjust node coordinates of smoothed surface to \n"
03271               "                   approximates the original's volume.\n"
03272               "                   Nodes on the filtered surface are repositioned such\n"
03273               "                   that the volume of the filtered surface equals, \n"
03274               "                   within tolerance tol, that of the original surface. \n"
03275               "                   See option -vol in SurfaceMetrics for information about\n"
03276               "                   and calculation of the volume of a closed surface.\n"
03277               "      -match_area tol: Adjust node coordinates of smoothed surface to \n"
03278               "                   approximates the original's surface.\n"
03279               "                   Nodes on the filtered surface are repositioned such\n"
03280               "                   that the surface of the filtered surface equals, \n"
03281               "                   within tolerance tol, that of the original surface. \n"
03282               "      -match_sphere rad: Project nodes of smoothed surface to a sphere\n"
03283               "                   of radius rad. Projection is carried out along the \n"
03284               "                   direction formed by the surface's center and the node.\n"
03285               "\n"
03286               "   Common options:\n"
03287               "      -Niter N: Number of smoothing iterations (default is 100)\n"
03288               "                For practical reasons, this number must be a multiple of 2\n"
03289               "          NOTE: For LB_FEM method, the number of iterations controls the\n"
03290               "                iteration steps (dt in Ref #1).\n"
03291               "                dt = fwhm*fwhm / (16*Niter*log(2));\n"
03292               "                dt must satisfy conditions that depend on the internodal\n"
03293               "                distance and the spatial derivatives of the signals being \n"
03294               "                filtered on the surface.\n"
03295               "                As a rule of thumb, if increasing Niter does not alter\n"
03296               "                the results then your choice is fine (smoothing has converged).\n"
03297               "                For an example of the artifact caused by small Niter see:\n"
03298               "          http://afni.nimh.nih.gov/sscc/staff/ziad/SUMA/SuSmArt/DSart.html\n"
03299               "      -output out.1D: Name of output file. \n"
03300               "                      The default is inData_sm.1D with LB_FEM method\n"
03301               "                      and NodeList_sm.1D with LM method.\n" 
03302               "      -add_index : Output the node index in the first column.\n"
03303               "                   This is not done by default.\n"
03304               "\n"
03305               "%s"
03306               "\n"
03307               "%s"
03308               "\n"
03309               "   Sample commands lines for data smoothing:\n"
03310               "      SurfSmooth  -spec quick.spec -surf_A NodeList.1D -met LB_FEM   \\\n"
03311               "                  -input in.1D -Niter 100 -fwhm 8 -add_index         \\\n"
03312               "                  -output in_sm8.1D \n"
03313               "         This command filters (on the surface) the data in in.1D\n"
03314               "         and puts the output in in_sm8.1D with the first column \n"
03315               "         containing the node index and the second containing the \n"
03316               "         filtered version of in.1D.\n"
03317               "\n"
03318               "         The surface used in this example had no spec file, so \n"
03319               "         a quick.spec was created using:\n"
03320               "         quickspec -tn 1D NodeList.1D FaceSetList.1D \n"
03321               "\n"
03322               "         You can colorize the input and output data using ScaleToMap:\n"
03323               "         ScaleToMap  -input in.1D 0 1 -cmap BGYR19       \\\n"
03324               "                     -clp MIN MAX > in.1D.col            \\\n"
03325               "         ScaleToMap  -input in_sm8.1D 0 1 -cmap BGYR19   \\\n"
03326               "                     -clp MIN MAX > in_sm8.1D.col        \\\n"
03327               "\n"
03328               "         For help on using ScaleToMap see ScaleToMap -help\n"
03329               "         Note that the MIN MAX represent the minimum and maximum\n"
03330               "         values in in.1D. You should keep them constant in both \n"
03331               "         commands in order to be able to compare the resultant colorfiles.\n"
03332               "         You can import the .col files with the 'c' command in SUMA.\n"
03333               "\n"
03334               "         You can send the data to SUMA with each iteration.\n"
03335               "         To do so, start SUMA with these options:\n"
03336               "         suma -spec quick.spec -niml &\n"
03337               "         and add these options to SurfSmooth's command line above:\n"
03338               "         -talk_suma -refresh_rate 5\n" 
03339               "\n"
03340               "   Sample commands lines for surface smoothing:\n"
03341               "      SurfSmooth  -spec quick.spec -surf_A NodeList.1D -met LM    \\\n"
03342               "                  -output NodeList_sm100.1D -Niter 100 -kpb 0.1   \n"
03343               "         This command smoothes the surface's geometry. The smoothed\n"
03344               "         node coordinates are written out to NodeList_sm100.1D. \n"
03345               "\n"
03346               "   Sample command for considerable surface smoothing and inflation\n"
03347               "   back to original volume:\n"
03348               "       SurfSmooth  -spec quick.spec -surf_A NodeList.1D -met NN_geom \\\n"
03349               "                   -output NodeList_inflated_mvol.1D -Niter 1500 \\\n"
03350               "                   -match_vol 0.01\n" 
03351               "   Sample command for considerable surface smoothing and inflation\n"
03352               "   back to original area:\n"
03353               "       SurfSmooth  -spec quick.spec -surf_A NodeList.1D -met NN_geom \\\n"
03354               "                   -output NodeList_inflated_marea.1D -Niter 1500 \\\n"
03355               "                   -match_area 0.01\n" 
03356               "\n"
03357               "   References: \n"
03358               "      (1) M.K. Chung et al.   Deformation-based surface morphometry\n"
03359               "                              applied to gray matter deformation. \n"
03360               "                              Neuroimage 18 (2003) 198-213\n"
03361               "          M.K. Chung   Statistical morphometry in computational\n"
03362               "                       neuroanatomy. Ph.D. thesis, McGill Univ.,\n"
03363               "                       Montreal, Canada\n"
03364               "      (2) G. Taubin.       Mesh Signal Processing. \n"
03365               "                           Eurographics 2000.\n"
03366               "\n"
03367               "   See Also:   \n"
03368               "       ScaleToMap to colorize the output, however it is better\n"
03369               "       to load surface datasets directly into SUMA and colorize\n"
03370               "       them interactively."
03371               "\n"
03372               "\n", st, s); SUMA_free(s); s = NULL; SUMA_free(st); st = NULL;
03373        s = SUMA_New_Additions(0, 1); printf("%s\n", s);SUMA_free(s); s = NULL;
03374        printf("       Ziad S. Saad SSCC/NIMH/NIH ziad@nih.gov     \n");
03375        exit (0);
03376    }
03377 
03378 
03379 #define SURFSMOOTH_MAX_SURF 1  /*!< Maximum number of input surfaces */
03380 
03381 typedef enum { SUMA_NO_METH, SUMA_LB_FEM, SUMA_LM, SUMA_BRUTE_FORCE, SUMA_NN_GEOM} SUMA_SMOOTHING_METHODS;
03382 
03383 typedef struct {
03384    float OffsetLim;
03385    float lim;
03386    float fwhm;
03387    float kpb;
03388    float l;
03389    float m;
03390    int ShowNode;
03391    int Method;
03392    int dbg;
03393    int N_iter;
03394    int AddIndex;
03395    int insurf_method; /* method used to specify input surfaces. 
03396                         0 then none input 
03397                         1 the old way
03398                         2 the new (-spec way) */
03399    SUMA_SO_File_Type iType;
03400    char *vp_name;
03401    char *sv_name;
03402    char *if_name;
03403    char *if_name2;
03404    char *in_name;
03405    char *out_name;   /* this one's dynamically allocated so you'll have to free it yourself */
03406    char *ShowOffset_DBG;
03407    char *surf_out;
03408    char *surf_names[SURFSMOOTH_MAX_SURF];
03409    char *spec_file;
03410    int MatchMethod;
03411 } SUMA_SURFSMOOTH_OPTIONS;
03412 
03413 /*!
03414    \brief parse the arguments for SurfSmooth program
03415    
03416    \param argv (char *)
03417    \param argc (int)
03418    \return Opt (SUMA_SURFSMOOTH_OPTIONS *) options structure.
03419                To free it, use 
03420                SUMA_free(Opt->out_name); 
03421                SUMA_free(Opt);
03422 */
03423 SUMA_SURFSMOOTH_OPTIONS *SUMA_SurfSmooth_ParseInput (char *argv[], int argc, SUMA_GENERIC_ARGV_PARSE *ps)
03424 {
03425    static char FuncName[]={"SUMA_SurfSmooth_ParseInput"}; 
03426    SUMA_SURFSMOOTH_OPTIONS *Opt=NULL;
03427    int kar, i, ind;
03428    char *outname;
03429    SUMA_Boolean brk = NOPE;
03430    SUMA_Boolean LocalHead = NOPE;
03431 
03432    SUMA_ENTRY;
03433    
03434    Opt = (SUMA_SURFSMOOTH_OPTIONS *)SUMA_malloc(sizeof(SUMA_SURFSMOOTH_OPTIONS));
03435    
03436    kar = 1;
03437    Opt->OffsetLim = 10.0;
03438    Opt->MatchMethod = 0;
03439    Opt->lim = 1000000.0;
03440    Opt->fwhm = -1;
03441    Opt->ShowNode = -1;
03442    Opt->Method = SUMA_NO_METH;
03443    Opt->dbg = 0;
03444    Opt->if_name = NULL;
03445    Opt->if_name2 = NULL;
03446    Opt->in_name = NULL;
03447    Opt->out_name = NULL;
03448    Opt->vp_name = NULL; 
03449    Opt->sv_name = NULL;
03450    Opt->surf_out = NULL;
03451    Opt->ShowOffset_DBG = NULL;
03452    Opt->iType = SUMA_FT_NOT_SPECIFIED;
03453    Opt->N_iter = 100;
03454    Opt->kpb = -1.0;
03455    Opt->l = -1.0;
03456    Opt->m = -1.0;
03457    Opt->AddIndex = 0;
03458    Opt->insurf_method = 0;
03459    Opt->spec_file = NULL;
03460    for (i=0; i<SURFSMOOTH_MAX_SURF; ++i) { Opt->surf_names[i] = NULL; }
03461    outname = NULL;
03462         brk = NOPE;
03463         while (kar < argc) { /* loop accross command ine options */
03464                 /*fprintf(stdout, "%s verbose: Parsing command line...\n", FuncName);*/
03465                 if (strcmp(argv[kar], "-h") == 0 || strcmp(argv[kar], "-help") == 0) {
03466                          usage_SUMA_SurfSmooth();
03467           exit (0);
03468                 }
03469                 
03470                 SUMA_SKIP_COMMON_OPTIONS(brk, kar);
03471 
03472       #if 0 /* now  in the SUMA_GENERIC_ARGV_PARSE struct */
03473       if (!brk && strcmp(argv[kar], "-ni_text") == 0)
03474                 {
03475          SUMA_GEOMCOMP_NI_MODE = NI_TEXT_MODE;
03476          brk = YUP;
03477       }
03478       
03479       if (!brk && strcmp(argv[kar], "-ni_binary") == 0)
03480                 {
03481          SUMA_GEOMCOMP_NI_MODE = NI_BINARY_MODE;
03482          brk = YUP;
03483       }
03484       
03485                 if (!brk && strcmp(argv[kar], "-sh") == 0)
03486                 {
03487                         kar ++;
03488                         if (kar >= argc)  {
03489                                 fprintf (SUMA_STDERR, "need argument after -sh \n");
03490                                 exit (1);
03491                         }
03492                         if (strcmp(argv[kar],"localhost") != 0) {
03493             Opt->suma_host_name = SUMA_copy_string(argv[kar]);
03494          }else {
03495            fprintf (SUMA_STDERR, "localhost is the default for -sh\nNo need to specify it.\n");
03496          }
03497 
03498                         brk = YUP;
03499                 }       
03500       if (!brk && strcmp(argv[kar], "-refresh_rate") == 0)
03501                 {
03502                         kar ++;
03503                         if (kar >= argc)  {
03504                                 fprintf (SUMA_STDERR, "need argument after -refresh_rate \n");
03505                                 exit (1);
03506                         }
03507                         Opt->rps = atof(argv[kar]);
03508          if (Opt->rps <= 0) {
03509             fprintf (SUMA_STDERR, "Bad value (%f) for refresh_rate\n", Opt->rps);
03510                                 exit (1);
03511          }
03512 
03513                         brk = YUP;
03514                 }
03515       
03516       if (!brk && (strcmp(argv[kar], "-talk_suma") == 0)) {
03517                         Opt->talk_suma = 1; 
03518                         brk = YUP;
03519                 }
03520       
03521       #endif
03522       if (!brk && strcmp(argv[kar], "-dist") == 0)
03523                 {
03524                         kar ++;
03525                         if (kar >= argc)  {
03526                                 fprintf (SUMA_STDERR, "need argument after -dist \n");
03527                                 exit (1);
03528                         }
03529                         Opt->OffsetLim = atof(argv[kar]);
03530          if (Opt->OffsetLim <= 0) {
03531             fprintf (SUMA_STDERR, "Bad value (%f) for refresh_rate\n", Opt->OffsetLim);
03532                                 exit (1);
03533          }
03534 
03535                         brk = YUP;
03536                 }
03537      
03538       if (!brk && (strcmp(argv[kar], "-dbg_n") == 0)) {
03539          kar ++;
03540                         if (kar+1 >= argc)  {
03541                                 fprintf (SUMA_STDERR, "need 2 arguments after -dbg_n \n");
03542                                 exit (1);
03543                         }
03544                         Opt->ShowNode = atoi(argv[kar]); kar ++;
03545          Opt->ShowOffset_DBG = argv[kar];
03546                         brk = YUP;
03547                 }
03548       
03549       if (!brk && (strcmp(argv[kar], "-Niter") == 0)) {
03550          kar ++;
03551                         if (kar >= argc)  {
03552                                 fprintf (SUMA_STDERR, "need 1 arguments after -Niter \n");
03553                                 exit (1);
03554                         }
03555                         Opt->N_iter = atoi(argv[kar]);
03556                         brk = YUP;
03557                 }
03558       
03559       if (!brk && (strcmp(argv[kar], "-kpb") == 0)) {
03560          kar ++;
03561                         if (kar >= argc)  {
03562                                 fprintf (SUMA_STDERR, "need 1 arguments after -kpb \n");
03563                                 exit (1);
03564                         }
03565          if (Opt->l != -1.0  || Opt->m != -1.0) {
03566             fprintf (SUMA_STDERR, "options -lm and -kpb are mutually exclusive\n");
03567                                 exit (1);
03568          }
03569                         Opt->kpb = atof(argv[kar]); 
03570                         brk = YUP;
03571                 }
03572       
03573       if (!brk && (strcmp(argv[kar], "-surf_out") == 0)) {
03574          kar ++;
03575                         if (kar >= argc)  {
03576                                 fprintf (SUMA_STDERR, "need 1 arguments after -surf_out\n");
03577                                 exit (1);
03578                         }
03579          if (outname) {
03580             fprintf (SUMA_STDERR, "-output and -surf_out are mutually exclusive.\n");
03581             exit(1);
03582          }
03583                         Opt->surf_out = argv[kar]; 
03584                         brk = YUP;
03585                 }
03586       
03587       if (!brk && (strcmp(argv[kar], "-lm") == 0)) {
03588          kar ++;
03589                         if (kar+1 >= argc)  {
03590                                 fprintf (SUMA_STDERR, "need 2 arguments after -lm \n");
03591                                 exit (1);
03592                         }
03593          if (Opt->kpb != -1.0) {
03594             fprintf (SUMA_STDERR, "options -lm and -kpb are mutually exclusive\n");
03595                                 exit (1);
03596          }
03597                         Opt->l = atof(argv[kar]); kar ++;
03598          Opt->m = atof(argv[kar]);  
03599                         brk = YUP;
03600                 }
03601       
03602       if (!brk && (strcmp(argv[kar], "-input") == 0)) {
03603          kar ++;
03604                         if (kar >= argc)  {
03605                                 fprintf (SUMA_STDERR, "need argument after -input \n");
03606                                 exit (1);
03607                         }
03608                         Opt->in_name = argv[kar];
03609                         brk = YUP;
03610                 }
03611       
03612       if (!brk && (strcmp(argv[kar], "-output") == 0)) {
03613          kar ++;
03614                         if (kar >= argc)  {
03615                                 fprintf (SUMA_STDERR, "need argument after -output\n");
03616                                 exit (1);
03617                         }
03618                         if (Opt->surf_out) {
03619             fprintf (SUMA_STDERR, "options -surf_out and -output are mutually exclusive\n");
03620                                 exit (1);
03621          }
03622          outname = argv[kar];
03623                         brk = YUP;
03624                 }
03625       
03626       if (!brk && (strcmp(argv[kar], "-add_index") == 0)) {
03627                         Opt->AddIndex = 1;
03628                         brk = YUP;
03629                 }
03630       
03631       if (!brk && (strcmp(argv[kar], "-i_fs") == 0)) {
03632          SUMA_SL_Err("Option -i_fs is obsolete.\nUse -spec and -surf_A instead.\n");
03633          exit(1);
03634          kar ++;
03635                         if (kar >= argc)  {
03636                                 fprintf (SUMA_STDERR, "need argument after -i_fs \n");
03637                                 exit (1);
03638                         }
03639                         Opt->if_name = argv[kar];
03640          Opt->iType = SUMA_FREE_SURFER;
03641          if (!Opt->insurf_method) Opt->insurf_method = 1;
03642          else {
03643             fprintf (SUMA_STDERR, "already specified input surface.\n");
03644             exit(1);
03645          }
03646                         brk = YUP;
03647                 }
03648       
03649       if (!brk && (strcmp(argv[kar], "-i_sf") == 0)) {
03650          SUMA_SL_Err("Option -i_sf is obsolete.\nUse -spec and -surf_A instead.\n");
03651          exit(1);
03652          kar ++;
03653                         if (kar+1 >= argc)  {
03654                                 fprintf (SUMA_STDERR, "need 2 argument after -i_sf\n");
03655                                 exit (1);
03656                         }
03657                         Opt->if_name = argv[kar]; kar ++;
03658          Opt->if_name2 = argv[kar];
03659          Opt->iType = SUMA_SUREFIT;
03660          if (!Opt->insurf_method) Opt->insurf_method = 1;
03661          else {
03662             fprintf (SUMA_STDERR, "already specified input surface.\n");
03663             exit(1);
03664          }
03665                         brk = YUP;
03666                 }
03667       
03668       if (!brk && (strcmp(argv[kar], "-i_vec") == 0)) {
03669          SUMA_SL_Err("Option -i_vec is obsolete.\nUse -spec and -surf_A instead.\n");
03670          exit(1);
03671          kar ++;
03672                         if (kar+1 >= argc)  {
03673                                 fprintf (SUMA_STDERR, "need 2 argument after -i_vec\n");
03674                                 exit (1);
03675                         }
03676                         Opt->if_name = argv[kar]; kar ++;
03677          Opt->if_name2 = argv[kar];
03678          Opt->iType = SUMA_VEC;
03679          if (!Opt->insurf_method) Opt->insurf_method = 1;
03680          else {
03681             fprintf (SUMA_STDERR, "already specified input surface.\n");
03682             exit(1);
03683          }
03684                         brk = YUP;
03685                 }
03686       
03687       if (!brk && (strcmp(argv[kar], "-i_dx") == 0)) {
03688          SUMA_SL_Err("Option -i_ply is obsolete.\nUse -spec and -surf_A instead.\n");
03689          exit(1);
03690          kar ++;
03691                         if (kar >= argc)  {
03692                                 fprintf (SUMA_STDERR, "need argument after -i_dx\n ");
03693                                 exit (1);
03694                         }
03695                         Opt->if_name = argv[kar];
03696          Opt->iType = SUMA_OPENDX_MESH;
03697          if (!Opt->insurf_method) Opt->insurf_method = 1;
03698          else {
03699             fprintf (SUMA_STDERR, "already specified input surface.\n");
03700             exit(1);
03701          }
03702                         brk = YUP;
03703                 }
03704       
03705       if (!brk && (strcmp(argv[kar], "-i_ply") == 0)) {
03706          SUMA_SL_Err("Option -i_ply is obsolete.\nUse -spec and -surf_A instead.\n");
03707          exit(1);
03708          kar ++;
03709                         if (kar >= argc)  {
03710                                 fprintf (SUMA_STDERR, "need argument after -i_ply\n ");
03711                                 exit (1);
03712                         }
03713                         Opt->if_name = argv[kar];
03714          Opt->iType = SUMA_PLY;
03715          if (!Opt->insurf_method) Opt->insurf_method = 1;
03716          else {
03717             fprintf (SUMA_STDERR, "already specified input surface.\n");
03718             exit(1);
03719          }
03720                         brk = YUP;
03721                 }
03722       
03723       if (!brk && (strcmp(argv[kar], "-spec") == 0)) {
03724          kar ++;
03725                         if (kar >= argc)  {
03726                                 fprintf (SUMA_STDERR, "need argument after -spec \n");
03727                                 exit (1);
03728                         }
03729                         Opt->spec_file = argv[kar];
03730          if (!Opt->insurf_method) Opt->insurf_method = 2;
03731          else {
03732             fprintf (SUMA_STDERR, "already specified spec file.\n");
03733             exit(1);
03734          }
03735                         brk = YUP;
03736                 }
03737       
03738       if (!brk && (strncmp(argv[kar], "-surf_", 6) == 0)) {
03739                         if (kar + 1>= argc)  {
03740                                 fprintf (SUMA_STDERR, "need argument after -surf_X SURF_NAME \n");
03741                                 exit (1);
03742                         }
03743                         ind = argv[kar][6] - 'A';
03744          if (ind < 0 || ind >= SURFSMOOTH_MAX_SURF) {
03745             fprintf (SUMA_STDERR, "-surf_X SURF_NAME option is out of range,\n"
03746                                   "   only surf_A allowed.\n");
03747                                 exit (1);
03748          }
03749          kar ++;
03750          Opt->surf_names[ind] = argv[kar];
03751                         if (Opt->insurf_method && Opt->insurf_method != 2) {
03752             fprintf (SUMA_STDERR, "-surf_X SURF_NAME option must be used with -spec option.\n");
03753             exit(1);
03754          }
03755          brk = YUP;
03756                 }
03757       
03758       
03759       if (!brk && (strcmp(argv[kar], "-lim") == 0)) {
03760          kar ++;
03761                         if (kar >= argc)  {
03762                                 fprintf (SUMA_STDERR, "need argument after -lim \n");
03763                                 exit (1);
03764                         }
03765                         Opt->lim = atof(argv[kar]);
03766                         brk = YUP;
03767                 }
03768       
03769       if (!brk && (strcmp(argv[kar], "-match_size") == 0)) {
03770          kar ++;
03771                         if (kar >= argc)  {
03772                                 fprintf (SUMA_STDERR, "need argument after -match_size \n");
03773                                 exit (1);
03774                         }
03775                         Opt->lim = atof(argv[kar]);
03776          Opt->MatchMethod = 1;
03777                         brk = YUP;
03778                 }
03779       
03780       if (!brk && (strcmp(argv[kar], "-match_vol") == 0)) {
03781          kar ++;
03782                         if (kar >= argc)  {
03783                                 fprintf (SUMA_STDERR, "need argument after -match_vol \n");
03784                                 exit (1);
03785                         }
03786                         Opt->lim = atof(argv[kar]);
03787          Opt->MatchMethod = 2;
03788                         brk = YUP;
03789                 }
03790       
03791       if (!brk && (strcmp(argv[kar], "-match_area") == 0)) {
03792          kar ++;
03793                         if (kar >= argc)  {
03794                                 fprintf (SUMA_STDERR, "need argument after -match_area \n");
03795                                 exit (1);
03796                         }
03797                         Opt->lim = atof(argv[kar]);
03798          Opt->MatchMethod = 3;
03799                         brk = YUP;
03800                 }
03801       
03802       if (!brk && (strcmp(argv[kar], "-match_sphere") == 0)) {
03803          kar ++;
03804                         if (kar >= argc)  {
03805                                 fprintf (SUMA_STDERR, "need argument after -match_sphere \n");
03806                                 exit (1);
03807                         }
03808                         Opt->lim = atof(argv[kar]);
03809          Opt->MatchMethod = 4;
03810                         brk = YUP;
03811                 }
03812       
03813       if (!brk && (strcmp(argv[kar], "-fwhm") == 0)) {
03814          kar ++;
03815                         if (kar >= argc)  {
03816                                 fprintf (SUMA_STDERR, "need argument after -fwhm \n");
03817                                 exit (1);
03818                         }
03819                         Opt->fwhm = atof(argv[kar]);
03820                         brk = YUP;
03821                 }
03822       
03823       if (!brk && (strcmp(argv[kar], "-met") == 0)) {
03824          kar ++;
03825                         if (kar >= argc)  {
03826                                 fprintf (SUMA_STDERR, "need argument after -met \n");
03827                                 exit (1);
03828                         }
03829                         if (strcmp(argv[kar], "LB_FEM") == 0)  Opt->Method = SUMA_LB_FEM;
03830          else if (strcmp(argv[kar], "LM") == 0)  Opt->Method = SUMA_LM;
03831          else if (strcmp(argv[kar], "BF") == 0)  Opt->Method = SUMA_BRUTE_FORCE;
03832          else if (strcmp(argv[kar], "NN_geom") == 0)  Opt->Method = SUMA_NN_GEOM;
03833          else {
03834             fprintf (SUMA_STDERR, "Method %s not supported.\n", argv[kar]);
03835                                 exit (1);
03836          }
03837                         brk = YUP;
03838                 }
03839       
03840       if (!brk && !ps->arg_checked[kar]) {
03841                         fprintf (SUMA_STDERR,"Error %s:\nOption %s not understood. Try -help for usage\n", FuncName, argv[kar]);
03842                         exit (1);
03843                 } else {        
03844                         brk = NOPE;
03845                         kar ++;
03846                 }
03847    }
03848 
03849    if (Opt->N_iter < 1) {
03850       fprintf (SUMA_STDERR,"Error %s:\nWith -Niter N option, N must be > 1\n", FuncName);
03851       exit (1);
03852    }
03853    
03854    if ( (Opt->N_iter % 2) &&
03855         (Opt->Method == SUMA_LB_FEM || Opt->Method == SUMA_LM) ) {
03856       fprintf (SUMA_STDERR, "Number of iterations must be a multiple of 2.\n%d is not a multiple of 2.\n", Opt->N_iter);
03857       exit(1);
03858    }
03859    
03860    if (Opt->ShowNode < 0 && Opt->ShowOffset_DBG) {
03861       fprintf (SUMA_STDERR,"Error %s:\nBad debug node index (%d) in option -dbg_n\n", FuncName, Opt->ShowNode);
03862       exit (1);
03863    }
03864    
03865    if (Opt->insurf_method == 1) {
03866       SUMA_SL_Err("Obsolete method for surface specification.\nShould not have gotten here.");
03867       exit(1);
03868    }
03869       
03870    /* can't test for file existence here because of square brackets */
03871    if (0 && Opt->in_name && !SUMA_filexists(Opt->in_name)) {
03872       fprintf (SUMA_STDERR,"Error %s:\n%s not found.\n", FuncName, Opt->if_name);
03873       exit(1);
03874    }
03875    
03876    if (Opt->Method == SUMA_NO_METH) {
03877       fprintf (SUMA_STDERR,"Error %s:\nNo method was specified.\n", FuncName);
03878       exit(1);  
03879    }
03880    
03881    if (ps->cs->talk_suma && Opt->insurf_method != 2) {
03882       fprintf (SUMA_STDERR,   "must specify surface using -spec option\n"
03883                               "if you whish to talk to suma.\n");
03884       exit(1); 
03885    }
03886    
03887    if (0 && ps->cs->talk_suma && Opt->Method != SUMA_LB_FEM) {
03888       fprintf (SUMA_STDERR,   "talk option only valid with -LB_FEM\n");
03889       exit(1); 
03890    }
03891    
03892    if (Opt->insurf_method == 2) {
03893       if (!Opt->surf_names[0] || !Opt->spec_file) {
03894          fprintf (SUMA_STDERR,   "failed to specify either -spec or -surf_X options.\n");
03895          exit(1);  
03896       }
03897    }
03898     
03899    if (outname) {
03900       if (SUMA_filexists(outname)) {
03901          fprintf (SUMA_STDERR,"Error %s:\noutput file %s exists.\n", FuncName, outname);
03902          exit(1);
03903       }
03904       Opt->out_name = SUMA_copy_string(outname);
03905    } else {
03906       switch (Opt->Method) {
03907          case SUMA_LB_FEM:
03908             /* form autoname  */
03909             Opt->out_name = SUMA_Extension(Opt->in_name, ".1D", YUP); /*remove .1D */
03910             Opt->out_name = SUMA_append_replace_string(Opt->out_name,"_sm", "", 1); /* add _sm to prefix */
03911             Opt->out_name = SUMA_append_replace_string(Opt->out_name,".1D", "", 1); /* add .1D */
03912             break;
03913          case SUMA_LM:
03914             /* form autoname  */
03915             Opt->out_name = SUMA_copy_string("NodeList_sm.1D");
03916             break;
03917          case SUMA_BRUTE_FORCE:
03918             /* form autoname  */
03919             Opt->out_name = SUMA_copy_string("NodeList_Offsetsm.1D");
03920             break;
03921          case SUMA_NN_GEOM:
03922             /* form autoname  */
03923             Opt->out_name = SUMA_copy_string("NodeList_NNsm.1D");
03924             break;
03925          default:
03926             fprintf (SUMA_STDERR,"Error %s:\nNot ready for this option here.\n", FuncName);
03927             exit(1);
03928             break;
03929       }
03930       if (SUMA_filexists(Opt->out_name)) {
03931          fprintf (SUMA_STDERR,"Error %s:\noutput file %s exists.\n", FuncName, Opt->out_name);
03932          exit(1);
03933       }
03934    }
03935 
03936    /* method specific checks */
03937    switch (Opt->Method) {
03938       case SUMA_LB_FEM:
03939          if (!Opt->in_name) {
03940             fprintf (SUMA_STDERR,"Error %s:\ninput data not specified.\n", FuncName);
03941             exit(1);
03942          }
03943          if (Opt->fwhm ==  -1.0) {
03944             fprintf (SUMA_STDERR,"Error %s:\n-fwhm option must be used with -met LB_FEM.\n", FuncName); 
03945             exit(1);
03946          }else if (Opt->fwhm <= 0.0) {
03947             fprintf (SUMA_STDERR,"Error %s:\nFWHM must be > 0\n", FuncName);
03948             exit(1);
03949          }
03950          if (Opt->kpb >= 0) {
03951             fprintf (SUMA_STDERR,"Error %s:\n-kpb option is not valid with -met LB_FEM.\n", FuncName); 
03952             exit(1);
03953          }         
03954          
03955          break;
03956       case SUMA_BRUTE_FORCE:
03957          
03958          break;
03959       case SUMA_LM:
03960          
03961          if ( (Opt->l != -1.0 || Opt->m != -1.0) && Opt->kpb != -1.0) {
03962             fprintf (SUMA_STDERR,"Error %s:\nYou cannot mix options -kpb and -lm \n", FuncName);
03963             exit(1);
03964          }
03965          if (Opt->kpb != -1.0 && (Opt->kpb < 0.000001 || Opt->kpb > 10)) {
03966             fprintf (SUMA_STDERR,"Error %s:\nWith -kpb k option, you should satisfy 0 < k < 10\n", FuncName);
03967             exit(1);
03968          }
03969          if (Opt->l == -1.0 && Opt->m == -1.0 && Opt->kpb == -1.0) {
03970             Opt->kpb = 0.1;
03971          }
03972 
03973          if (Opt->l == -1.0 || Opt->m == -1.0) { /* convert kpb into l and m */
03974             if (!SUMA_Taubin_Smooth_Coef (Opt->kpb, &(Opt->l), &(Opt->m))) {
03975                SUMA_SL_Err("Failed to find smoothing coefficients");
03976                exit(1);            
03977             }
03978          } 
03979 
03980          if (Opt->in_name) {
03981             fprintf (SUMA_STDERR,"Error %s:\nOption -input not valid with -met LM.\n", FuncName);
03982             exit(1);
03983          }
03984          
03985          if (Opt->fwhm !=  -1.0) {
03986             fprintf (SUMA_STDERR,"Error %s:\nOption -fwhm not valid with -met LM.\n", FuncName);
03987             exit(1);
03988          }
03989          
03990          break;
03991       case SUMA_NN_GEOM:
03992          if (Opt->in_name) {
03993             fprintf (SUMA_STDERR,"Error %s:\nOption -input not valid with -met NN_geom.\n", FuncName);
03994             exit(1);
03995          }
03996          
03997          if (0 && Opt->lim > 1000) {
03998             fprintf (SUMA_STDERR,"Error %s:\n-lim option not specified.\n", FuncName);
03999             exit(1);
04000          }
04001          
04002          if (Opt->fwhm !=  -1.0) {
04003             fprintf (SUMA_STDERR,"Error %s:\nOption -fwhm not valid with -met NN_geom.\n", FuncName);
04004             exit(1);
04005          }
04006          break;
04007          
04008       default:
04009          fprintf (SUMA_STDERR,"Error %s:\nNot ready for this option here.\n", FuncName);
04010          exit(1);
04011          break;
04012    }
04013    
04014    SUMA_RETURN (Opt);
04015 }
04016 
04017 int main (int argc,char *argv[])
04018 {/* Main */    
04019    static char FuncName[]={"SurfSmooth"}; 
04020         int kar, icol, nvec, ncol=0, i, ii;
04021    float *data_old = NULL, *far = NULL;
04022    float **DistFirstNeighb;
04023    void *SO_name = NULL;
04024    SUMA_SurfaceObject *SO = NULL, *SOnew = NULL;
04025    MRI_IMAGE *im = NULL;
04026    SUMA_SFname *SF_name = NULL;
04027    struct  timeval start_time, start_time_all;
04028    float etime_GetOffset, etime_GetOffset_all;   
04029    SUMA_GET_OFFSET_STRUCT *OffS = NULL;
04030    SUMA_SURFSMOOTH_OPTIONS *Opt;  
04031    FILE *fileout=NULL; 
04032    float **wgt=NULL, *dsmooth=NULL;
04033    SUMA_INDEXING_ORDER d_order=SUMA_NO_ORDER;
04034    SUMA_COMM_STRUCT *cs = NULL;
04035         SUMA_SurfSpecFile Spec; 
04036    SUMA_GENERIC_ARGV_PARSE *ps=NULL;
04037    SUMA_Boolean LocalHead = NOPE;
04038    
04039         SUMA_mainENTRY;
04040    SUMA_STANDALONE_INIT;
04041    
04042    
04043         /* Allocate space for DO structure */
04044         SUMAg_DOv = SUMA_Alloc_DisplayObject_Struct (SUMA_MAX_DISPLAYABLE_OBJECTS);
04045    ps = SUMA_Parse_IO_Args(argc, argv, "-o;-i;-sv;-talk;");
04046    
04047    if (argc < 6)
04048        {
04049           usage_SUMA_SurfSmooth();
04050           exit (1);
04051        }
04052    
04053    Opt = SUMA_SurfSmooth_ParseInput (argv, argc, ps);
04054    cs = ps->cs;
04055    if (!cs) exit(1);
04056    
04057    /* now for the real work */
04058    if (Opt->insurf_method == 1) { /* method 1 */
04059       SUMA_SL_Err("Input in this method is no longer supported.\n");
04060       exit(1);
04061    } else { /* method 2 */
04062       int SO_read = -1;
04063       
04064       if (!SUMA_Read_SpecFile (Opt->spec_file, &Spec)) {
04065                         fprintf(SUMA_STDERR,"Error %s: Error in SUMA_Read_SpecFile\n", FuncName);
04066                         exit(1);
04067                 }
04068       SO_read = SUMA_spec_select_surfs(&Spec, Opt->surf_names, SURFSMOOTH_MAX_SURF, 0);
04069       if ( SO_read != 1 )
04070       {
04071               fprintf(SUMA_STDERR,"Error %s: Found %d surfaces, expected only 1.\n", FuncName,  SO_read);
04072          exit(1);
04073       }
04074       /* now read into SUMAg_DOv */
04075       if (!SUMA_LoadSpec_eng(&Spec, SUMAg_DOv, &SUMAg_N_DOv, Opt->sv_name, 0, SUMAg_CF->DsetList) ) {
04076               fprintf(SUMA_STDERR,"Error %s: Failed in SUMA_LoadSpec_eng\n", FuncName);
04077          exit(1);
04078       }
04079       /* now identify surface needed */
04080       SO = SUMA_find_named_SOp_inDOv(Opt->surf_names[0], SUMAg_DOv, SUMAg_N_DOv);
04081    }
04082    
04083    if (!SO) {
04084       fprintf (SUMA_STDERR,"Error %s: Failed to read input surface.\n", FuncName);
04085       exit (1);
04086    }
04087 
04088    if (Opt->ShowNode >= 0 && Opt->ShowNode >= SO->N_Node) {
04089       fprintf (SUMA_STDERR,"Error %s: Requesting debugging info for a node index (%d) \n"
04090                            "that does not exist in a surface of %d nodes.\nRemember, indexing starts at 0.\n", 
04091                            FuncName, Opt->ShowNode, SO->N_Node);
04092       exit (1);
04093    }
04094    
04095       
04096    /* form EL and FN */
04097    if (!SO->EL || !SO->FN) {
04098       /* normally you'd call SUMA_SurfaceMetrics_eng (SO, "EdgeList", NULL, 0) 
04099       but that should be done in SUMA_SurfaceMetrics_eng. */
04100       SUMA_SLP_Err("Unexpexted NULL SO->EL or SO->FN");
04101       exit(1); 
04102    }
04103 
04104    
04105    /* see if SUMA talk is turned on */
04106    if (ps->cs->talk_suma) {
04107       cs->istream = SUMA_GEOMCOMP_LINE;
04108       
04109       if (Opt->Method == SUMA_LB_FEM) { 
04110          if (!SUMA_SendToSuma (SO, cs, NULL, SUMA_NO_DSET_TYPE, 0)) {
04111             SUMA_SL_Err("Failed to initialize SUMA_SendToSuma");
04112             cs->Send = NOPE;
04113             ps->cs->talk_suma = NOPE;
04114          }
04115       }else if (Opt->Method == SUMA_LM) { 
04116          if (!SUMA_SendToSuma (SO, cs, NULL, SUMA_NO_DSET_TYPE, 0)) {
04117             SUMA_SL_Err("Failed to initialize SUMA_SendToSuma");
04118             cs->Send = NOPE;
04119             ps->cs->talk_suma = NOPE;
04120          }
04121       }else if (Opt->Method == SUMA_NN_GEOM) { 
04122          if (!SUMA_SendToSuma (SO, cs, NULL, SUMA_NO_DSET_TYPE, 0)) {
04123             SUMA_SL_Err("Failed to initialize SUMA_SendToSuma");
04124             cs->Send = NOPE;
04125             ps->cs->talk_suma = NOPE;
04126          }
04127       }else if (Opt->Method == SUMA_BRUTE_FORCE) { 
04128          if (!SUMA_SendToSuma (SO, cs, NULL, SUMA_NO_DSET_TYPE, 0)) {
04129             SUMA_SL_Err("Failed to initialize SUMA_SendToSuma");
04130             cs->Send = NOPE;
04131             ps->cs->talk_suma = NOPE;
04132          }
04133       }else {
04134          SUMA_SL_Err("Can't talk to suma with the chosen method.\n");
04135          ps->cs->talk_suma = NOPE;
04136       }
04137    }
04138   
04139    ncol = 3; /* default for geometry smoothing. That is changed below for data smoothing. */
04140    switch (Opt->Method) {
04141       case SUMA_LB_FEM: 
04142          /* Moo Chung's method for interpolation weights */
04143          {
04144             /* now load the input data */
04145             im = mri_read_1D (Opt->in_name);
04146 
04147             if (!im) {
04148                SUMA_SL_Err("Failed to read 1D file");
04149                exit(1);
04150             }
04151 
04152             far = MRI_FLOAT_PTR(im);
04153             nvec = im->nx;
04154             ncol = im->ny;
04155             d_order = SUMA_COLUMN_MAJOR;
04156             
04157             if (!nvec) {
04158                SUMA_SL_Err("Empty file");
04159                exit(1);
04160             }
04161             if (nvec != SO->N_Node) {
04162                fprintf(SUMA_STDERR, "Error %s:\n"
04163                                     "Expecting 1D file to have %d rows\n"
04164                                     "                    found %d rows instead.\n",
04165                                      FuncName, SO->N_Node, nvec);
04166                exit(1); 
04167             }
04168             if (LocalHead) SUMA_etime(&start_time,0);
04169             wgt = SUMA_Chung_Smooth_Weights(SO);
04170             if (!wgt) {
04171                SUMA_SL_Err("Failed to compute weights.\n");
04172                exit(1);
04173             }
04174             
04175             if (LocalHead) {
04176                etime_GetOffset = SUMA_etime(&start_time,1);
04177                fprintf(SUMA_STDERR, "%s: weight computation took %f seconds for %d nodes.\n"
04178                                  "Projected time per 100000 nodes is: %f minutes\n", 
04179                                        FuncName, etime_GetOffset, SO->N_Node, 
04180                                        etime_GetOffset * 100000 / 60.0 / (SO->N_Node));
04181             }
04182             
04183             
04184             dsmooth = SUMA_Chung_Smooth (SO, wgt, Opt->N_iter, Opt->fwhm, far, ncol, SUMA_COLUMN_MAJOR, NULL, cs);
04185             
04186             if (LocalHead) {
04187                etime_GetOffset = SUMA_etime(&start_time,1);
04188                fprintf(SUMA_STDERR, "%s: Total processing took %f seconds for %d nodes.\n"
04189                                  "Projected time per 100000 nodes is: %f minutes\n", 
04190                                        FuncName, etime_GetOffset, SO->N_Node, 
04191                                        etime_GetOffset * 100000 / 60.0 / (SO->N_Node));
04192             }
04193             
04194             #if 0
04195             /* writing is now done below ... */
04196             fileout = fopen(Opt->out_name, "w");
04197             if (Opt->AddIndex) SUMA_disp_vecmat (dsmooth, SO->N_Node, ncol, 1, d_order, fileout, YUP);
04198             else SUMA_disp_vecmat (dsmooth, SO->N_Node, ncol, 1, d_order, fileout, NOPE);
04199             fclose(fileout); fileout = NULL;
04200 
04201             if (dsmooth) SUMA_free(dsmooth); dsmooth = NULL;
04202             #endif
04203             if (wgt) SUMA_free2D ((char **)wgt, SO->N_Node); wgt = NULL;
04204          }
04205          break;
04206          
04207       case SUMA_NN_GEOM:
04208          /* brute forcem nearset neighbor interpolation */
04209          {
04210             if (LocalHead) SUMA_etime(&start_time,0);
04211             
04212             d_order =  SUMA_ROW_MAJOR; 
04213             
04214             dsmooth = SUMA_NN_GeomSmooth( SO, Opt->N_iter, SO->NodeList,
04215                                           3, d_order, NULL, cs);
04216             if (0 && LocalHead) {
04217                SUMA_LH("See dsmooth.1D");
04218                SUMA_disp_vecmat (dsmooth, SO->N_Node, 3, 1,  d_order, NULL, YUP);
04219             }
04220             if (!dsmooth) {
04221                SUMA_SL_Err("Failed in SUMA_NN_Geom_Smooth");
04222                exit(1);
04223             }
04224             
04225             /* writing of results is done below */
04226          }
04227          break;  
04228       case SUMA_BRUTE_FORCE:
04229          /* a method that will likely be dropped NOT FINISHED  */
04230          {
04231             if (LocalHead) SUMA_etime(&start_time,0);
04232             
04233             d_order =  SUMA_ROW_MAJOR; 
04234             
04235             SUMA_etime(&start_time_all,0);
04236             dsmooth = SUMA_Offset_GeomSmooth( SO, Opt->N_iter, Opt->OffsetLim, SO->NodeList,
04237                                               3, d_order, NULL, cs);
04238             if (0 && LocalHead) {
04239                SUMA_LH("See dsmooth.1D");
04240                SUMA_disp_vecmat (dsmooth, SO->N_Node, 3, 1,  d_order, NULL, YUP);
04241             }
04242             if (!dsmooth) {
04243                SUMA_SL_Err("Failed in SUMA_Offset_Geom_Smooth");
04244                exit(1);
04245             }
04246             
04247             /* writing of results is done below */
04248             
04249             etime_GetOffset_all = SUMA_etime(&start_time_all,1);
04250             fprintf(SUMA_STDERR, "%s: Done.\nSearch to %f mm took %f minutes for %d nodes.\n" , 
04251                                  FuncName, Opt->lim, etime_GetOffset_all / 60.0 , SO->N_Node);
04252 
04253          }
04254          break;
04255       case SUMA_LM:
04256          /* Taubin's */
04257          {
04258             
04259             if (LocalHead) SUMA_etime(&start_time,0);
04260             
04261             d_order =  SUMA_ROW_MAJOR; 
04262             dsmooth = SUMA_Taubin_Smooth (SO, NULL, 
04263                             Opt->l, Opt->m, SO->NodeList, 
04264                             Opt->N_iter, 3, d_order,
04265                             NULL, cs, NULL); 
04266 
04267             if (LocalHead) {
04268                etime_GetOffset = SUMA_etime(&start_time,1);
04269                fprintf(SUMA_STDERR, "%s: Total processing took %f seconds for %d nodes.\n"
04270                                     "Projected time per 100000 nodes is: %f minutes\n", 
04271                                        FuncName, etime_GetOffset, SO->N_Node, 
04272                                        etime_GetOffset * 100000 / 60.0 / (SO->N_Node));
04273             }
04274             /* writing of results is done below */
04275          }
04276          break;
04277       default:
04278          SUMA_SL_Err("Bad method, should not be here.");
04279          exit(1);
04280          break;
04281    }
04282    
04283    
04284    if (Opt->Method == SUMA_BRUTE_FORCE || Opt->Method == SUMA_NN_GEOM)
04285    {
04286       if (Opt->MatchMethod) {   
04287          SUMA_LH("Fixing shrinkage...");
04288          /* Create a surface that is a descendant of SO, use the new coordinates */
04289          SOnew = SUMA_CreateChildSO( SO,
04290                                      dsmooth, SO->N_Node,
04291                                      NULL, -1,
04292                                      0); /* SOnew->NodeList is now == dsmooth */
04293 
04294          /* fix the shrinking ..*/
04295 
04296          switch (Opt->MatchMethod) {
04297             case 1:
04298                if (!SUMA_EquateSurfaceSize(SOnew, SO, Opt->lim, cs)) {
04299                   SUMA_SL_Warn("Failed to fix surface size.\nTrying to finish ...");
04300                }
04301 
04302                /* send the unshrunk bunk */
04303                if (cs->Send) {
04304                   SUMA_LH("Sending last fix to SUMA ...");
04305                   if (!SUMA_SendToSuma (SO, cs, (void *)SOnew->NodeList, SUMA_NODE_XYZ, 1)) {
04306                      SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCommunication halted.");
04307                   }
04308                }
04309 
04310                /* to make matters parallel with the other methods, keep dsmooth and free SOnew */
04311                dsmooth = SOnew->NodeList; /* CHECK IF THAT's the case here... coordinates have a new pointer after Equating surface size */
04312                SOnew->NodeList = NULL; /* new coordinates will stay alive in dsmooth */
04313                SUMA_Free_Surface_Object(SOnew); SOnew=NULL;
04314 
04315                break;
04316             case 2:
04317                if (!SUMA_EquateSurfaceVolumes(SOnew, SO, Opt->lim, cs)) {
04318                   SUMA_SL_Warn("Failed to fix surface size.\nTrying to finish ...");
04319                }
04320 
04321                /* send the unshrunk bunk */
04322                if (cs->Send) {
04323                   SUMA_LH("Sending last fix to SUMA ...");
04324                   if (!SUMA_SendToSuma (SO, cs, (void *)SOnew->NodeList, SUMA_NODE_XYZ, 1)) {
04325                      SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCommunication halted.");
04326                   }
04327                }
04328                /* to make matters parallel with the other methods, keep dsmooth and free SOnew */
04329                dsmooth = SOnew->NodeList; /* coordinates have a new pointer after Equating surface volumes */
04330                SOnew->NodeList = NULL; /* new coordinates will stay alive in dsmooth */
04331                SUMA_Free_Surface_Object(SOnew); SOnew=NULL;
04332                break;
04333             case 3:
04334                if (!SUMA_EquateSurfaceAreas(SOnew, SO, Opt->lim, cs)) {
04335                   SUMA_SL_Warn("Failed to fix surface size.\nTrying to finish ...");
04336                }
04337 
04338                /* send the unshrunk bunk */
04339                if (cs->Send) {
04340                   SUMA_LH("Sending last fix to SUMA ...");
04341                   if (!SUMA_SendToSuma (SO, cs, (void *)SOnew->NodeList, SUMA_NODE_XYZ, 1)) {
04342                      SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCommunication halted.");
04343                   }
04344                }
04345                /* to make matters parallel with the other methods, keep dsmooth and free SOnew */
04346                dsmooth = SOnew->NodeList; /* coordinates have a new pointer after Equating surface volumes */
04347                SOnew->NodeList = NULL; /* new coordinates will stay alive in dsmooth */
04348                SUMA_Free_Surface_Object(SOnew); SOnew=NULL;
04349                break;
04350             case 4:
04351                if (!SUMA_ProjectSurfaceToSphere(SOnew, SO, Opt->lim, cs)) {
04352                   SUMA_SL_Warn("Failed to fix surface size.\nTrying to finish ...");
04353                }
04354 
04355                /* send the unshrunk bunk */
04356                if (cs->Send) {
04357                   SUMA_LH("Sending last fix to SUMA ...");
04358                   if (!SUMA_SendToSuma (SO, cs, (void *)SOnew->NodeList, SUMA_NODE_XYZ, 1)) {
04359                      SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCommunication halted.");
04360                   }
04361                }
04362 
04363                /* to make matters parallel with the other methods, keep dsmooth and free SOnew */
04364                dsmooth = SOnew->NodeList; /* CHECK IF THAT's the case here... coordinates have a new pointer after Equating surface size */
04365                SOnew->NodeList = NULL; /* new coordinates will stay alive in dsmooth */
04366                SUMA_Free_Surface_Object(SOnew); SOnew=NULL;
04367                break;
04368             case 0:
04369                break;
04370             default:
04371                SUMA_SL_Err("Huh ?");
04372                break;   
04373          }
04374       }
04375       if (LocalHead) {
04376          etime_GetOffset = SUMA_etime(&start_time,1);
04377          fprintf(SUMA_STDERR, "%s: Total processing took %f seconds for %d nodes.\n"
04378                               "Projected time per 100000 nodes is: %f minutes\n", 
04379                                  FuncName, etime_GetOffset, SO->N_Node, 
04380                                  etime_GetOffset * 100000 / 60.0 / (SO->N_Node));
04381       }
04382 
04383    }
04384    
04385    /* write out the filtered geometry. Should not be executed for data smoothing */
04386    if (Opt->surf_out) {
04387       if (!dsmooth) {
04388          SUMA_SL_Err("NULL dsmooth for geometry smoothing. Either failed to smooth or logical error.");
04389          exit(1);
04390       }
04391        
04392       SUMA_free(SO->NodeList); SO->NodeList = dsmooth; dsmooth = NULL; /* replace NodeList */
04393       switch (SO->FileType) {
04394          case SUMA_SUREFIT:
04395             SF_name = (SUMA_SFname *) SUMA_malloc(sizeof(SUMA_SFname));
04396             sprintf(SF_name->name_coord,"%s", Opt->surf_out);
04397             SF_name->name_topo[0] = '\0'; 
04398             SO_name = (void *)SF_name;
04399             if (!SUMA_Save_Surface_Object (SO_name, SO, SUMA_SUREFIT, SUMA_ASCII, NULL)) {
04400                fprintf (SUMA_STDERR,"Error %s: Failed to write surface object.\n", FuncName);
04401                exit (1);
04402             }
04403             break;
04404          case SUMA_VEC:
04405             SF_name = (SUMA_SFname *) SUMA_malloc(sizeof(SUMA_SFname));
04406             sprintf(SF_name->name_coord,"%s", Opt->surf_out);
04407             SF_name->name_topo[0] = '\0';
04408             SO_name = (void *)SF_name;
04409             if (!SUMA_Save_Surface_Object (SO_name, SO, SUMA_VEC, SUMA_ASCII, NULL)) {
04410                fprintf (SUMA_STDERR,"Error %s: Failed to write surface object.\n", FuncName);
04411                exit (1);
04412             }
04413             break;
04414          case SUMA_FREE_SURFER:
04415             SO_name = (void *)Opt->surf_out; 
04416             if (!SUMA_Save_Surface_Object (SO_name, SO, SUMA_FREE_SURFER, SUMA_ASCII, NULL)) {
04417                fprintf (SUMA_STDERR,"Error %s: Failed to write surface object.\n", FuncName);
04418                exit (1);
04419             }
04420             break;
04421          case SUMA_FREE_SURFER_PATCH:
04422             fprintf (SUMA_STDERR,"Error %s: No support for writing Free Surfer patches.\n", FuncName);
04423             exit (1);  
04424             break;
04425          case SUMA_PLY:
04426             SO_name = (void *)Opt->surf_out; 
04427             if (!SUMA_Save_Surface_Object (SO_name, SO, SUMA_PLY, SUMA_FF_NOT_SPECIFIED, NULL)) {
04428                fprintf (SUMA_STDERR,"Error %s: Failed to write surface object.\n", FuncName);
04429                exit (1);
04430             }
04431             break;
04432          case SUMA_OPENDX_MESH:
04433             SO_name = (void *)Opt->surf_out; 
04434             if (!SUMA_Save_Surface_Object (SO_name, SO, SUMA_OPENDX_MESH, SUMA_ASCII, NULL)) {
04435                fprintf (SUMA_STDERR,"Error %s: Failed to write surface object.\n", FuncName);
04436                exit (1);
04437             }
04438             break;  
04439          default:
04440             fprintf (SUMA_STDERR,"Error %s: Bad format.\n", FuncName);
04441             exit(1);
04442       }
04443    } else {
04444       if (!dsmooth) {
04445          SUMA_SL_Err("NULL dsmooth for data smoothing. Either failed to smooth or logical error.");
04446          exit(1);
04447       }      
04448       fileout = fopen(Opt->out_name, "w");
04449       if (Opt->AddIndex) SUMA_disp_vecmat (dsmooth, SO->N_Node, ncol, 1, d_order, fileout, YUP);
04450       else SUMA_disp_vecmat (dsmooth, SO->N_Node, ncol, 1, d_order, fileout, NOPE);
04451       fclose(fileout); fileout = NULL;
04452    }
04453 
04454 
04455 
04456    /* you don't want to exit rapidly because the SUMA might not be done processing the last elements*/
04457    if (cs->Send && !cs->GoneBad) {
04458       /* cleanup and close connections */
04459       if (Opt->Method == SUMA_LB_FEM) {
04460          if (!SUMA_SendToSuma (SO, cs, NULL, SUMA_NODE_RGBAb, 2)) {
04461             SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCleanup failed");
04462          }
04463       }else if (Opt->Method == SUMA_LM) {
04464          if (!SUMA_SendToSuma (SO, cs, NULL, SUMA_NODE_XYZ, 2)) {
04465             SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCleanup failed");
04466          }
04467       }else if (Opt->Method == SUMA_NN_GEOM) {
04468          if (!SUMA_SendToSuma (SO, cs, NULL, SUMA_NODE_XYZ, 2)) {
04469             SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCleanup failed");
04470          }
04471       }
04472    }   
04473       
04474    
04475    SUMA_LH("clean up");
04476    if (SO->NodeList != dsmooth) {
04477       SUMA_LH("Freeing dsmooth...:");
04478       if (dsmooth) SUMA_free(dsmooth); dsmooth = NULL;
04479       SUMA_LH("Done:");
04480    }
04481    mri_free(im); im = NULL;   /* done with that baby */
04482    if (cs) cs = NULL; /* ps->cs if freed below */
04483    if (SF_name) SUMA_free(SF_name);
04484    if (Opt->insurf_method == 1) { if (SO) SUMA_Free_Surface_Object(SO); }
04485    if (data_old) SUMA_free(data_old);  
04486    if (Opt->out_name) SUMA_free(Opt->out_name); Opt->out_name = NULL;
04487    if (Opt) SUMA_free(Opt);
04488         if (ps) SUMA_FreeGenericArgParse(ps); ps = NULL;
04489    if (!SUMA_Free_Displayable_Object_Vect (SUMAg_DOv, SUMAg_N_DOv)) {
04490       SUMA_SL_Err("DO Cleanup Failed!");
04491    }
04492 
04493    exit(0);
04494 }
04495 #endif
04496 
04497 #ifdef SUMA_getPatch_STANDALONE
04498 #define SURFPATCH_MAX_SURF 10  /*!< Maximum number of input surfaces */
04499 
04500 void usage_SUMA_getPatch ()
04501    {
04502       static char FuncName[]={"usage_SUMA_getPatch"};
04503       char * s = NULL;
04504       s = SUMA_help_basics();
04505       printf ( "\nUsage:\n"
04506                "  SurfPatch <-spec SpecFile> <-surf_A insurf> <-surf_B insurf> ...\n"
04507                "            <-input nodefile inode ilabel> <-prefix outpref>  \n"
04508                "            [-hits min_hits] [-masklabel msk] [-vol]\n"
04509                "\n"
04510                "Usage 1:\n"
04511                "  The program creates a patch of surface formed by nodes \n"
04512                "  in nodefile.\n"
04513                "  Mandatory parameters:\n"
04514                "     -spec SpecFile: Spec file containing input surfaces.\n"
04515                "     -surf_X: Name of input surface X where X is a character\n"
04516                "              from A to Z. If surfaces are specified using two\n"
04517                "              files, use the name of the node coordinate file.\n"
04518                "     -input nodefile inode ilabel: \n"
04519                "            nodefile is the file containing nodes defining the patch.\n"
04520                "            inode is the index of the column containing the nodes\n"
04521                "            ilabel is the index of the column containing labels of\n"
04522                "                   the nodes in column inode. If you want to use\n"
04523                "                   all the nodes in column indode, then set this \n"
04524                "                   parameter to -1 (default). \n"
04525                "                   If ilabel is not equal to 0 then the corresponding \n"
04526                "                   node is used in creating the patch.\n"
04527                "                   See -masklabel option for one more variant.\n"
04528                "     -prefix outpref: Prefix of output patch. If more than one surface\n"
04529                "                      are entered, then the prefix will have _X added\n"
04530                "                      to it, where X is a character from A to Z.\n"
04531                "                      Output format depends on the input surface's.\n"
04532                "                      With that setting, checking on pre-existing files\n"
04533                "                      is only done before writing the new patch, which is\n"
04534                "                      annoying. You can set the output type ahead of time\n"
04535                "                      using -out_type option. This way checking for pre-existing\n"
04536                "                      output files can be done at the outset.\n"
04537                "\n" 
04538                "  Optional parameters:\n"
04539                "     -out_type TYPE: Type of all output patches, regardless of input surface type.\n"
04540                "                     Choose from: FreeSurfer, SureFit, 1D and Ply.\n"
04541                "     -hits min_hits: Minimum number of nodes specified for a triangle\n"
04542                "                     to be made a part of the patch (1 <= min_hits <= 3)\n"
04543                "                     default is 2.\n"
04544                "     -masklabel msk: If specified, then only nodes that are labeled with\n"
04545                "                     with msk are considered for the patch.\n"
04546                "                     This option is useful if you have an ROI dataset file\n"
04547                "                     and whish to create a patch from one out of many ROIs\n"
04548                "                     in that file. This option must be used with ilabel \n"
04549                "                     specified (not = -1)\n"
04550                "\n"
04551                "Usage 2:\n"
04552                "  The program can also be used to calculate the volume between the same patch\n"
04553                "  on two isotopic surfaces. See -vol option below.\n"
04554                "      -vol: Calculate the volume formed by the patch on surf_A and\n"
04555                "            and surf_B. For this option, you must specify two and\n"
04556                "            only two surfaces with surf_A and surf_B options.\n"
04557                "      -vol_only: Only calculate the volume, don't write out patches.\n"
04558                "\n"
04559                "%s"
04560                "\n",s); SUMA_free(s); s = NULL;
04561        s = SUMA_New_Additions(0, 1); printf("%s\n", s);SUMA_free(s); s = NULL;
04562        printf("       Ziad S. Saad SSCC/NIMH/NIH ziad@nih.gov     \n");
04563        exit (0);
04564    }
04565 
04566 typedef struct {
04567    SUMA_SO_File_Type iType;
04568    SUMA_SO_File_Type oType;
04569    char *out_prefix;
04570    char *sv_name;
04571    char *surf_names[SURFPATCH_MAX_SURF];
04572    int N_surf;
04573    char *spec_file;
04574    char *in_name;
04575    int minhits;
04576    int thislabel;
04577    int labelcol;
04578    int nodecol;
04579    int DoVol;
04580    int VolOnly;
04581 } SUMA_GETPATCH_OPTIONS;
04582 
04583 /*!
04584    \brief parse the arguments for SurfSmooth program
04585    
04586    \param argv (char *)
04587    \param argc (int)
04588    \return Opt (SUMA_GETPATCH_OPTIONS *) options structure.
04589                To free it, use 
04590                SUMA_free(Opt->out_prefix); 
04591                SUMA_free(Opt);
04592 */
04593 SUMA_GETPATCH_OPTIONS *SUMA_GetPatch_ParseInput (char *argv[], int argc)
04594 {
04595    static char FuncName[]={"SUMA_GetPatch_ParseInput"}; 
04596    SUMA_GETPATCH_OPTIONS *Opt=NULL;
04597    int kar, i, ind;
04598    char *outprefix;
04599    SUMA_Boolean brk = NOPE;
04600    SUMA_Boolean LocalHead = NOPE;
04601 
04602    SUMA_ENTRY;
04603    
04604    Opt = (SUMA_GETPATCH_OPTIONS *)SUMA_malloc(sizeof(SUMA_GETPATCH_OPTIONS));
04605 
04606    kar = 1;
04607    Opt->iType = SUMA_FT_NOT_SPECIFIED;
04608    Opt->out_prefix = NULL;
04609    Opt->sv_name = NULL;
04610    Opt->spec_file = NULL;
04611    Opt->in_name = NULL;
04612    Opt->minhits = 2;
04613    Opt->labelcol = -1;
04614    Opt->nodecol = -1;
04615    Opt->thislabel = -1;
04616    Opt->N_surf = -1;
04617    Opt->DoVol = 0;
04618    Opt->VolOnly = 0;
04619    Opt->oType = SUMA_FT_NOT_SPECIFIED;
04620    for (i=0; i<SURFPATCH_MAX_SURF; ++i) { Opt->surf_names[i] = NULL; }
04621         brk = NOPE;
04622    
04623         while (kar < argc) { /* loop accross command ine options */
04624                 /*fprintf(stdout, "%s verbose: Parsing command line...\n", FuncName);*/
04625                 if (strcmp(argv[kar], "-h") == 0 || strcmp(argv[kar], "-help") == 0) {
04626                          usage_SUMA_getPatch();
04627           exit (0);
04628                 }
04629                 
04630       SUMA_SKIP_COMMON_OPTIONS(brk, kar);
04631       
04632       if (!brk && (strcmp(argv[kar], "-spec") == 0)) {
04633          kar ++;
04634                         if (kar >= argc)  {
04635                                 fprintf (SUMA_STDERR, "need argument after -spec \n");
04636                                 exit (1);
04637                         }
04638                         Opt->spec_file = argv[kar];
04639                         brk = YUP;
04640                 }
04641       
04642       if (!brk && (strcmp(argv[kar], "-hits") == 0)) {
04643          kar ++;
04644                         if (kar >= argc)  {
04645                                 fprintf (SUMA_STDERR, "need argument after -hits \n");
04646                                 exit (1);
04647                         }
04648                         Opt->minhits = atoi(argv[kar]);
04649                         brk = YUP;
04650                 }
04651       
04652       if (!brk && (strcmp(argv[kar], "-masklabel") == 0)) {
04653          kar ++;
04654                         if (kar >= argc)  {
04655                                 fprintf (SUMA_STDERR, "need argument after -masklabel \n");
04656                                 exit (1);
04657                         }
04658                         Opt->thislabel = atoi(argv[kar]);
04659                         brk = YUP;
04660                 }
04661       
04662       if (!brk && (strcmp(argv[kar], "-vol") == 0)) {
04663                         Opt->DoVol = 1;
04664                         brk = YUP;
04665                 }
04666       
04667       if (!brk && (strcmp(argv[kar], "-vol_only") == 0)) {
04668                         Opt->DoVol = 1;
04669          Opt->VolOnly = 1;
04670                         brk = YUP;
04671                 }
04672       
04673       if (!brk && (strcmp(argv[kar], "-prefix") == 0)) {
04674          kar ++;
04675                         if (kar >= argc)  {
04676                                 fprintf (SUMA_STDERR, "need argument after -prefix \n");
04677                                 exit (1);
04678                         }
04679                         Opt->out_prefix = SUMA_copy_string(argv[kar]);
04680                         brk = YUP;
04681                 }
04682       
04683       if (!brk && (strcmp(argv[kar], "-out_type") == 0)) {
04684          kar ++;
04685                         if (kar >= argc)  {
04686                                 fprintf (SUMA_STDERR, "need argument after -out_type \n");
04687                                 exit (1);
04688                         }
04689                         Opt->oType = SUMA_guess_surftype_argv(argv[kar]);
04690                         brk = YUP;
04691                 }
04692 
04693       if (!brk && (strcmp(argv[kar], "-input") == 0)) {
04694          kar ++;
04695                         if (kar+2 >= argc)  {
04696                                 fprintf (SUMA_STDERR, "need 3 arguments after -input \n");
04697                                 exit (1);
04698                         }
04699                         Opt->in_name = argv[kar]; kar ++;
04700          Opt->nodecol = atoi(argv[kar]); kar ++;
04701          Opt->labelcol = atoi(argv[kar]); 
04702                         brk = YUP;
04703                 }
04704       
04705       if (!brk && (strncmp(argv[kar], "-surf_", 6) == 0)) {
04706                         if (kar + 1>= argc)  {
04707                                 fprintf (SUMA_STDERR, "need argument after -surf_X SURF_NAME \n");
04708                                 exit (1);
04709                         }
04710                         ind = argv[kar][6] - 'A';
04711          if (ind < 0 || ind >= SURFPATCH_MAX_SURF) {
04712             fprintf (SUMA_STDERR, "-surf_X SURF_NAME option is out of range.\n");
04713                                 exit (1);
04714          }
04715          kar ++;
04716          Opt->surf_names[ind] = argv[kar];
04717          Opt->N_surf = ind+1;
04718          brk = YUP;
04719                 }
04720       
04721       if (!brk) {
04722                         fprintf (SUMA_STDERR,"Error %s:\nOption %s not understood. Try -help for usage\n", FuncName, argv[kar]);
04723                         exit (1);
04724                 } else {        
04725                         brk = NOPE;
04726                         kar ++;
04727                 }
04728       
04729    }
04730    
04731    /* sanity checks */
04732    if (!Opt->out_prefix) Opt->out_prefix = SUMA_copy_string("SurfPatch");
04733    
04734    if (Opt->thislabel >= 0 && Opt->labelcol < 0) {
04735       SUMA_SL_Err("Cannot use -masklabel without specifying ilabel in -input option");
04736       exit(1);
04737    } 
04738    if (Opt->minhits < 1 || Opt->minhits > 3) {
04739       SUMA_SL_Err("minhits must be > 0 and < 3");
04740       exit(1);
04741    }
04742    if (Opt->N_surf < 1) {
04743       SUMA_SL_Err("No surface specified.");
04744       exit(1);
04745    }
04746    if (!Opt->in_name) {
04747       SUMA_SL_Err("No input specified.");
04748       exit(1);
04749    }
04750    if (Opt->DoVol && Opt->N_surf != 2) {
04751       SUMA_SL_Err("Must specify 2 and only 2 surfaces with -vol options");
04752       exit(1);
04753    }
04754    SUMA_RETURN (Opt);
04755      
04756 }
04757 
04758 int main (int argc,char *argv[])
04759 {/* Main */    
04760    static char FuncName[]={"SurfPatch"};
04761    SUMA_GETPATCH_OPTIONS *Opt; 
04762    char *ppref=NULL, ext[5]; 
04763    float *far=NULL;
04764    MRI_IMAGE *im = NULL;
04765    int SO_read = -1;
04766    int *NodePatch=NULL, N_NodePatch=-1, *FaceSetList=NULL , N_FaceSet = -1;          
04767    int i, inodeoff=-1, ilabeloff=-1, nvec, ncol, cnt;
04768    SUMA_SurfaceObject *SO = NULL;
04769    SUMA_PATCH *ptch = NULL; 
04770    SUMA_SurfSpecFile Spec;
04771    SUMA_INDEXING_ORDER d_order;
04772    void *SO_name = NULL;
04773    SUMA_Boolean exists = NOPE;
04774    SUMA_SO_File_Type typetmp;
04775    SUMA_Boolean LocalHead = NOPE;
04776         
04777    SUMA_mainENTRY;
04778    
04779    SUMA_STANDALONE_INIT;
04780    
04781    
04782         /* Allocate space for DO structure */
04783         SUMAg_DOv = SUMA_Alloc_DisplayObject_Struct (SUMA_MAX_DISPLAYABLE_OBJECTS);
04784    
04785    if (argc < 4)
04786        {
04787           usage_SUMA_getPatch();
04788           exit (1);
04789        }
04790    
04791    Opt = SUMA_GetPatch_ParseInput (argv, argc);
04792    
04793    if (Opt->oType != SUMA_FT_NOT_SPECIFIED && !Opt->VolOnly) { 
04794       for (i=0; i < Opt->N_surf; ++i) {
04795          if (Opt->N_surf > 1) {
04796             sprintf(ext, "_%c", 65+i);
04797             ppref = SUMA_append_string(Opt->out_prefix, ext);
04798          } else {
04799             ppref = SUMA_copy_string(Opt->out_prefix);
04800          }
04801          
04802          SO_name = SUMA_Prefix2SurfaceName(ppref, NULL, NULL, Opt->oType, &exists);
04803          if (exists) {
04804             fprintf(SUMA_STDERR,"Error %s:\nOutput file(s) %s* on disk.\nWill not overwrite.\n", FuncName, ppref);
04805             exit(1);
04806          }
04807          if (ppref) SUMA_free(ppref); ppref = NULL; 
04808          if (SO_name) SUMA_free(SO_name); SO_name = NULL;
04809       } 
04810    }
04811    
04812    /* read all surfaces */
04813    if (!SUMA_Read_SpecFile (Opt->spec_file, &Spec)) {
04814                 fprintf(SUMA_STDERR,"Error %s: Error in SUMA_Read_SpecFile\n", FuncName);
04815                 exit(1);
04816         }
04817    SO_read = SUMA_spec_select_surfs(&Spec, Opt->surf_names, SURFPATCH_MAX_SURF, 0);
04818    if ( SO_read != Opt->N_surf )
04819    {
04820            if (SO_read >=0 )
04821          fprintf(SUMA_STDERR,"Error %s:\nFound %d surfaces, expected %d.\n", FuncName,  SO_read, Opt->N_surf);
04822       exit(1);
04823    }
04824    /* now read into SUMAg_DOv */
04825    if (!SUMA_LoadSpec_eng(&Spec, SUMAg_DOv, &SUMAg_N_DOv, Opt->sv_name, 0, SUMAg_CF->DsetList) ) {
04826            fprintf(SUMA_STDERR,"Error %s: Failed in SUMA_LoadSpec_eng\n", FuncName);
04827       exit(1);
04828    }
04829    
04830    /* read in the file containing the node information */
04831    im = mri_read_1D (Opt->in_name);
04832 
04833    if (!im) {
04834       SUMA_SL_Err("Failed to read 1D file");
04835       exit(1);
04836    }
04837 
04838    far = MRI_FLOAT_PTR(im);
04839    nvec = im->nx;
04840    ncol = im->ny;
04841    d_order = SUMA_COLUMN_MAJOR;
04842 
04843    if (!nvec) {
04844       SUMA_SL_Err("Empty file");
04845       exit(1);
04846    }
04847    /* form the node vector */
04848    NodePatch = (int *)SUMA_malloc(sizeof(int)*nvec);
04849    if (!NodePatch) {
04850       SUMA_SL_Crit("Failed to allocate.");
04851       exit(1);
04852    }
04853    inodeoff = Opt->nodecol*nvec;
04854    if (Opt->labelcol < 0) { /* all listed nodes */ 
04855       for (i=0; i<nvec; ++i) {
04856          NodePatch[i] = far[i+inodeoff];
04857       }
04858       N_NodePatch = nvec;
04859    } else {
04860       ilabeloff =  Opt->labelcol*nvec;
04861       if (Opt->thislabel < 0) { /* all nodes with non zero labels */
04862          cnt = 0;
04863          for (i=0; i<nvec; ++i) {
04864             if (far[i+ilabeloff]) {
04865                NodePatch[cnt] = far[i+inodeoff];
04866                ++cnt;
04867             }
04868          }
04869          N_NodePatch = cnt;     
04870       } else { /* select labels */
04871          cnt = 0;
04872          for (i=0; i<nvec; ++i) {
04873             if (far[i+ilabeloff] == Opt->thislabel) {
04874                NodePatch[cnt] = far[i+inodeoff];
04875                ++cnt;
04876             }
04877          }
04878          N_NodePatch = cnt;    
04879       }
04880       NodePatch = (int *) SUMA_realloc(NodePatch , sizeof(int)*N_NodePatch);
04881    }
04882    
04883    /* done with im, free it */
04884    mri_free(im); im = NULL;   
04885    
04886    if (Opt->DoVol) {
04887       SUMA_SurfaceObject *SO1 = SUMA_find_named_SOp_inDOv(Opt->surf_names[0], SUMAg_DOv, SUMAg_N_DOv);
04888       SUMA_SurfaceObject *SO2 = SUMA_find_named_SOp_inDOv(Opt->surf_names[1], SUMAg_DOv, SUMAg_N_DOv);
04889       double Vol = 0.0;
04890       
04891       if (!SO1 || !SO2) {
04892          SUMA_SL_Err("Failed to load surfaces.");
04893          exit(1);
04894       }
04895       /* a chunk used to test SUMA_Pattie_Volume */
04896       Vol = SUMA_Pattie_Volume(SO1, SO2, NodePatch, N_NodePatch, NULL, Opt->minhits);
04897       fprintf (SUMA_STDERR,"Volume = %f\n", fabs(Vol));
04898    }
04899    
04900    
04901    if (!Opt->VolOnly) {
04902       FaceSetList = NULL;
04903       N_FaceSet = -1;
04904       for (i=0; i < Opt->N_surf; ++i) {/* loop to read in surfaces */
04905          /* now identify surface needed */
04906          SO = SUMA_find_named_SOp_inDOv(Opt->surf_names[i], SUMAg_DOv, SUMAg_N_DOv);
04907          if (!SO) {
04908             fprintf (SUMA_STDERR,"Error %s:\n"
04909                                  "Failed to find surface %s\n"
04910                                  "in spec file. Use full name.\n",
04911                                  FuncName, Opt->surf_names[i]);
04912             exit(1);
04913          }
04914          /* extract the patch */
04915          if (!SO->MF) {
04916             SUMA_SL_Warn ("NULL MF");
04917          }
04918          ptch = SUMA_getPatch (NodePatch, N_NodePatch, SO->FaceSetList, SO->N_FaceSet, SO->MF, Opt->minhits);
04919          if (!ptch) {
04920             SUMA_SL_Err("Failed to form patch.");
04921             exit(1);
04922          }
04923          if (LocalHead) SUMA_ShowPatch(ptch, NULL);
04924 
04925          /* Now create a surface with that patch */
04926          if (Opt->N_surf > 1) {
04927             sprintf(ext, "_%c", 65+i);
04928             ppref = SUMA_append_string(Opt->out_prefix, ext);
04929          } else {
04930             ppref = SUMA_copy_string(Opt->out_prefix);
04931          }
04932          /* save the original type */
04933          typetmp = SO->FileType;
04934          if (Opt->oType != SUMA_FT_NOT_SPECIFIED) SO->FileType = Opt->oType;
04935          SO_name = SUMA_Prefix2SurfaceName(ppref, NULL, NULL, SO->FileType, &exists);
04936          if (ppref) SUMA_free(ppref); ppref = NULL;
04937          /* save the original pointers to the facesets and their number */
04938          FaceSetList = SO->FaceSetList;
04939          N_FaceSet = SO->N_FaceSet;
04940          /* replace with Patch */
04941          SO->FaceSetList = ptch->FaceSetList;
04942          SO->N_FaceSet = ptch->N_FaceSet; 
04943          if (!SUMA_Save_Surface_Object (SO_name, SO, SO->FileType, SUMA_ASCII, NULL)) {
04944                fprintf (SUMA_STDERR,"Error %s: Failed to write surface object.\n", FuncName);
04945                exit (1);
04946          }
04947          /* bring SO back to shape */
04948          SO->FileType = typetmp;
04949          SO->FaceSetList = FaceSetList; FaceSetList = NULL;
04950          SO->N_FaceSet = N_FaceSet; N_FaceSet = -1;
04951          if (SO_name) SUMA_free(SO_name); SO_name = NULL;
04952          if (ptch) SUMA_freePatch(ptch); ptch = NULL;
04953       }
04954    } 
04955    
04956    SUMA_LH("clean up");
04957    if (Opt->out_prefix) SUMA_free(Opt->out_prefix); Opt->out_prefix = NULL;
04958    if (Opt) SUMA_free(Opt);   
04959    if (!SUMA_Free_Displayable_Object_Vect (SUMAg_DOv, SUMAg_N_DOv)) {
04960       SUMA_SL_Err("DO Cleanup Failed!");
04961    }
04962    
04963    if (!SUMA_Free_CommonFields(SUMAg_CF)) {SUMA_SL_Err("SUMAg_CF Cleanup Failed!");}
04964    
04965    SUMA_RETURN(0);
04966 } 
04967 #endif
04968 /*!
04969    \brief determine overall orientation of triangle normals and change triangle orientation if required
04970    \param NodeList (float *) xyz vector of node coords
04971    \param N_Node (int) number of nodes
04972    \param FaceSetList (int *) [n1 n2 n3] vector of triangles
04973    \param N_FaceSet (int) number of triangles
04974    \param orient (int) 0: Do not change orientation
04975                         1: make sure most normals point outwards from center. Flip all triangles if necessary (unless Force is used)
04976                         -1: make sure most normals point towards center. Flip all triangles if necessary (unless Force is used)
04977    \param Force (int) 1: Force the flipping of only those triangles whose normals point in the wrong direction (opposite to orient).
04978                            With this option, you will destroy the winding consistency of a surface!  
04979    \return ans (int):   0: error
04980                        1: most normals were pointing outwards
04981                        -1:  most normals were pointing inwards
04982 */
04983 int SUMA_OrientTriangles (float *NodeList, int N_Node, int *FaceSetList, int N_FaceSet, int orient, int Force)
04984 {
04985    static char FuncName[]={"SUMA_OrientTriangles"};
04986    int i, j, ip, negdot, posdot, sgn, NP, ND, n1, n2, n3, flip;
04987    float d1[3], d2[3], c[3], tc[3], U[3], dot, *norm, mag;
04988    FILE *fout = NULL;
04989    SUMA_Boolean LocalHead = NOPE;
04990    
04991    SUMA_ENTRY;
04992    
04993    
04994    if (!NodeList || !FaceSetList || !N_Node || !N_FaceSet) {
04995       SUMA_SL_Err("Null or no input");
04996       SUMA_RETURN(0);
04997    }
04998    norm = (float *)SUMA_calloc(3*N_FaceSet, sizeof(float));
04999    if (!norm) {
05000       SUMA_SL_Crit("Failed to allocate for norm"); SUMA_RETURN(0);
05001    }
05002    if (Force) {
05003       SUMA_SL_Warn("Using Force option! You might destroy triangulation consistency of surface!");
05004    }
05005    NP = ND = 3;
05006    /* calculate the center coordinate */
05007    c[0] = c[1] = c[2];
05008    for (i=0; i < N_Node; ++i) {
05009       ip = ND * i; c[0] += NodeList[ip]; c[1] += NodeList[ip+1]; c[2] += NodeList[ip+2];    
05010    }
05011    c[0] /= N_Node; c[1] /= N_Node; c[2] /= N_Node;
05012    
05013    /* calculate normals for each triangle, taken from SUMA_SurfNorm*/
05014    if (0 && LocalHead) {
05015       SUMA_SL_Note("Writing SUMA_OrientTriangles.1D");
05016       fout = fopen("SUMA_OrientTriangles.1D", "w");
05017    }
05018    negdot = 0; posdot = 0;
05019    for (i=0; i < N_FaceSet; i++) {
05020       ip = NP * i;
05021       n1 = FaceSetList[ip]; n2 = FaceSetList[ip+1]; n3 = FaceSetList[ip+2];   /* node indices making up triangle */
05022       tc[0] = (NodeList[3*n1]   + NodeList[3*n2]   + NodeList[3*n3]  )/3; /* centroid of triangle */
05023       tc[1] = (NodeList[3*n1+1] + NodeList[3*n2+1] + NodeList[3*n3+1])/3; 
05024       tc[2] = (NodeList[3*n1+2] + NodeList[3*n2+2] + NodeList[3*n3+2])/3; 
05025       /* calc normal */
05026       for (j=0; j < 3; j++) {
05027          d1[j] = NodeList[(ND*n1)+j] - NodeList[(ND*n2)+j];
05028          d2[j] = NodeList[(ND*n2)+j] - NodeList[(ND*n3)+j];
05029       }
05030       norm[ip] = d1[1]*d2[2] - d1[2]*d2[1];
05031       norm[ip+1] = d1[2]*d2[0] - d1[0]*d2[2];
05032       norm[ip+2] = d1[0]*d2[1] - d1[1]*d2[0];
05033       
05034       /* dot the normal with vector from center to node */
05035       U[0] = tc[0] - c[0]; U[1] = tc[1] - c[1]; U[2] = tc[2] - c[2];
05036       SUMA_DOTP_VEC(U, &(norm[ip]), dot, 3, float, float);
05037       if (dot < 0) {
05038          ++negdot;
05039          if (0 && LocalHead) { fprintf (SUMA_STDERR,"%s: Triangle %d has a negative dot product %f\nc  =[%.3f %.3f %.3f]\ntc =[%.3f %.3f %.3f]\nnorm=[%.3f %.3f %.3f]\n",
05040                       FuncName, i, dot, c[0], c[1], c[2], tc[0], tc[1], tc[2], norm[ip+0], norm[ip+1], norm[ip+2]); }
05041          
05042       } else {
05043          if (fout) { 
05044                SUMA_NORM_VEC(norm,3,mag); if (!mag) mag = 1; mag /= 5; 
05045                if (fout) fprintf (fout,"%.3f %.3f %.3f %.3f %.3f %.3f\n", tc[0], tc[1], tc[2], tc[0]+norm[ip+0]/mag, tc[1]+norm[ip+1]/mag, tc[2]+norm[ip+2]/mag);
05046          }
05047          ++posdot;
05048       }      
05049       
05050       if (Force) {
05051          if ( (dot < 0 && orient > 0) || (dot > 0 && orient < 0)) {
05052             n1 = FaceSetList[ip]; FaceSetList[ip] = FaceSetList[ip+2]; FaceSetList[ip+2] = n1;  
05053          }
05054       }
05055    }
05056    if (fout) fclose(fout); fout = NULL;
05057    flip = 0; sgn = 0;
05058    if (posdot >= negdot) {
05059       SUMA_LH("Normals appear to point away from center");
05060       sgn = 1;
05061       if (orient < 0) flip = 1;
05062    } else {
05063       SUMA_LH("Normals appear to point towards center");
05064       sgn = -1;
05065       if (orient > 0) flip = 1;
05066    }
05067    if (LocalHead) {
05068       fprintf(SUMA_STDERR,"%s:\n Found %d positive dot products and %d negative ones.\n", FuncName, posdot, negdot);
05069    }
05070    
05071    if (flip && !Force) {
05072       SUMA_LH("Flipping");
05073       for (i=0; i < N_FaceSet; i++) {
05074          ip = NP * i;
05075          n1 = FaceSetList[ip]; FaceSetList[ip] = FaceSetList[ip+2]; FaceSetList[ip+2] = n1;
05076       }
05077    }
05078    
05079    if (norm) SUMA_free(norm); norm = NULL;
05080    
05081    SUMA_RETURN(sgn);
05082 }
05083 
05084 /* 
05085    \brief a function to turn a surface patch (not all vertices are in use) into a surface where all nodes are used.
05086    \param NodeList (float *) N_Nodelist * 3 vector containing xyz triplets for vertex coordinates
05087    \param N_NodeList (int) you know what
05088    \param PatchFaces (int *) N_PatchFaces * PatchDim vector containing node indices forming triangulation
05089    \param N_PatchFaces (int) obvious
05090    \param PatchDim (int) 3 for triangular, 4 for rectangular patches etc. ..
05091    \return SO (SUMA_SurfaceObject *) surface object structure with NodeList, N_NodeList, FaceSetList and N_FaceSetList
05092                                      filled. Note node indexing in SO is not related to the indexing in patch.
05093    - Nothing but NodeList and FaceSetList is created here. KEEP IT THAT WAY
05094 */
05095 
05096 SUMA_SurfaceObject *SUMA_Patch2Surf(float *NodeList, int N_NodeList, int *PatchFaces, int N_PatchFaces, int PatchDim)
05097 {
05098    static char FuncName[]={"SUMA_Patch2Surf"};
05099    SUMA_SurfaceObject *SO=NULL;
05100    int i = 0, cnt = 0;
05101    int *imask = NULL;
05102    int N_Node = 0;
05103    SUMA_Boolean LocalHead = NOPE;
05104    
05105    SUMA_ENTRY;
05106    
05107    if (!NodeList || !PatchFaces) {
05108       SUMA_SL_Err("Null input");
05109       SUMA_RETURN(SO);
05110    }
05111    
05112    imask = (int*)SUMA_calloc(N_NodeList , sizeof(int));
05113    if (!imask) {
05114       SUMA_SL_Err("Failed to allocate");
05115       SUMA_RETURN(SO);
05116    }  
05117    /* count the number of nodes and initialize imask*/
05118    SO = SUMA_Alloc_SurfObject_Struct(1);
05119    if (!SO) {
05120       SUMA_SL_Err("Failed to allocate");
05121       SUMA_RETURN(SO);
05122    }
05123    SO->N_FaceSet = N_PatchFaces;
05124    SO->N_Node = 0;
05125    for (i=0; i<3*N_PatchFaces; ++i) {
05126       if (!imask[PatchFaces[i]]) {
05127          imask[PatchFaces[i]] = -1;
05128          ++SO->N_Node;
05129       }
05130    }
05131    if (LocalHead) {
05132       fprintf (SUMA_STDERR,"%s: %d nodes in patch\n", FuncName, SO->N_Node);
05133    }
05134    SO->NodeList = (float *)SUMA_malloc(sizeof(float)*3*SO->N_Node);
05135    SO->FaceSetList = (int *)SUMA_malloc(sizeof(int)*PatchDim*N_PatchFaces);
05136    if (!SO->NodeList || !SO->FaceSetList) {
05137       SUMA_SL_Err("Failed to allocate");
05138       SUMA_RETURN(SO);
05139    }
05140    SO->NodeDim = 3;
05141    SO->FaceSetDim = PatchDim;
05142    
05143    cnt = 0;
05144    for (i=0; i<3*N_PatchFaces; ++i) {
05145       if (imask[PatchFaces[i]] < 0) {
05146          imask[PatchFaces[i]] = cnt;
05147          SO->NodeList[3*cnt  ] = NodeList[3*PatchFaces[i]  ];
05148          SO->NodeList[3*cnt+1] = NodeList[3*PatchFaces[i]+1];
05149          SO->NodeList[3*cnt+2] = NodeList[3*PatchFaces[i]+2];
05150          ++cnt;
05151       }
05152       SO->FaceSetList[i] = imask[PatchFaces[i]];
05153    }
05154    
05155    SUMA_RETURN(SO);
05156 }
05157 /*!
05158    \brief a function to return a mask indicating if a node is 
05159    part of a patch or not
05160    isNodeInPatch = SUMA_MaskOfNodesInPatch( SUMA_SurfaceObject *SO, int * N_NodesUsedInPatch);
05161 
05162    \param SO (SUMA_SurfaceObject *) the surface object
05163    \param N_NodesUsedInPatch (int *) will contain the number of nodes used in the mesh of the patch (that is SO->FaceSetList)
05164                                      if *N_NodesUsedInPatch == SO->N_Node then all nodes in the nodelist are used
05165                                      in the mesh
05166    \return isNodeInPatch (SUMA_Boolean *) a vector SO->N_Node long such that if isNodeInPatch[n] = YUP then node n is used
05167                                     in the mesh 
05168 */
05169 SUMA_Boolean *SUMA_MaskOfNodesInPatch(SUMA_SurfaceObject *SO, int *N_NodesUsedInPatch)
05170 {
05171    static char FuncName[]={"SUMA_MaskOfNodesInPatch"};
05172    int k;
05173    SUMA_Boolean *NodesInPatchMesh = NULL;
05174 
05175    SUMA_ENTRY;
05176 
05177    *N_NodesUsedInPatch = 0;
05178 
05179    if (!SO) {
05180       SUMA_SL_Err("NULL SO");
05181       SUMA_RETURN(NULL);
05182    }
05183    if (!SO->FaceSetList || !SO->N_FaceSet) {
05184       SUMA_SL_Err("NULL or empty SO->FaceSetList");
05185       SUMA_RETURN(NULL);
05186    }
05187 
05188    NodesInPatchMesh = (SUMA_Boolean *)SUMA_calloc(SO->N_Node, sizeof(SUMA_Boolean)); 
05189    if (!NodesInPatchMesh) {
05190       SUMA_SL_Crit("Failed to allocate for NodesInPatchMesh");
05191       SUMA_RETURN(NULL);
05192    }
05193    for (k=0; k<SO->FaceSetDim*SO->N_FaceSet; ++k) {
05194       if (!NodesInPatchMesh[SO->FaceSetList[k]]) { 
05195          ++*N_NodesUsedInPatch;
05196          NodesInPatchMesh[SO->FaceSetList[k]] = 1;         
05197       }
05198    }
05199 
05200    SUMA_RETURN(NodesInPatchMesh);  
05201 }
05202 
05203 /*!
05204    Given a set of node indices, return a patch of the original surface that contains them
05205    
05206    Patch = SUMA_getPatch (NodesSelected, N_Nodes, Full_FaceSetList, N_Full_FaceSetList, Memb, MinHits)
05207 
05208    \param NodesSelected (int *) N_Nodes x 1 Vector containing indices of selected nodes. 
05209             These are indices into NodeList making up the surface formed by Full_FaceSetList.
05210    \param N_Nodes (int) number of elements in NodesSelected
05211    \param Full_FaceSetList (int *) N_Full_FaceSetList  x 3 vector containing the triangles forming the surface 
05212    \param N_Full_FaceSetList (int) number of triangular facesets forming the surface
05213    \param Memb (SUMA_MEMBER_FACE_SETS *) structure containing the node membership information (result of SUMA_MemberFaceSets function)
05214    \param MinHits (int) minimum number of nodes to be in a patch before the patch is selected. 
05215          Minimum is 1, Maximum logical is 3, assuming you do not have repeated node indices
05216          in NodesSelected.
05217    \ret Patch (SUMA_PATCH *) Structure containing the patch's FaceSetList, FaceSetIndex (into original surface) and number of elements.
05218          returns NULL in case of trouble.   Free Patch with SUMA_freePatch(Patch);
05219 
05220    \sa SUMA_MemberFaceSets, SUMA_isinbox, SUMA_PATCH
05221 */
05222 
05223 SUMA_PATCH * SUMA_getPatch (  int *NodesSelected, int N_Nodes, 
05224                               int *Full_FaceSetList, int N_Full_FaceSetList, 
05225                               SUMA_MEMBER_FACE_SETS *Memb, int MinHits)
05226 {
05227    int * BeenSelected;
05228    int i, j, node, ip, ip2, NP;
05229    SUMA_PATCH *Patch;
05230    static char FuncName[]={"SUMA_getPatch"};
05231    
05232    SUMA_ENTRY;
05233    
05234    NP = 3;
05235    BeenSelected = (int *)SUMA_calloc (N_Full_FaceSetList, sizeof(int));
05236    Patch = (SUMA_PATCH *)SUMA_malloc(sizeof(SUMA_PATCH));
05237    
05238    if (!BeenSelected || !Patch) {
05239       fprintf (SUMA_STDERR,"Error %s: Could not allocate for BeenSelected or patch.\n", FuncName);
05240       SUMA_RETURN(NULL);
05241    }
05242    /* find out the total number of facesets these nodes are members of */
05243    Patch->N_FaceSet = 0; /* total number of facesets containing these nodes */
05244    for (i=0; i < N_Nodes; ++i) {
05245       node = NodesSelected[i];
05246       for (j=0; j < Memb->N_Memb[node]; ++j) {
05247          if (!BeenSelected[Memb->NodeMemberOfFaceSet[node][j]]) {
05248             /* this faceset has not been selected, select it */
05249             ++ Patch->N_FaceSet;
05250          }
05251          ++ BeenSelected[Memb->NodeMemberOfFaceSet[node][j]];
05252       }   
05253    }
05254    
05255    /* now load these facesets into a new matrix */
05256    
05257    Patch->FaceSetList = (int *) SUMA_calloc (Patch->N_FaceSet * 3, sizeof(int));
05258    Patch->FaceSetIndex = (int *) SUMA_calloc (Patch->N_FaceSet, sizeof(int));
05259    Patch->nHits = (int *) SUMA_calloc (Patch->N_FaceSet, sizeof(int));
05260    
05261    if (!Patch->FaceSetList || !Patch->FaceSetIndex || !Patch->nHits) {
05262       fprintf (SUMA_STDERR,"Error %s: Could not allocate for Patch->FaceSetList || Patch_FaceSetIndex.\n", FuncName);
05263       SUMA_RETURN(NULL);
05264    }
05265    
05266    j=0;
05267    for (i=0; i < N_Full_FaceSetList; ++i) {
05268       if (BeenSelected[i] >= MinHits) {
05269          Patch->nHits[j] = BeenSelected[i];
05270          Patch->FaceSetIndex[j] = i;
05271          ip = NP * j;
05272          ip2 = NP * i;
05273          Patch->FaceSetList[ip] = Full_FaceSetList[ip2];
05274          Patch->FaceSetList[ip+1] = Full_FaceSetList[ip2+1];
05275          Patch->FaceSetList[ip+2] = Full_FaceSetList[ip2+2];
05276          ++j;
05277       }
05278    }
05279    
05280    /* reset the numer of facesets because it might have changed given the MinHits condition,
05281    It won't change if MinHits = 1.
05282    It's OK not to change the allocated space as long as you are using 1D arrays*/
05283    Patch->N_FaceSet = j;
05284    
05285    if (BeenSelected) SUMA_free(BeenSelected);
05286    
05287    SUMA_RETURN(Patch);   
05288 }
05289 
05290 /*!
05291    ans = SUMA_freePatch (SUMA_PATCH *Patch) ;
05292    frees Patch pointer 
05293    \param Patch (SUMA_PATCH *) Surface patch pointer
05294    \ret ans (SUMA_Boolean)
05295    \sa SUMA_getPatch
05296 */
05297 
05298 SUMA_Boolean SUMA_freePatch (SUMA_PATCH *Patch) 
05299 {
05300    static char FuncName[]={"SUMA_freePatch"};
05301    
05302    SUMA_ENTRY;
05303    
05304    
05305    if (Patch->FaceSetIndex) SUMA_free(Patch->FaceSetIndex);
05306    if (Patch->FaceSetList) SUMA_free(Patch->FaceSetList);
05307    if (Patch->nHits) SUMA_free(Patch->nHits);
05308    if (Patch) SUMA_free(Patch);
05309    SUMA_RETURN(YUP);
05310    
05311 }
05312 
05313 SUMA_Boolean SUMA_ShowPatch (SUMA_PATCH *Patch, FILE *Out) 
05314 {
05315    static char FuncName[]={"SUMA_freePatch"};
05316    int ip, i;
05317    
05318    SUMA_ENTRY;
05319    
05320    if (!Out) Out = stderr;
05321    
05322    fprintf (Out, "Patch Contains %d triangles:\n", Patch->N_FaceSet);
05323    fprintf (Out, "FaceIndex (nHits): FaceSetList[0..2]\n");
05324    for (i=0; i < Patch->N_FaceSet; ++i) {
05325       ip = 3 * i;   
05326       fprintf (Out, "%d(%d):   %d %d %d\n",
05327             Patch->FaceSetIndex[i], Patch->nHits[i], Patch->FaceSetList[ip],
05328             Patch->FaceSetList[ip+1], Patch->FaceSetList[ip+2]);
05329    }
05330    
05331    SUMA_RETURN(YUP);
05332 }
05333 
05334 /*!
05335    \brief Returns the contour of a patch 
05336    if you have the patch already created, then pass it in the last argument
05337    mode (int) 0: nice contour, not necessarily outermost boundary
05338               1: outermost edge, might look a tad jagged
05339 */
05340 SUMA_CONTOUR_EDGES * SUMA_GetContour (SUMA_SurfaceObject *SO, int *Nodes, int N_Node, int *N_ContEdges, int ContourMode, SUMA_PATCH *UseThisPatch)
05341 {
05342    static char FuncName[]={"SUMA_GetContour"};
05343    SUMA_EDGE_LIST * SEL=NULL;
05344    SUMA_PATCH *Patch = NULL;
05345    int i, Tri, Tri1, Tri2, sHits;
05346    SUMA_CONTOUR_EDGES *CE = NULL;
05347    SUMA_Boolean *isNode=NULL;
05348    SUMA_Boolean LocalHead = NOPE;
05349    
05350    SUMA_ENTRY;
05351 
05352    *N_ContEdges = -1;
05353    
05354    /* get the Node member structure if needed*/
05355    if (!SO->MF) {
05356       SUMA_SLP_Err("Member FaceSet not created.\n");
05357       SUMA_RETURN(CE);
05358    }  
05359 
05360    /* create a flag vector of which node are in Nodes */
05361    isNode = (SUMA_Boolean *) SUMA_calloc(SO->N_Node, sizeof(SUMA_Boolean));
05362    if (!isNode) {
05363       SUMA_SLP_Crit("Failed to allocate for isNode");
05364       SUMA_RETURN(CE);
05365    }
05366    
05367    for (i=0; i < N_Node; ++i) isNode[Nodes[i]] = YUP;
05368   
05369    if (UseThisPatch) {
05370       SUMA_LH("Using passed patch");
05371       Patch = UseThisPatch;
05372    } else { 
05373       SUMA_LH("Creating patch");
05374       switch (ContourMode) {
05375          case 0:
05376             Patch = SUMA_getPatch (Nodes, N_Node, SO->FaceSetList, SO->N_FaceSet, SO->MF, 2);
05377             break;
05378          case 1:
05379             Patch = SUMA_getPatch (Nodes, N_Node, SO->FaceSetList, SO->N_FaceSet, SO->MF, 1);
05380             break;
05381          default:
05382             SUMA_SL_Err("Bad contour mode"); SUMA_RETURN(NULL);
05383             break;
05384       }
05385    }
05386    if (LocalHead) SUMA_ShowPatch (Patch,NULL);
05387    
05388    if (Patch->N_FaceSet) {
05389       SEL = SUMA_Make_Edge_List_eng (Patch->FaceSetList, Patch->N_FaceSet, SO->N_Node, SO->NodeList, 0, NULL);
05390    
05391       if (0 && LocalHead) SUMA_Show_Edge_List (SEL, NULL);
05392       /* allocate for maximum */
05393       CE = (SUMA_CONTOUR_EDGES *) SUMA_malloc(SEL->N_EL * sizeof(SUMA_CONTOUR_EDGES));
05394       if (!CE) {
05395          SUMA_SLP_Crit("Failed to allocate for CE");
05396          SUMA_RETURN(CE);
05397       }
05398    
05399       switch (ContourMode) {
05400          case 0: /* a pretty contour, edges used here may not be the outermost of the patch */
05401             /* edges that are part of unfilled triangles are good */
05402             i = 0;
05403             *N_ContEdges = 0;
05404             while (i < SEL->N_EL) {
05405                if (SEL->ELps[i][2] == 2) {
05406                   Tri1 = SEL->ELps[i][1];
05407                   Tri2 = SEL->ELps[i+1][1];
05408                   sHits = Patch->nHits[Tri1] + Patch->nHits[Tri2];
05409                   if (sHits == 5 || sHits == 4) { /* one tri with 3 hits and one with 2 hits or 2 Tris with 2 hits each */
05410                      /* Pick edges that are part of only one triangle with three hits */
05411                                                  /* or two triangles with two hits */
05412                      /* There's one more condition, both nodes have to be a part of the original list */
05413                      if (isNode[SEL->EL[i][0]] && isNode[SEL->EL[i][1]]) {
05414                         CE[*N_ContEdges].n1 = SEL->EL[i][0];
05415                         CE[*N_ContEdges].n2 = SEL->EL[i][1];
05416                         ++ *N_ContEdges;
05417 
05418                         if (LocalHead) {
05419                            fprintf (SUMA_STDERR,"%s: Found edge made up of nodes [%d %d]\n",
05420                               FuncName, SEL->EL[i][0], SEL->EL[i][1]);
05421                         }
05422                      }
05423                   }
05424                }
05425 
05426                if (SEL->ELps[i][2] > 0) {
05427                   i += SEL->ELps[i][2];
05428                } else {
05429                   i ++;
05430                }
05431             }
05432             break;
05433          case 1: /* outermost contour, not pretty, but good for getting the outermost edge */
05434             i = 0;
05435             *N_ContEdges = 0;
05436             while (i < SEL->N_EL) {
05437                if (SEL->ELps[i][2] == 1) {
05438                   CE[*N_ContEdges].n1 = SEL->EL[i][0];
05439                   CE[*N_ContEdges].n2 = SEL->EL[i][1];
05440                   ++ *N_ContEdges;
05441                   if (LocalHead) {
05442                            fprintf (SUMA_STDERR,"%s: Found edge made up of nodes [%d %d]\n",
05443                               FuncName, SEL->EL[i][0], SEL->EL[i][1]);
05444                   }
05445                }
05446                if (SEL->ELps[i][2] > 0) {
05447                   i += SEL->ELps[i][2];
05448                } else {
05449                   i ++;
05450                }
05451             }
05452             break;
05453          default:
05454             SUMA_SL_Err("Bad ContourMode");
05455             SUMA_RETURN(NULL);
05456             break;            
05457       }
05458       
05459       /* Now reallocate */
05460       if (! *N_ContEdges) {
05461          SUMA_free(CE); CE = NULL;
05462          SUMA_RETURN(CE);
05463       }else {
05464          CE = (SUMA_CONTOUR_EDGES *) SUMA_realloc (CE, *N_ContEdges * sizeof(SUMA_CONTOUR_EDGES));
05465          if (!CE) {
05466             SUMA_SLP_Crit("Failed to reallocate for CE");
05467             SUMA_RETURN(CE);
05468          }
05469       }
05470          
05471       SUMA_free_Edge_List (SEL); 
05472    }
05473    
05474    if (!UseThisPatch) {
05475       SUMA_freePatch (Patch); 
05476    }
05477    Patch = NULL;
05478    
05479    SUMA_free(isNode);
05480    
05481    SUMA_RETURN(CE);
05482 }
05483 
05484 /*!
05485    \brief Stitch together two isotopic patches to calculate the volume between them
05486    
05487    \param SO1 : The first surface of the pattie
05488    \param SO2 : The second surface of the pattie
05489    \param Nodes (int *): N_Node x 1 vector of indices containing nodes that form the patch
05490    \param N_Node (int): Number of nodes in SO. 
05491    \param UseThisSo : If you send a pointer to an empty (but allocated) surface structure,
05492                       The pattie's surface is returned in UseThisSo. Otherwise, the temporary
05493                       surface is tossed in the trash can.
05494    \pram minPatchHits (int): Since you're forming a patch from nodes, you'll need to select the
05495                              minimum number of nodes to be in a patch (triangle) before the patch 
05496                              is selected. Minimum is 1, Maximum logical is 3. 
05497                              If you choose 1, you will have nodes in the patch that are not included
05498                              in Nodes vector. But that is the only way to get something back for just
05499                              one node. 
05500                              If you choose 3, you will have the same number of nodes in the patch as you
05501                              do in the vector Nodes. However, you'll get no patches formed if your Nodes
05502                              vector contains, one or two nodes for example ...
05503    
05504    Testing so far in /home/ziad/SUMA_test/afni:
05505    for a bunch of nodes:
05506       SurfMeasures -func node_vol -spec ../SurfData/SUMA/DemoSubj_lh.spec -surf_A lh.smoothwm.asc -surf_B lh.pial.asc -nodes_1D lhpatch.1D.roi'[0]' -out_1D SM_out.1D
05507       SurfPatch -spec ../SurfData/SUMA/DemoSubj_lh.spec -surf_A lh.smoothwm -surf_B lh.pial.asc -hits 3 -input lhpatch.1D.roi 0 1
05508       answer is 326, 13% different from sum in SM_out.1D's second column (373)... 
05509       
05510    for a single node (remember change -hits option to 1):
05511       SurfMeasures -func node_vol -spec ../SurfData/SUMA/DemoSubj_lh.spec -surf_A lh.smoothwm.asc -surf_B lh.pial.asc -nodes_1D lhpatch_1node.1D'[0]' -out_1D SM_out_1node.1D
05512       SurfPatch -spec ../SurfData/SUMA/DemoSubj_lh.spec -surf_A lh.smoothwm -surf_B lh.pial.asc -hits 1 -input lhpatch_1node.1D 0 1
05513       (divide answer of 2.219910 by 3 (0.73997), difference at 3rd signifcant digit from SM_out_1node.1D of 0.731866)
05514    
05515    for a box:
05516       SurfMeasures -func node_vol -spec RectPly.spec -surf_A RectSurf.ply -surf_B RectSurf2.ply -nodes_1D RectAllPatch.1D'[1]' -out_1D SM_out_Rect.1D
05517       SurfPatch -spec RectPly.spec -surf_A RectSurf.ply -surf_B RectSurf2.ply -hits 3 -input RectAllPatch.1D 0 1 -vol_only
05518         
05519 */
05520 double SUMA_Pattie_Volume (SUMA_SurfaceObject *SO1, SUMA_SurfaceObject *SO2, int *Nodes, int N_Node, SUMA_SurfaceObject *UseThisSO, int minPatchHits)
05521 {
05522    static char FuncName[]={"SUMA_Pattie_Volume"};
05523    double Vol = 0.0;
05524    int N_ContEdges=0, i,  i3, n, NodesPerPatch, *NewIndex = NULL, inew3, cnt, n1, n2, trouble;
05525    SUMA_PATCH *P1 = NULL;
05526    FILE *fid=NULL;
05527    SUMA_CONTOUR_EDGES *CE = NULL;
05528    SUMA_SurfaceObject *SOc = NULL;
05529    SUMA_SURF_NORM SN;
05530    SUMA_Boolean LocalHead = NOPE;
05531    
05532    SUMA_ENTRY;
05533    
05534    if (!SO1 || !SO2 || !Nodes || !N_Node) {
05535       SUMA_SL_Err("Bad input.");
05536       SUMA_RETURN(Vol);
05537    }
05538    if (SO1->N_Node != SO2->N_Node || SO1->N_FaceSet != SO2->N_FaceSet) {
05539       SUMA_SL_Err("Surfaces Not Isotopic");
05540       SUMA_RETURN(Vol);
05541    }
05542    
05543    /* form the patch */
05544    SUMA_LH("Forming patch...");
05545    P1 = SUMA_getPatch (Nodes, N_Node, SO1->FaceSetList, SO1->N_FaceSet, SO1->MF, minPatchHits);
05546    if (!P1) {
05547       SUMA_SL_Err("Failed to create patches.\n");
05548       SUMA_RETURN(Vol);
05549    }
05550    if (!P1->N_FaceSet) {
05551       SUMA_SL_Err("No patch could be formed");
05552       SUMA_RETURN(Vol);
05553    }
05554    /* form the contour */
05555    SUMA_LH("Forming contour...");
05556    CE = SUMA_GetContour (SO1, Nodes, N_Node, &N_ContEdges, 1, P1);
05557    if (!N_ContEdges) {
05558       SUMA_SL_Err("No contour edges found.\n"
05559                   "It looks like patches form\n"
05560                   "closed surfaces.\n");
05561       SUMA_RETURN(Vol);
05562    }
05563    if (LocalHead) {
05564       fprintf(SUMA_STDERR,"%s:\n Found %d contour segments.\n", FuncName, N_ContEdges);
05565    }
05566    
05567    /* create a mapping from old numbering scheme to new one */
05568    SUMA_LH("Creating Mapping Index...");
05569    NewIndex = (int *)SUMA_malloc(SO1->N_Node * sizeof(int));
05570    if (!NewIndex) {
05571       SUMA_SL_Crit("Failed to allocate for NewIndex");
05572       SUMA_RETURN(Vol);
05573    }
05574    SUMA_INIT_VEC(NewIndex, SO1->N_Node, -1, int);
05575    NodesPerPatch = 0;
05576    for (i=0; i < P1->N_FaceSet; ++i) {
05577       i3 = 3*i;
05578       n = P1->FaceSetList[i3];   if (NewIndex[n] < 0) { NewIndex[n] = NodesPerPatch; ++NodesPerPatch; }   
05579       n = P1->FaceSetList[i3+1]; if (NewIndex[n] < 0) { NewIndex[n] = NodesPerPatch; ++NodesPerPatch; }   
05580       n = P1->FaceSetList[i3+2]; if (NewIndex[n] < 0) { NewIndex[n] = NodesPerPatch; ++NodesPerPatch; }
05581    }
05582    if (LocalHead) {
05583       fprintf(SUMA_STDERR,"%s:\n"
05584                   "Number of nodes in patch (%d), in N_Node (%d)\n"
05585                   , FuncName, NodesPerPatch, N_Node);
05586    }
05587    if (NodesPerPatch != N_Node) {
05588       fprintf(SUMA_STDERR, "Note:\n"
05589                            "Have %d nodes in patch, %d nodes in input.\n", NodesPerPatch, N_Node);
05590    }
05591    
05592    /* Building composite surface */
05593    SUMA_LH("Building composite surface...");
05594    if (UseThisSO) { 
05595       SOc = UseThisSO;
05596       if (SOc->NodeList || SOc->FaceSetList) {
05597          SUMA_SL_Err("You want me to use a filled SurfaceObject structure!\n"
05598                      "How rude!");
05599          SUMA_RETURN(Vol);
05600       }
05601    } else {
05602       SOc = SUMA_Alloc_SurfObject_Struct(1);
05603    }
05604    SOc->N_Node = NodesPerPatch*2;
05605    SOc->N_FaceSet = P1->N_FaceSet*2+2*N_ContEdges;
05606    SOc->NodeDim = 3;
05607    SOc->FaceSetDim = 3;
05608    SOc->NodeList = (float *)SUMA_malloc(SOc->NodeDim*SOc->N_Node*sizeof(float));
05609    SOc->FaceSetList = (int *)SUMA_malloc(SOc->FaceSetDim*SOc->N_FaceSet*sizeof(int));
05610    /* first create the NodeList from S01 && SO2*/
05611    for (i=0; i<SO1->N_Node; ++i) {
05612       if (NewIndex[i] >=0) { /* this node is used */
05613          i3 = 3*i;
05614          inew3 = 3 * NewIndex[i];
05615          SOc->NodeList[inew3  ] = SO1->NodeList[i3  ];
05616          SOc->NodeList[inew3+1] = SO1->NodeList[i3+2];
05617          SOc->NodeList[inew3+2] = SO1->NodeList[i3+1];
05618          inew3 = 3 * (NewIndex[i]+NodesPerPatch);
05619          SOc->NodeList[inew3  ] = SO2->NodeList[i3  ];
05620          SOc->NodeList[inew3+1] = SO2->NodeList[i3+2];
05621          SOc->NodeList[inew3+2] = SO2->NodeList[i3+1];
05622       }
05623    }
05624    /* Now add the pre-existing patches */
05625    cnt = 0;
05626    for (i=0; i<P1->N_FaceSet; ++i) {
05627       i3 = 3*i;
05628       n = P1->FaceSetList[i3  ]; SOc->FaceSetList[cnt] = NewIndex[n]; ++cnt;
05629       n = P1->FaceSetList[i3+1]; SOc->FaceSetList[cnt] = NewIndex[n]; ++cnt;
05630       n = P1->FaceSetList[i3+2]; SOc->FaceSetList[cnt] = NewIndex[n]; ++cnt;                     
05631    }
05632    for (i=0; i<P1->N_FaceSet; ++i) { /* Now for SO2's */
05633       i3 = 3*i;
05634       n = P1->FaceSetList[i3  ]; SOc->FaceSetList[cnt] = NewIndex[n]+NodesPerPatch; ++cnt;
05635       n = P1->FaceSetList[i3+1]; SOc->FaceSetList[cnt] = NewIndex[n]+NodesPerPatch; ++cnt;
05636       n = P1->FaceSetList[i3+2]; SOc->FaceSetList[cnt] = NewIndex[n]+NodesPerPatch; ++cnt;                     
05637    }
05638    
05639    /* Now you need to add the stitches, for each segment you'll need 2 triangles*/
05640    for (i=0; i<N_ContEdges; ++i) {
05641       n1 = NewIndex[CE[i].n1]; n2 = NewIndex[CE[i].n2];
05642       SOc->FaceSetList[cnt] = n1; ++cnt;
05643       SOc->FaceSetList[cnt] = n2; ++cnt;
05644       SOc->FaceSetList[cnt] = n2+NodesPerPatch; ++cnt;
05645       SOc->FaceSetList[cnt] = n1; ++cnt;
05646       SOc->FaceSetList[cnt] = n2+NodesPerPatch; ++cnt;
05647       SOc->FaceSetList[cnt] = n1+NodesPerPatch; ++cnt;
05648    }
05649    
05650    /* calculate EdgeList */
05651    if (!SUMA_SurfaceMetrics_eng(SOc, "EdgeList", NULL, 0, SUMAg_CF->DsetList)){
05652       SUMA_SL_Err("Failed to create EdgeList");
05653       SUMA_RETURN(Vol);
05654    }
05655    
05656    /* make sure that's a closed surface */
05657    if (SOc->EL->max_N_Hosts != 2 || SOc->EL->min_N_Hosts != 2) {
05658       SUMA_SL_Err("Created surface is not a closed one.\n"
05659                   "Or patches have tessellation problems.");
05660       SUMA_RETURN(Vol);
05661    }
05662    
05663    /* fix the winding */
05664    if (!SUMA_MakeConsistent(SOc->FaceSetList, SOc->N_FaceSet, SOc->EL, 0, &trouble)) {
05665       SUMA_SL_Err("Failed to make surface consistent");
05666       SUMA_RETURN(Vol);
05667    }
05668    
05669    /* Now calculate FaceSetNormals and triangle areas*/
05670    SN = SUMA_SurfNorm(SOc->NodeList,  SOc->N_Node, SOc->FaceSetList, SOc->N_FaceSet );
05671    SOc->NodeNormList = SN.NodeNormList;
05672    SOc->FaceNormList = SN.FaceNormList;
05673 
05674    if (!SUMA_SurfaceMetrics_eng(SOc, "PolyArea", NULL, 0, SUMAg_CF->DsetList)){
05675       SUMA_SL_Err("Failed to create EdgeList");
05676       SUMA_RETURN(Vol);
05677    }
05678       
05679    /* debug */
05680    if (LocalHead) {
05681       fid = fopen("Junk_NodeList.1D", "w");
05682       SUMA_disp_vecmat (SOc->NodeList, SOc->N_Node, SOc->NodeDim, 1, SUMA_ROW_MAJOR, fid, NOPE);
05683       fclose(fid);
05684       fid = fopen("Junk_FaceSetList.1D", "w");
05685       SUMA_disp_vecdmat(SOc->FaceSetList, SOc->N_FaceSet, SOc->FaceSetDim, 1, SUMA_ROW_MAJOR, fid , NOPE);
05686       fclose(fid);
05687    }
05688    
05689    /* calculate the volume */
05690    SUMA_LH("Calculating volume");
05691    Vol = SUMA_Mesh_Volume(SOc, NULL, -1);
05692    if (LocalHead) {
05693       fprintf (SUMA_STDERR,"%s:\n"
05694                            "Volume = %f\n", FuncName, Vol);
05695    }
05696    
05697    /* cleanup */
05698    SUMA_LH("Cleanup");
05699    if (P1) SUMA_freePatch(P1); P1 = NULL;
05700    if (NewIndex) SUMA_free(NewIndex); NewIndex = NULL;
05701    if (SOc != UseThisSO) SUMA_Free_Surface_Object(SOc); SOc = NULL;
05702    if (CE) SUMA_free(CE); CE=NULL;
05703    
05704    SUMA_RETURN(Vol);
05705 }
05706 
05707 /*!
05708    \brief Calculate the volume of a mesh per the
05709    method in Hughes, S.W. et al. Phys. Med. Biol. 1996
05710    
05711    Tested with Icosahedron surface (see direct vol computation in CreateIcosahedron)
05712    
05713    Tested with tetrahedron below, which has a volume of 0.166667
05714    Also, the same volume was obtained with a rotated version of the same tetrahedron
05715 TetraFaceSetList.1D
05716 0 1 2
05717 0 3 1
05718 0 2 3
05719 2 1 3
05720 
05721 TetraNodeList.1D
05722 0 0 0
05723 1 0 0
05724 0 1 0
05725 1 1 1
05726 which should have a volume of (1/3)A*h of 1/3 * 0.5 * 1
05727    
05728    Volume of smoothwm surface did not change if surface was rotated and shifted, 
05729    a very good thing.
05730 
05731 If you try to calculate the volume of a boxe's surface with the box's axes 
05732 in alignment with the X, Y and Z directions, you will get nan for an answer
05733 because the sum of the weights is ~= 0 . If you rotate the surface, the problem
05734 will be solved. 
05735 
05736    - It is extremely important that the surface mesh be consistently defined. 
05737    - Detecting consistency must be done ahead of time (need program to do that) 
05738    because doing it here will slow the computations a lot and consistency fix 
05739    requires recalculating the normals and all that depends on them (quite a bit).
05740     
05741 */
05742 
05743 double SUMA_Mesh_Volume(SUMA_SurfaceObject *SO, int *FSI, int N_FaceSet) 
05744 {
05745    static char FuncName[]={"SUMA_Mesh_Volume"};
05746    double Vol = 0.0, c[3], anx, any, anz, sx, sy, sz, kx, ky, kz, kt;
05747    float *pa = NULL;
05748    int i, fc;
05749    SUMA_Boolean LocalHead = NOPE;
05750    
05751    SUMA_ENTRY;
05752    
05753    if (!SO) { SUMA_SL_Err("NULL SO"); SUMA_RETURN(Vol);  }
05754    if (!SO->FaceNormList) { SUMA_SL_Err("NULL SO->FaceNormList"); SUMA_RETURN(Vol);  }
05755    if (!SO->PolyArea) { 
05756       if (!SUMA_SurfaceMetrics_eng (SO, "PolyArea", NULL, 0, SUMAg_CF->DsetList)) {
05757          SUMA_SL_Err("Failed to compute SO->PolyArea"); SUMA_RETURN(Vol);  
05758       }
05759    }
05760    pa = SO->PolyArea;
05761    
05762    if (FSI || N_FaceSet != -1) {
05763       SUMA_SL_Err("FSI and N_FaceSet are two stupid options never to be used.\nUse NULL and -1, respectively.");
05764       SUMA_RETURN(Vol);
05765    }
05766    
05767    if (!FSI) { 
05768       N_FaceSet = SO->N_FaceSet;    
05769    }
05770 
05771    
05772    /* calculate vector of areas * normals */
05773    kx = ky = kz = sx = sy = sz = 0.0;
05774    for (i=0; i<N_FaceSet; ++i) {
05775       if (FSI) fc = FSI[i];
05776       else fc = i;
05777       SUMA_FACE_CENTROID(SO, fc, c);
05778       #if 0 
05779          if (LocalHead) fprintf(SUMA_STDERR,"Area: %f , normal (%f, %f, %f)\n", 
05780                pa[fc], SO->FaceNormList[3*fc], SO->FaceNormList[3*fc+1], SO->FaceNormList[3*fc+2]);
05781       #endif
05782       anx = pa[fc] * SO->FaceNormList[3*fc];   kx += anx;  sx += c[0] * anx;
05783       any = pa[fc] * SO->FaceNormList[3*fc+1]; ky += any;  sy += c[1] * any;
05784       anz = pa[fc] * SO->FaceNormList[3*fc+2]; kz += anz;  sz += c[2] * anz;
05785    }
05786    kt = (kx+ky+kz); /* need to normalize k so that sum is 1, kx, ky and kz are supposed to 
05787                   "weight the volume according to its orientation relative to the axes."
05788                   For a sphere, you can use 1/3 for kx, ky and kz...   */
05789    if (fabs(kt) < 1e-15) { 
05790       SUMA_SL_Warn("Weight constants sum to ~= 0.\n"
05791                    "Volume measurements may be off.\n"
05792                    "If your surface's axes are along\n"
05793                    "the X, Y and Z directions, as you \n"
05794                    "could have with a box's surface, rotating\n"
05795                    "the surface will solve the problem.");
05796       fprintf(SUMA_STDERR, "%s:\n"
05797                            "kx + ky + kz = kt\n"
05798                            "%f + %f + %f = %f\n"
05799                            "sx, sy, sz = %f, %f, %f\n",
05800                            FuncName, kx, ky, kz, kx+ky+kz, sx, sy, sz);   }
05801    kx /= kt;
05802    ky /= kt;
05803    kz /= kt;
05804    if (LocalHead) {
05805       fprintf(SUMA_STDERR, "%s:\n"
05806                            "%f + %f + %f = %f\n"
05807                            "sx, sy, sz = %f, %f, %f\n",
05808                            FuncName, kx, ky, kz, kx+ky+kz, sx, sy, sz);
05809    }
05810    Vol = kx * sx + ky *sy + kz * sz;
05811    
05812    SUMA_RETURN(Vol);
05813 }
05814 
05815 /*!
05816    \brief computes the total area of a surface.
05817    NOTE: This function will replace whatever values you have in SO->PolyArea.
05818    If SO->PolyArea is NULL, it will remain that way.
05819 */
05820 double SUMA_Mesh_Area(SUMA_SurfaceObject *SO, int *FaceSets, int N_FaceSet) 
05821 {
05822    static char FuncName[]={"SUMA_Mesh_Area"};
05823    double A = 0.0, a = 0.0;
05824    int i, i3;
05825    float *n0, *n1, *n2;
05826    SUMA_Boolean LocalHead = NOPE;
05827    
05828    SUMA_ENTRY;
05829 
05830    if (!SO) { SUMA_SL_Err("NULL SO"); SUMA_RETURN(A);  }
05831    if (!SO->FaceSetList) { SUMA_SL_Err("NULL SO->FaceSetList"); SUMA_RETURN(A);  }
05832       
05833    if (!FaceSets ) { 
05834       if (N_FaceSet != -1) {
05835          SUMA_SL_Err("With NULL FaceSets, use -1 for N_FaceSet");
05836          SUMA_RETURN(A);
05837       }
05838       N_FaceSet = SO->N_FaceSet; 
05839       FaceSets = SO->FaceSetList;    
05840    }else {
05841       if (N_FaceSet < 0) {
05842          SUMA_SL_Err("N_FaceSet < 0");
05843          SUMA_RETURN(A);
05844       }
05845    }
05846 
05847    A = 0.0;
05848    if (SO->PolyArea) {
05849       for (i=0;  i<N_FaceSet; ++i) {
05850          i3 = 3*i;
05851          n0 = &(SO->NodeList[3*FaceSets[i3]]);
05852          n1 = &(SO->NodeList[3*FaceSets[i3+1]]);
05853          n2 = &(SO->NodeList[3*FaceSets[i3+2]]);
05854          SUMA_TRI_AREA( n0, n1, n2, a);
05855          SO->PolyArea[i] = (float)a;
05856          A += a;
05857       }
05858    } else {
05859       for (i=0;  i<N_FaceSet; ++i) {
05860          i3 = 3*i;
05861          n0 = &(SO->NodeList[3*FaceSets[i3]]);
05862          n1 = &(SO->NodeList[3*FaceSets[i3+1]]);
05863          n2 = &(SO->NodeList[3*FaceSets[i3+2]]);
05864          SUMA_TRI_AREA( n0, n1, n2, a);
05865          A += a;
05866       }
05867    }
05868    if (LocalHead) {
05869       fprintf(SUMA_STDERR,"%s:\n   A = %f\n", FuncName, A);
05870    }
05871    SUMA_RETURN(A);
05872 }
05873 /*!
05874  
05875 From File : Plane_Equation.c
05876 Author : Ziad Saad
05877 Date : Thu Nov 19 14:55:54 CST 1998
05878  
05879 \brief   finds the plane passing through 3 points
05880  
05881  
05882 Usage : 
05883       Eq = SUMA_Plane_Equation (P1, P2, P3, thisEq)
05884  
05885  
05886    \param P1 (float *) 1x3 vector Coordinates of Point1
05887    \param P2 (float *) 1x3 vector Coordinates of Point2
05888    \param P3 (float *) 1x3 vector Coordinates of Point3
05889    \param thisEq (float *) use this pointer to store Eq rather than create a new one (set to NULL if you want a new one)
05890    \return Eq (float *) 1x4 vector containing the equation of the plane containing the three points.
05891          The equation of the plane is : 
05892          Eq[0] X + Eq[1] Y + Eq[2] Z + Eq[3] = 0
05893    
05894          If the three points are colinear, Eq = [0 0 0 0]
05895  
05896 */
05897 float * SUMA_Plane_Equation (float * P1, float *P2, float *P3, float *usethisEq)
05898 {/*SUMA_Plane_Equation*/
05899    float *Eq;
05900    static char FuncName[] = {"SUMA_Plane_Equation"}; 
05901     
05902    SUMA_ENTRY;
05903    if (usethisEq) Eq = usethisEq;
05904    else Eq = (float *) SUMA_calloc(4,sizeof(float));
05905    if (!Eq)
05906       {
05907          fprintf (SUMA_STDERR, "Error %s: Failed to allocate.\n", FuncName);
05908          SUMA_RETURN (NULL);
05909       }
05910    
05911    Eq[0] = P1[1] * (P2[2]-P3[2]) 
05912          + P2[1] * (P3[2]-P1[2]) 
05913          + P3[1] * (P1[2]-P2[2]);
05914          
05915    Eq[1] = P1[2] * (P2[0]-P3[0]) 
05916          + P2[2] * (P3[0]-P1[0]) 
05917          + P3[2] * (P1[0]-P2[0]);
05918          
05919    Eq[2] = P1[0] * (P2[1]-P3[1]) 
05920          + P2[0] * (P3[1]-P1[1]) 
05921          + P3[0] * (P1[1]-P2[1]);
05922          
05923    Eq[3] =  - P1[0] * (P2[1] * P3[2] - P3[1] * P2[2]) 
05924             - P2[0] * (P3[1] * P1[2] - P1[1] * P3[2]) 
05925             - P3[0] * (P1[1] * P2[2] - P2[1] * P1[2]);
05926 
05927    SUMA_RETURN (Eq);
05928 }/*SUMA_Plane_Equation*/
05929 
05930 /*! 
05931  
05932 \brief Determines the intersection of a plane and a surface 
05933 
05934  
05935    
05936    SPI = SUMA_Surf_Plane_Intersect (SO, PlaneEq)
05937 
05938 \param SO (SUMA_SurfaceObject *) Pointer to surface object structure.
05939 \param  PlaneEq (float *) : 4x1 vector containing the 4 coefficients of the equation 
05940                      of the plane to intersect the surface
05941                      PlaneEq[0] X + PlaneEq[1] Y + PlaneEq[2] Z + PlaneEq[3] = 0
05942 \return SPI (SUMA_SURF_PLANE_INTERSECT *) Pointer to intersection structure. See help on fields in SUMA_define.h
05943                                              NULL in case of error.
05944      
05945  
05946 
05947 \sa an older matlab version in Surf_Plane_Intersect_2.m  
05948 \sa SUMA_PlaneEq
05949 \sa SUMA_Allocate_SPI
05950 \sa SUMA_free_SPI
05951     
05952    
05953 */
05954  
05955 SUMA_SURF_PLANE_INTERSECT *SUMA_Surf_Plane_Intersect (SUMA_SurfaceObject *SO, float *PlaneEq)
05956 {/*SUMA_Surf_Plane_Intersect*/
05957    static char FuncName[]={"SUMA_Surf_Plane_Intersect"};
05958    int  i, k , k3, i3, n1, n2;
05959    float DT_ABVBEL, DT_POSNEG, u;
05960    float *NodePos;
05961    SUMA_SURF_PLANE_INTERSECT *SPI;
05962    struct  timeval start_time, start_time2;
05963    SUMA_Boolean LocalHead = NOPE;
05964    SUMA_Boolean  Hit;
05965    
05966    SUMA_ENTRY;
05967    
05968    /* Start timer for next function */
05969    SUMA_etime(&start_time2,0);
05970       
05971    if (LocalHead)   fprintf(SUMA_STDERR, "%s : Determining intersecting segments ...\n", FuncName);
05972    
05973    /* allocate for the return structure.
05974    NOTE: If (in a different form of this function) you do not allocate for SPI each time you call the function, make sure you reset all 
05975    elements of the following vector fields: 
05976    IsTriHit[] = NOPE;
05977    TriBranch[] = 0
05978    */
05979    SPI = SUMA_Allocate_SPI (SO);
05980    if (!SPI) {
05981       fprintf (SUMA_STDERR, "Error %s: Failed in SUMA_Allocate_SPI\n", FuncName);
05982       SUMA_RETURN (SPI);
05983    }
05984    
05985    /* allocate for temporary stuff */
05986    NodePos = (float *) SUMA_calloc (SO->N_Node , sizeof(float));
05987 
05988    if (!NodePos )
05989       {
05990          fprintf (SUMA_STDERR, "Error %s: Could not allocate in SUMA_Surf_Plane_Intersect\n", FuncName);
05991          SUMA_free_SPI (SPI); SPI = NULL;
05992          SUMA_RETURN (SPI);
05993       }
05994 
05995       
05996    /* Start timer for next function */
05997    SUMA_etime(&start_time,0);
05998                            
05999    /* Find out which nodes are above and which are below the plane */
06000    for (k=0;k<SO->N_Node; ++k)
06001       {
06002          k3 = 3*k;
06003          NodePos[k] = PlaneEq[0] * SO->NodeList[k3] + PlaneEq[1] * SO->NodeList[k3+1] 
06004                      +PlaneEq[2] * SO->NodeList[k3+2] + PlaneEq[3] ;
06005       }
06006       
06007    /* stop timer */
06008    DT_ABVBEL = SUMA_etime(&start_time,1);
06009                               
06010    
06011    /*
06012       NodePos is < 0 for nodes below the plane and > 0 for points above the plane
06013       Go through each connection and determine if it intersects the plane
06014       If a segment intersects the surface, it means that the sign
06015       of p  would be <= 0 (each point is on a different side of the plane)
06016    */
06017    
06018    /* Start timer for next function */
06019    SUMA_etime(&start_time,0);
06020    
06021    /*
06022       Determine the segments intersecting the surface,
06023       The triangles that contain these segments.
06024       The nodes that form the intersected segments 
06025    */
06026    SPI->N_IntersEdges = 0;
06027    SPI->N_IntersTri = 0;
06028    SPI->N_NodesInMesh = 0;
06029    k=0; 
06030    Hit = NOPE;
06031    while (k < SO->EL->N_EL)
06032       {
06033          /* find out if segment intersects */
06034          /* if (SUMA_IS_NEG(NodePos[SO->EL->EL[k][0]] * NodePos[SO->EL->EL[k][1]])) { *//* can speed this check by explicitly checking for sign difference: 
06035                                                                                     if (SUMA_SIGN(a) != SUMA_SIGN(b)) ... */   
06036          if (SUMA_SIGN(NodePos[SO->EL->EL[k][0]]) != SUMA_SIGN(NodePos[SO->EL->EL[k][1]]) ) {
06037             Hit = YUP;
06038             /* find the intersection point in that segment */
06039             u = -NodePos[SO->EL->EL[k][0]] / (NodePos[SO->EL->EL[k][1]] - NodePos[SO->EL->EL[k][0]]);
06040             i3 = 3 * k;
06041             n1 = 3 * SO->EL->EL[k][0];
06042             n2 = 3 * SO->EL->EL[k][1];
06043             
06044             SPI->IntersNodes[i3] = SO->NodeList[n1] + u * ( SO->NodeList[n2] - SO->NodeList[n1] ); ++i3; ++n2; ++n1;
06045             SPI->IntersNodes[i3] = SO->NodeList[n1] + u * ( SO->NodeList[n2] - SO->NodeList[n1] ); ++i3; ++n2; ++n1;
06046             SPI->IntersNodes[i3] = SO->NodeList[n1] + u * ( SO->NodeList[n2] - SO->NodeList[n1] ); ++i3; ++n2; ++n1;
06047             
06048             /* 
06049             fprintf (SUMA_STDERR,"%s: Edge %d, IntersNodes[%d]= [%f, %f, %f]\n", 
06050                FuncName, k, 3*k, SPI->IntersNodes[3*k], SPI->IntersNodes[3*k+1], SPI->IntersNodes[3*k+2]);
06051             */
06052                
06053             /* Store the intersected segment */
06054             SPI->IntersEdges[SPI->N_IntersEdges] = k;
06055             ++SPI->N_IntersEdges;
06056             
06057             /* mark this segment in the boolean vector to speed up some other functions */
06058             SPI->isEdgeInters[k] = YUP;
06059                   
06060             /* Store the index of the triangle hosting this edge*/
06061             if (!SPI->isTriHit[SO->EL->ELps[k][1]]) {
06062                SPI->IntersTri[SPI->N_IntersTri] = SO->EL->ELps[k][1];
06063                ++(SPI->N_IntersTri);
06064                SPI->isTriHit[SO->EL->ELps[k][1]] = YUP;
06065             }
06066             
06067             /* mark the nodes forming the intersection edges */
06068             if (!SPI->isNodeInMesh[SO->EL->EL[k][0]]) {
06069                SPI->isNodeInMesh[SO->EL->EL[k][0]] = YUP;
06070                ++(SPI->N_NodesInMesh);
06071             }
06072             if (!SPI->isNodeInMesh[SO->EL->EL[k][1]]) {
06073                SPI->isNodeInMesh[SO->EL->EL[k][1]] = YUP;
06074                ++(SPI->N_NodesInMesh);
06075             } 
06076          } else {
06077             Hit = NOPE;
06078          }
06079             
06080             /*  skip ahead of duplicate edge listings */
06081             if (SO->EL->ELps[k][2] > 0) {
06082                if (Hit) { /* you must mark these triangles */
06083                   i3 = 3 * k;
06084                   for (i=1; i < SO->EL->ELps[k][2]; ++i) {
06085                      SPI->isEdgeInters[k+i] = YUP;
06086                      n1 = 3 * (k+i);
06087                      SPI->IntersNodes[n1] = SPI->IntersNodes[i3]; ++i3; ++n1;
06088                      SPI->IntersNodes[n1] = SPI->IntersNodes[i3]; ++i3; ++n1;
06089                      SPI->IntersNodes[n1] = SPI->IntersNodes[i3]; ++i3; ++n1;
06090                      /*
06091                      fprintf (SUMA_STDERR,"%s: Edge %d, IntersNodes[%d]= [%f, %f, %f]\n", 
06092                         FuncName, k+i, n1, SPI->IntersNodes[3*(k+i)], SPI->IntersNodes[3*(k+i)+1], SPI->IntersNodes[3*(k+i)+2]);
06093                      */
06094                      if (!SPI->isTriHit[SO->EL->ELps[k+i][1]]) {
06095                         SPI->IntersTri[SPI->N_IntersTri] = SO->EL->ELps[k+i][1];
06096                         ++(SPI->N_IntersTri);
06097                         SPI->isTriHit[SO->EL->ELps[k+i][1]] = YUP;   
06098                      }
06099                   }
06100                }
06101                k += SO->EL->ELps[k][2];
06102             } else ++k;
06103       }
06104             
06105       
06106    /* stop timer */
06107    DT_POSNEG = SUMA_etime(&start_time,1);
06108    
06109    if (LocalHead) fprintf (SUMA_STDERR, "%s: Found %d intersect segments, %d intersected triangles, %d nodes in mesh (exec time %f + %f = %f secs).\n", 
06110       FuncName, SPI->N_IntersEdges, SPI->N_IntersTri, SPI->N_NodesInMesh, DT_ABVBEL, DT_POSNEG, DT_ABVBEL + DT_POSNEG);
06111    
06112 /* free locally allocated memory */
06113 if (NodePos) SUMA_free (NodePos);
06114    
06115 
06116 SUMA_RETURN (SPI);
06117 }/*SUMA_Surf_Plane_Intersect*/
06118 
06119 /*!
06120    \brief a wrapper function for SUMA_Surf_Plane_Intersect that returns the intersection 
06121    in the form of an ROI datum
06122    
06123    \param SO (SUMA_SurfaceObject *)
06124    \param Nfrom (int) index of node index on SO from which the path should begin
06125    \param Nto (int) index of node index on SO where the path will end
06126    \param P (float *) XYZ of third point that will form the cutting plane with Nfrom and Nto's coordinates
06127             This point is usually, the Near Plane clipping point of Nto's picking line.
06128    \return ROId (SUMA_ROI_DATUM *) pointer to ROI datum structure which contains the NodePath from Nfrom to Nto
06129    along with other goodies.      
06130 */
06131 SUMA_ROI_DATUM *SUMA_Surf_Plane_Intersect_ROI (SUMA_SurfaceObject *SO, int Nfrom, int Nto, float *P)
06132 {
06133    static char FuncName[]={"SUMA_Surf_Plane_Intersect_ROI"};
06134    SUMA_ROI_DATUM *ROId=NULL;
06135    SUMA_Boolean LocalHead = NOPE;
06136    int N_left;
06137    SUMA_SURF_PLANE_INTERSECT *SPI = NULL;
06138    SUMA_ROI *ROIe = NULL, *ROIt = NULL, *ROIn = NULL, *ROIts = NULL;
06139    float *Eq = NULL;
06140    /* The 3 flags below are for debugging. */
06141    SUMA_Boolean DrawIntersEdges=NOPE; /* Draw edges intersected by plane   */
06142    SUMA_Boolean DrawIntersTri = NOPE; /* Draw triangles intersected by plane */
06143    SUMA_Boolean DrawIntersNodeStrip = NOPE; /* Draw intersection node strip which is the shortest path between beginning and ending nodes */ 
06144    SUMA_Boolean DrawIntersTriStrip=NOPE; /* Draw intersection triangle strip which is the shortest path between beginning and ending nodes */
06145    
06146    SUMA_ENTRY;
06147    
06148    /* computing plane's equation */
06149    Eq = SUMA_Plane_Equation ( &(SO->NodeList[3*Nfrom]), 
06150                               P,
06151                               &(SO->NodeList[3*Nto]) , NULL);
06152    
06153    if (!Eq) {
06154       fprintf(SUMA_STDOUT,"Error %s: Failed in SUMA_Plane_Equation.\n", FuncName);
06155       SUMA_RETURN(ROId);
06156    } 
06157    
06158    if (LocalHead) fprintf (SUMA_STDERR, "%s: Computing Intersection with Surface.\n", FuncName);
06159    SPI = SUMA_Surf_Plane_Intersect (SO, Eq);
06160    if (!SPI) {
06161       fprintf(SUMA_STDOUT,"Error %s: Failed in SUMA_Surf_Plane_Intersect.\n", FuncName);
06162       SUMA_RETURN(ROId);
06163    }
06164    
06165    if (DrawIntersEdges) {
06166       /* Show all intersected edges */
06167       ROIe =  SUMA_AllocateROI (SO->idcode_str, SUMA_ROI_EdgeGroup, "SurfPlane Intersection - Edges", SPI->N_IntersEdges, SPI->IntersEdges);
06168       if (!ROIe) {
06169          fprintf(SUMA_STDERR,"Error %s: Failed in SUMA_AllocateROI.\n", FuncName);
06170       } else {
06171          if (!SUMA_AddDO (SUMAg_DOv, &SUMAg_N_DOv, (void *)ROIe, ROIO_type, SUMA_LOCAL)) {
06172             fprintf(SUMA_STDERR,"Error %s: Failed in SUMA_AddDO.\n", FuncName);
06173          }
06174       }
06175    }
06176    
06177    if (DrawIntersTri) {
06178       /* Show all intersected triangles */
06179       ROIt =  SUMA_AllocateROI (SO->idcode_str, SUMA_ROI_FaceGroup, "SurfPlane Intersection - Triangles", SPI->N_IntersTri, SPI->IntersTri);
06180       if (!ROIt) {
06181          fprintf(SUMA_STDERR,"Error %s: Failed in SUMA_AllocateROI.\n", FuncName);
06182       } else {
06183          if (!SUMA_AddDO (SUMAg_DOv, &SUMAg_N_DOv, (void *)ROIt, ROIO_type, SUMA_LOCAL)) {
06184             fprintf(SUMA_STDERR,"Error %s: Failed in SUMA_AddDO.\n", FuncName);
06185          }
06186       }
06187    }
06188 
06189    /* create ROId */
06190    ROId = SUMA_AllocROIDatum ();
06191    ROId->Type = SUMA_ROI_NodeSegment;
06192 
06193    /* calculate shortest path */
06194    N_left = SPI->N_NodesInMesh;
06195    ROId->nPath = SUMA_Dijkstra (SO, Nfrom, Nto, SPI->isNodeInMesh, &N_left, 1, &(ROId->nDistance), &(ROId->N_n));
06196    if (ROId->nDistance < 0 || !ROId->nPath) {
06197       fprintf(SUMA_STDERR,"\aError %s: Failed in fast SUMA_Dijkstra.\n*** Two points are not connected by intersection. Repeat last selection.\n", FuncName);
06198 
06199       /* clean up */
06200       if (SPI) SUMA_free_SPI (SPI); 
06201       SPI = NULL;
06202       if (ROId) SUMA_FreeROIDatum (ROId);
06203       SUMA_RETURN(NULL);   
06204    }
06205    
06206    if (LocalHead) fprintf (SUMA_STDERR, "%s: Shortest inter nodal distance along edges between nodes %d <--> %d (%d nodes) is %f.\n", 
06207       FuncName, Nfrom, Nto, ROId->N_n, ROId->nDistance);
06208    
06209 
06210    #if 0
06211    
06212       /* FOR FUTURE USAGE:
06213          When drawing on the surface, it is possible to end up with node paths for which the 
06214          triangle strip tracing routines fail. That's paretly because it is possible for these
06215          node paths to visit one node more than once, eg: ... 34 23 34 .... 
06216          That is OK for drawing purposes but not for say, making measurements on the surface.
06217       */
06218       
06219       /* calculate shortest path along the intersection of the plane with the surface */
06220       /* get the triangle path corresponding to shortest distance between Nx and Ny */
06221 
06222       /* Old Method: Does not result is a strip of triangle that is continuous or connected
06223       by an intersected edge. Function is left here for historical reasons. 
06224          tPath = SUMA_NodePath_to_TriPath_Inters (SO, SPI, Path, N_Path, &N_Tri); */
06225 
06226       /* you should not need to go much larger than NodeDist except when you are going for 
06227       1 or 2 triangles away where discrete jumps in d might exceed the limit. 
06228       Ideally, you want this measure to be 1.5 NodeDist or say, 15 mm, whichever is less.... */
06229 
06230       /* THIS SHOULD BE OPTIONAL */
06231       ROId->tPath = SUMA_IntersectionStrip (SO, SPI, ROId->nPath, ROId->N_n, &(ROId->tDistance), 2.5 *ROId->nDistance, &(ROId->N_t));                      
06232       if (!ROId->tPath) {
06233          fprintf(SUMA_STDERR,"Error %s: Failed in SUMA_IntersectionStrip. Proceeding\n", FuncName);
06234          /* do not stop here, it is OK if you can't find the triangle strip */
06235          /* if (ROId) SUMA_FreeROIDatum (ROId);
06236          SUMA_RETURN(NULL);   */
06237       } else {
06238          /* ROId->tPath has a potentially enourmous chunk of memory allocated for it. Trim the fat. */
06239          {
06240             int *tPath_tmp=NULL, i_tmp=0;
06241             tPath_tmp = (int *)SUMA_calloc (ROId->N_t, sizeof(int));
06242             if (!tPath_tmp) {
06243                SUMA_RegisterMessage (SUMAg_CF->MessageList, "Failed to allocate for tpath_tmp", FuncName, SMT_Critical, SMA_LogAndPopup);
06244                SUMA_RETURN(NULL);
06245             }
06246             for (i_tmp=0; i_tmp<ROId->N_t; ++i_tmp) tPath_tmp[i_tmp] = ROId->tPath[i_tmp];
06247             SUMA_free(ROId->tPath);
06248             ROId->tPath = tPath_tmp;
06249          } 
06250 
06251          fprintf (SUMA_STDERR, "%s: Shortest inter nodal distance along surface between nodes %d <--> %d is %f.\nTiangle 1 is %d\n", 
06252             FuncName, Nfrom, Nto, ROId->tDistance, ROId->tPath[0]);
06253 
06254          if (DrawIntersTriStrip) {
06255             /* Show intersected triangles, along shortest path */
06256             ROIts =  SUMA_AllocateROI (SO->idcode_str, SUMA_ROI_FaceGroup, "SurfPlane Intersection - Triangles- Shortest", ROId->N_t, ROId->tPath);
06257             if (!ROIts) {
06258                fprintf(SUMA_STDERR,"Error %s: Failed in SUMA_AllocateROI.\n", FuncName);
06259                if (ROIn) SUMA_freeROI(ROIn);
06260                if (ROIts) SUMA_freeROI(ROIts);
06261                if (ROId) SUMA_FreeROIDatum (ROId);
06262                SUMA_RETURN(NULL);   
06263             }
06264             if (!SUMA_AddDO (SUMAg_DOv, &SUMAg_N_DOv, (void *)ROIts, ROIO_type, SUMA_LOCAL)) {
06265                fprintf(SUMA_STDERR,"Error %s: Failed in SUMA_AddDO.\n", FuncName);
06266                if (ROIn) SUMA_freeROI(ROIn);
06267                if (ROIts) SUMA_freeROI(ROIts);
06268                if (ROId) SUMA_FreeROIDatum (ROId);
06269                SUMA_RETURN(NULL);
06270             }
06271          }
06272 
06273          if (ROId->nPath && DrawIntersNodeStrip) {
06274             #if 0
06275                /* Show me the Path */
06276                for (ii=0; ii < ROId->N_n; ++ii) fprintf(SUMA_STDERR," %d\t", ROId->nPath[ii]);
06277             #endif
06278 
06279             /* Show Path */
06280             ROIn =  SUMA_AllocateROI (SO->idcode_str, SUMA_ROI_NodeGroup, "SurfPlane Intersection - Nodes", ROId->N_n, ROId->nPath);
06281             if (!ROIn) {
06282                fprintf(SUMA_STDERR,"Error %s: Failed in SUMA_AllocateROI.\n", FuncName);
06283                if (ROIn) SUMA_freeROI(ROIn);
06284                if (ROIts) SUMA_freeROI(ROIts);
06285                if (ROId) SUMA_FreeROIDatum (ROId);
06286                SUMA_RETURN(NULL);
06287             }
06288             if (!SUMA_AddDO (SUMAg_DOv, &SUMAg_N_DOv, (void *)ROIn, ROIO_type, SUMA_LOCAL)) {
06289                fprintf(SUMA_STDERR,"Error %s: Failed in SUMA_AddDO.\n", FuncName);
06290                if (ROIn) SUMA_freeROI(ROIn);
06291                if (ROIts) SUMA_freeROI(ROIts);
06292                if (ROId) SUMA_FreeROIDatum (ROId);
06293                SUMA_RETURN(NULL);
06294             }
06295 
06296          }
06297       }                        
06298    #endif
06299    
06300    if (LocalHead) fprintf(SUMA_STDERR,"%s: Freeing Eq...\n", FuncName);
06301    if (Eq) SUMA_free(Eq);
06302 
06303    if (LocalHead) fprintf(SUMA_STDERR,"%s: Freeing SPI...\n", FuncName);
06304    if (SPI) SUMA_free_SPI (SPI);
06305    
06306    if (LocalHead) fprintf(SUMA_STDERR,"%s:Done Freeing...\n", FuncName);      
06307    
06308    SUMA_RETURN(ROId);
06309 }
06310 
06311 /*!
06312 
06313 \brief 
06314 SBv =  SUMA_AssignTriBranch (SO, SPI, Nx, BranchCount, DoCopy)
06315 \param SO (SUMA_SurfaceObject *) Pointer to Surface Object structure 
06316 \param SPI (SUMA_SURF_PLANE_INTERSECT *) Pointer to Surface Plane Intersection structure 
06317 \param Nx (int) Node index to start the first branch at. This parameter is optional. pass -1 if you do not want it set.
06318 \param BranchCount (int *) Pointer to the total number of branches found.
06319 \param DoCopy (SUMA_Boolean) flag indicating whether to preserve (YUP) the values in SPI->IntersEdges and SPI->N_IntersEdges or not (NOPE).
06320                              If you choose YUP, a copy of SPI->IntersEdges is made and manipulated. 
06321 \return Bv (SUMA_TRI_BRANCH*) Pointer to a vector of *BranchCount branches formed by the intersections. 
06322       A branch is formed by a series of connected triangles. A Branch can be a loop but that is not determined in this function.
06323       NULL if trouble is encountered. 
06324       On some surfaces with cuts you may have a branch split in two which requires welding. 
06325       No such thing is done here but a warning is printed out.
06326                   
06327 NOTE: The vector SPI->IntersEdges is modified by this function. 
06328 \sa SUMA_free_STB for freeing Bv
06329 */
06330 
06331 #define SUMA_MAX_BRANCHES 300
06332 
06333 /* assign a branch to each triangle intersected */
06334 SUMA_TRI_BRANCH* SUMA_AssignTriBranch (SUMA_SurfaceObject *SO, SUMA_SURF_PLANE_INTERSECT *SPI, 
06335                                        int Nx, int *BranchCount, SUMA_Boolean DoCopy)
06336 {
06337    static char FuncName[]={"SUMA_AssignTriBranch"};
06338    int *IntersEdgesCopy = NULL, N_IntersEdgesCopy, i_Branch, E1, kedge, i, 
06339          N_iBranch[SUMA_MAX_BRANCHES], NBlist[SUMA_MAX_BRANCHES], iBranch = 0, 
06340          N_Branch, Bcnt, ilist, j, ivisit, *VisitationOrder, TriCheck;
06341    SUMA_Boolean Local_IntersEdgesCopy = NOPE;
06342    int *TriBranch = NULL; /*!< Vector of SO->EL->N_EL / 3 elements but with only N_IntersTri meaningful values. If TriBranch[j] = b
06343                      then triangle j in SO->FaceSet is a member of Branch b. Branch numbering starts at 1 and may not be consecutive. 
06344                      A branch is a collection of connected triangles and may form a closed loop */
06345    SUMA_TRI_BRANCH *Bv = NULL;
06346    SUMA_Boolean LocalHead = NOPE;
06347    
06348    SUMA_ENTRY;
06349 
06350 
06351    i_Branch = 0;
06352    
06353    /* make a copy of SPI->N_IntersEdges since this vector will be modified and might be needed later, 
06354       might make that optional later and just copy pointers if IntersEdgesCopy is not needed elsewhere.
06355       IntersEdgesCopy flag is used to decide on freeing IntersEdgesCopy or not.*/
06356 
06357    VisitationOrder = (int *)SUMA_calloc (SO->N_FaceSet, sizeof (int)); /* keeps track of the order in which triangles are visited. This is used for creating branches with the proper sequence */
06358    TriBranch = (int *)SUMA_calloc (SO->EL->N_EL / 3,  sizeof(int));
06359    
06360    if (!VisitationOrder || !TriBranch) {
06361       fprintf (SUMA_STDERR, "Error %s: Failed to allocate.\n", FuncName);
06362       if (TriBranch) SUMA_free(TriBranch);
06363       if (VisitationOrder) SUMA_free(VisitationOrder);
06364       SUMA_RETURN (NULL);   
06365    }
06366    
06367    N_IntersEdgesCopy = SPI->N_IntersEdges;
06368    if (DoCopy) {
06369       IntersEdgesCopy = (int *) SUMA_calloc (N_IntersEdgesCopy, sizeof (int));
06370       Local_IntersEdgesCopy = YUP;
06371       for (i=0; i < N_IntersEdgesCopy; ++i) {
06372          IntersEdgesCopy[i] = SPI->IntersEdges[i];
06373       }
06374    }else {
06375       Local_IntersEdgesCopy = NOPE;
06376       IntersEdgesCopy = SPI->IntersEdges;
06377    }
06378 
06379    if (!IntersEdgesCopy) {
06380      fprintf (SUMA_STDERR, "Error %s: Failed to allocate for or receive IntersEdgesCopy.\n", FuncName);
06381      if (TriBranch) SUMA_free(TriBranch);
06382      if (VisitationOrder) SUMA_free(VisitationOrder);
06383      SUMA_RETURN (NULL);
06384    }
06385 
06386    ivisit = 0;
06387    while (N_IntersEdgesCopy) {
06388    
06389       if (!i_Branch && Nx >= 0) {
06390          /* start from edge containing Nx, this is only done at the starting point (i_Branch = 0) */
06391          E1 = -1;
06392          i=0;
06393          while (i < N_IntersEdgesCopy && E1 < 0) {
06394             if ( (SO->EL->EL[IntersEdgesCopy[i]][0] == Nx) || (SO->EL->EL[IntersEdgesCopy[i]][1] == Nx) ) {
06395                E1 = IntersEdgesCopy[i];
06396                kedge = i;
06397             }  
06398             ++i;
06399          }
06400       }else {
06401          /* no starting orders, start from any decent edge */
06402          /* find an edge with one hosting triangle */
06403          E1 = SUMA_Find_Edge_Nhost (SO->EL, IntersEdgesCopy, N_IntersEdgesCopy, &kedge, 1);
06404       }      
06405 
06406       if (E1 < 0) { /* no such edge found, take first edge in InInter */
06407             kedge = 0;
06408             E1 = IntersEdgesCopy[kedge];
06409             if (LocalHead) fprintf (SUMA_STDERR, "%s: No 1 host edge edge found.\n", FuncName);
06410       }else {
06411             if (LocalHead) fprintf (SUMA_STDERR, "%s: Found edge.\n", FuncName);
06412       }
06413       
06414       /* remove this edge from the list */
06415       --(N_IntersEdgesCopy);
06416       if (LocalHead) fprintf (SUMA_STDERR, "%s: kedge = %d, N_IntersEdgesCopy = %d.\n", FuncName, kedge, N_IntersEdgesCopy);
06417       IntersEdgesCopy[kedge] = IntersEdgesCopy[N_IntersEdgesCopy];
06418 
06419       /* start a new i_Branch - All i_Branch indices must be > 0*/
06420       ++i_Branch;   
06421       if (i_Branch > SUMA_MAX_BRANCHES-1) {
06422          fprintf (SUMA_STDERR, "Error %s: No more than %d branches allowed.\n", FuncName, SUMA_MAX_BRANCHES);
06423          SUMA_RETURN (NULL); 
06424       } 
06425       
06426       /* mark the triangle containing E1 */
06427       if (LocalHead) fprintf (SUMA_STDERR, "%s: Marking triangle %d with branch %d.\n", FuncName, SO->EL->ELps[E1][1], i_Branch);
06428       TriBranch[SO->EL->ELps[E1][1]] = i_Branch;
06429       VisitationOrder[ivisit] = SO->EL->ELps[E1][1]; ++ivisit;
06430       
06431       if (LocalHead) fprintf (SUMA_STDERR, "%s: Called recursive SUMA_Mark_Tri.\n", FuncName);
06432       if (!SUMA_Mark_Tri (SO->EL, E1, i_Branch, TriBranch, IntersEdgesCopy, &(N_IntersEdgesCopy), VisitationOrder, &ivisit)) {
06433          fprintf (SUMA_STDERR, "Error %s: Failed in SUMA_Mark_Tri.\n", FuncName);
06434       }
06435       if (LocalHead) fprintf (SUMA_STDERR, "%s: Returned from recursive SUMA_Mark_Tri.\n", FuncName);
06436 
06437       /* repeat till all edges are used up */
06438    }
06439 
06440    if (Local_IntersEdgesCopy) {
06441       if (LocalHead) fprintf (SUMA_STDERR, "%s: freeing IntersEdgesCopy.\n", FuncName);
06442       SUMA_free(IntersEdgesCopy); 
06443    }else {
06444       /* also change N_IntersEdges */
06445       SPI->N_IntersEdges = N_IntersEdgesCopy;
06446    }
06447 
06448    /* SUMA_disp_dvect (TriBranch, SO->N_FaceSet);  */
06449 
06450    N_Branch = i_Branch;
06451 
06452    /* determine the number of branch elements to allocate for - IDIOT PROOF, doing it in the recursive function SUMA_Mark_Tri was annoying*/
06453    for (i=0; i <= N_Branch; ++i) N_iBranch[i] = 0; /* remember, Branch numbering starts at 1 */
06454    if (LocalHead) fprintf (SUMA_STDERR, "%s: Searching all %d intersected triangles.\n", FuncName, SPI->N_IntersTri);
06455    Bcnt = 0;
06456    for (i=0; i < SO->N_FaceSet; ++i) {
06457       if (TriBranch[i]) {
06458          /* fprintf (SUMA_STDERR, "%d:%d\t", TriBranch[i], N_iBranch[TriBranch[i]]); */
06459          ++Bcnt;
06460          N_iBranch[TriBranch[i]] = N_iBranch[TriBranch[i]] + 1;
06461       }
06462    }
06463    
06464    #if 0
06465       fprintf (SUMA_STDERR, "Values in N_iBranch, idiot proof:\n");
06466       SUMA_disp_dvect (N_iBranch, N_Branch+1);
06467       fprintf (SUMA_STDERR, "\n");
06468    #endif
06469    
06470    if (LocalHead) fprintf (SUMA_STDERR, "%s: Found %d triangles belonging to a branch out of %d intersected triangles.\n", FuncName, Bcnt, SPI->N_IntersTri);
06471      
06472    /* Now you want to create a vector of N_Branches to represent the intersection */
06473    Bv = (SUMA_TRI_BRANCH *) SUMA_malloc (sizeof(SUMA_TRI_BRANCH)*(N_Branch+1)); /* you should only need N_Branch, but that 1 won't hurt ...*/
06474    if (!Bv) {
06475       fprintf (SUMA_STDERR, "Error %s: Could not allocate for Bv.\n", FuncName);
06476       SUMA_RETURN (NULL);
06477    }
06478    
06479    /* initialize allocated Bv elements */
06480    for (i=0; i<= N_Branch; ++i) { /* You have allocated for N_Branch+1*/
06481       Bv[i].list = NULL;
06482       Bv[i].N_list = 0;
06483    } 
06484    
06485    Bcnt = 0;
06486    for (i=0; i<= N_Branch; ++i) { /* Branch numbering starts at 1 */
06487       if (N_iBranch[i]) {
06488          /* something in that branch, allocate and initialize*/
06489          if (LocalHead) fprintf (SUMA_STDERR, "%s: Allocating for %d elements, Old Branch %d, New Branch %d.\n", FuncName, N_iBranch[i], i, Bcnt); 
06490          Bv[Bcnt].list = (int *) SUMA_calloc (N_iBranch[i]+1, sizeof(int));
06491          Bv[Bcnt].N_list = N_iBranch[i];
06492          Bv[Bcnt].iBranch = Bcnt;
06493          NBlist[i] = Bcnt; /* store new indexing for Branches */
06494          ++Bcnt;
06495       }
06496       
06497    }
06498    
06499    /* store the total number of used branches */
06500    *BranchCount = Bcnt;
06501    
06502    /* now fill up the branches*/
06503    if (LocalHead) fprintf (SUMA_STDERR, "%s: Filling up branches...\n", FuncName);
06504    for (i=0; i <= N_Branch; ++i) N_iBranch[i] = 0; /* now use this vector as a counter for the filling at each new branch index */
06505    for (i=0; i < SPI->N_IntersTri; ++i) { /* only go over visited triangles */
06506       TriCheck = TriBranch[VisitationOrder[i]]; /* returns the branch number of triangle VisitationOrder[i] */
06507       if (TriCheck) {
06508          Bcnt = NBlist[TriCheck]; /* get the new branch number from the original (old) branch number */
06509          #if 0
06510          fprintf (SUMA_STDERR,"%s: Tricheck = %d\n", FuncName, TriCheck); */
06511          if (Bcnt >= *BranchCount) {
06512             fprintf (SUMA_STDERR, "\aError %s: BranchCount = %d <= Bcnt = %d.\n", FuncName, *BranchCount, Bcnt);
06513          }
06514          if (N_iBranch[Bcnt] >= Bv[Bcnt].N_list) {
06515             fprintf (SUMA_STDERR, "\aError %s: Bcnt = %d. N_iBranch[Bcnt] = %d >= Bv[Bcnt].N_list = %d\n", FuncName, Bcnt, N_iBranch[Bcnt], Bv[Bcnt].N_list);
06516          }
06517          #endif
06518          Bv[Bcnt].list[N_iBranch[Bcnt]] = VisitationOrder[i]; /* store the index of the visited triangle in that branch */
06519          N_iBranch[Bcnt] += 1; /* store the number of elements in that branch */
06520       }
06521    }
06522    
06523    if (LocalHead) fprintf (SUMA_STDERR, "%s: freeing ...\n", FuncName);
06524    if (VisitationOrder) SUMA_free(VisitationOrder);
06525    if (TriBranch) SUMA_free(TriBranch);
06526    SUMA_RETURN (Bv);    
06527 }
06528 
06529 /*! 
06530    Function to show the contents of a SUMA_TRI_BRANCH structure
06531 */
06532 SUMA_Boolean SUMA_show_STB (SUMA_TRI_BRANCH *B, FILE *Out)
06533 {
06534    static char FuncName[]={"SUMA_show_STB"};
06535    int i;
06536    
06537    SUMA_ENTRY;
06538   
06539    if (!Out) Out = SUMA_STDERR;
06540    
06541    if (!B) {
06542       fprintf (Out, "%s: Empy structure.\n", FuncName);
06543    }
06544    
06545    fprintf (Out, "%s:\tBranch #%d. %d elements in list\nlist:\t", FuncName, B->iBranch, B->N_list);
06546    for (i=0; i < B->N_list; ++i) {
06547       fprintf (Out, "%d\t", B->list[i]);
06548    }
06549    fprintf (Out, "\n");
06550    
06551    SUMA_RETURN (YUP);
06552 }
06553 
06554 /*!
06555    Function to free a vector of SUMA_TRI_BRANCH structures.
06556 */
06557 
06558 void SUMA_free_STB (SUMA_TRI_BRANCH *Bv, int N_Bv) 
06559 {
06560 
06561    static char FuncName[]={"SUMA_free_STB"};
06562    int i;
06563    
06564    SUMA_ENTRY;
06565    
06566    for (i=0; i < N_Bv; ++i) {
06567       if (Bv[i].list) SUMA_free(Bv[i].list);
06568    }
06569    if (Bv) SUMA_free(Bv);
06570    
06571    SUMA_RETURNe;
06572     
06573 }
06574 
06575 /*!
06576    \brief Allocates a structure for computing the intersection of a surface with a plane
06577    The allocation is done conservatively, expecting the worse case scenario. 
06578    
06579    \param SO (SUMA_SurfaceObject *) Surface Object structure used to get number of nodes, edges, etc ...
06580    \return SPI (SUMA_SURF_PLANE_INTERSECT *) Pointer to surface plane intersection structure.
06581          see structure definition for more info.
06582 
06583 */
06584 SUMA_SURF_PLANE_INTERSECT * SUMA_Allocate_SPI (SUMA_SurfaceObject *SO) 
06585 {
06586    static char FuncName[]={"SUMA_Allocate_SPI"};
06587    int i;
06588    SUMA_SURF_PLANE_INTERSECT *SPI = NULL;
06589    
06590    SUMA_ENTRY;
06591    
06592    SPI = (SUMA_SURF_PLANE_INTERSECT *) SUMA_malloc(sizeof(SUMA_SURF_PLANE_INTERSECT));
06593    if (!SPI) {
06594       fprintf (SUMA_STDERR, "Error %s: Could not allocate for SPI\n", FuncName);
06595       SUMA_RETURN (SPI);
06596    }
06597    
06598    SPI->IntersEdges = (int *) SUMA_calloc (SO->EL->N_EL, sizeof(int)); /* allocate for the max imaginable*/
06599    SPI->IntersNodes = (float *) SUMA_calloc (3 * SO->EL->N_EL, sizeof(float));
06600    SPI->isEdgeInters = (SUMA_Boolean *) SUMA_calloc (SO->EL->N_EL, sizeof(SUMA_Boolean));
06601    SPI->IntersTri = (int *) SUMA_calloc (SO->N_FaceSet, sizeof(int));
06602    SPI->isNodeInMesh = (SUMA_Boolean *) SUMA_calloc (SO->N_Node, sizeof(SUMA_Boolean));
06603    SPI->isTriHit = (SUMA_Boolean *) SUMA_calloc (SO->N_FaceSet, sizeof(SUMA_Boolean));
06604 
06605    if (!SPI->IntersEdges || !SPI->IntersTri || !SPI->IntersNodes || !SPI->isTriHit || !SPI->isEdgeInters)
06606       {
06607          fprintf (SUMA_STDERR, "Error %s: Could not allocate \n", FuncName);
06608          SUMA_RETURN (SPI);
06609       }
06610    
06611    SPI->N_IntersEdges = 0;
06612    SPI->N_IntersTri = 0;
06613    SPI->N_NodesInMesh = 0;  
06614    SUMA_RETURN (SPI);
06615 }
06616 
06617 /*!
06618 free the SPI structure    
06619 */
06620 void SUMA_free_SPI (SUMA_SURF_PLANE_INTERSECT *SPI)
06621 {
06622    static char FuncName[]={"SUMA_free_SPI"};
06623    
06624    SUMA_ENTRY;
06625    
06626    if (!SPI) SUMA_RETURNe;
06627    if (SPI->IntersTri) SUMA_free(SPI->IntersTri);
06628    if (SPI->IntersNodes) SUMA_free(SPI->IntersNodes);
06629    if (SPI->IntersEdges) SUMA_free(SPI->IntersEdges);
06630    if (SPI->isNodeInMesh) SUMA_free(SPI->isNodeInMesh); 
06631    if (SPI->isTriHit) SUMA_free (SPI->isTriHit);
06632    if (SPI->isEdgeInters) SUMA_free (SPI->isEdgeInters);
06633      
06634    if (SPI) SUMA_free(SPI);
06635    
06636    SUMA_RETURNe;
06637 }
06638 
06639 /*! 
06640 Show the SPI structure 
06641 */
06642 SUMA_Boolean SUMA_Show_SPI (SUMA_SURF_PLANE_INTERSECT *SPI, FILE * Out, SUMA_SurfaceObject *SO)
06643 {
06644    static char FuncName[]={"SUMA_Show_SPI"};
06645    int i;
06646    
06647    SUMA_ENTRY;
06648    
06649    if (!Out) Out = SUMA_STDERR;
06650    
06651    if (!SPI) {
06652       fprintf (Out,"Error %s: NULL POINTER.\n", FuncName);
06653    }
06654    
06655    fprintf (Out,"Intersection Edges: %d\n[", SPI->N_IntersEdges);
06656    for (i=0; i < SPI->N_IntersEdges; ++i) {
06657       fprintf (Out, "%d, %d\n", SO->EL->EL[SPI->IntersEdges[i]][0], SO->EL->EL[SPI->IntersEdges[i]][1]);
06658    }
06659    fprintf (Out," ]\n");
06660    
06661    fprintf (Out,"Intersection Nodes: %d\n[", SPI->N_IntersEdges);
06662    for (i=0; i < SO->EL->N_EL; ++i) {
06663       if (SPI->isEdgeInters[i]) fprintf (Out, "%f, %f, %f, ", SPI->IntersNodes[3*i], SPI->IntersNodes[3*i+1], SPI->IntersNodes[3*i+2]);
06664    }
06665    fprintf (Out," ]\n");
06666    
06667    fprintf (Out,"Intersected Triangles: %d\n[", SPI->N_IntersTri);
06668    for (i=0; i < SPI->N_IntersTri; ++i) {
06669       fprintf (Out, "t%d\t", SPI->IntersTri[i]);
06670    }
06671    fprintf (Out," ]\n");
06672    SUMA_RETURN(YUP);
06673 }
06674 
06675 #define NO_LOG
06676 SUMA_Boolean SUMA_Mark_Tri (SUMA_EDGE_LIST  *EL, int E1, int iBranch, int *TriBranch, int *IsInter, int *N_IsInter, int *VisitationOrder, int *ivisit)
06677 {
06678    static char FuncName[]={"SUMA_Mark_Tri"};
06679    int Tri = -1, Found, k, kedge = 0, E2, Ntri = 0;
06680    static int In = 0;
06681    SUMA_Boolean LocalHead = NOPE;
06682    
06683    /* this is a recursive function, you don't want to log every time it is called */
06684    /* SUMA_ENTRY;    */
06685    
06686    ++In;
06687    if (LocalHead) fprintf (SUMA_STDERR, "%s: Entered #%d.\n", FuncName, In);
06688    
06689    /* find the next triangle hosting E1, if possible, otherwise it is the end of the branch. */
06690    if (EL->ELps[E1][2] != 2) { /* reached a dead end , end of branch */
06691       /* mark triangle, remove E1 from list and return */
06692       if (LocalHead) fprintf (SUMA_STDERR, "%s: reached end of branch.\n", FuncName);
06693       kedge = 0;
06694       Found = NOPE;
06695       while (!Found && kedge < *N_IsInter) {
06696          if (IsInter[kedge] == E1) {
06697             Found = YUP;
06698             *N_IsInter = *N_IsInter - 1;
06699             IsInter[kedge] = IsInter[*N_IsInter];
06700          } else ++kedge;
06701       }
06702       return (YUP);
06703    }else {
06704       Tri = EL->ELps[E1][1];
06705       if (TriBranch[Tri]) { /* try second triangle */
06706          Tri = EL->ELps[E1+1][1];
06707       }
06708       if (LocalHead) fprintf (SUMA_STDERR, "%s: moving on to triangle %d.\n", FuncName, Tri);
06709    }
06710    
06711    if (!TriBranch[Tri]) { 
06712       /* unvisited, mark with iBranch */
06713       TriBranch[Tri] = iBranch;
06714       VisitationOrder[*ivisit] = Tri;
06715       ++(*ivisit);
06716       /* find other edges in this triangle that have been intersected */
06717       Found = NOPE; 
06718       k = 0;
06719       while (!Found && k < 3) {
06720          E2 = EL->Tri_limb[Tri][k]; /* this may not be the first occurence of this edge since the list contains duplicates */
06721          if (LocalHead) {
06722             fprintf (SUMA_STDERR, "%s: Trying edge E2 %d (%d %d), tiangle %d, edge %d.\n", 
06723                      FuncName, E2, EL->EL[E2][0], EL->EL[E2][1], Tri, k);
06724          }
06725          while (EL->ELps[E2][2] < 0) { /* find the first occurence of this edge in the list */
06726             E2--;
06727          }
06728          if (LocalHead) fprintf (SUMA_STDERR, "%s: E2 changed to %d. E1 is %d\n", FuncName, E2, E1);
06729          if (E2 != E1) {
06730             /* was E2 intersected ? */
06731             kedge = 0;
06732             while (!Found && kedge < *N_IsInter) {
06733                if (IsInter[kedge] == E2) {
06734                   Found = YUP;
06735                   if (LocalHead) fprintf (SUMA_STDERR, "%s: E2 is intersected.\n", FuncName);
06736                }
06737                else ++kedge;
06738             }
06739          }
06740          ++k;
06741       }
06742       
06743       if (!Found) {
06744          fprintf (SUMA_STDERR, "Error %s: No second edge found.\n", FuncName);
06745          return (NOPE);
06746       } else {
06747          if (LocalHead) fprintf (SUMA_STDERR, "%s: Removing E2 from List and calling SUMA_Mark_Tri.\n", FuncName);
06748          /* remove this new edge from the list */
06749          *N_IsInter = *N_IsInter - 1;
06750          IsInter[kedge] = IsInter[*N_IsInter];
06751          
06752          /* continue visitation */
06753          if (!SUMA_Mark_Tri (EL, E2, iBranch, TriBranch, IsInter, N_IsInter, VisitationOrder, ivisit)) {
06754             fprintf (SUMA_STDERR, "Error %s: Failed in SUMA_Mark_Tri.\n", FuncName);
06755             return (NOPE);
06756          } 
06757          return (YUP);
06758       }
06759    } else {
06760       if (TriBranch[Tri] != iBranch) {
06761          fprintf (SUMA_STDERR, "\a%s: Branches colliding, Must weld %d to %d.\n", FuncName, iBranch, TriBranch[Tri]);
06762          
06763          /* DO NOT MODIFY THE VALUE OF BRANCH or you will mistakingly link future branches*/ 
06764       }
06765       /* visited, end of branch return */
06766       if (LocalHead) fprintf (SUMA_STDERR, "%s: End of branch. Returning.\n", FuncName);
06767       return (YUP);
06768    }
06769    
06770    fprintf (SUMA_STDERR, "Error %s: Should not be here.\n", FuncName);
06771    return (NOPE);
06772 }
06773 
06774 /*!
06775    E = SUMA_Find_Edge_N_Host (EL, IsInter, N_IsInter, Nhost);
06776    \brief Finds an edge that has Nhost hosting triangles. Only the edges indexed in IsInter are examined 
06777    \param EL (SUMA_EDGE_LIST *) Complete Edge list structure for surface
06778    \param IsInter (int *) vector containing indices into EL->EL matrix which contains the EdgeList
06779    \param N_IsInter (int) number of elements in IsInter
06780    \param kedge (int *) pointer to index into IsInter where E was found
06781    \param Nhost number of hosting triangles (should be 2 for a closed surface, 1 for edge edges and more than 2 for errors in tessellation
06782    \return E (int) index into EL->EL of the first edge (of those listed in IsInter) encountered that has N hosting triangles.
06783       -1 is returned if no edges are found
06784    
06785    This function is meant to be used with SUMA_Surf_Plane_Intersect
06786 */
06787 int SUMA_Find_Edge_Nhost (SUMA_EDGE_LIST  *EL, int *IsInter, int N_IsInter, int *i, int Nhost)
06788 {
06789    static char FuncName[]={"SUMA_Find_Edge_Nhost"};
06790    
06791    SUMA_ENTRY;
06792 
06793    for (*i=0; *i < N_IsInter; ++(*i)) {
06794       if (EL->ELps[IsInter[*i]][2] == Nhost) SUMA_RETURN (IsInter[*i]);
06795    }
06796    
06797    SUMA_RETURN (-1);
06798 
06799 }
06800 
06801 /*! 
06802 \brief Path = SUMA_Dijkstra (SO, Nx, Ny, isNodeInMesh, N_isNodeInMesh, Method_Number, Path_length, N_Path);
06803       Finds the shortest distance between nodes Nx and Ny on SO with a restriction on the number of nodes
06804       available for travel. In other terms, the search space is limited to a subset of the nodes forming SO. 
06805       The subset of nodes is stored in isNodeInMesh. This subset is typically specified in SUMA_Surf_Plane_Intersect.
06806       
06807       
06808  Path = SUMA_Dijkstra (SO, Nx,  Ny, isNodeInMesh, N_isNodeInMesh, Method_Number, Path_length, N_Path)
06809 \param SO (SUMA_SurfaceObject *) The surface Object structure. NodeList, EL and FN are needed. 
06810 \param Nx (int) The node index (referring to SO's nodes) where the search begins.
06811 \param Ny (int) The node index (referring to SO's nodes) where the search ends.
06812 \param isNodeInMesh (SUMA_Boolean *) Pointer to SO->N_Node long vector such that 
06813                                        if (isNodeInMesh[i]) then node i is part of the 
06814                                        mesh that is used in the search path. This mesh is a subset 
06815                                        of SO->FaceSetList and is typically obtained when one 
06816                                        runs SUMA_Surf_Plane_Intersect. Running SUMA_Dijkstra on 
06817                                        a complete surface is only for very patient people.
06818                                 NOTE:  This vector is modified as a node is visited. Make sure you 
06819                                        do not use it after this function has been called.
06820 \param N_isNodeInMesh (int *) Pointer to the total number of nodes that make up the mesh (subset of SO)
06821                This parameter is passed as a pointer because as nodes in the mesh are visited, that
06822                number is reduced and represents when the function returns, the number of nodes that were
06823                never visited in the search. 
06824 \param Method_Number (int) selector for which algorithm to use. Choose from:
06825                      0 - Straight forward implementation, slow
06826                      1 - Variation to eliminate long searches for minimum of L, much much much faster than 0, 5 time more memory.
06827 \param Path_length (float *) The distance between Nx and Ny. This value is negative if no path between Nx and Ny was found.
06828 \param N_Path (int *) Number of nodes forming the Path vector
06829 
06830 \return Path (float) A vector of N_Path node indices forming the shortest path, from Nx (Path[0]) to Ny (Path[*N_Path - 1]). 
06831                   NULL is returned in case of error.
06832 
06833 \sa Graph Theory by Ronald Gould and labbook NIH-2 page 154 for path construction
06834 */
06835 #define LARGE_NUM 9e300
06836 /* #define LOCALDEBUG */ /* lots of debugging info. */
06837 int * SUMA_Dijkstra (SUMA_SurfaceObject *SO, int Nx, int Ny, SUMA_Boolean *isNodeInMesh, int *N_isNodeInMesh, int Method_Number, float *Lfinal, int *N_Path)
06838 {
06839    static char FuncName[] = {"SUMA_Dijkstra"};
06840    SUMA_Boolean LocalHead = NOPE;
06841    float *L = NULL, Lmin = -1.0, le = 0.0, DT_DIJKSTRA;
06842    int i, iw, iv, v, w, N_Neighb, *Path = NULL;
06843    struct  timeval  start_time;
06844    SUMA_DIJKSTRA_PATH_CHAIN *DC = NULL, *DCi, *DCp;
06845    SUMA_Boolean Found = NOPE;
06846    /* variables for method 2 */
06847    int N_Lmins, *vLmins, *vLocInLmins, iLmins, ReplacingNode, ReplacedNodeLocation;
06848    float *Lmins; 
06849    
06850    
06851    SUMA_ENTRY;
06852    
06853    *Lfinal = -1.0;
06854    *N_Path = 0;
06855    
06856    /* make sure Both Nx and Ny exist in isNodeInMesh */
06857    if (!isNodeInMesh[Nx]) {
06858       fprintf (SUMA_STDERR,"\aError %s: Node %d (Nx) is not in mesh.\n", FuncName, Nx);
06859       SUMA_RETURN (NULL);
06860    }  
06861    if (!isNodeInMesh[Ny]) {
06862       fprintf (SUMA_STDERR,"\aError %s: Node %d (Ny) is not in mesh.\n", FuncName, Ny);
06863       SUMA_RETURN (NULL);
06864    }
06865 
06866    if (!SO->FN) {
06867       fprintf (SUMA_STDERR, "Error %s: SO does not have FN structure.\n", FuncName);
06868       SUMA_RETURN (NULL);
06869    }
06870 
06871    if (LocalHead) {
06872       /* Start timer for next function */
06873       SUMA_etime(&start_time,0);      
06874    }
06875    
06876    /* allocate for chain */
06877    DC = (SUMA_DIJKSTRA_PATH_CHAIN *) SUMA_malloc (sizeof(SUMA_DIJKSTRA_PATH_CHAIN) * SO->N_Node);
06878    if (!DC) {
06879       fprintf (SUMA_STDERR, "Error %s: Could not allocate. \n", FuncName);
06880       SUMA_RETURN (NULL);
06881    }
06882    
06883    switch (Method_Number) {
06884    
06885       case 0:  /* Method 0, Brute force */
06886          /* allocate for vertices labels */
06887          L = (float *) SUMA_calloc (SO->N_Node, sizeof (float));
06888          if (!L) {
06889             fprintf (SUMA_STDERR, "Error %s: Failed to allocate.\n", FuncName);
06890             SUMA_free(DC);
06891             SUMA_RETURN (NULL);
06892          }
06893 
06894          /* label all vertices with very large numbers, initialize path previous pointers to null */
06895          for (i=0; i < SO->N_Node; ++i) {
06896             L[i] = LARGE_NUM;   
06897             DC[i].Previous = NULL;
06898          }
06899          /* label starting vertex with 0 */
06900          L[Nx] = 0.0;
06901          Lmin = 0.0;
06902          v = Nx;
06903          *Lfinal = -1.0;
06904          /* initialize path at Nx */
06905          DC[Nx].Previous = NULL;
06906          DC[Nx].node = Nx;
06907          DC[Nx].le = 0.0;
06908          DC[Nx].order = 0;
06909          *N_Path = 0;
06910          /* Brute force method */
06911          do {
06912             /* find v in Mesh / L(v) is minimal */
06913             /* this sucks up a lot of time because it is searching the entire set of SO->N_Node instead of the one that was intersected only.
06914             This can be sped up, considerably */
06915             SUMA_MIN_LOC_VEC(L, SO->N_Node, Lmin, v);   /* locates and finds the minimum of L, nodes not in mesh will keep their large values and will not be picked*/
06916             if (!isNodeInMesh[v]) {
06917                fprintf (SUMA_STDERR, "\aERROR %s: Dijkstra derailed. v = %d, Lmin = %f\n. Try another point.", FuncName, v, Lmin);
06918                SUMA_free (L);
06919                SUMA_free(DC);
06920                SUMA_RETURN (NULL); 
06921             }
06922             if (v == Ny) {
06923                if (LocalHead) fprintf (SUMA_STDERR, "%s: Done.\n", FuncName);
06924                *Lfinal = L[v];
06925                Found = YUP;
06926             } else {
06927                N_Neighb = SO->FN->N_Neighb[v];
06928                for (i=0; i < N_Neighb; ++i) {
06929                   w = SO->FN->FirstNeighb[v][i];
06930                   if (isNodeInMesh[w]) {
06931                      iw = 3*w;
06932                      iv = 3*v;
06933                      le = sqrt ( (SO->NodeList[iw] - SO->NodeList[iv]) * (SO->NodeList[iw] - SO->NodeList[iv]) +
06934                                  (SO->NodeList[iw+1] - SO->NodeList[iv+1]) * (SO->NodeList[iw+1] - SO->NodeList[iv+1]) +
06935                                  (SO->NodeList[iw+2] - SO->NodeList[iv+2]) * (SO->NodeList[iw+2] - SO->NodeList[iv+2]) );
06936                      if (L[w] > L[v] + le ) {
06937                         L[w] = L[v] + le;  
06938                         /* update the path */
06939                         DCp = &(DC[v]); /* previous path */
06940                         DC[w].Previous = (void *) DCp;
06941                         DC[w].le = le;
06942                         DC[w].node = w;
06943                         DC[w].order = DCp->order + 1;
06944                      } 
06945                   }
06946                }
06947 
06948                /* remove node v from isNodeInMesh and reset their distance value to a very large one, 
06949                   this way you do not have to reinitialize this variable. */
06950                isNodeInMesh[v] = NOPE;
06951                *N_isNodeInMesh -= 1;
06952                L[v] = LARGE_NUM; 
06953                Found = NOPE;
06954             }
06955          } while (*N_isNodeInMesh > 0 && !Found);
06956 
06957          if (!Found) {
06958             fprintf (SUMA_STDERR, "Error %s: No more nodes in mesh, failed to reach target.\n", FuncName);
06959             SUMA_free (L);
06960             SUMA_free(DC);
06961             SUMA_RETURN (NULL);
06962          }else {
06963             if (LocalHead) fprintf (SUMA_STDERR, "%s: Path between Nodes %d and %d is %f.\n", FuncName, Nx, Ny, *Lfinal);
06964          }
06965 
06966 
06967          if (LocalHead) {
06968             /* stop timer */
06969             DT_DIJKSTRA = SUMA_etime(&start_time,1);
06970             fprintf (SUMA_STDERR, "%s: Method 1- Elapsed time in function %f seconds.\n", FuncName, DT_DIJKSTRA);
06971          }
06972 
06973          SUMA_free(L);
06974          break;
06975 
06976       case 1:  /********* Method 1- faster minimum searching *******************/
06977          if (LocalHead) {
06978             /* Start timer for next function */
06979             SUMA_etime(&start_time,0);      
06980          }
06981 
06982          /* allocate for vertices labels and minimums vectors*/
06983          L = (float *) SUMA_calloc (SO->N_Node, sizeof (float));        /* L[i] = distance to a node i*/
06984          Lmins = (float *) SUMA_calloc (SO->N_Node, sizeof (float));    /* Lmins = vector containing minimum calculated distances to node */
06985          vLmins = (int *) SUMA_calloc (SO->N_Node, sizeof (int));       /* vLmins[i] = index (into L) of the node having a distance Lmins[i] */
06986          vLocInLmins = (int *) SUMA_calloc (SO->N_Node, sizeof (int));  /* vLocInLmin[j] = index (into Lmins) of a node having index j (into L) */
06987 
06988          if (!L || !Lmins || !vLmins || !vLocInLmins) {
06989             fprintf (SUMA_STDERR, "Error %s: Failed to allocate.\n", FuncName);
06990             SUMA_RETURN (NULL);
06991          }
06992 
06993          /* label all vertices with very large numbers and initialize vLocInLmins to -1*/
06994          for (i=0; i < SO->N_Node; ++i) {
06995             L[i] = LARGE_NUM;
06996             Lmins[i] = LARGE_NUM;   
06997             vLocInLmins[i] = -1;            
06998             DC[i].Previous = NULL;
06999          }
07000 
07001          /* label starting vertex with 0 */
07002          L[Nx] = 0.0;
07003          *Lfinal = -1.0;
07004 
07005          /* initialize values of vectors used to keep track of minimum values of L and their corresponding nodes */
07006          Lmins[0] = 0.0;
07007          vLmins[0] = Nx;
07008          vLocInLmins[Nx] = 0;
07009          N_Lmins = 1;
07010 
07011          /* initialize path at Nx */
07012          DC[Nx].Previous = NULL;
07013          DC[Nx].node = Nx;
07014          DC[Nx].le = 0.0;
07015          DC[Nx].order = 0;
07016          *N_Path = 0;
07017          
07018          /* method with efficient tracking of minimum */
07019          if (LocalHead) fprintf (SUMA_STDERR, "%s: about to MIN_LOC ....N_isNodeInMesh = %d\n", FuncName, *N_isNodeInMesh);
07020          do {
07021             /* find v in Mesh / L(v) is minimal */
07022             SUMA_MIN_LOC_VEC(Lmins, N_Lmins, Lmin, iLmins);   /* locates the minimum value in Lmins vector */
07023             v = vLmins[iLmins];   /* get the node for this Lmin value */
07024             if (!isNodeInMesh[v]) {
07025                fprintf (SUMA_STDERR, "\aERROR %s: Dijkstra derailed. v = %d, Lmin = %f\n. Try another point.", FuncName, v, Lmin);
07026                SUMA_free (L);
07027                SUMA_free (Lmins);
07028                SUMA_free(vLmins);
07029                SUMA_free(vLocInLmins);
07030                SUMA_free(DC);
07031                SUMA_RETURN (NULL);
07032             }
07033             #ifdef LOCALDEBUG
07034                fprintf (SUMA_STDERR, "%s: Node v = %d.\n", FuncName, v);
07035             #endif
07036             if (v == Ny) {
07037                if (LocalHead) fprintf (SUMA_STDERR, "%s: Done.\n", FuncName);
07038                *Lfinal = L[v];
07039                Found = YUP;
07040             } else {
07041                N_Neighb = SO->FN->N_Neighb[v];
07042                for (i=0; i < N_Neighb; ++i) {
07043                   w = SO->FN->FirstNeighb[v][i];
07044                   if (isNodeInMesh[w]) {
07045                      iw = 3*w;
07046                      iv = 3*v;
07047                      le = sqrt ( (SO->NodeList[iw] - SO->NodeList[iv]) * (SO->NodeList[iw] - SO->NodeList[iv]) +
07048                                  (SO->NodeList[iw+1] - SO->NodeList[iv+1]) * (SO->NodeList[iw+1] - SO->NodeList[iv+1]) +
07049                                  (SO->NodeList[iw+2] - SO->NodeList[iv+2]) * (SO->NodeList[iw+2] - SO->NodeList[iv+2]) );
07050                      if (L[w] > L[v] + le ) {
07051                         #ifdef LOCALDEBUG
07052                            fprintf (SUMA_STDERR, "%s: L[%d]=%f > L[%d] = %f + le = %f.\n", FuncName, w, L[w], v, L[v], le);
07053                         #endif
07054                         L[w] = L[v] + le; 
07055                         /* update the path */
07056                         DCp = &(DC[v]); /* previous path */
07057                         DC[w].Previous = (void *) DCp;
07058                         DC[w].le = le;
07059                         DC[w].node = w;
07060                         DC[w].order = DCp->order + 1;
07061                         
07062                         if (vLocInLmins[w] < 0) { 
07063                            #ifdef LOCALDEBUG
07064                               fprintf (SUMA_STDERR, "%s: adding entry for w = %d - First Hit. \n", FuncName, w);
07065                            #endif
07066                            Lmins[N_Lmins] = L[w]; /* add this value to Lmins vector */
07067                            vLmins[N_Lmins] = w; /* store the node for this Lmins value */
07068                            vLocInLmins[w] = N_Lmins; /* store where that node is represented in Lmins */
07069                            ++N_Lmins;  /* increment N_Lmins */  
07070                         } else {
07071                            #ifdef LOCALDEBUG
07072                               fprintf (SUMA_STDERR, "%s: modifying entry for w = %d  Second Hit.\n", FuncName, w); */
07073                            #endif
07074                            Lmins[vLocInLmins[w]] = L[w]; /* update value for Lmins */
07075                         }                        
07076                      }else {
07077                         #ifdef LOCALDEBUG
07078                            fprintf (SUMA_STDERR, "%s: L[%d]=%f < L[%d] = %f + le = %f.\n", FuncName, w, L[w], v, L[v], le); */
07079                         #endif
07080                      } 
07081                   }
07082                }
07083 
07084                /* remove node v from isNodeInMesh and reset their distance value to a very large one, 
07085                   this way you do not have to reinitialize this variable. */
07086                isNodeInMesh[v] = NOPE;
07087                *N_isNodeInMesh -= 1;
07088                L[v] = LARGE_NUM; 
07089                Found = NOPE;
07090 
07091                /* also remove the values (by swapping it with last element) for this node from Lmins */
07092                #ifdef LOCALDEBUG
07093                   {
07094                      int kkk;
07095                      fprintf (SUMA_STDERR,"Lmins\tvLmins\tvLocInLmins\n");
07096                      for (kkk=0; kkk < N_Lmins; ++kkk) fprintf (SUMA_STDERR,"%f\t%d\t%d\n", Lmins[kkk], vLmins[kkk], vLocInLmins[vLmins[kkk]] );
07097                
07098                   }
07099                #endif
07100                
07101                if (vLocInLmins[v] >= 0) { /* remove its entry if there is one */
07102                   #ifdef LOCALDEBUG
07103                      fprintf (SUMA_STDERR, "%s: removing node v = %d. N_Lmins = %d\n", FuncName,  v, N_Lmins);
07104                   #endif
07105                   --N_Lmins;
07106                   ReplacingNode = vLmins[N_Lmins];
07107                   ReplacedNodeLocation = vLocInLmins[v];
07108                   Lmins[vLocInLmins[v]] = Lmins[N_Lmins];
07109                   vLmins[vLocInLmins[v]] = vLmins[N_Lmins];
07110                   vLocInLmins[ReplacingNode] = ReplacedNodeLocation;
07111                   vLocInLmins[v] = -1;
07112                   Lmins[N_Lmins] = LARGE_NUM; 
07113                }
07114             }
07115          } while (*N_isNodeInMesh > 0 && !Found);
07116 
07117          if (!Found) {
07118             fprintf (SUMA_STDERR, "Error %s: No more nodes in mesh, failed to reach target %d. NLmins = %d\n", FuncName, Ny, N_Lmins);
07119             SUMA_free (L);
07120             SUMA_free (Lmins);
07121             SUMA_free(vLmins);
07122             SUMA_free(vLocInLmins);
07123             SUMA_free(DC);
07124             SUMA_RETURN (NULL);
07125          }else {
07126             if (LocalHead) fprintf (SUMA_STDERR, "%s: Path between Nodes %d and %d is %f.\n", FuncName, Nx, Ny, *Lfinal);
07127          }
07128 
07129 
07130          if (LocalHead) {
07131             /* stop timer */
07132             DT_DIJKSTRA = SUMA_etime(&start_time,1);
07133             fprintf (SUMA_STDERR, "%s: Method 2- Elapsed time in function %f seconds.\n", FuncName, DT_DIJKSTRA);
07134          }
07135 
07136          SUMA_free(L);
07137          SUMA_free(Lmins);
07138          SUMA_free(vLmins);
07139          SUMA_free(vLocInLmins);
07140          break;   /********** Method 1- faster minimum searching **************/
07141       default: 
07142          fprintf (SUMA_STDERR, "Error %s: No such method (%d).\n", FuncName, Method_Number);
07143          if (DC) SUMA_free(DC);
07144          SUMA_RETURN (NULL);
07145          break;
07146    }
07147    
07148    /* now reconstruct the path */
07149    *N_Path = DC[Ny].order+1;
07150    Path = (int *) SUMA_calloc (*N_Path, sizeof(int));
07151    if (!Path) {
07152       fprintf (SUMA_STDERR, "Error %s: Failed to allocate.\n", FuncName);
07153       if (DC) SUMA_free(DC);
07154       SUMA_RETURN (NULL);
07155    }
07156    
07157    DCi = &(DC[Ny]);
07158    iv = *N_Path - 1;
07159    Path[iv] = Ny;
07160    if (iv > 0) {
07161       do {
07162          --iv;
07163          DCp = (SUMA_DIJKSTRA_PATH_CHAIN *) DCi->Previous;
07164          Path[iv] = DCp->node;
07165          DCi = DCp;
07166       } while (DCi->Previous);
07167    }
07168    
07169    if (iv != 0) {
07170       fprintf (SUMA_STDERR, "Error %s: iv = %d. This should not be.\n", FuncName, iv);
07171    }  
07172    
07173    SUMA_free(DC);
07174    SUMA_RETURN (Path);
07175 }
07176 
07177 /*!
07178 \brief Converts a path formed by a series of connected nodes to a series of edges
07179    ePath = SUMA_NodePath_to_EdgePath (EL, Path, N_Path, N_Edge);
07180    \param EL (SUMA_EDGE_LIST *) Pointer to edge list structure
07181    \param Path (int *) vector of node indices forming a path.
07182                        Sequential nodes in Path must be connected on the surface mesh.
07183    \param N_Path (int) number of nodes in the path 
07184    \param N_Edge (int *) pointer to integer that will contain the number of edges in the path
07185                         usually equal to N_Path if path is a loop (closed) or N_Path - 1. 
07186                         0 if function fails.
07187    \param ePath (int *) pointer to vector containing indices of edges forming the path. 
07188                         The indices are into EL->EL and represent the first occurence of the
07189                         edge between Path[i] and Path[i+1].
07190                         NULL if trouble is encountered.
07191                         
07192    \sa SUMA_NodePath_to_EdgePath_Inters
07193    \sa Path S in labbook NIH-2 page 153
07194 */
07195 int *SUMA_NodePath_to_EdgePath (SUMA_EDGE_LIST *EL, int *Path, int N_Path, int *N_Edge)
07196 {
07197    static char FuncName[]={"SUMA_NodePath_to_EdgePath"};
07198    int *ePath = NULL, i, i0;
07199    SUMA_Boolean LocalHead = NOPE;
07200    
07201    SUMA_ENTRY;
07202  
07203  
07204   *N_Edge = 0;
07205    ePath = (int *) SUMA_calloc(N_Path, sizeof(int));
07206    if (!ePath) {
07207       fprintf (SUMA_STDERR, "Error %s: Failed to allocate.\n", FuncName);
07208       SUMA_RETURN (NULL);
07209    }
07210 
07211    for (i=1; i<N_Path; ++i) {
07212       i0 = Path[i-1];
07213       /* find the location of the edge between i0 and i1 */
07214       ePath[i-1] = SUMA_FindEdge (EL, i0, Path[i]); 
07215       if (ePath[i-1] < 0) { /* error */
07216          fprintf (SUMA_STDERR, "Error %s: Failed in SUMA_FindEdge.\n", FuncName);
07217          SUMA_free(ePath);
07218          *N_Edge = 0;
07219          SUMA_RETURN (NULL);   
07220       }else {
07221          ++(*N_Edge);
07222       }
07223    }
07224 
07225    SUMA_RETURN (ePath);   
07226 }   
07227 
07228 /*!
07229 \brief determines whether to edges are identical or not. Recall
07230 that an edge can be represented multiple times in SO->EL, once for
07231 each triangle that uses it. Two edges are the same if and only if
07232 EL->EL[E1][0] == EL->EL[E2][0] && EL->EL[E1][1] == EL->EL[E2][1]
07233 
07234 ans = SUMA_isSameEdge ( EL, E1, E2); 
07235 
07236 \param EL (SUMA_EDGE_LIST *) Edge List structure.
07237 \param E1 (int) edge index
07238 \param E2 (int) edge index
07239 \return ans (SUMA_Boolean) YUP/NOPE
07240 */
07241 
07242 SUMA_Boolean SUMA_isSameEdge (SUMA_EDGE_LIST *EL, int E1, int E2) 
07243 {
07244    static char FuncName[]={"SUMA_isSameEdge"};
07245    
07246    SUMA_ENTRY;
07247 
07248    if (EL->EL[E1][0] == EL->EL[E2][0] && EL->EL[E1][1] == EL->EL[E2][1]) {
07249       SUMA_RETURN (YUP);
07250    } else {
07251       SUMA_RETURN (NOPE);
07252    }
07253    
07254 }
07255 
07256 /*!
07257 \brief This function determines the strip of triangles necessary to go from one node to another
07258 along intersected edges. 
07259 tPath = SUMA_IntersectionStrip (SO, SPI, 
07260             int *nPath, int N_nPath, float *dinters, float dmax, int *N_tPath)
07261 \param SO (SUMA_SurfaceObject *) pointer to Surface Object structure
07262 \param SPI (SUMA_SURF_PLANE_INTERSECT *) pointer surface/plane intersection structure
07263 \param nPath (int *) series of nodes forming the Dijkstra path between nodes Nx (nPath[0] and NynPath[N_nPath-1])
07264 \param N_nPath (int) number of nodes in nPath. Note: Only nPath[0], nPath[1] and nPath[N_nPath-1] are used by this function
07265 \param dinters (float *) pointer sum of distances between intersection points on intersected edges for all triangles in tPath.
07266                This distance is a better approximation for the distance along the cortical surface than the distance obtained
07267                along the shortest path.
07268 \param dmax (float) distance beyond which to quit searching. Usually this distance is slightly larger than the distance
07269                   along the path returned by SUMA_Dijkstra but dinters should always be less than the distance along the shortest path.
07270 \param N_tPath (int *) pointer to the number of triangle indices in tPath
07271 \return tPath (int*) pointer to vector containing the indices of triangles travelled from 
07272          nPath[0] to nPath[N_nPath] (or vice versa if nPath[0] = SO->N_Node-1).
07273 
07274 NOTE: Although the number of elements in tPath may be small, the number of elements allocated for is SO->N_FaceSet
07275 Make sure you free tPath when you are done with it.
07276 
07277 NOTE: This function can be used to create a node path formed by intersection points along edges but that is not implemented yet.
07278 
07279 \sa SUMA_Surf_Plane_Intersect
07280 \sa SUMA_Dijkstra
07281 \sa SUMA_FromIntEdgeToIntEdge
07282 
07283 */
07284 int * SUMA_IntersectionStrip (SUMA_SurfaceObject *SO, SUMA_SURF_PLANE_INTERSECT *SPI, 
07285             int *nPath, int N_nPath, float *dinters, float dmax, int *N_tPath)
07286 {
07287    static char FuncName[]={"SUMA_IntersectionStrip"};
07288    int *tPath1 = NULL, *tPath2 = NULL, Incident[50], N_Incident, Nx = -1, 
07289       Ny = -1, Tri = -1, Tri1 = -1, istart, n2 = -1, n3 = -1, E1, E2, cnt, N_tPath1, N_tPath2;
07290    float d1, d2;
07291    SUMA_Boolean *Visited = NULL, Found, LocalHead = NOPE;
07292    
07293    SUMA_ENTRY;
07294    
07295    /* find the edge containing the 1st 2 nodes of the Dijkstra path */
07296    /* find the triangle that contains the edge formed by the 1st 2 nodes of the Dijkstra path and is intersected by the plane*/
07297    Tri1 = -1;
07298    if (LocalHead) {
07299       fprintf (SUMA_STDERR, "%s: Looking for a triangle containing nodes [%d %d].\n", FuncName, nPath[0], nPath[1]);
07300    }
07301    
07302    Found = SUMA_Get_Incident(nPath[0], nPath[1], SO->EL, Incident, &N_Incident, 1);
07303    if (!Found) {
07304       /* no such triangle, get a triangle that contains nPath[0] and is intersected */
07305       fprintf (SUMA_STDERR, "%s: No triangle contains nodes [%d %d].\n", FuncName, nPath[0], nPath[1]);
07306       if (nPath[0] == SO->N_Node - 1) {
07307          fprintf (SUMA_STDERR, "Warning %s: 1st node is last node of surface, traversing path backwards.\n", FuncName);
07308          Nx = nPath[N_nPath - 1];
07309          Ny = nPath[0];
07310       }else {
07311          Nx = nPath[0];
07312          Ny = nPath[N_nPath - 1];
07313       }
07314       istart = SO->EL->ELloc[Nx];
07315       /* find an edge containing the first node and belonging to an intersected triangle */
07316       Found = NOPE;
07317       while (SO->EL->EL[istart][0] == Nx && !Found) {
07318          Tri = SO->EL->ELps[istart][1];
07319          if (SPI->isTriHit[Tri]) {
07320             Found = YUP;
07321             Tri1 = Tri;
07322          }
07323          ++istart;
07324       } 
07325    }else {
07326       Nx = nPath[0];
07327       Ny = nPath[N_nPath - 1];
07328       
07329       /* find which of these triangles was intersected */
07330       if (LocalHead) {
07331          fprintf (SUMA_STDERR, "%s: Found %d triangles containing nodes [%d %d].\n", FuncName, N_Incident, nPath[0], nPath[1]);
07332          for (cnt = 0; cnt < N_Incident; ++cnt) fprintf (SUMA_STDERR, "%d isHit %d\n", Incident[cnt], SPI->isTriHit[Incident[cnt]]);
07333          fprintf (SUMA_STDERR, "\n"); 
07334       }
07335       Found = NOPE;
07336       cnt = 0;
07337       while (cnt < N_Incident && !Found) {
07338          if (SPI->isTriHit[Incident[cnt]]) {
07339             Found = YUP;
07340             Tri1 = Incident[cnt];
07341          }
07342          ++cnt;
07343       }
07344    }
07345    
07346    if (!Found) {
07347       fprintf (SUMA_STDERR, "Error %s: Starting Edge could not be found.\n", FuncName);
07348       SUMA_RETURN (NULL);
07349    }else if (LocalHead) {
07350       fprintf (SUMA_STDERR, "%s: Starting with triangle %d.\n", FuncName, Tri1);
07351    }
07352 
07353    /* found starting triangle edge, begin with side 1 */
07354    if (SO->FaceSetList[3*Tri1] == Nx) {
07355       n2 = SO->FaceSetList[3*Tri1+1];
07356       n3 = SO->FaceSetList[3*Tri1+2];
07357    } else if (SO->FaceSetList[3*Tri1+1] == Nx) {
07358       n2 = SO->FaceSetList[3*Tri1];
07359       n3 = SO->FaceSetList[3*Tri1+2];
07360    } else if (SO->FaceSetList[3*Tri1+2] == Nx) {
07361       n2 = SO->FaceSetList[3*Tri1];
07362       n3 = SO->FaceSetList[3*Tri1+1];
07363    } else {
07364       fprintf (SUMA_STDERR, "Error %s: Triangle %d does not contain Nx %d.\n", FuncName, Tri1, Nx);
07365       SUMA_RETURN (NULL);
07366    }  
07367    
07368    
07369    
07370    E1 = SUMA_FindEdgeInTri (SO->EL, Nx, n2, Tri1);
07371    if (!SPI->isEdgeInters[E1]) {
07372       E1 = SUMA_FindEdgeInTri (SO->EL, Nx, n3, Tri1);
07373    }
07374    /* now choose E2 such that E2 is also intersected */
07375    if (!SUMA_isSameEdge (SO->EL, SO->EL->Tri_limb[Tri1][0], E1) && SPI->isEdgeInters[SO->EL->Tri_limb[Tri1][0]]) {
07376       E2 = SO->EL->Tri_limb[Tri1][0];
07377    }else if (!SUMA_isSameEdge (SO->EL, SO->EL->Tri_limb[Tri1][1], E1) && SPI->isEdgeInters[SO->EL->Tri_limb[Tri1][1]]) {
07378       E2 = SO->EL->Tri_limb[Tri1][1];
07379    }else if (!SUMA_isSameEdge (SO->EL, SO->EL->Tri_limb[Tri1][2], E1) && SPI->isEdgeInters[SO->EL->Tri_limb[Tri1][2]]) {
07380       E2 = SO->EL->Tri_limb[Tri1][2];
07381    }else {
07382       fprintf (SUMA_STDERR,"Error %s: No E2 found.\n", FuncName);
07383       SUMA_RETURN (NULL);
07384    }
07385 
07386    Visited = (SUMA_Boolean *) SUMA_calloc (SO->N_FaceSet, sizeof(SUMA_Boolean));
07387    tPath1 = (int *) SUMA_calloc (SO->N_FaceSet, sizeof(int));
07388    if (!Visited || !tPath1) {
07389       fprintf (SUMA_STDERR, "Error %s: Failed to allocate.\n", FuncName);
07390       if (Visited) SUMA_free(Visited);
07391       if (tPath2) SUMA_free(tPath1); 
07392       SUMA_RETURN (NULL);
07393    }
07394    
07395    N_tPath1 = 0;
07396    if (!SUMA_FromIntEdgeToIntEdge (Tri1, E1, E2, SO->EL, SPI, Ny, Visited, &d1, dmax, tPath1, &N_tPath1)) {
07397       fprintf (SUMA_STDERR, "Error %s: Failed in SUMA_FromIntEdgeToIntEdge.\n", FuncName);
07398       if (Visited) SUMA_free(Visited);
07399       if (tPath2) SUMA_free(tPath1);      
07400       SUMA_RETURN (NULL);
07401    }
07402    
07403    if (LocalHead) {
07404       fprintf (SUMA_STDERR, "%s: Found a distance of %f.\n\n\n", FuncName, d1);
07405    }
07406    
07407    /* Now try going in the other direction, E2->E1 */
07408    cnt = E2;
07409    E2 = E1;
07410    E1 = cnt;
07411    
07412    /* reset the values of Visited */
07413    for (cnt=0; cnt < SO->N_FaceSet; ++cnt) if (Visited[cnt]) Visited[cnt] = NOPE;
07414    
07415    tPath2 = (int *) SUMA_calloc (SO->N_FaceSet, sizeof(int));
07416    if (!Visited || !tPath2) {
07417       fprintf (SUMA_STDERR, "Error %s: Failed to allocate.\n", FuncName);
07418       if (Visited) SUMA_free(Visited);
07419       if (tPath1) SUMA_free(tPath1);
07420       if (tPath2) SUMA_free(tPath2);
07421       SUMA_RETURN (NULL);
07422    }
07423 
07424    N_tPath2 = 0;
07425    if (!SUMA_FromIntEdgeToIntEdge (Tri1, E1, E2, SO->EL, SPI, Ny, Visited, &d2, dmax, tPath2, &N_tPath2)) {
07426       fprintf (SUMA_STDERR, "Error %s: Failed in SUMA_FromIntEdgeToIntEdge.\n", FuncName);
07427       if (Visited) SUMA_free(Visited);
07428       if (tPath1) SUMA_free(tPath1);
07429       if (tPath2) SUMA_free(tPath2);
07430       SUMA_RETURN (NULL);
07431    }
07432    
07433    if (Visited) SUMA_free(Visited);
07434    
07435    if (LocalHead) {
07436       fprintf (SUMA_STDERR, "%s: Found a distance of %f.\n", FuncName, d2);
07437    }
07438    
07439    if (d2 < d1) {
07440       *N_tPath = N_tPath2;
07441       *dinters = d2;
07442       if (tPath1) SUMA_free(tPath1);
07443       SUMA_RETURN (tPath2);
07444    } else {
07445       *dinters = d1;
07446       *N_tPath = N_tPath1;
07447       if (tPath2) SUMA_free(tPath2);
07448       SUMA_RETURN (tPath1);
07449    }
07450    
07451 }
07452 
07453 /*!
07454 \brief This function moves from one intersected edge to the next until a certain node is encountered or a 
07455 a certain distance is exceeded. By intersected edge, I mean an edge of the surface's mesh that was 
07456 intersected by a plane.
07457 
07458 ans = SUMA_FromIntEdgeToIntEdge (int Tri, int E1, int E2, SUMA_EDGE_LIST *EL, SPI, int Ny,
07459          Visited, float *d, float dmax, int *tPath, int *N_tPath);
07460 
07461 \param Tri (int) index of triangle to start with (index into SO->FaceSetList)
07462 \param E1 (int) index of edge in Tri to start from
07463 \param E2 (int) index of edge in Tri to move in the direction of (Both E1 and E2  must be intersected edges)
07464 \param EL (SUMA_EDGE_LIST *) pointer to the edge list structure, typically SO->EL
07465 \param SPI (SUMA_SURF_PLANE_INTERSECT *) pointer to structure containing intersection of plane with surface
07466 \param Ny (int) node index to stop at (index into SO->NodeList)
07467 \param Visited (SUMA_Boolean *) pointer to vector (SO->N_FaceSet elements) that keeps track of triangles visited.
07468       This vector should be all NOPE when you first call this function. 
07469 \param d (float *) pointer to total distance from first intersected edge E1 to the last edge that contains E2
07470 \param dmax (float) maximum distance to go for before reversing and going in the other direction. Typically 
07471          this measure should be a bit larger than the distance of a Dijkstra path although you should never get a 
07472          distance that is larger than the Dijkstra path.
07473 \param tPath (int *) vector of indices of triangles visited from first edge to last edge (make sure you allocate a bundle for tPath)
07474 \param N_tPath (int *) number of elements in tPath
07475 \return ans (SUMA_Boolean) YUP/NOPE, for success/failure. 
07476 
07477          NOTE: This function is recursive.
07478 
07479 \sa SUMA_Surf_Plane_Intersect
07480 \sa SUMA_IntersectionStrip
07481 
07482 */
07483 SUMA_Boolean SUMA_FromIntEdgeToIntEdge (int Tri, int E1, int E2, SUMA_EDGE_LIST *EL, SUMA_SURF_PLANE_INTERSECT *SPI, int Ny,
07484          SUMA_Boolean *Visited, float *d, float dmax, int *tPath, int *N_tPath)
07485 {  static char FuncName[]={"SUMA_FromIntEdgeToIntEdge"};
07486    int Tri2 = 0, cnt, Incident[5], N_Incident;
07487    float dx, dy, dz;
07488    SUMA_Boolean Found, LocalHead = NOPE;
07489    
07490    SUMA_ENTRY;
07491 
07492    if (Tri < 0 || E1 < 0 || E2 < 0) {
07493       fprintf (SUMA_STDERR, "Error %s: Tri (%d) or E1 (%d) or E2 (%d) is negative!\n", FuncName, Tri, E1, E2);
07494       SUMA_RETURN (NOPE);
07495    }
07496    
07497    
07498    dx = (SPI->IntersNodes[3*E2] - SPI->IntersNodes[3*E1]);
07499    dy = (SPI->IntersNodes[3*E2+1] - SPI->IntersNodes[3*E1+1]);
07500    dz = (SPI->IntersNodes[3*E2+2] - SPI->IntersNodes[3*E1+2]);
07501    if (LocalHead) {
07502       fprintf (SUMA_STDERR, "%s: Entered - Tri %d, E1 %d [%d %d], E2 %d [%d %d]\n\tdx = %f dy = %f dz = %f\n", 
07503          FuncName, Tri, E1, EL->EL[E1][0], EL->EL[E1][1], E2, EL->EL[E2][0], EL->EL[E2][1], dx, dy, dz);
07504    }
07505    *d += sqrt( dx * dx + dy * dy + dz * dz);
07506    
07507    if (*d > dmax) {
07508       /* path already longer than Dijkstra path, no need to search further in this direction, get out with this d value */
07509       fprintf (SUMA_STDERR, "%s: Path longer than dmax. Returning.\n", FuncName);
07510       SUMA_RETURN (YUP);
07511    }
07512    
07513    if (EL->EL[E2][0] == Ny || EL->EL[E2][1] == Ny) {
07514       fprintf (SUMA_STDERR, "%s: Found Ny, d = %f\n", FuncName, *d);
07515       if (!Visited[Tri]) {
07516          /* add triangle to path */
07517          tPath[*N_tPath] = Tri;
07518          ++*N_tPath;
07519       }
07520       SUMA_RETURN (YUP);
07521    } else if (Visited[Tri]) {
07522       fprintf (SUMA_STDERR, "Error %s: Triangle %d already visited.\n",FuncName, Tri); 
07523       SUMA_RETURN (NOPE);
07524    }
07525      
07526    /* mark triangle as visited */
07527    if (LocalHead) fprintf (SUMA_STDERR, "%s: Marking triangle %d and adding %dth element to tPath.\n", FuncName, Tri, *N_tPath);
07528    Visited[Tri] = YUP;
07529    
07530    /* add triangle to path */
07531    tPath[*N_tPath] = Tri;
07532    ++*N_tPath;
07533    
07534    /* now get the second intersected triangle, incident to E2 */
07535    if (LocalHead) fprintf (SUMA_STDERR, "%s: Searching for triangles incident to E2 %d.\n", FuncName, E2);
07536    if (!SUMA_Get_Incident(EL->EL[E2][0], EL->EL[E2][1], EL, Incident, &N_Incident, 1)) {
07537       fprintf (SUMA_STDERR,"Error %s: Failed to get Incident triangles.\n", FuncName);
07538       SUMA_RETURN (NOPE);
07539    }
07540    
07541    /* find Tri2 such that Tri2 != Tri and Tri2 is an intersected triangle */
07542    cnt = 0;
07543    Found = NOPE;
07544    while (cnt < N_Incident && !Found) {
07545       if (SPI->isTriHit[Incident[cnt]] && Incident[cnt] != Tri && !Visited[Incident[cnt]]) {
07546          Found = YUP;
07547          Tri2 = Incident[cnt];
07548       }
07549       ++cnt;
07550    }
07551    
07552    if (!Found) {
07553       fprintf (SUMA_STDERR,"Error %s: Could not find next triangle.\n", FuncName);
07554       SUMA_RETURN (NOPE);
07555    }
07556    
07557    Tri = Tri2;
07558    E1 = E2;
07559    
07560    /* now find the new E2 */
07561    if (LocalHead) fprintf (SUMA_STDERR, "%s: Finding new E2.\n", FuncName);
07562    
07563    if (!SUMA_isSameEdge (EL, EL->Tri_limb[Tri][0], E1) && SPI->isEdgeInters[EL->Tri_limb[Tri][0]]) {
07564       E2 = EL->Tri_limb[Tri][0];
07565    }else if (!SUMA_isSameEdge (EL, EL->Tri_limb[Tri][1], E1) && SPI->isEdgeInters[EL->Tri_limb[Tri][1]]) {
07566       E2 = EL->Tri_limb[Tri][1];
07567    }else if (!SUMA_isSameEdge (EL, EL->Tri_limb[Tri][2], E1) && SPI->isEdgeInters[EL->Tri_limb[Tri][2]]) {
07568       E2 = EL->Tri_limb[Tri][2];
07569    }else {
07570       fprintf (SUMA_STDERR,"Error %s: No E2 found.\n", FuncName);
07571       SUMA_RETURN (NOPE);
07572    }
07573    
07574    /* call the same function again */
07575    if (!SUMA_FromIntEdgeToIntEdge (Tri, E1, E2, EL, SPI, Ny, Visited, d, dmax, tPath, N_tPath)) {
07576       fprintf (SUMA_STDERR,"Error %s: Failed in SUMA_FromIntEdgeToIntEdge.\n", FuncName);
07577       SUMA_RETURN (NOPE);
07578    }
07579    
07580    SUMA_RETURN (YUP);
07581 }
07582 /*!
07583 \brief Converts a series of connected nodes into a series of connected triangles that were intersected by 
07584 the plane. 
07585 There is no guarantee that two nodes that belong to triangles intersected by the plane and part of the shortest path
07586 (as returned by SUMA_Dijkstra) for an edge that belongs to a triangle intersected by the plane. See labbook NIH-2 page 
07587 158 for sketches illustrating this point. So the strip of triangles that you will get back may have holes in it since 
07588 it only allows intersected triangles to be members of the path. 
07589 
07590    tPath = SUMA_NodePath_to_TriPath_Inters (SO, SPI, int *nPath, int N_nPath, int *N_tPath)
07591    
07592    \param SO (SUMA_SurfaceObject *) structure containing surface object
07593    \param SPI (SUMA_SURF_PLANE_INTERSECT *) surface plane intersection structure
07594    \param nPath (int *) vector containing the shortest path defined by its nodes (output of SUMA_Dijkstra)
07595    \param N_nPath (int) number of elements in nPath
07596    \param N_tPath (int *)pointer to number of elements returned in tPath
07597    \return tPath (int *) vector of *N_tPath indices of triangles that form a strip along the shortest path.
07598    
07599    \sa SUMA_Dijkstra
07600    \sa SUMA_NodePath_to_EdgePath
07601    \sa SUMA_Surf_Plane_Intersect
07602    
07603    \sa labbook NIH-2 pages 158, and 159 for MissingTriangles
07604 */
07605 
07606 int *SUMA_NodePath_to_TriPath_Inters ( SUMA_SurfaceObject *SO, SUMA_SURF_PLANE_INTERSECT *SPI, int *nPath, int N_nPath, int *N_tPath)
07607 {
07608    static char FuncName[]={"SUMA_NodePath_to_TriPath_Inters"};
07609    int *tPath = NULL, e, i, N_nc, nc[3], N_HostTri, E, j, 
07610       HostTri, PrevTri, k, N1[2], N2[2], cnt, MissTri = 0, candidate;
07611    SUMA_Boolean Found, LocalHead = NOPE;
07612    
07613    SUMA_ENTRY;
07614    
07615    tPath = (int *) SUMA_calloc(2*N_nPath, sizeof(int));
07616    if (!tPath) {
07617       fprintf (SUMA_STDERR, "Error %s: Failed to allocate.\n", FuncName);
07618       SUMA_RETURN (NULL);
07619    }
07620    
07621    *N_tPath = 0;
07622    for (i=0; i < N_nPath - 1; ++i) {
07623       /* find the edge corresponding to two consecutive nodes in the path */
07624       E = SUMA_FindEdge (SO->EL, nPath[i], nPath[i+1]);
07625       if (E < 0) {
07626          fprintf (SUMA_STDERR, "Error %s: Failed in SUMA_FindEdge.\n", FuncName);
07627          SUMA_free(tPath);
07628          SUMA_RETURN(NULL);
07629       }
07630       /* find the triangles containing E and intersected by the plane */
07631       N_HostTri = SO->EL->ELps[E][2]; /* number of hosting triangles */
07632       if (N_HostTri > 2) {
07633          fprintf (SUMA_STDERR, "Warning %s: Surface is not a surface, Edge %d has more than %d hosting triangles.\n", FuncName, E, N_HostTri);
07634       }
07635       candidate = 0;
07636       /* search for a hosting triangle that was intersected */
07637       for (j=0; j < N_HostTri; ++j) {
07638          HostTri = SO->EL->ELps[E+j][1];
07639          if (SPI->isTriHit[HostTri]) { /* a candidate for adding to the path */
07640             ++candidate;
07641             if (*N_tPath > 2*N_nPath) {
07642                fprintf (SUMA_STDERR, "Error %s: N_tPath = %d > %d allocated.\n", FuncName, *N_tPath, 2*N_nPath);
07643             }  
07644             #if 1
07645             /* This block is an attempt to get All triangles intersected by plane AND having a node as part of the shortest path.
07646             It does not work well, probably because some of the functions called need fixing... */
07647             if (*N_tPath == 0) { /* if that is the first triangle in the path, add it without much fuss */
07648                tPath[*N_tPath] = HostTri; /* hosting triangle index */
07649                ++ (*N_tPath);
07650             } else { /* make sure there is continuation along edges */
07651                PrevTri = tPath[*N_tPath - 1];
07652                N_nc = SUMA_isTriLinked (&(SO->FaceSetList[3*PrevTri]), &(SO->FaceSetList[3*HostTri]), nc);
07653                if (!N_nc) {
07654                   fprintf (SUMA_STDERR, "Warning %s: Triangles %d and %d are not linked.\nAdding triangle %d anyway.\n", 
07655                      FuncName, PrevTri, HostTri, HostTri);
07656                   /* add triangle, anyway */
07657                   tPath[*N_tPath] = HostTri; 
07658                   ++ (*N_tPath);
07659                }else if (N_nc == 1) {
07660                   /* must fill triangle gap get the triangle with the common node and common edges*/
07661                   /* first, find remaining nodes in PrevTri  */
07662                   e = 0;
07663                   for (k=0; k <3; ++k) {
07664                      if (SO->FaceSetList[3*PrevTri+k] != nc[0]) {
07665                         N1[e] = SO->FaceSetList[3*PrevTri+k]; ++e;
07666                      }
07667                   }
07668                   /* then find remaining nodes in HostTri  */
07669                   e = 0;
07670                   for (k=0; k <3; ++k) {
07671                      if (SO->FaceSetList[3*HostTri+k] != nc[0]) {
07672                         N2[e] = SO->FaceSetList[3*HostTri+k]; ++e;
07673                      }
07674                   }
07675                   /* find a triangle that has either one of the following node combinations, in addition to nc[0]:
07676                   N1[0], N2[0] or N1[0], N2[1] or N1[1], N2[0] or N1[1], N2[1] */
07677                   Found = NOPE;
07678                   cnt = 0;
07679                   while (!Found && cnt < 4) {
07680                      switch (cnt) {
07681                         case 0:
07682                            MissTri = SUMA_whichTri (SO->EL, nc[0], N1[0], N2[0], 1);
07683                            if (LocalHead) fprintf (SUMA_STDERR, "%s: looking for triangle with nodes %d and %d... Tri = %d\n", 
07684                                  FuncName, N1[0], N2[0], MissTri);
07685                            break;
07686                         case 1:
07687                            MissTri = SUMA_whichTri (SO->EL, nc[0], N1[0], N2[1], 1);
07688                            if (LocalHead) fprintf (SUMA_STDERR, "%s: looking for triangle with nodes %d and %d... Tri = %d\n", 
07689                                  FuncName, N1[0], N2[1], MissTri);
07690                            break;
07691                         case 2:
07692                            MissTri = SUMA_whichTri (SO->EL, nc[0], N1[1], N2[0], 1);
07693                            if (LocalHead) fprintf (SUMA_STDERR, "%s: looking for triangle with nodes %d and %d... Tri = %d\n", 
07694                                  FuncName, N1[1], N2[0], MissTri);
07695                            break;
07696                         case 3:
07697                            MissTri = SUMA_whichTri (SO->EL, nc[0], N1[1], N2[1], 1);
07698                            if (LocalHead) fprintf (SUMA_STDERR, "%s: looking for triangle with nodes %d and %d... Tri = %d\n", 
07699                                  FuncName, N1[1], N2[1], MissTri);
07700                            break;
07701                      }
07702                      if (MissTri >= 0) {
07703                         Found = YUP;
07704                      }
07705                      ++cnt;
07706                   }
07707                   if (!Found) {
07708                      fprintf (SUMA_STDERR, "Warning %s: Failed to find missing triangle.\n", FuncName);
07709                      tPath[*N_tPath] = HostTri; 
07710                      ++ (*N_tPath);
07711                   }else {
07712                      /* add the missing triangle first, then the HostTri */
07713                      tPath[*N_tPath] = MissTri; 
07714                      ++ (*N_tPath);
07715                      tPath[*N_tPath] = HostTri; 
07716                      ++ (*N_tPath);
07717                   }
07718                }else if (N_nc == 2) {
07719                   /* Triangles share an edge so no problem, insert the new triangle in the path */
07720                   tPath[*N_tPath] = HostTri; 
07721                   ++ (*N_tPath);
07722                }else {
07723                   fprintf (SUMA_STDERR, "Error %s: Triangles %d and %d are identical.\n", FuncName, PrevTri, HostTri);
07724                   SUMA_free(tPath);
07725                   SUMA_RETURN(NULL);
07726                }
07727             }
07728             #else 
07729                tPath[*N_tPath] = HostTri; 
07730                ++ (*N_tPath);
07731             #endif   
07732          }
07733       }
07734       if (!candidate) {
07735          fprintf (SUMA_STDERR, "\aWarning %s: Nodes %d and %d of edge %d had no intersected hosting triangle.\n", FuncName, nPath[i], nPath[i+1], E);
07736          
07737       }
07738    }
07739    
07740    SUMA_RETURN (tPath);   
07741 }
07742 /*!
07743 \brief Converts a series of connected nodes into a series of connected triangles that belong to a branch.
07744 The function fails at times, picking the long instead of the short path but it is left here in case I 
07745 need it in the future.
07746  
07747    tPath = SUMA_NodePath_to_TriPath_Inters_OLD (SO, Bv, Path, N_Path, N_Tri);
07748    \param SO (SUMA_SurfaceObject *) Pointer to surface object
07749    \param Bv (SUMA_TRI_BRANCH*) Pointer to tiangle branch containing nodes in path.
07750    \param Path (int *) vector of node indices forming a path.
07751                        Sequential nodes in Path must be connected on the surface mesh.
07752    \param N_Path (int) number of nodes in the path 
07753    \param N_Tri (int *) pointer to integer that will contain the number of triangles in the path
07754                         0 if function fails.
07755    \return tPath (int *) pointer to vector containing indices of triangles forming the path. 
07756                         The indices are into SO->FaceSetList.
07757                         NULL if trouble is encountered.
07758                         
07759    \sa SUMA_NodePath_to_EdgePath
07760    \sa Path I in NIH-2, labbook page 153
07761 */
07762 int *SUMA_NodePath_to_TriPath_Inters_OLD (SUMA_SurfaceObject *SO, SUMA_TRI_BRANCH *Bv, int *Path, int N_Path, int *N_Tri)
07763 {
07764    static char FuncName[]={"SUMA_NodePath_to_TriPath_Inters_OLD"};
07765    int *tPath = NULL, ilist, i0, Tri, eTri, EdgeBuf, Tri0, Tri1, Direction, i1, loc2f, iDirSet;
07766    SUMA_Boolean LocalHead = NOPE, Found = NOPE;
07767    
07768    SUMA_ENTRY;
07769  
07770  
07771   *N_Tri = 0;
07772    tPath = (int *) SUMA_calloc(Bv->N_list+1, sizeof(int));
07773    if (!tPath) {
07774       fprintf (SUMA_STDERR, "Error %s: Failed to allocate.\n", FuncName);
07775       SUMA_RETURN (NULL);
07776    }
07777    
07778    /* the first triangle should contain the first node in the path */
07779       i0 = Path[0];
07780       Tri0 = Bv->list[0];
07781       if (SO->FaceSetList[3*Tri0] != i0 && SO->FaceSetList[3*Tri0+1] != i0 && SO->FaceSetList[3*Tri0+2] != i0) {
07782          fprintf (SUMA_STDERR, "Error %s: Did not find node %d in first triangle in branch.\n", FuncName, i0);
07783          SUMA_free(tPath);
07784          *N_Tri = 0;
07785          SUMA_RETURN (NULL);  
07786       }
07787    
07788    
07789    /* initiliaze first node results and look for the second node */
07790    tPath[0] = Tri0;
07791    *N_Tri = 1;
07792    Found = NOPE;
07793    ilist = 0;
07794 
07795    if (LocalHead)   fprintf(SUMA_STDERR, "%s: Going forward looking for third node\n", FuncName);
07796    if (N_Path > 2) {
07797       iDirSet = 2; /* search for third node in list, that helps determine the direction more reliably */
07798    }else {
07799       iDirSet = 1; /* settle for the second node */
07800    }
07801    
07802    ilist = 1;
07803    while (!Found && ilist < Bv->N_list) {
07804       tPath[*N_Tri] = Bv->list[ilist];
07805       if (SO->FaceSetList[3*Bv->list[ilist]] == Path[iDirSet] || 
07806          SO->FaceSetList[3*Bv->list[ilist]+1] == Path[iDirSet] ||
07807          SO->FaceSetList[3*Bv->list[ilist]+2] == Path[iDirSet]) {
07808             Found = YUP;
07809       }
07810       ++(*N_Tri);
07811       ++ilist;      
07812    }
07813     
07814    if (!Found) {
07815       fprintf (SUMA_STDERR, "Error %s: Did not find next node %d in branch.\n", FuncName, Path[iDirSet]);
07816       SUMA_free(tPath);
07817       *N_Tri = 0;
07818       SUMA_RETURN (NULL); 
07819    }
07820   
07821    loc2f = *N_Tri; /* number of steps to find second node in the forward direction */
07822 
07823    /* do the same in the backwards direction */
07824    tPath[0] = Tri0;
07825    *N_Tri = 1;
07826    Found = NOPE;
07827    ilist = 0;
07828    
07829    if (LocalHead) fprintf(SUMA_STDERR, "%s: Going backwards looking for third node\n", FuncName);
07830    ilist = Bv->N_list - 1;
07831    while (!Found && ilist >=  0) {
07832       tPath[*N_Tri] = Bv->list[ilist];
07833       if (LocalHead) fprintf(SUMA_STDERR, "%s: trying triangle %d for node %d.\n", FuncName, Bv->list[ilist], Path[N_Path-1]);
07834       if (SO->FaceSetList[3*Bv->list[ilist]] == Path[iDirSet] || 
07835          SO->FaceSetList[3*Bv->list[ilist]+1] == Path[iDirSet] ||
07836          SO->FaceSetList[3*Bv->list[ilist]+2] == Path[iDirSet]) {
07837             Found = YUP;
07838       }
07839       ++(*N_Tri);
07840       --ilist;      
07841    }
07842    
07843    if (*N_Tri < loc2f) { 
07844       /* go backwards, shorter. This is based on triangle count, 
07845          it would be more accurate based on distance of intersected edge */
07846       Direction = -1;
07847    } else Direction = 1;
07848    
07849    /* now do the whole thing */
07850    
07851    tPath[0] = Tri0;
07852    *N_Tri = 1;
07853    Found = NOPE;
07854    ilist = 0;
07855    if (Direction == 1) { /* move forward until you reach the last node */
07856      if (LocalHead)   fprintf(SUMA_STDERR, "%s: Going forward, final pass \n", FuncName);
07857      ilist = 1;
07858       while (!Found && ilist < Bv->N_list) {
07859          tPath[*N_Tri] = Bv->list[ilist];
07860          if (SO->FaceSetList[3*Bv->list[ilist]] == Path[N_Path-1] || 
07861             SO->FaceSetList[3*Bv->list[ilist]+1] == Path[N_Path-1] ||
07862             SO->FaceSetList[3*Bv->list[ilist]+2] == Path[N_Path-1]) {
07863                Found = YUP;
07864          }
07865          ++(*N_Tri);
07866          ++ilist;      
07867       }
07868    } else { /* move backwards */
07869       if (LocalHead) fprintf(SUMA_STDERR, "%s: Going backwards, final pass \n", FuncName);
07870       ilist = Bv->N_list - 1;
07871       while (!Found && ilist >=  0) {
07872          tPath[*N_Tri] = Bv->list[ilist];
07873          if (LocalHead) fprintf(SUMA_STDERR, "%s: trying triangle %d for node %d.\n", FuncName, Bv->list[ilist], Path[N_Path-1]);
07874          if (SO->FaceSetList[3*Bv->list[ilist]] == Path[N_Path-1] || 
07875             SO->FaceSetList[3*Bv->list[ilist]+1] == Path[N_Path-1] ||
07876             SO->FaceSetList[3*Bv->list[ilist]+2] == Path[N_Path-1]) {
07877                Found = YUP;
07878          }
07879          ++(*N_Tri);
07880          --ilist;      
07881       }
07882       
07883    }   
07884 
07885    if (!Found) {
07886       fprintf (SUMA_STDERR, "Error %s: Path not completed.\n", FuncName);
07887       SUMA_free(tPath);
07888       *N_Tri = 0;
07889       SUMA_RETURN (NULL);
07890    }else {
07891       if (LocalHead) {
07892          fprintf (SUMA_STDERR,"%s: Path is %d triangles long:\n", FuncName, *N_Tri); 
07893          for (ilist=0; ilist< *N_Tri; ++ilist) {
07894             fprintf (SUMA_STDERR,"t%d\t", tPath[ilist]);
07895          }
07896          fprintf (SUMA_STDERR,"\n");
07897       }
07898    }
07899    SUMA_RETURN (tPath);
07900 }   
07901 
07902 
07903 
07904 #ifdef SUMA_SurfQual_STANDALONE
07905 #define SURFQUAL_MAX_SURF 10  /*!< Maximum number of input surfaces */
07906 
07907 void usage_SUMA_SurfQual ()
07908    {
07909       static char FuncName[]={"usage_SUMA_SurfQual"};
07910       char * s = NULL;
07911       s = SUMA_help_basics();
07912       printf ( "\nUsage: A program to check the quality of surfaces.\n"
07913                "  SurfQual <-spec SpecFile> <-surf_A insurf> <-surf_B insurf> ...\n"
07914                "             <-sphere> [-prefix OUTPREF]  \n"
07915                "\n"
07916                "  Mandatory parameters:\n"
07917                "     -spec SpecFile: Spec file containing input surfaces.\n"
07918                "     -surf_X: Name of input surface X where X is a character\n"
07919                "              from A to Z. If surfaces are specified using two\n"
07920                "              files, use the name of the node coordinate file.\n"
07921                "  Mesh winding consistency and 2-manifold checks are performed\n"
07922                "  on all surfaces.\n"
07923                "  Most other checks are specific to spherical surfaces (see option below).\n"
07924                "     -sphere: Indicates that surfaces read are spherical.\n"
07925                "              With this option you get the following output.\n"
07926                "              - Absolute deviation between the distance (d) of each\n"
07927                "                node from the surface's center and the estimated\n"
07928                "                radius(r). The distances, abs (d - r), are \n"
07929                "                and written to the file OUTPREF_Dist.1D.dset .\n"
07930                "                The first column represents node index and the \n"
07931                "                second is the absolute distance. A colorized \n"
07932                "                version of the distances is written to the file \n"
07933                "                OUTPREF_Dist.1D.col (node index followed \n"
07934                "                by r g b values). A list of the 10 largest absolute\n"
07935                "                distances is also output to the screen.\n"
07936                "              - Also computed is the cosine of the angle between \n"
07937                "                the normal at a node and the direction vector formed\n"
07938                "                formed by the center and that node. Since both vectors\n"
07939                "                are normalized, the cosine of the angle is the dot product.\n"
07940                "                On a sphere, the abs(dot product) should be 1 or pretty \n"
07941                "                close. Nodes where abs(dot product) < 0.9 are flagged as\n"
07942                "                bad and written out to the file OUTPREF_BadNodes.1D.dset .\n"
07943                "                The file OUTPREF_dotprod.1D.dset contains the dot product \n"
07944                "                values for all the nodes. The files with colorized results\n"
07945                "                are OUTPREF_BadNodes.1D.col and OUTPREF_dotprod.1D.col .\n"
07946                "                A list of the bad nodes is also output to the screen for\n"
07947                "                convenience. You can use the 'j' option in SUMA to have\n"
07948                "                the cross-hair go to a particular node. Use 'Alt+l' to\n"
07949                "                have the surface rotate and place the cross-hair at the\n"
07950                "                center of your screen.\n"
07951                "              NOTE: For detecting topological problems with spherical\n"
07952                "                surfaces, I find the dot product method to work best.\n"              
07953                "  Optional parameters:\n"
07954                "     -prefix OUTPREF: Prefix of output files. If more than one surface\n"
07955                "                      are entered, then the prefix will have _X added\n"
07956                "                      to it, where X is a character from A to Z.\n"
07957                "                      THIS PROGRAM WILL OVERWRITE EXISTING FILES.\n"
07958                "                      Default prefix is the surface's label.\n"
07959                "\n"
07960                "  Comments:\n"
07961                "     - The colorized (.col) files can be loaded into SUMA (with the 'c' \n"
07962                "     option. By focusing on the bright spots, you can find trouble spots\n"
07963                "     which would otherwise be very difficult to locate.\n"
07964                "     - You should also pay attention to the messages output when the \n"
07965                "     surfaces are being loaded, particularly to edges (segments that \n"
07966                "     join 2 nodes) are shared by more than 2 triangles. For a proper\n"
07967                "     closed surface, every segment should be shared by 2 triangles. \n"
07968                "     For cut surfaces, segments belonging to 1 triangle only form\n"
07969                "     the edge of that surface.\n"
07970                "     - There are no utilities within SUMA to correct these defects.\n"
07971                "     It is best to fix these problems with the surface creation\n"
07972                "     software you are using.\n"
07973                "     - Some warnings may be redundant. That should not hurt you.\n"
07974                "%s"
07975                "\n", s);
07976        SUMA_free(s); s = NULL;        
07977        s = SUMA_New_Additions(0, 1); printf("%s\n", s);SUMA_free(s); s = NULL;
07978        printf("       Ziad S. Saad SSCC/NIMH/NIH ziad@nih.gov     \n");
07979        exit (0);
07980    }
07981 
07982 typedef struct {
07983    SUMA_SO_File_Type iType;
07984    char *out_prefix;
07985    char *sv_name;
07986    char *surf_names[SURFQUAL_MAX_SURF];
07987    int N_surf;
07988    char *spec_file;
07989    char *surftype;
07990 } SUMA_SURFQUAL_OPTIONS;
07991 
07992 /*!
07993    \brief parse the arguments for SurfQual program
07994    
07995    \param argv (char *)
07996    \param argc (int)
07997    \return Opt (SUMA_SURFQUAL_OPTIONS *) options structure.
07998                To free it, use 
07999                SUMA_free(Opt->out_prefix); 
08000                SUMA_free(Opt);
08001 */
08002 SUMA_SURFQUAL_OPTIONS *SUMA_SurfQual_ParseInput (char *argv[], int argc)
08003 {
08004    static char FuncName[]={"SUMA_SurfQual_ParseInput"}; 
08005    SUMA_SURFQUAL_OPTIONS *Opt=NULL;
08006    int kar, i, ind;
08007    char *outprefix;
08008    SUMA_Boolean brk = NOPE;
08009    SUMA_Boolean LocalHead = NOPE;
08010 
08011    SUMA_ENTRY;
08012    
08013    Opt = (SUMA_SURFQUAL_OPTIONS *)SUMA_malloc(sizeof(SUMA_SURFQUAL_OPTIONS));
08014 
08015    kar = 1;
08016    Opt->iType = SUMA_FT_NOT_SPECIFIED;
08017    Opt->out_prefix = NULL;
08018    Opt->sv_name = NULL;
08019    Opt->spec_file = NULL;
08020    Opt->N_surf = -1;
08021    Opt->surftype = NULL;
08022    for (i=0; i<SURFQUAL_MAX_SURF; ++i) { Opt->surf_names[i] = NULL; }
08023         brk = NOPE;
08024    
08025         while (kar < argc) { /* loop accross command ine options */
08026                 /*fprintf(stdout, "%s verbose: Parsing command line...\n", FuncName);*/
08027                 if (strcmp(argv[kar], "-h") == 0 || strcmp(argv[kar], "-help") == 0) {
08028                          usage_SUMA_SurfQual();
08029           exit (0);
08030                 }
08031                 
08032       /* skip the options parsed in SUMA_ParseInput_basics */
08033                 SUMA_SKIP_COMMON_OPTIONS(brk, kar);
08034       
08035       if (!brk && (strcmp(argv[kar], "-sphere") == 0)) {
08036                         if (Opt->surftype) {
08037             SUMA_S_Err("Surface type already specified.\nOnly one type allowed.");
08038             exit(1);
08039          }
08040          Opt->surftype = argv[kar];
08041                         brk = YUP;
08042                 }
08043       
08044       if (!brk && (strcmp(argv[kar], "-spec") == 0)) {
08045          kar ++;
08046                         if (kar >= argc)  {
08047                                 fprintf (SUMA_STDERR, "need argument after -spec \n");
08048                                 exit (1);
08049                         }
08050                         Opt->spec_file = argv[kar];
08051                         brk = YUP;
08052                 }
08053             
08054       if (!brk && (strcmp(argv[kar], "-prefix") == 0)) {
08055          kar ++;
08056                         if (kar >= argc)  {
08057                                 fprintf (SUMA_STDERR, "need argument after -prefix \n");
08058                                 exit (1);
08059                         }
08060                         Opt->out_prefix = SUMA_copy_string(argv[kar]);
08061                         brk = YUP;
08062                 }
08063             
08064       if (!brk && (strncmp(argv[kar], "-surf_", 6) == 0)) {
08065                         if (kar + 1>= argc)  {
08066                                 fprintf (SUMA_STDERR, "need argument after -surf_X SURF_NAME \n");
08067                                 exit (1);
08068                         }
08069                         ind = argv[kar][6] - 'A';
08070          if (ind < 0 || ind >= SURFQUAL_MAX_SURF) {
08071             fprintf (SUMA_STDERR, "-surf_X SURF_NAME option is out of range.\n");
08072                                 exit (1);
08073          }
08074          kar ++;
08075          Opt->surf_names[ind] = argv[kar];
08076          Opt->N_surf = ind+1;
08077          brk = YUP;
08078                 }
08079       
08080       
08081       if (!brk) {
08082                         fprintf (SUMA_STDERR,"Error %s:\nOption %s not understood. Try -help for usage\n", FuncName, argv[kar]);
08083                         exit (1);
08084                 } else {        
08085                         brk = NOPE;
08086                         kar ++;
08087                 }
08088       
08089    }
08090    
08091    if (Opt->N_surf < 1) {
08092       SUMA_SL_Err("No surface specified.");
08093       exit(1);
08094    }
08095       
08096    SUMA_RETURN (Opt);
08097      
08098 }
08099 
08100 int main (int argc,char *argv[])
08101 {/* Main */    
08102    static char FuncName[]={"SurfQual"};
08103    char *OutName = NULL, ext[5], *prefix = NULL, *shist=NULL;
08104    SUMA_SURFQUAL_OPTIONS *Opt; 
08105    int SO_read = -1;
08106    int i, cnt, trouble;
08107    SUMA_SurfaceObject *SO = NULL;
08108    SUMA_SurfSpecFile Spec;
08109    void *SO_name = NULL;
08110    SUMA_Boolean DoConv = NOPE, DoSphQ = NOPE;   
08111    SUMA_Boolean LocalHead = NOPE;
08112         
08113    SUMA_mainENTRY;
08114    
08115    SUMA_STANDALONE_INIT;
08116    
08117         /* Allocate space for DO structure */
08118         SUMAg_DOv = SUMA_Alloc_DisplayObject_Struct (SUMA_MAX_DISPLAYABLE_OBJECTS);
08119    
08120    if (argc < 4)
08121        {
08122           usage_SUMA_SurfQual();
08123           exit (1);
08124        }
08125    
08126    Opt = SUMA_SurfQual_ParseInput (argv, argc);
08127    
08128    /* read all surfaces */
08129    if (!SUMA_Read_SpecFile (Opt->spec_file, &Spec)) {
08130                 fprintf(SUMA_STDERR,"Error %s: Error in SUMA_Read_SpecFile\n", FuncName);
08131                 exit(1);
08132         }
08133    SO_read = SUMA_spec_select_surfs(&Spec, Opt->surf_names, SURFQUAL_MAX_SURF, 0);
08134    if ( SO_read != Opt->N_surf )
08135    {
08136            if (SO_read >=0 )
08137          fprintf(SUMA_STDERR,"Error %s:\nFound %d surfaces, expected %d.\n", FuncName,  SO_read, Opt->N_surf);
08138       exit(1);
08139    }
08140    /* now read into SUMAg_DOv */
08141    if (!SUMA_LoadSpec_eng(&Spec, SUMAg_DOv, &SUMAg_N_DOv, Opt->sv_name, 0, SUMAg_CF->DsetList) ) {
08142            fprintf(SUMA_STDERR,"Error %s: Failed in SUMA_LoadSpec_eng\n", FuncName);
08143       exit(1);
08144    }
08145   
08146    DoConv = NOPE;
08147    DoSphQ = NOPE;   
08148    if (Opt->surftype) {
08149       if (!strcmp(Opt->surftype, "-sphere")) { 
08150          DoSphQ = YUP;
08151       }else {
08152          /* Don't complain anymore, maybe winding checking is all users need */
08153       }
08154    }
08155    
08156    for (i=0; i < Opt->N_surf; ++i) {/* loop to read in surfaces */
08157       /* now identify surface needed */
08158       SO = SUMA_find_named_SOp_inDOv(Opt->surf_names[i], SUMAg_DOv, SUMAg_N_DOv);
08159       if (!SO) {
08160          fprintf (SUMA_STDERR,"Error %s:\n"
08161                               "Failed to find surface %s\n"
08162                               "in spec file. Use full name.\n",
08163                               FuncName, Opt->surf_names[i]);
08164          exit(1);
08165       }
08166       
08167       if (!SO->EL) SUMA_SurfaceMetrics(SO, "EdgeList", NULL);
08168       if (!SO->MF) SUMA_SurfaceMetrics(SO, "MemberFace", NULL);
08169       if (!SO->Label) SUMA_SurfaceFileName(SO, NOPE);
08170       
08171       
08172       /* do the quality thing based on the Opt->surftype */
08173       if (!Opt->out_prefix) prefix = SUMA_copy_string(SO->Label);
08174       else prefix = SUMA_copy_string (Opt->out_prefix);
08175       
08176       /* check the winding */
08177       if (!SUMA_MakeConsistent (SO->FaceSetList, SO->N_FaceSet, SO->EL, 0, &trouble)) {
08178          SUMA_S_Warn("Failed to make sure surface's mesh is consistently wound.\n"
08179                      "You should fix the mesh.\n");
08180       }
08181       if (DoConv) {
08182          float *Cx = NULL;
08183          if (Opt->N_surf > 1) {
08184             sprintf(ext,"_%c", 65+i);
08185             OutName = SUMA_append_replace_string (prefix, "_Conv_detail.1D.dset", ext, 0);
08186          } else { 
08187             OutName = SUMA_append_string (prefix, "_Conv_detail.1D.dset");
08188          }
08189          Cx = SUMA_Convexity_Engine ( SO->NodeList, SO->N_Node, 
08190                                       SO->NodeNormList, SO->FN, OutName);
08191          if (Cx) SUMA_free(Cx); Cx = NULL;
08192          if (OutName) SUMA_free(OutName); OutName = NULL;
08193       } 
08194       if (DoSphQ) {
08195          if (Opt->N_surf > 1) {
08196             sprintf(ext,"_%c", 65+i);
08197             OutName = SUMA_append_string (prefix, ext);
08198          } else { 
08199             OutName = SUMA_copy_string (prefix);
08200          }
08201          shist = SUMA_HistString (NULL, argc, argv, NULL);
08202          SUMA_SphereQuality (SO, OutName, shist);   
08203          if (shist) SUMA_free(shist); shist = NULL;
08204          if (OutName) SUMA_free(OutName); OutName = NULL;
08205       }
08206       
08207       if (prefix) SUMA_free(prefix); prefix = NULL;
08208    }
08209    
08210   
08211    if (trouble) { /* put winding problem here to make it visible */
08212       fprintf (SUMA_STDERR,"\n");
08213       SUMA_S_Warn("Mesh is not consistent, use ConvertSurface's -make_consistent \n"
08214                   "option to fix the problem before proceeding further.\n"
08215                   "Other results reported by this and other programs\n"
08216                   "may be incorrect if mesh is not consistently wound.\n" ); 
08217    } else {
08218       fprintf (SUMA_STDERR,"\n");
08219       fprintf (SUMA_STDERR,"Surface is consistently wound\n");
08220    }
08221    { 
08222       int eu;
08223       SUMA_EULER_SO(SO, eu);
08224       fprintf (SUMA_STDERR,"\n");
08225       fprintf(SUMA_STDERR,"Surface Euler number is: %d\n", eu);
08226    }
08227    if ((SO->EL->min_N_Hosts == 1 || SO->EL->max_N_Hosts == 1)) {
08228          fprintf (SUMA_STDERR,"\n");
08229          fprintf(SUMA_STDERR,"Warning %s:\n Min/Max number of edge hosting triangles: [%d/%d] \n", FuncName, SO->EL->min_N_Hosts, SO->EL->max_N_Hosts);
08230          fprintf(SUMA_STDERR," You have edges that form a border in the surface.\n");
08231    }
08232    if (SO->EL->min_N_Hosts == 2 && SO->EL->max_N_Hosts == 2) {
08233       fprintf (SUMA_STDERR,"\n");
08234       fprintf(SUMA_STDERR,"Surface is closed and is a 2-manifold.");
08235    }
08236    if (SO->EL->min_N_Hosts > 2 || SO->EL->max_N_Hosts > 2) {
08237       fprintf (SUMA_STDERR,"\n");
08238       fprintf(SUMA_STDERR, "Warning %s:\n"
08239                            "Min/Max number of edge hosting triangles: [%d/%d] \n", FuncName, SO->EL->min_N_Hosts, SO->EL->max_N_Hosts);
08240       fprintf(SUMA_STDERR, "Warning %s:\n"
08241                            " You have edges that belong to more than two triangles.\n"
08242                            " Bad for analysis assuming surface is a 2-manifold.\n", FuncName);
08243       if (1) {
08244          int iii=0;
08245          fprintf(SUMA_STDERR, " These edges are formed by the following nodes:\n");
08246          for (iii = 0; iii < SO->EL->N_EL; ++iii) { 
08247             if (SO->EL->ELps[iii][2] > 2) fprintf (SUMA_STDERR," %d: Edge [%d %d] shared by %d triangles.\n", 
08248                                              iii+1, SO->EL->EL[iii][0], SO->EL->EL[iii][1] , SO->EL->ELps[iii][2] );
08249          }
08250       }
08251    }
08252    
08253    fprintf (SUMA_STDERR,"\n");
08254    
08255    SUMA_LH("clean up");
08256    if (Opt->out_prefix) SUMA_free(Opt->out_prefix); Opt->out_prefix = NULL;
08257    if (Opt) SUMA_free(Opt);   
08258    if (!SUMA_Free_Displayable_Object_Vect (SUMAg_DOv, SUMAg_N_DOv)) {
08259       SUMA_SL_Err("DO Cleanup Failed!");
08260    }
08261    if (!SUMA_Free_CommonFields(SUMAg_CF)) SUMA_error_message(FuncName,"SUMAg_CF Cleanup Failed!",1);
08262    
08263    SUMA_RETURN(0);
08264 } 
08265 #endif
08266 
08267 
08268 
08269 #if 0
08270    /************************** BEGIN Branch Functions **************************/ 
08271    /* these are functions that were ported to support the first version of SUMA_Surf_Plane_Intersect was was to be identical to
08272    Surf_Plane_Intersect. They are left here in case I need them in the future. */
08273    
08274                   /***
08275 
08276                   File : SUMA_FindBranch.c
08277                   Author : Ziad Saad
08278                   Date : Thu Nov 12 16:33:34 CST 1998
08279 
08280                   Purpose : 
08281                       This is a C version of the matlab function SUMA_FindBranch2, check out the help 
08282                      over there.
08283 
08284                       This version is supposed to be faster than the working previous one called SUMA_FindBranch.c_V1
08285                      If you want to use SUMA_FindBranch.c_V1, you need to rename it to SUMA_FindBranch.c and make the appropriate 
08286                      changes in prototype.h
08287 
08288                      the working version of SUMA_FindBranch.c_V1 is in Backup010499 directory and should be used with 
08289                      Surf_Plane_Intersect.c_V1
08290 
08291                   Usage : 
08292 
08293 
08294                   Input paramters : 
08295                             InterMat (int **) pointer to a 2D int array of dimention [IMsz, 4]
08296                            IMsz (int) number or rows in InterMat
08297                            InterNodes (float **) pointer to a 2D float array that contains the
08298                               intersection nodes XYZ coordinates, size of the array is [INsz,3]
08299                            verbose (int) verbose flag (0/1)
08300                            WBsz (int *) the number of elements in WeldedBranch
08301 
08302                   Returns : 
08303                             WeldedBranch (SUMA_BRANCH *) is a pointer to structures branch that will contain 
08304                               the various branches. You need to pass a pointer only, allocation for this 
08305                               pointer should be done from the calling function.
08306                               see : /home/ziad/Programs/C/Z/Zlib/mystructs.h for details on
08307                               the fields of the structures branch
08308 
08309                            NOTE : The function uses static allocation for WeldedBranch
08310                            do not try to free WeldedBranch
08311 
08312 
08313 
08314                   ***/
08315                   SUMA_BRANCH * SUMA_FindBranch (int ** InterMat, int N_InterMat, float ** InterNodes, int ** NodeLoc_in_InterMat, int verbose,  int * WBsz)
08316                   {/*SUMA_FindBranch*/
08317                      int DBG , VeryFirstSeed, Seed, sz_Branch, kk;
08318                      int n_comp = 0, ntmpint, nunqrow , NodeIndex , curnode , BranchIndex;
08319                      int ntmpint2D_V2, N_vunq , brEnd1 , brEnd2 , i, k;
08320                      int tmpint2D_V2[1][2], *v, *vunq;
08321                      int *tmpint, *unqrow, iii, GotSeed;
08322                      static char FuncName[]={"SUMA_FindBranch"};
08323                      float Dprecision;
08324                      static SUMA_BRANCH * branch;
08325                      struct  timeval  start_time, tt_sub, start_time2;
08326                      float DT_WELDSUMA_BRANCH, DT_BUILDSUMA_BRANCH, DT_WELDSUMA_BRANCHONLY ,DT_FINDININTVECT, DT_VUNQ;
08327                      FILE *TimeOut;
08328                      SUMA_Boolean LocalHead = NOPE; 
08329 
08330                      SUMA_ENTRY;
08331 
08332                      if (LocalHead) SUMA_disp_dmat (NodeLoc_in_InterMat, 20, 4, 1); 
08333 
08334                      /* open a file to output timing info and run some stats on them */
08335                      TimeOut = fopen("FB.TimeOut","a");   
08336 
08337                      DBG = 1;
08338                      Dprecision = 0.001;
08339 
08340                      VeryFirstSeed = 0;
08341 
08342                      /* Now you need to find the different branches */
08343 
08344                      Seed = VeryFirstSeed;   /* That should not matter */
08345 
08346                      /* Allocate for branch */
08347                      if (!branch)
08348                         {
08349                            branch = (SUMA_BRANCH *) SUMA_calloc(SUMA_BRANCHMAX, sizeof(SUMA_BRANCH));
08350                            if (!branch )
08351                               {
08352                                  fprintf (SUMA_STDERR, "Error %s: Could not allocate for branch", FuncName);
08353                                  SUMA_RETURN (NULL);
08354                               }
08355                         }
08356 
08357                      if (LocalHead) fprintf(SUMA_STDERR, "%s : Determining branches\n", FuncName);
08358 
08359                      /* Start timer for next function */
08360                      SUMA_etime(&start_time,0);
08361 
08362                      /* initialize the first branch */
08363                      BranchIndex = 0;
08364                      NodeIndex = 0;
08365                      branch[BranchIndex].start = Seed;
08366                      branch[BranchIndex].list[NodeIndex] = branch[BranchIndex].start;
08367                      curnode = branch[BranchIndex].start;
08368                      n_comp = N_InterMat;
08369                      ntmpint2D_V2 = 0;
08370                      while (n_comp)
08371                         {
08372                            /* see if you can find the node */
08373                            /*printf ("curnode = %d, n_comp = %d\n", curnode, n_comp);                   */
08374                            if (NodeLoc_in_InterMat[curnode][2] > -1)
08375                               {
08376                                  tmpint2D_V2[0][0] = NodeLoc_in_InterMat[curnode][2];
08377                                  tmpint2D_V2[0][1] = NodeLoc_in_InterMat[curnode][3];
08378                                  NodeLoc_in_InterMat[curnode][2] = -1;
08379                                  ntmpint2D_V2 = 1;
08380                               }
08381                            else
08382                            if (NodeLoc_in_InterMat[curnode][0] > -1)
08383                               {
08384                                  tmpint2D_V2[0][0] = NodeLoc_in_InterMat[curnode][0];
08385                                  tmpint2D_V2[0][1] = NodeLoc_in_InterMat[curnode][1];
08386                                  NodeLoc_in_InterMat[curnode][0] = -1;
08387                                  ntmpint2D_V2 = 1;
08388                               }
08389                            else
08390                               ntmpint2D_V2 = 0;
08391 
08392                            if (!ntmpint2D_V2)  /* Nothing found */
08393                               {
08394                                  /* store the last point as a stopping point */
08395                                  branch[BranchIndex].last = branch[BranchIndex].list[NodeIndex];
08396                                  branch[BranchIndex].listsz = NodeIndex + 1;
08397 
08398                                  /* start a new branch */
08399                                  /*pick any seed one that does not have a -1 entry in NodeLoc_in_InterMat*/
08400                                  iii = 0;
08401                                  GotSeed = 0;
08402                                  while (!GotSeed)
08403                                  {
08404                                     if (NodeLoc_in_InterMat[iii][2] > -1)
08405                                        {
08406 
08407                                           Seed = InterMat[NodeLoc_in_InterMat[iii][2]][NodeLoc_in_InterMat[iii][3]];
08408                                           GotSeed = 1;
08409                                        }
08410                                     else
08411                                     if (NodeLoc_in_InterMat[iii][0] > -1)
08412                                        {
08413                                           Seed = InterMat[NodeLoc_in_InterMat[iii][0]][NodeLoc_in_InterMat[iii][1]];
08414                                           GotSeed = 1;
08415                                        }
08416                                     else
08417                                        {
08418                                           ++iii;
08419                                           GotSeed = 0;
08420                                        }
08421                                  }
08422                                  ++BranchIndex;
08423                                  NodeIndex=0;
08424                                  branch[BranchIndex].start = Seed;
08425                                  branch[BranchIndex].list[NodeIndex] = branch[BranchIndex].start;
08426                                  curnode = branch[BranchIndex].start;
08427                               }
08428                            else /* That's a normal point, add it */
08429                               {
08430                                  ++NodeIndex;
08431                                  if (tmpint2D_V2[0][1]) /* take the first element */
08432                                     branch[BranchIndex].list[NodeIndex] = 
08433                                        InterMat[tmpint2D_V2[0][0]][0];
08434                                     else /* take second element */
08435                                     branch[BranchIndex].list[NodeIndex] = 
08436                                        InterMat[tmpint2D_V2[0][0]][1];
08437 
08438                                  /* make the new node current */
08439                                  curnode = branch[BranchIndex].list[NodeIndex];
08440 
08441                                  --n_comp;
08442                               }
08443 
08444                         }
08445 
08446                      /* now store the very last point as a stopping point */
08447 
08448                      branch[BranchIndex].last = branch[BranchIndex].list[NodeIndex];
08449                      branch[BranchIndex].listsz = NodeIndex + 1;
08450 
08451                      sz_Branch = BranchIndex + 1;
08452 
08453                      /* stop timer */
08454                      DT_BUILDSUMA_BRANCH = SUMA_etime(&start_time,1);
08455 
08456                      if (LocalHead) fprintf(SUMA_STDERR, "%s : Welding branches\n", FuncName);
08457 
08458                      /* now, if possible, link the branches together */
08459                      /* Start timer for next function */
08460                      SUMA_etime(&start_time,0);
08461 
08462                      /* initialize some variables */
08463                      v = (int *)SUMA_calloc(2*sz_Branch,sizeof(int));
08464                      if (!v)
08465                         {
08466                            fprintf (SUMA_STDERR, "Error %s: Could not allocate", FuncName);
08467                            SUMA_RETURN (NULL);
08468                         }
08469                      for (i=0;i<sz_Branch;++i)
08470                         {
08471                            v[i] = branch[i].start;
08472                            v[i+sz_Branch] = branch[i].last;
08473                         }
08474 
08475 
08476                      vunq = SUMA_UniqueInt (v, 2*sz_Branch, &N_vunq, 0);
08477 
08478                      for (i=0;i<N_vunq;++i)
08479                         {
08480                            /* find out how many time each end of a branch is used */
08481 
08482                            tmpint = SUMA_Find_inIntVect (v, 2*sz_Branch, vunq[i], &ntmpint);
08483 
08484                            if (ntmpint == 2)
08485                               {
08486                                  /*good, two branches can be joined together */
08487                                  if (tmpint[0] >= sz_Branch)
08488                                     {   
08489                                        tmpint[0] = tmpint[0] - sz_Branch;
08490                                        brEnd1 = 1;
08491                                     }
08492                                  else
08493                                     brEnd1 = 0;
08494                                  if (tmpint[1] >= sz_Branch)
08495                                     {   
08496                                        tmpint[1] = tmpint[1] - sz_Branch;
08497                                        brEnd2 = 1;
08498                                     }
08499                                  else
08500                                     brEnd2 = 0;
08501 
08502                                  if (tmpint[1] != tmpint[0])
08503                                     {   /*   Path is not circular, join together */
08504 
08505                                        SUMA_WeldBranches (branch, &sz_Branch, tmpint[0] ,tmpint[1] , brEnd1, brEnd2);
08506 
08507                                        for (k=0;k<sz_Branch;++k)
08508                                           {
08509                                              v[k] = branch[k].start;
08510                                              v[k+sz_Branch] = branch[k].last;
08511                                           }
08512                                     }
08513                               }
08514                            SUMA_free(tmpint);
08515                         }
08516 
08517                      /* Now go through and determine which branches are closed loops */
08518                      for (i=0;i<sz_Branch; ++i)
08519                         {
08520                            if (branch[i].start == branch[i].last)
08521                               branch[i].closed = 1;
08522                            else
08523                               branch[i].closed = 0;
08524 
08525                         }
08526 
08527 
08528                      *WBsz = sz_Branch; /* store the number of branches to SUMA_RETURN it */
08529 
08530                      /* stop timer */
08531                      DT_WELDSUMA_BRANCH = SUMA_etime(&start_time,1);
08532 
08533                      if (LocalHead) fprintf(SUMA_STDERR, "%s : Freeing allocation\n", FuncName);
08534 
08535                      SUMA_free(vunq);
08536                      SUMA_free(v);
08537 
08538                      /* Printout timing info on screen */
08539                      if (LocalHead) {
08540                         printf ("\n\t\t%s, time fractions :\n",FuncName);
08541                         printf ("\t\t\tDT_WELDSUMA_BRANCH time: %f sec\n", DT_WELDSUMA_BRANCH);
08542                         printf ("\t\t\t DT_BUILDSUMA_BRANCH percent time : %f sec\n",  DT_BUILDSUMA_BRANCH);
08543                      }
08544                      fprintf(TimeOut, "%f\t%f\n", 
08545                         DT_BUILDSUMA_BRANCH, DT_WELDSUMA_BRANCH ); 
08546 
08547 
08548                      fclose(TimeOut);
08549                      SUMA_RETURN (branch);
08550 
08551                   }/*SUMA_FindBranch*/
08552 
08553                   /***
08554 
08555                   File : SUMA_WeldBranches.c
08556                   Author : Ziad Saad
08557                   Date : Sat Nov 14 19:30:19 CST 1998
08558 
08559                   Purpose : 
08560                      mimics the function SUMA_WeldBranches.m, check out the help over there.
08561 
08562                       Except that the SUMA_RETURNeed welded branches are not in the same order as
08563                      those SUMA_RETURNeed by the matlab function
08564 
08565                   Usage : 
08566                    void SUMA_WeldBranches ( BRANCH *branch, int *sz_Branch, int brIndx1, int brIndx2 , int brEnd1, int brEnd2 );
08567 
08568 
08569                   Input paramters : 
08570                       branch   (BRANCH *)   a vector of structures BRANCH
08571                      sz_Branch   (int *)   pointer to the scalar containing the number of elements of branch
08572                      brIndx1   (int)   index (into branch) of the first branch to weld
08573                      brIndx2    (int)   index (into branch) of the second branch to weld
08574                      brEnd1   (int)   if 0 then weld at start of branch 1
08575                                     if 1 then weld at end of branch 1
08576                      brEnd2   (int) same as brEnd1 but for branch 2
08577 
08578                   Returns : 
08579                      nothing, but what it does is weld branch1 to branch2 and puts the welded branch in the position of
08580                      min(branch1, branch2). The returned branch is always one branch shorter than the branch sent into the
08581                      function.
08582 
08583 
08584                   Support : 
08585 
08586 
08587 
08588                   Side effects : 
08589 
08590 
08591 
08592                   ***/
08593                   void SUMA_WeldBranches ( SUMA_BRANCH *branch, int *sz_Branch, int brIndx1, int brIndx2 , int brEnd1, int brEnd2 )
08594                   {/*SUMA_WeldBranches*/
08595                      SUMA_BRANCH tmp;
08596                      int nlst1, nlst2, k, tmpreplace, tmpmove;
08597                      static char FuncName[]={"SUMA_WeldBranches"};
08598                      SUMA_Boolean LocalHead = NOPE;
08599 
08600                      SUMA_ENTRY;
08601 
08602                      nlst1 = branch[brIndx1].listsz;
08603                      nlst2 = branch[brIndx2].listsz;
08604                      tmp.listsz = nlst1 + nlst2 - 1;
08605 
08606                      if (!brEnd1  && !brEnd2)
08607                         {
08608                            tmp.last = branch[brIndx2].last;
08609                            tmp.start = branch[brIndx1].last;
08610                            for (k= nlst1; k>0; --k)
08611                               tmp.list[nlst1-k] =  branch[brIndx1].list[k-1];   
08612                            for (k=1;k<nlst2;++k) /*skip the common element */
08613                               tmp.list[nlst1+k-1] = branch[brIndx2].list[k];
08614                         }
08615                      else if (brEnd1  && brEnd2)
08616                         {
08617                            tmp.last = branch[brIndx2].start;
08618                            tmp.start = branch[brIndx1].start;
08619                            for (k= 0; k <nlst1; ++k)
08620                               tmp.list[k] = branch[brIndx1].list[k];
08621                            for (k=nlst2; k >1 ; --k)
08622                               tmp.list[nlst1+nlst2-k] = branch[brIndx2].list[k-2];
08623                         }   
08624                      else if (!brEnd1 && brEnd2)
08625                         {
08626                            tmp.last = branch[brIndx2].start;
08627                            tmp.start = branch[brIndx1].last;
08628                            for (k=nlst1; k > 0; --k)
08629                               tmp.list[nlst1 - k] = branch[brIndx1].list[k-1];
08630                            for (k=nlst2; k > 1; --k)
08631                               tmp.list[nlst1+nlst2-k] = branch[brIndx2].list[k-2];
08632                         }
08633                      else if (brEnd1 && !brEnd2)
08634                         {
08635                            tmp.last = branch[brIndx2].last;
08636                            tmp.start = branch[brIndx1].start;
08637                            for (k=0;k<nlst1;++k)
08638                               tmp.list[k] = branch[brIndx1].list[k];
08639                            for (k=0;k<nlst2-1;++k)
08640                               tmp.list[nlst1+k] = branch[brIndx2].list[k+1];
08641                         }
08642 
08643                      /* decide where to put the welded branch and whether to move the last branch (or the one before it) up */
08644                      if (brIndx1 > brIndx2)
08645                         {
08646                            tmpreplace = brIndx2;
08647                            tmpmove = brIndx1;
08648                         }
08649                      else
08650                         {
08651                            tmpreplace = brIndx1;
08652                            tmpmove = brIndx2;
08653                         }
08654                      /* replace branch[tmpreplace]   with tmp */
08655                      branch[tmpreplace].start = tmp.start;
08656                      branch[tmpreplace].last = tmp.last;
08657                      branch[tmpreplace].listsz = tmp.listsz;
08658                      for(k=0;k<branch[tmpreplace].listsz;++k)
08659                         branch[tmpreplace].list[k] = tmp.list[k];
08660 
08661                      /*copy branch[sz_Branch-1] (the last branch) into position brIndx2 */
08662                      /*by now, tmpmove is definetly larger than tmpreplace*/
08663                      /* if tmpmove is not the last branch, then move the last branch up one*/
08664                      /* otherwise, no need to move anything */
08665 
08666                      if (tmpmove < *sz_Branch-1)
08667                         {
08668                            branch[tmpmove].start = branch[*sz_Branch-1].start;
08669                            branch[tmpmove].last = branch[*sz_Branch-1].last;
08670                            branch[tmpmove].listsz = branch[*sz_Branch-1].listsz;
08671                            for(k=0;k<branch[tmpmove].listsz;++k)
08672                               branch[tmpmove].list[k] = branch[*sz_Branch-1].list[k];
08673                         }
08674 
08675                      /* change the size of the branch vector */
08676                      --*sz_Branch;
08677 
08678                      SUMA_RETURNe;
08679 
08680                   }/*SUMA_WeldBranches*/
08681 
08682    /************************** END Branch Functions **************************/                   
08683 
08684 
08685 #endif 
 

Powered by Plone

This site conforms to the following standards: