1 #include "SUMA_suma.h"
2 
3 extern int *z_rand_order(int bot, int top, long int seed) ;
4 
5 /*!
6    Fill a 3x4 affine matrix that performs a rotation
7    about an axis Ax passing though point C
8    if C is NULL rotation it is set to 0, 0, 0
9    Ax should be a unit vector
10    rotation angle is in radians
11 */
SUMA_BuildRotationMatrix(double * C,double * Ax,double alpha,double mat[4][4])12 SUMA_Boolean SUMA_BuildRotationMatrix(double *C, double *Ax,
13                                       double alpha, double mat[4][4])
14 {
15    static char FuncName[] = {"SUMA_BuildRotationMatrix"};
16    double mm[3], Cr[3];
17 
18    SUMA_ENTRY;
19 
20    if (!mat || !Ax) SUMA_RETURN(NOPE);
21 
22    SUMA_3Dax_Rotation_Matrix(Ax, alpha, mat);
23    if (C) {
24       SUMA_ROTATE_ABOUT_AXIS(C, Ax, alpha, Cr);
25       mat[0][3] = -Cr[0]+C[0];
26       mat[1][3] = -Cr[1]+C[1];
27       mat[2][3] = -Cr[2]+C[2];
28    } else {
29       mat[0][3] = mat[1][3] = mat[2][3] = 0.0;
30    }
31    mat[3][0] = mat[3][1] = mat[3][2] = 0.0; mat[3][3] = 1.0;
32 
33    SUMA_RETURN(YUP);
34 }
35 
36 /*!
37    \brief Find boundary triangles,
38    those that have an edge that is shared by less than 2 triangles.
39 
40    \param SO
41    \param boundt if not NULL, it is a pre-allocated vector that will
42                 contain the boundary triangles on return
43                 If bount_asmask then boundt should be SO->N_FaceSet long
44                 and upon return, if bount[k] then triangle k is a boundary
45                 triangle.
46                 If ! boundt_asmask then boundt the 1st N_boundt values
47                 of boundt contain indices to the boundary triangles.
48    \param boundt_asmask: Flag indicating how boundt is to be interpreted.
49    \return N_boundt: total number of boundary triangles
50 */
SUMA_BoundaryTriangles(SUMA_SurfaceObject * SO,int * boundt,int boundt_asmask)51 int SUMA_BoundaryTriangles (SUMA_SurfaceObject *SO, int *boundt,
52                             int boundt_asmask )
53 {
54    static char FuncName[]={"SUMA_BoundaryTriangles"};
55    int k, N_boundt=0;
56    byte *visited=NULL;
57    SUMA_Boolean LocalHead = NOPE;
58 
59    SUMA_ENTRY;
60 
61    if (!SO->EL) SUMA_SurfaceMetrics(SO, "EdgeList", NULL);
62    if (!(visited = (byte *)SUMA_calloc(SO->N_FaceSet, sizeof(byte)))) {
63       SUMA_S_Err("Failed to allocate");
64       SUMA_RETURN(0);
65    }
66    if (boundt) {
67       if (boundt_asmask) for (k=0; k<SO->N_FaceSet; ++k) boundt[k]=0;
68    }
69    N_boundt=0;
70    k=0;
71    while (k<SO->EL->N_EL) {
72       /* find edges that form boundaries */
73       if (SO->EL->ELps[k][2] == 1 && !visited[SO->EL->ELps[k][1]]) {
74          if (boundt) {
75             if (boundt_asmask) boundt[SO->EL->ELps[k][1]] = 1;
76             else  {
77                boundt[N_boundt] = SO->EL->ELps[k][1];
78             }
79          }
80          visited[SO->EL->ELps[k][1]]=1;
81          ++N_boundt;
82       }
83       ++k;
84    }
85    if (visited) SUMA_free(visited); visited=NULL;
86    SUMA_RETURN(N_boundt);
87 }
88 
89 /*!
90    \brief Calculate the sine and cosines of angles in a triangle
91    return -2 where calculation fails
92 */
SUMA_TriTrig(float * p1,float * p2,float * p3,double * s,double * c,double * a)93 SUMA_Boolean SUMA_TriTrig( float *p1, float *p2, float *p3,
94                            double *s, double *c, double *a)
95 {
96    static char FuncName[]={"SUMA_TriTrig"};
97    double U13[3], U12[3], U23[3], U21[3], X[3];
98    double Xn, Un13, Un12, Un23, Up1, Up2, Up3;
99    int k;
100    SUMA_Boolean LocalHead = NOPE;
101 
102    SUMA_ENTRY;
103    if (!p1 || !p2 || !p3 || !s || !c) SUMA_RETURN(NOPE);
104 
105 #if 0
106    if (LocalHead) {
107       fprintf(SUMA_STDERR, "%s:\n"
108                            "n1=[%f, %f, %f];\n"
109                            "n2=[%f, %f, %f];\n"
110                            "n3=[%f, %f, %f];\n",
111                            FuncName,
112                            p1[0], p1[1], p1[2],
113                            p2[0], p2[1], p2[2],
114                            p3[0], p3[1], p3[2]);
115    }
116 #endif
117    /* vectors and their norms */
118    Un12 = Un13 = Un23 = 0.0f;
119    for (k=0;k<3;++k) {
120       U12[k] = p2[k] - p1[k]; Un12 += (U12[k]*U12[k]);
121       U21[k] = p1[k] - p2[k];
122       U13[k] = p3[k] - p1[k]; Un13 += (U13[k]*U13[k]);
123       U23[k] = p3[k] - p2[k]; Un23 += (U23[k]*U23[k]);
124    }
125 
126 #if 0
127    if (LocalHead) {
128       fprintf(SUMA_STDERR, "%s:\n"
129                            "U12=[%f, %f, %f]; Un12^2=%f;\n"
130                            "U13=[%f, %f, %f]; Un13^2=%f;\n"
131                            "U23=[%f, %f, %f]; Un23^2=%f;\n",
132                            FuncName,
133                            U12[0], U12[1], U12[2], Un12,
134                            U13[0], U13[1], U13[2], Un13,
135                            U23[0], U23[1], U23[2], Un23);
136    }
137 #endif
138    Up1 = Un12*Un13;
139    Up2 = Un12*Un23;
140    Up3 = Un13*Un23;
141 
142    if (Up1 > 0.0f) {
143       /* sine of angle at n1 */
144       SUMA_MT_CROSS(X, U12, U13);
145       Xn = X[0]*X[0] +  X[1]*X[1] + X[2]*X[2];
146       s[0] = sqrtf(Xn/Up1);
147       /* now cosine */
148       c[0] = SUMA_MT_DOT(U12,U13)/(sqrtf(Up1));
149    } else {
150       s[0] = -2.0;
151       c[0] = -2.0;
152    }
153    if (Up2 > 0.0f) {
154       /* sine of angle at n2 */
155       SUMA_MT_CROSS(X, U23, U21);
156       Xn = X[0]*X[0] +  X[1]*X[1] + X[2]*X[2];
157       s[1] = sqrtf(Xn/Up2);
158       /* now cosine */
159       c[1] = SUMA_MT_DOT(U23,U21)/(sqrtf(Up2));
160    } else {
161       s[1] = -2.0;
162       c[1] = -2.0;
163    }
164    if (Up3 > 0.0f) {
165       /* sine of angle at n3 */
166       SUMA_MT_CROSS(X, U13, U23);
167       Xn = X[0]*X[0] +  X[1]*X[1] + X[2]*X[2];
168       s[2] = sqrtf(Xn/Up3);
169       /* now cosine */
170       c[2] = SUMA_MT_DOT(U13,U23)/(sqrtf(Up3));
171    } else {
172       s[2] = -2.0;
173       c[2] = -2.0;
174    }
175 
176    /* now angles */
177    if (a) {
178       for (k=0; k<3; ++k) {
179          if (s[k] >= 0.0f) { /* always the case, unless you have a reference
180                                 direction to compare with cross product.
181                                 Unsigned angles only then.*/
182             a[k] = acos(c[k]);   /* You could do 180-a1-a2 but that goes awry
183                                     is calculations fail on a1 or a2... */
184          } else { a[k] = -2.0; }
185       }
186    }
187 #if 0
188    if (LocalHead) {
189       fprintf(SUMA_STDERR, "%s:\n"
190                            "s =[%f, %f, %f]; \n"
191                            "c =[%f, %f, %f]; \n"
192                            ,FuncName,
193                            s[0], s[1], s[2],
194                            c[0], c[1], c[2]);
195    }
196 #endif
197    SUMA_RETURN(YUP);
198 }
199 /*!
200 
201    \brief A function to calculate the geodesic distance of nodes connected to node n
202           See labbook NIH-3 pp 138 and on for notes on algorithm
203    \param n (int) index of center node
204    \param SO (SUMA_SurfaceObject *) structure containing surface object
205    \param off (float *) a vector such that off[i] = the geodesic distance of node i
206                         to node n. The vector should be initialized to -1.0
207    \param lim (float) maximum geodesic distance to travel
208 
209    - This function is too slow. See SUMA_getoffsets2
210    \sa SUMA_getoffsets2
211 */
212 #define DBG 1
213 #define DoCheck 1
214 
215 #if 0 /* now set in default arguments */
216 int SUMA_GEOMCOMP_NI_MODE = NI_BINARY_MODE;
217 #endif
218 
SUMA_Set_SurfSmooth_NodeDebug(int n)219 void SUMA_Set_SurfSmooth_NodeDebug(int n)
220 {
221    SUMA_SSidbg = n;
222 }
223 
224 
225 /*!
226    \brief function to subdivide triangles to meet a maxarea criterion
227    Divisions are done by adding a node at the centroid of the triangle
228    to be subdivided. Bad idea, for very large triangles, such as produced
229    by convex hull, you could end up with nodes that have hundreds of neighbors...
230 */
SUMA_Subdivide_Mesh(float ** NodeListp,int * N_Nodep,int ** FaceSetListp,int * N_FaceSetp,float maxarea)231 int SUMA_Subdivide_Mesh(   float **NodeListp, int *N_Nodep, int **FaceSetListp,
232                            int *N_FaceSetp, float maxarea)
233 {
234    static char FuncName[]={"SUMA_Subdivide_Mesh"};
235    int in, it, N_NodeAlloc, N_FaceSetAlloc, N_Node,
236          N_FaceSet, it3, in0, in1, in2, inc3, inc, itn, itn3;
237    float c[3];
238    float *NodeList = NULL, a, *n1, *n2, *n0;
239    int *FaceSetList = NULL;
240    SUMA_SurfaceObject SObuf, *SO=NULL;
241    SUMA_Boolean LocalHead = NOPE;
242 
243    SUMA_ENTRY;
244 
245    SUMA_S_Warn("Function is very basic\n"
246                "Divisions are done by adding a node at the centroid of the\n"
247                "triangle to be subdivided. Bad idea, for very large triangles,\n"
248                "such as those produced by convex hull. You could end up with\n"
249                "nodes that have hundreds of neighbors\n");
250 
251    SO = &SObuf;
252 
253    N_NodeAlloc = N_Node = *N_Nodep;
254    N_FaceSetAlloc = N_FaceSet = *N_FaceSetp;
255    NodeList = *NodeListp;
256    FaceSetList = *FaceSetListp;
257    SO->NodeList = NodeList; SO->FaceSetList = FaceSetList;
258    if (!NodeList || !FaceSetList) {
259       SUMA_SL_Err("NULL input"); SUMA_RETURN(NOPE); }
260 
261    it = 0; /* triangle index */
262    while (it < N_FaceSet) {
263       it3 = 3*it;
264       in0 = FaceSetList[it3]; in1 = FaceSetList[it3+1]; in2 = FaceSetList[it3+2]; /* node indices */
265       n0 = &(NodeList[3*in0]); n1 = &(NodeList[3*in1]); n2 = &(NodeList[3*in2]);   /* node coordinates */
266       SUMA_TRI_AREA(n0, n1, n2, a); /* area of triangle */
267       if (a > maxarea) {
268          if (N_NodeAlloc <= N_Node) { /* need to realloc ?*/
269             N_NodeAlloc += 20000;
270             NodeList = (float *)SUMA_realloc(NodeList, N_NodeAlloc * 3 * sizeof(float));
271             /* you always add 2 triangles per new node here */
272             N_FaceSetAlloc += 40000;
273             FaceSetList = (int *)SUMA_realloc(FaceSetList, N_FaceSetAlloc * 3 * sizeof(int));
274             if (!NodeList || !FaceSetList) { SUMA_SL_Crit("Failed to realloc"); SUMA_RETURN(NOPE); }
275             SO->NodeList = NodeList; SO->FaceSetList = FaceSetList;
276          }
277          SUMA_FACE_CENTROID(SO, it, c); /* c is the centroid of triangle it */
278          inc = N_Node; inc3 = inc*3;  ++N_Node; /* index of new centroid node */
279          NodeList[inc3] = c[0]; NodeList[inc3+1] = c[1]; NodeList[inc3+2] = c[2];   /* add new centroid to bottom of list */
280          FaceSetList[it3+2] = inc; /* old triangle is now 1st new triangle in0 in1 inc */
281          itn = N_FaceSet; itn3 = 3 * itn; ++N_FaceSet; /* index of new second triangle */
282          FaceSetList[itn3] = inc; FaceSetList[itn3+1] = in1; FaceSetList[itn3+2] = in2;
283          itn = N_FaceSet; itn3 = 3 * itn; ++N_FaceSet; /* index of new third triangle */
284          FaceSetList[itn3] = inc; FaceSetList[itn3+1] = in2; FaceSetList[itn3+2] = in0;
285       } else {
286          ++it;
287       }
288    }
289 
290    /* reallocate */
291    FaceSetList = (int *)SUMA_realloc(FaceSetList, N_FaceSet * 3 * sizeof(int));
292    NodeList = (float *)SUMA_realloc(NodeList, N_Node * 3 * sizeof(float));
293 
294    *NodeListp = NodeList;
295    *FaceSetListp = FaceSetList;
296    *N_FaceSetp = N_FaceSet;
297    *N_Nodep = N_Node;
298 
299    SUMA_RETURN(YUP);
300 }
301 
302 
303 /*!
304    \brief Function to allocate and initialize a SUMA_VTI *  structure
305    \param N_TriIndex (int): Number of triangles whose intersections will
306                             be sought
307    \param *TriIndex (int *):
308       Pointer to vector containing indices of triangles
309        whose intersections will be sought. This vector will
310        be duplicated into vti if you supply it.
311        If this parameter is NULL, then an empty vti->TriIndex is
312        created and initialized from 0 to N_TriIndex - 1
313    \return vti (SUMA_VTI *): Initialized structure containing allocated
314                               TriIndex, N_IntersectedVoxels, IntersectedVoxels vectors.
315    - Free with SUMA_FreeVTI
316 */
SUMA_CreateVTI(int N_TriIndex,int * TriIndexU)317 SUMA_VTI *SUMA_CreateVTI(int N_TriIndex, int *TriIndexU)
318 {
319    static char FuncName[]={"SUMA_CreateVTI"};
320    SUMA_VTI *vti = NULL;
321    int i;
322 
323    SUMA_ENTRY;
324    if (!N_TriIndex) {
325       SUMA_SL_Err("Nothing to do !");
326       SUMA_RETURN(vti);
327    }
328 
329    vti = (SUMA_VTI *)SUMA_malloc(sizeof(SUMA_VTI));
330    vti->N_TriIndex = N_TriIndex;
331    vti->TriIndex = (int *)SUMA_calloc(N_TriIndex, sizeof(int));
332    if (!vti->TriIndex) {
333       SUMA_SL_Crit("Failed to allocate for vti->TriIndex");
334       SUMA_RETURN(NULL);
335    }
336    if (TriIndexU ) {
337       memcpy(vti->TriIndex, TriIndexU, N_TriIndex*sizeof(int));
338    }else {
339       /* init default */
340       for (i=0; i<N_TriIndex; ++i) vti->TriIndex[i]=i;
341    }
342    vti->N_IntersectedVoxels = (int *)SUMA_calloc(N_TriIndex, sizeof(int));
343    vti->IntersectedVoxels = (int **)SUMA_calloc(N_TriIndex, sizeof(int*));
344    vti->SignedIJKDistance = (float **)SUMA_calloc(N_TriIndex, sizeof(float*));
345    if (!vti->N_IntersectedVoxels || !vti->IntersectedVoxels) {
346          SUMA_SL_Crit("Failed to allocate for vti's innerds");
347          SUMA_RETURN(NULL);
348    }
349 
350    SUMA_RETURN(vti);
351 }
352 
SUMA_FreeVTI(SUMA_VTI * vti)353 SUMA_VTI * SUMA_FreeVTI(SUMA_VTI *vti)
354 {
355    static char FuncName[]={"SUMA_FreeVTI"};
356    int i;
357 
358    SUMA_ENTRY;
359 
360    if (!vti) SUMA_RETURN(NULL);
361    if (vti->TriIndex) SUMA_free(vti->TriIndex);
362    if (vti->IntersectedVoxels) {
363       for (i=0; i<vti->N_TriIndex; ++i) {
364          if (vti->IntersectedVoxels[i]) free(vti->IntersectedVoxels[i]);
365          if (vti->SignedIJKDistance[i]) free(vti->SignedIJKDistance[i]);
366       }
367       SUMA_free(vti->IntersectedVoxels);
368    }
369    if (vti->N_IntersectedVoxels) SUMA_free(vti->N_IntersectedVoxels);
370    SUMA_free(vti);
371 
372    SUMA_RETURN(NULL);
373 }
374 
375 /*!
376    \brief Function to return a set of voxels that are intersected by
377    a triangle.
378 
379    \param SO (SUMA_SurfaceObject *)
380    \param VolPar (SUMA_VOLPAR *)
381    \param NodeIJKlist (float *) the equivalent of SO->NodeList
382                            only in i,j,k indices into VolPar's grid.
383                            Set it to NULL to have this function generate it from
384                            SO->NodeList and VolPar.
385    \param vti (SUMA_VTI *) properly initialized Voxel Triangle                                               Intersection structure
386    \return vti (SUMA_VTI *) filled up VTI structure.
387    - Closely based on section in function SUMA_SurfGridIntersect
388    If you find bugs here, fix them there too.
389 
390    Significant bug fixes Oct. 2012
391 */
392 
SUMA_GetVoxelsIntersectingTriangle(SUMA_SurfaceObject * SO,SUMA_VOLPAR * VolPar,float * NodeIJKlistU,SUMA_VTI * vti)393 SUMA_VTI *SUMA_GetVoxelsIntersectingTriangle(
394    SUMA_SurfaceObject *SO, SUMA_VOLPAR *VolPar, float *NodeIJKlistU,
395    SUMA_VTI *vti)
396 {
397    static char FuncName[]={"SUMA_GetVoxelsIntersectingTriangle"};
398    int ti, nx, ny, nz, nxy, nxyz, N_inbox, n1, n2, n3, nt, nt3, nijk, nf;
399    int N_alloc, N_realloc, en, *voxelsijk=NULL, N_voxels1d = 0, *voxels1d = NULL;
400    int *TriIndex=NULL, N_TriIndex;
401    int tdebug=-1; /* Pick a triangle index to debug. Set to -1 to keep mum */
402    float dxyz[3];
403    float tol_dist = 0; /* A way to fatten up the shell a little bit.
404                            Set to 0 if no fat is needed */
405    int inside_only = 0; /* if 1, look for voxels located opposite
406                            normal direction only */
407 
408    float *p1, *p2, *p3, min_v[3], max_v[3], p[3], dist,
409          *NodeIJKlist=NULL, *signdist=NULL;
410    FILE *fp=NULL;
411    SUMA_Boolean LocalHead = NOPE;
412 
413    SUMA_ENTRY;
414 
415    if (SO->FaceSetDim != 3 || SO->NodeDim != 3) {
416       SUMA_SL_Err("SO->FaceSetDim != 3 || SO->NodeDim != 3");
417       SUMA_RETURN(NULL);
418    }
419    if (!vti) {
420       SUMA_SL_Err("vti must be non NULL");
421       SUMA_RETURN(NULL);
422    }
423    if (vti->N_TriIndex <= 0) {
424       SUMA_SL_Err("vti must be initialized");
425       SUMA_RETURN(NULL);
426    }
427    nx = VolPar->nx;
428    ny = VolPar->ny;
429    nz = VolPar->nz;
430    nxy = nx * ny; nxyz = nx * ny * nz;
431    if (!NodeIJKlistU) {
432       NodeIJKlist = (float *)SUMA_malloc(SO->N_Node * 3 * sizeof(float));
433       memcpy ((void*)NodeIJKlist, (void *)SO->NodeList,
434                   SO->N_Node * 3 * sizeof(float));
435       /* transform the surface's coordinates from RAI to 3dfind */
436       if (!SUMA_vec_dicomm_to_3dfind (NodeIJKlist, SO->N_Node, VolPar)) {
437          SUMA_SL_Err("Failed to effectuate coordinate transform.");
438          SUMA_free(NodeIJKlist); NodeIJKlist = NULL;
439          SUMA_RETURN(NULL);
440       }
441    } else {
442       NodeIJKlist = NodeIJKlistU;
443       for (ti=0; ti<SO->N_Node ; ++ti) { /* check */
444          if (NodeIJKlist[3*ti  ] < 0 || NodeIJKlist[3*ti  ]>= nx ||
445              NodeIJKlist[3*ti+1] < 0 || NodeIJKlist[3*ti+1]>= ny ||
446              NodeIJKlist[3*ti+2] < 0 || NodeIJKlist[3*ti+2]>= nz ) {
447             SUMA_S_Errv("Looks like NodeIJKlist is not in index units.\n"
448                         "At node %d, have %f %f %f\n"
449                         , ti,
450                         NodeIJKlist[3*ti  ],
451                         NodeIJKlist[3*ti+1],
452                         NodeIJKlist[3*ti+2]);
453             SUMA_RETURN(NULL);
454          }
455       }
456    }
457    TriIndex = vti->TriIndex;
458    N_TriIndex = vti->N_TriIndex;
459 
460 
461    if (LocalHead) {
462       SUMA_LH("Debug mode, writing file: SUMA_GetVoxelsIntersectingTriangle.1D");
463       fp = fopen("SUMA_GetVoxelsIntersectingTriangle.1D","w");
464       if (fp) fprintf(fp, "# Voxels from %s that intersect the triangles \n",
465                            VolPar->filecode);
466    }
467    /* cycle through all triangles and find voxels that intersect them */
468    N_alloc = 2000; /* expected maximum # of voxels in triangle's bounding box */
469    N_realloc = 0;
470    voxelsijk = (int *)SUMA_malloc(sizeof(int)*N_alloc*3);
471    if (!voxelsijk) { SUMA_SL_Crit("Failed to Allocate!"); SUMA_RETURN(NULL);  }
472    /* ZSS Oct 2012, see similar comment in SUMA_SurfGridIntersect
473    dxyz[0] = VolPar->dx; dxyz[1] = VolPar->dy; dxyz[2] = VolPar->dz;
474    */
475    dxyz[0] = dxyz[1] = dxyz[2] = 1.0; /* Now we're in ijk coordinate system */
476    for (ti=0; ti<N_TriIndex; ++ti) {
477       nf = TriIndex[ti];
478       n1 = SO->FaceSetList[SO->FaceSetDim*nf];
479       n2 = SO->FaceSetList[SO->FaceSetDim*nf+1];
480       n3 = SO->FaceSetList[SO->FaceSetDim*nf+2];
481       if (LocalHead || nf == tdebug)
482          fprintf(SUMA_STDERR,
483             "%s: Now processing %dth triangle indexed %d "
484             "made up of  nodes %d, %d. %d .\n",
485              FuncName, ti, nf, n1, n2, n3);
486       /* find the bounding box of the triangle */
487       p1 = &(NodeIJKlist[3*n1]);
488       p2 = &(NodeIJKlist[3*n2]);
489       p3 = &(NodeIJKlist[3*n3]);
490       SUMA_TRIANGLE_BOUNDING_BOX(p1, p2, p3, min_v, max_v);
491 
492       /* quick check of preallocate size of voxelsijk */
493       en =( (int)(max_v[0] - min_v[0] + 5) *
494             (int)(max_v[1] - min_v[1] + 5) *
495             (int)(max_v[2] - min_v[2] + 5) );
496       if ( en > N_alloc) {
497          ++N_realloc;
498          if (N_realloc > 5) {
499             SUMA_SL_Warn("Reallocating, increase limit to improve speed.\n"
500                          "Either triangles too large or grid too small");
501          }
502          N_alloc = 2*en;
503          voxelsijk = (int *)SUMA_realloc(voxelsijk, 3*N_alloc*sizeof(int));
504          if (!voxelsijk) {
505             SUMA_SL_Crit("Failed to Allocate!");
506             SUMA_RETURN(NULL);
507          }
508       }
509       /* find the list of voxels inhabiting this box */
510       N_inbox = 0;
511       if (!SUMA_VoxelsInBox(voxelsijk, &N_inbox, min_v, max_v)) {
512          SUMA_SL_Err("Unexpected error!"); SUMA_RETURN(NULL);
513       }
514       if (!N_inbox) {
515          SUMA_SL_Err("Unexpected error, no voxels in box!");
516          SUMA_RETURN(NULL);
517       }
518       if (N_inbox >= N_alloc) {
519          SUMA_SL_Err("Allocation trouble!");
520          SUMA_RETURN(NULL);
521       }
522       if (LocalHead || nf == tdebug) {
523          fprintf(SUMA_STDERR,"\t\t%d nodes in box\n", N_inbox);
524          if (nf == tdebug) {
525             FILE *ffout=fopen("voxelsinbox.1D","w");
526             if (ffout) {
527                SUMA_S_Warn("Dumping debugging file: voxelsinbox.1D");
528                fprintf(ffout,
529                         "#IJK of Voxels to be considered for triangle %d\n",nf);
530                fprintf(ffout,"#%d %f %f %f\n"
531                              "#%d %f %f %f\n"
532                              "#%d %f %f %f\n",
533                              n1, p1[0], p1[1], p1[2],
534                              n2, p2[0], p2[1], p2[2],
535                              n3, p3[0], p3[1], p3[2]);
536                for (nt=0; nt < N_inbox; ++nt) {
537                   nt3 = 3*nt;
538                   fprintf(ffout,"%d %d %d\n",
539                            voxelsijk[nt3  ], voxelsijk[nt3+1], voxelsijk[nt3+2]);
540                }
541                fclose(ffout); ffout=NULL;
542             }
543          }
544       }
545 
546       /* allocate for 1D indices of voxels intersecting the triangle */
547       if (voxels1d) {
548          SUMA_SL_Err("NULL pointer expected here");
549          SUMA_RETURN(NULL);
550       }
551       voxels1d = (int *)malloc(N_inbox * sizeof(int)); /* Too many SUMA_mallocs
552                   keep it simple here, this function is called many many times */
553       signdist = (float *)malloc(N_inbox * sizeof(float));
554       if (LocalHead || nf == tdebug)
555          fprintf(SUMA_STDERR,"\t\tabout to process %d voxels in box\n", N_inbox);
556       N_voxels1d=0;
557       if (!voxels1d || !signdist) {
558          SUMA_SL_Crit("Failed to allocate voxels1d");
559          SUMA_RETURN(NULL);
560       }
561       /* mark these voxels as inside the business */
562       for (nt=0; nt < N_inbox; ++nt) {
563          nt3 = 3*nt;
564          if (  voxelsijk[nt3  ] >= 0 && voxelsijk[nt3] < nx &&
565                voxelsijk[nt3+1] >= 0 && voxelsijk[nt3+1] < ny &&
566                voxelsijk[nt3+2] >= 0 && voxelsijk[nt3+2] < nz) {
567             nijk = SUMA_3D_2_1D_index( voxelsijk[nt3],
568                                        voxelsijk[nt3+1],
569                                        voxelsijk[nt3+2], nx , nxy);
570             {
571                /* what side of the plane is this voxel on ? */
572                p[0] = (float)voxelsijk[nt3];
573                p[1] = (float)voxelsijk[nt3+1];
574                p[2] = (float)voxelsijk[nt3+2];
575                SUMA_DIST_FROM_PLANE(p1, p2, p3, p, dist);
576                if (nf == tdebug) {
577                   fprintf(SUMA_STDERR,
578                           "\t\tVox %f %f %f at dist %f\n",
579                            p[0], p[1], p[2], dist);
580                }
581                /* Does voxel contain any of the nodes ? */
582                if (tol_dist && SUMA_ABS(dist) < tol_dist) dist = tol_dist;
583                   /* Fatten the representation a little bit
584                    There are holes in the mask created using
585                    the condition below alone. I am not sure
586                    why that is the case but whatever gap there
587                    is in one plane, results from a thick line in
588                    the other. Don't know if that is a bug yet
589                    or an effect of discretization. At any rate
590                    it should not affect what I plan to do with
591                    this. Could the bug be in
592                    SUMA_isVoxelIntersect_Triangle?
593                    Thu Dec 22 17:03:48 EST 2005, Update:
594                    SUMA_isVoxelIntersect_Triangle had a precision
595                    bug. It has been fixed but I have not reexamined
596                    this block yet*/
597                if (!inside_only || !(SUMA_IS_STRICT_NEG(VolPar->Hand * dist))) {
598                                        /* voxel is outside (along normal) */
599                   /* does this triangle actually intersect this voxel ?*/
600                   if (SUMA_isVoxelIntersect_Triangle (p, dxyz, p1, p2, p3)) {
601                      /* looks good, store it */
602                      if (LocalHead || nf == tdebug)
603                         fprintf(SUMA_STDERR,
604                                 "\tnt %d, N_voxels1d %d\n", nt, N_voxels1d);
605                      voxels1d[N_voxels1d] = nijk; ++N_voxels1d;
606                      signdist[N_voxels1d] = VolPar->Hand * dist;
607                      if (fp) fprintf(fp, "%d %d %d\n",
608                            voxelsijk[nt3], voxelsijk[nt3+1], voxelsijk[nt3+2]);
609                   }
610                }
611 
612             }
613          }
614       }
615       /* store the results */
616       vti->IntersectedVoxels[ti] = voxels1d;
617       vti->N_IntersectedVoxels[ti] = N_voxels1d;
618       vti->SignedIJKDistance[ti] =  signdist;
619       voxels1d = NULL; N_voxels1d = 0; signdist = NULL;
620    }
621 
622    if (LocalHead) {
623       if (fp) fclose(fp); fp = NULL;
624    }
625    if (!NodeIJKlistU) SUMA_free(NodeIJKlist);
626    SUMA_RETURN(vti);
627 }
628 
629 /*!
630    A function to create a volume mask from the interestction
631    of triangles with a volumetric grid.
632    This function is meant to work with a bunch of triangles
633    that are not necessarily consistently wound.
634    The returned volume contains the shorted distance* of a voxel's centroid
635    from any triangle it touches.
636    The sign of the distance is relative to the triangle's normal and
637    is likely not consistent across voxels or triangles because the surface
638    may not be consistenly wound.
639 
640    *The distance is in index units, not mm, so the value is not
641    quite accurate for non-cubic voxels.
642 
643    If this is not a function you like, you are probably looking for:
644    SUMA_SurfGridIntersect
645 
646    \sa SUMA_VoxelizeSurface
647 */
SUMA_SurfaceIntersectionVolume(SUMA_SurfaceObject * SOo,THD_3dim_dataset * gdset)648 THD_3dim_dataset *SUMA_SurfaceIntersectionVolume(
649                         SUMA_SurfaceObject *SOo, THD_3dim_dataset *gdset)
650 {
651    static char FuncName[]={"SUMA_SurfaceIntersectionVolume"};
652    THD_3dim_dataset *odset=NULL;
653    SUMA_VOLPAR *vp=NULL;
654    SUMA_VTI *vti=NULL;
655    int ti, vi, ivox;
656    short *sbr=NULL;
657    float *fv=NULL, factor;
658 
659    SUMA_ENTRY;
660 
661    if (!SOo || !gdset) {
662       SUMA_S_Err("NULL input");
663       SUMA_RETURN(NULL);
664    }
665    if (!(vp = SUMA_VolParFromDset(gdset))) {
666       SUMA_S_Err("Failed to create volpar");
667       SUMA_RETURN(NULL);
668    }
669    vti = SUMA_CreateVTI(SOo->N_FaceSet, NULL);
670    vti = SUMA_GetVoxelsIntersectingTriangle(SOo, vp, NULL, vti);
671    if (!vti) {
672       SUMA_S_Err("Failed to get interesctions");
673       SUMA_RETURN(NULL);
674    }
675    odset = EDIT_empty_copy(gdset);
676    EDIT_dset_items( odset ,
677                     ADN_prefix    , FuncName ,
678                     ADN_datum_all , MRI_short ,
679                     ADN_nvals     , 1 ,
680                     ADN_ntt       , 0 ,
681                     ADN_none ) ;
682 
683    fv = (float *)SUMA_calloc(DSET_NVOX(odset), sizeof(float));
684    for (ti=0; ti<vti->N_TriIndex; ++ti) {
685       for (vi=0; vi<vti->N_IntersectedVoxels[ti]; ++vi) {
686          ivox = vti->IntersectedVoxels[ti][vi];
687          if (fv[ivox] == 0.0f ||
688              SUMA_ABS(fv[ivox]) > SUMA_ABS(vti->SignedIJKDistance[ti][vi])){
689             fv[ivox] = vti->SignedIJKDistance[ti][vi];
690          }
691       }
692    }
693 
694    EDIT_substitute_brick( odset , 0 , MRI_short , NULL ) ;
695    sbr = DSET_BRICK_ARRAY(odset,0);
696    factor = EDIT_coerce_autoscale_new( DSET_NVOX(odset), MRI_float, fv,
697                                        MRI_short, sbr);
698    if (factor > 0.0f) {
699       factor = 1.0 / factor;
700    } else factor = 0.0;
701    EDIT_BRICK_LABEL (odset, 0, "minIJKdistance");
702    EDIT_BRICK_FACTOR (odset, 0, factor);
703 
704    SUMA_free(fv); fv = NULL;
705 
706    vti = SUMA_FreeVTI(vti);
707    SUMA_Free_VolPar(vp); vp=NULL;
708 
709    SUMA_RETURN(odset);
710 }
711 
712 /*!
713    \sa SUMA_VoxelizeSurface
714 */
SUMA_MaskizeSurface(SUMA_SurfaceObject * SO,THD_3dim_dataset * gdset,int method)715 THD_3dim_dataset *SUMA_MaskizeSurface(SUMA_SurfaceObject *SO,
716                                        THD_3dim_dataset *gdset,
717                                        int method)
718 {
719    static char FuncName[]={"SUMA_MaskizeSurface"};
720    short *is_in=NULL;
721    int N_in=0, ii=0;
722    THD_3dim_dataset *dset=NULL;
723    SUMA_VOLPAR *vp=NULL;
724    SUMA_Boolean LocalHead = NOPE;
725 
726    SUMA_ENTRY;
727 
728    if (!SO || !gdset) SUMA_RETURN(NULL);
729 
730    if (!(vp = SUMA_VolParFromDset(gdset))) {
731       SUMA_S_Err("Failed to create volpar");
732       SUMA_RETURN(NULL);
733    }
734 
735       switch (method) {
736          case 1:
737             SUMA_LH("Voxel mask, fast, OK for good closed surfaces");
738             if (!(is_in = SUMA_FindVoxelsInSurface(SO, vp, &N_in, 1, NULL))) {
739                SUMA_S_Err("No voxels in closed surface!");
740                SUMA_RETURN(NULL);
741             }
742             break;
743          case 2:
744             SUMA_LH("Voxel mask, slow but more robust for closed surfaces");
745             if (!(is_in = SUMA_FindVoxelsInSurface_SLOW(SO, vp, &N_in, 0))) {
746                SUMA_S_Err("No voxels in closed surface!");
747                SUMA_RETURN(NULL);
748             }
749             break;
750          default:
751             SUMA_S_Errv("Bad method of %d\n", method);
752             SUMA_RETURN(NULL);
753       }
754 
755    dset = EDIT_empty_copy(gdset);
756    tross_Copy_History( gdset , dset ) ;
757    EDIT_dset_items( dset ,
758                     ADN_prefix      , FuncName ,
759                     ADN_type        , HEAD_ANAT_TYPE ,
760                     ADN_func_type   , ANAT_BUCK_TYPE ,
761                     ADN_none ) ;
762    EDIT_substitute_brick(dset, 0, MRI_short, is_in); is_in = NULL;
763    SUMA_Free_VolPar(vp); vp=NULL;
764    SUMA_RETURN(dset);
765 }
766 
767 /*! Voxelize a surface.
768    gdset: Provides voxel grid
769    automask: Different methods for restricting voxel set
770              if mask is NULL.
771              0: No automasking
772              1: find voxels inside surface first using fast
773                 method. Needs closed surfaces, might get some
774                 holes left.
775              2: find voxels inside surface first using slow
776                 method. Needs closed surfaces. More accurate
777                 interiosity.
778              3: find voxels in bounding box. Need not worry
779                 about closed surfaces, but sign of distance
780                 is not reliable in places where normals are
781                 bad. Sign is determined by the voxelizing function.
782    mask: If provided, restrict analysis to voxels in mask
783          If not provided and automask != 0, a mask is created
784          Otherwise all voxels are processed
785    \sa SUMA_SurfaceIntersectionVolume or SUMA_SurfGridIntersect
786    \sa SUMA_MaskizeSurface
787 */
SUMA_VoxelizeSurface(SUMA_SurfaceObject * SO,THD_3dim_dataset * gdset,int automask,byte * mask)788 THD_3dim_dataset *SUMA_VoxelizeSurface(SUMA_SurfaceObject *SO,
789                                        THD_3dim_dataset *gdset,
790                                        int automask,
791                                        byte *mask)
792 {
793    static char FuncName[]={"SUMA_VoxelizeSurface"};
794    short *is_in=NULL;
795    int N_in=0, ii=0;
796    THD_3dim_dataset *dset=NULL;
797    SUMA_VOLPAR *vp=NULL;
798    SUMA_Boolean LocalHead = NOPE;
799 
800    SUMA_ENTRY;
801 
802    if (!SO || !gdset) SUMA_RETURN(NULL);
803 
804    if (!(vp = SUMA_VolParFromDset(gdset))) {
805       SUMA_S_Err("Failed to create volpar");
806       SUMA_RETURN(NULL);
807    }
808 
809    if (!mask) {
810       switch (automask) {
811          case 1:
812             SUMA_LH("Voxel mask, fast, OK for good closed surfaces");
813             if (!(is_in = SUMA_FindVoxelsInSurface(SO, vp, &N_in, 1, NULL))) {
814                SUMA_S_Err("No voxels in closed surface!");
815                SUMA_RETURN(NULL);
816             }
817             SUMA_LH("Now Voxelizing, this is slow...");
818             dset = SUMA_VoxelToSurfDistances(SO, gdset, NULL, is_in, 0);
819             SUMA_free(is_in);
820             break;
821          case 2:
822             SUMA_LH("Voxel mask, slow but more robust for closed surfaces");
823             if (!(is_in = SUMA_FindVoxelsInSurface_SLOW(SO, vp, &N_in, 0))) {
824                SUMA_S_Err("No voxels in closed surface!");
825                SUMA_RETURN(NULL);
826             }
827             SUMA_LH("Now Voxelizing, this is slow...");
828             dset = SUMA_VoxelToSurfDistances(SO, gdset, NULL, is_in, 2);
829             SUMA_free(is_in);
830             break;
831          case 3:
832             SUMA_LH("Voxel mask, very fast, rely on voxelize for interiosity\n"
833                     "But that may not be quite accurate at bad normals\n");
834             if (!(is_in = SUMA_FindVoxelsInSurface_SLOW(SO, vp, &N_in, 1))) {
835                SUMA_S_Err("No voxels in closed surface!");
836                SUMA_RETURN(NULL);
837             }
838             mask = (byte *)SUMA_calloc(DSET_NVOX(gdset),sizeof(byte));
839             for (ii=0; ii<DSET_NVOX(gdset); ++ii) {
840                if (is_in[ii]) mask[ii] = 1;
841             }
842             SUMA_free(is_in);
843             SUMA_LH("Now Voxelizing, this is slow, sign may not be accurate\n");
844             dset = SUMA_VoxelToSurfDistances(SO, gdset, mask, NULL, 0);
845             SUMA_free(mask); mask=NULL;
846             break;
847          case 0:
848             SUMA_LH("No mask, get a drink");
849             dset = SUMA_VoxelToSurfDistances(SO, gdset, NULL, NULL, 0);
850             break;
851          default:
852             SUMA_S_Errv("Bad automask of %d\n", automask);
853             SUMA_RETURN(NULL);
854       }
855    } else {
856       SUMA_LH("Voxelizing, with user mask this is slow...");
857       dset = SUMA_VoxelToSurfDistances(SO, gdset, mask, NULL, 0);
858    }
859 
860    /* I  want shorts back */
861    {
862       float *fv = (float *)SUMA_malloc(sizeof(float)*DSET_NVOX(dset));
863       memcpy(fv, DSET_ARRAY(dset,0), sizeof(float)*DSET_NVOX(dset));
864       EDIT_substscale_brick(dset, 0, MRI_float,
865                             fv, MRI_short, -1.0);
866       SUMA_free(fv); fv = NULL;
867    }
868    SUMA_Free_VolPar(vp); vp=NULL;
869    SUMA_RETURN(dset);
870 }
871 
872 
SUMA_VoxelToSurfDistances(SUMA_SurfaceObject * SO,THD_3dim_dataset * master,byte * mask,short * isin,short inval)873 THD_3dim_dataset *SUMA_VoxelToSurfDistances(SUMA_SurfaceObject *SO,
874                      THD_3dim_dataset *master, byte *mask, short *isin,
875                      short inval) {
876    static char FuncName[]={"SUMA_VoxelToSurfDistances"};
877    int i, j, k, n, nxyz, ijk, *closest=NULL, n_mask, n3;
878    float *dist=NULL, *Points=NULL;
879    byte *sgn=NULL;
880    THD_fvec3 ncoord, ndicom;
881    THD_ivec3 nind3;
882    THD_3dim_dataset *dset=NULL;
883    SUMA_FORM_AFNI_DSET_STRUCT *OptDs = NULL;
884    SUMA_Boolean LocalHead = NOPE;
885 
886    SUMA_ENTRY;
887 
888    if (isin && mask) {
889       SUMA_S_Err("Not supposed to mix the two ");
890       SUMA_RETURN(NULL);
891    }
892    /* get Points vectors */
893    nxyz = DSET_NVOX(master);
894    n_mask=0;
895    if (isin) {
896       mask = (byte *)SUMA_calloc(nxyz, sizeof(byte));
897       for (i=0; i<nxyz; ++i) {
898          if (isin[i]) { mask[i] = 1; ++n_mask;}
899       }
900       if (inval) {
901          sgn = (byte *)SUMA_calloc(n_mask, sizeof(byte));
902          j = 0;
903          for (i=0; i<nxyz; ++i) {
904             if (isin[i]) {
905                if (isin[i]>=inval) sgn[j] = 2;
906                else sgn[j] = 1;
907                ++j;
908             }
909          }
910       }
911    } else {
912       if (!mask) {
913          n_mask = nxyz;
914       } else {
915          for (i=0; i<nxyz; ++i) if (mask[i]) ++n_mask;
916       }
917    }
918    SUMA_LHv("Have %d points in mask on %dx%dx%d grid\n",
919             n_mask, DSET_NX(master), DSET_NY(master), DSET_NZ(master));
920    Points = (float *)SUMA_calloc(3*n_mask, sizeof(float));
921    n = 0; ijk=0;
922    for (k=0; k<DSET_NZ(master); ++k) {
923       for (j=0; j<DSET_NY(master); ++j ) {
924          for (i=0; i<DSET_NX(master); ++i)  {
925             if (!mask || mask[ijk]) {
926                n3 = 3*n;
927                nind3.ijk[0] = i; nind3.ijk[1] = j; nind3.ijk[2] = k;
928                ncoord = THD_3dind_to_3dmm(master, nind3);
929                ndicom = THD_3dmm_to_dicomm(master,ncoord);
930                Points[n3  ] = ndicom.xyz[0];
931                Points[n3+1] = ndicom.xyz[1];
932                Points[n3+2] = ndicom.xyz[2];
933                SUMA_LHv("Added [%f %f %f]\n",
934                   Points[n3  ], Points[n3+1], Points[n3+2]);
935                ++n;
936             }
937             ++ijk;
938          }
939       }
940    }
941 
942    /* get distance of each point to surface */
943    if (sgn == NULL) { /* Don't have sign yet.
944       Let SUMA_Shortest_Point_To_Triangles_Distance compute sign
945       based on dot product with normals.
946       This computation is not accurate everywhere ... */
947       if (!SUMA_Shortest_Point_To_Triangles_Distance(
948             Points, n_mask,
949             SO->NodeList, SO->FaceSetList, SO->N_FaceSet,
950             SO->FaceNormList, &dist, &closest, &sgn, 0)) {
951          SUMA_S_Err("Failed to get shortys");
952          SUMA_RETURN(NULL);
953       }
954    } else {/* have sign already */
955       if (!SUMA_Shortest_Point_To_Triangles_Distance(
956             Points, n_mask,
957             SO->NodeList, SO->FaceSetList, SO->N_FaceSet,
958             NULL, &dist, &closest, NULL , 0)) {
959          SUMA_S_Err("Failed to get shortys");
960          SUMA_RETURN(NULL);
961       }
962    }
963 
964    for (i=0; i<n_mask; ++i) {
965       if (sgn[i]==2) dist[i] = sqrt(dist[i]);
966       else dist[i] = -sqrt(dist[i]);
967    }
968    SUMA_LHv("Point 0: [%f %f %f], Node 0: [%f %f %f]\n"
969             "dist %f, closest triangle %d, sign %d\n",
970             Points[0], Points[1], Points[2],
971             SO->NodeList[0], SO->NodeList[1], SO->NodeList[2],
972             dist[0], closest[0], sgn[0]);
973    OptDs = SUMA_New_FormAfniDset_Opt();
974    OptDs->prefix = SUMA_copy_string("3dVoxelToSurfDistances");
975    OptDs->prefix_path = SUMA_copy_string("./");
976 
977    /* master dset */
978    OptDs->datum = MRI_float; /* you have to do the scaling yourself otherwise */
979    OptDs->full_list = 0;
980    OptDs->do_ijk = 0;
981    OptDs->coorder_xyz = 0;
982    OptDs->fval = 0.0;
983    OptDs->mset = master;
984    dset = SUMA_FormAfnidset (Points, dist, n_mask, OptDs);
985    OptDs->mset=NULL; OptDs = SUMA_Free_FormAfniDset_Opt(OptDs);
986    if (!dset) {
987       SUMA_SL_Err("Failed to create output dataset!");
988    }
989 
990    /* free if needed */
991    if (isin) SUMA_free(mask); mask=NULL;
992 
993    SUMA_RETURN(dset);
994 }
995 
996 /*!
997    \brief Function to detect surface self intersection
998    returns -1 in case of error,
999             0 in case of no intersection
1000             1 in case of intersection
1001 */
SUMA_isSelfIntersect(SUMA_SurfaceObject * SO,int StopAt,byte * report)1002 int SUMA_isSelfIntersect(SUMA_SurfaceObject *SO, int StopAt, byte *report)
1003 {
1004    static char FuncName[]={"SUMA_isSelfIntersect"};
1005    float *NodePos = NULL, *p1=NULL, *p2=NULL, *p3 = NULL, p[3], *ep1=NULL, *ep2=NULL;
1006    int hit = 0, k, t1, t2, it, it3, n1, n2, n3;
1007    SUMA_MT_INTERSECT_TRIANGLE *MTI = NULL;
1008    SUMA_Boolean LocalHead = NOPE;
1009 
1010    SUMA_ENTRY;
1011 
1012    if (!SO->EL) {
1013       SUMA_SL_Err("NULL SO->EL");
1014       SUMA_RETURN(-1);
1015    }
1016 
1017    if (StopAt < 1) StopAt = 1;
1018 
1019    hit = 0; k = 0;
1020    while (k < SO->EL->N_EL) {
1021          t1 = SO->EL->ELps[k][1];
1022          t2 = SO->EL->ELps[SUMA_MIN_PAIR(k+1, SO->EL->N_EL-1)][1];
1023          ep1 = &(SO->NodeList[3*SO->EL->EL[k][0]]);
1024          ep2 = &(SO->NodeList[3*SO->EL->EL[k][1]]);
1025          /* find out if segment intersects */
1026          MTI = SUMA_MT_intersect_triangle(ep1, ep2, SO->NodeList, SO->N_Node,
1027                                        SO->FaceSetList, SO->N_FaceSet, MTI, 0);
1028          for (it=0; it<SO->N_FaceSet; ++it) {
1029             if (MTI->isHit[it] && it != t1 && it != t2 &&
1030                 MTI->u[it] > SUMA_EPSILON && MTI->v[it] > SUMA_EPSILON) {
1031                /* ray hit triangle, is intersection inside segment ? */
1032                /* SUMA_LH("Checking hit..."); */
1033                it3 = SO->FaceSetDim*it;
1034                n1 = SO->FaceSetList[it3];
1035                n2 = SO->FaceSetList[it3+1];
1036                n3 = SO->FaceSetList[it3+2];
1037                p1 = &(SO->NodeList[SO->NodeDim*n1]);
1038                p2 = &(SO->NodeList[SO->NodeDim*n2]);
1039                p3 = &(SO->NodeList[SO->NodeDim*n3]);
1040                SUMA_FROM_BARYCENTRIC(MTI->u[it], MTI->v[it], p1, p2, p3, p);
1041 
1042                if (p[0] > ep1[0] && p[0] < ep2[0]) {
1043                   if (p[1] > ep1[1] && p[1] < ep2[1]) {
1044                      if (p[2] > ep1[2] && p[2] < ep2[2]) {
1045                         /* point in segment, self intersection detected. */
1046                         if (report || LocalHead)
1047                             fprintf(SUMA_STDERR,
1048                                "%s: Triangle %d (%d, %d, %d) was hit by segment "
1049                                "formed by nodes [%d, %d]\n",
1050                                  FuncName, it, n1, n2, n3,
1051                                  SO->EL->EL[k][0], SO->EL->EL[k][1]);
1052                            ++ hit;
1053                            if (report) {
1054                               report[SO->EL->EL[k][0]] =
1055                                  report[SO->EL->EL[k][1]] = 1;
1056                            }
1057                         break;
1058                      }
1059                   }
1060                }
1061             }
1062          }
1063          if (hit >= StopAt) break;
1064          /* skip duplicate edges */
1065          if (SO->EL->ELps[k][2] > 0) {
1066                k += SO->EL->ELps[k][2];
1067          } else ++k;
1068    }
1069 
1070    if (MTI) MTI = SUMA_Free_MT_intersect_triangle(MTI);
1071 
1072    if (report || LocalHead) {
1073       if (!hit) {
1074          SUMA_LH("Surface does not self intersect.");
1075       } else {
1076          SUMA_LH("Surface self intersects.");
1077       }
1078    }
1079    SUMA_RETURN(hit);
1080 }
1081 
1082 /*!
1083    \brief find the neighbors to a voxel.
1084    \param ijk (int) a voxel's 1D index
1085    \param ni, nj, nk (int) number of voxels in each of the three directions
1086    \param ntype (SUMA_VOX_NEIGHB_TYPES) neighborhood type
1087                   SUMA_VOX_NEIGHB_FACE a maximum total of 6 neighbors
1088                   SUMA_VOX_NEIGHB_EDGE a maximum total of 6 + 12 neighbors
1089                   SUMA_VOX_NEIGHB_CORNER a maximum total of 6 + 12 + 8 neighbors
1090    \param nl (int *) vector to contain the 1D indices of neighboring voxels. Voxels
1091                      outside the volume boundaries are not considered. You should make sure nl
1092                      can hold a total of 26 values.
1093    \param N_n (int) number of neighbors.
1094 */
SUMA_VoxelNeighbors(int ijk,int ni,int nj,int nk,SUMA_VOX_NEIGHB_TYPES ntype,int * nl)1095 int SUMA_VoxelNeighbors (int ijk, int ni, int nj, int nk, SUMA_VOX_NEIGHB_TYPES ntype, int *nl)
1096 {
1097    static char FuncName[]={"SUMA_VoxelNeighbors"};
1098    int i, j, k;
1099    int nij, N_n;
1100 
1101    SUMA_ENTRY;
1102 
1103    N_n = 0; nij = ni * nj;
1104 
1105    /* change ijk to 3D */
1106    SUMA_1D_2_3D_index(ijk, i, j, k, ni, nij);
1107    /*
1108    if (ijk == 5030) {
1109       fprintf(SUMA_STDERR,"%s:[%d] %d %d %d\n", FuncName, ijk, i, j, k);
1110    }
1111    */
1112    if (i >= ni || i < 0) {
1113       SUMA_SL_Err("Voxel out of bounds along i direction"); SUMA_RETURN(N_n); }
1114    if (j >= nj || j < 0) {
1115       SUMA_SL_Err("Voxel out of bounds along j direction"); SUMA_RETURN(N_n); }
1116    if (k >= nk || k < 0) {
1117       SUMA_SL_Err("Voxel out of bounds along k direction"); SUMA_RETURN(N_n); }
1118 
1119    /* start with the face neighbors */
1120    if (i-1 >= 0) { nl[N_n] = SUMA_3D_2_1D_index(i-1, j, k, ni, nij); ++N_n; }
1121    if (j-1 >= 0) { nl[N_n] = SUMA_3D_2_1D_index(i, j-1, k, ni, nij); ++N_n; }
1122    if (k-1 >= 0) { nl[N_n] = SUMA_3D_2_1D_index(i, j, k-1, ni, nij); ++N_n; }
1123    if (i+1 < ni) { nl[N_n] = SUMA_3D_2_1D_index(i+1, j, k, ni, nij); ++N_n; }
1124    if (j+1 < nj) { nl[N_n] = SUMA_3D_2_1D_index(i, j+1, k, ni, nij); ++N_n; }
1125    if (k+1 < nk) { nl[N_n] = SUMA_3D_2_1D_index(i, j, k+1, ni, nij); ++N_n; }
1126    /*
1127    if (ijk == 5030) {
1128       fprintf(SUMA_STDERR,"%s:[%d] %d %d %d %d %d %d\n", FuncName, ijk,
1129                               nl[0],nl[1],nl[2],nl[3],nl[4],nl[5] );
1130    }
1131    */
1132    if ( ntype < SUMA_VOX_NEIGHB_EDGE) { SUMA_RETURN(N_n); }
1133 
1134    /* add edge neighbors */
1135    if (i-1 >= 0 && j-1 >= 0) {
1136       nl[N_n] = SUMA_3D_2_1D_index(i-1, j-1, k, ni, nij); ++N_n; }
1137    if (i-1 >= 0 && j+1 < nj) {
1138       nl[N_n] = SUMA_3D_2_1D_index(i-1, j+1, k, ni, nij); ++N_n; }
1139    if (i-1 >= 0 && k-1 >= 0) {
1140       nl[N_n] = SUMA_3D_2_1D_index(i-1, j, k-1, ni, nij); ++N_n; }
1141    if (i-1 >= 0 && k+1 < nk) {
1142       nl[N_n] = SUMA_3D_2_1D_index(i-1, j, k+1, ni, nij); ++N_n; }
1143    if (j-1 >= 0 && k-1 >= 0) {
1144       nl[N_n] = SUMA_3D_2_1D_index(i, j-1, k-1, ni, nij); ++N_n; }
1145    if (j-1 >= 0 && k+1 < nk) {
1146       nl[N_n] = SUMA_3D_2_1D_index(i, j-1, k+1, ni, nij); ++N_n; }
1147    if (i+1 < ni && j-1 >= 0) {
1148       nl[N_n] = SUMA_3D_2_1D_index(i+1, j-1, k, ni, nij); ++N_n; }
1149    if (i+1 < ni && j+1 < nj) {
1150       nl[N_n] = SUMA_3D_2_1D_index(i+1, j+1, k, ni, nij); ++N_n; }
1151    if (i+1 < ni && k-1 >= 0) {
1152       nl[N_n] = SUMA_3D_2_1D_index(i+1, j, k-1, ni, nij); ++N_n; }
1153    if (i+1 < ni && k+1 < nk) {
1154       nl[N_n] = SUMA_3D_2_1D_index(i+1, j, k+1, ni, nij); ++N_n; }
1155    if (j+1 < nj && k-1 >= 0) {
1156       nl[N_n] = SUMA_3D_2_1D_index(i, j+1, k-1, ni, nij); ++N_n; }
1157    if (j+1 < nj && k+1 < nk) {
1158       nl[N_n] = SUMA_3D_2_1D_index(i, j+1, k+1, ni, nij); ++N_n; }
1159 
1160    if ( ntype < SUMA_VOX_NEIGHB_CORNER) { SUMA_RETURN(N_n); }
1161 
1162    /* add corner neighbors */
1163    if (i-1 >= 0 && j-1 >= 0 && k-1 >= 0) {
1164       nl[N_n] = SUMA_3D_2_1D_index(i-1, j-1, k-1, ni, nij); ++N_n; }
1165    if (i-1 >= 0 && j-1 >= 0 && k+1 < nk) {
1166       nl[N_n] = SUMA_3D_2_1D_index(i-1, j-1, k+1, ni, nij); ++N_n; }
1167    if (i-1 >= 0 && j+1 < nj && k-1 >= 0) {
1168       nl[N_n] = SUMA_3D_2_1D_index(i-1, j+1, k-1, ni, nij); ++N_n; }
1169    if (i-1 >= 0 && j+1 < nj && k+1 < nk) {
1170       nl[N_n] = SUMA_3D_2_1D_index(i-1, j+1, k+1, ni, nij); ++N_n; }
1171    if (i+1 < ni && j-1 >= 0 && k-1 >= 0) {
1172       nl[N_n] = SUMA_3D_2_1D_index(i+1, j-1, k-1, ni, nij); ++N_n; }
1173    if (i+1 < ni && j-1 >= 0 && k+1 < nk) {
1174       nl[N_n] = SUMA_3D_2_1D_index(i+1, j-1, k+1, ni, nij); ++N_n; }
1175    if (i+1 < ni && j+1 < nj && k-1 >= 0) {
1176       nl[N_n] = SUMA_3D_2_1D_index(i+1, j+1, k-1, ni, nij); ++N_n; }
1177    if (i+1 < ni && j+1 < nj && k+1 < nk) {
1178       nl[N_n] = SUMA_3D_2_1D_index(i+1, j+1, k+1, ni, nij); ++N_n; }
1179 
1180 
1181    SUMA_RETURN(N_n);
1182 }
1183 
1184 /*!
1185    \brief Function to fill the volume enclose in a mask
1186    \param ijkmask (byte *) mask (nvox x 1), typically the result of the intersection of a closed surface with a volume
1187    \param ijkseed (int) 1D index of seed voxel. Must be inside the mask and not a part of it.
1188    \param ni (int) number of voxels in the i direction
1189    \param nj (int) number of voxels in the j direction
1190    \param nk (int) number of voxels in the k direction
1191    \param N_in (int *) to contain the number of voxels inside the mask
1192    \parm usethisisin (byte *)store results in this mask vector rather than allocate for a new one.
1193    \param fillhole (int) fills small holes, intended to correct for volume masks created from surfaces with minor intersections
1194    \return isin (byte *) a nvox x 1 vector containing:
1195       0: for voxels outside mask
1196       1: for voxels inside mask
1197 
1198 */
SUMA_FillToVoxelMask(byte * ijkmask,int ijkseed,int ni,int nj,int nk,int * N_in,byte * usethisisin)1199 byte *SUMA_FillToVoxelMask(byte *ijkmask, int ijkseed, int ni, int nj,
1200                            int nk, int *N_in, byte *usethisisin)
1201 {
1202    static char FuncName[]={"SUMA_FillToVoxelMask"};
1203    byte *isin = NULL, *visited=NULL;
1204    DList*candlist=NULL;
1205    DListElmt *dothiselm=NULL;
1206    int dothisvoxel;
1207    void * dtmp=NULL;
1208    int nl[50], N_n, in ,neighb, nijk, i, j, k, nij;
1209    SUMA_Boolean LocalHead = NOPE;
1210 
1211    SUMA_ENTRY;
1212 
1213    *N_in = 0;
1214 
1215    if (!ijkmask) {
1216       SUMA_SL_Err("Nothing to do");
1217       SUMA_RETURN(NULL);
1218    }
1219    if (ijkmask[ijkseed]) {
1220       SUMA_SL_Err("Seed is on mask. Bad business.");
1221       SUMA_RETURN(NULL);
1222    }
1223 
1224    nij = ni * nj;
1225    nijk = ni * nj * nk;
1226 
1227    if (LocalHead) {
1228       SUMA_1D_2_3D_index (ijkseed, i, j, k, ni, nij);
1229       fprintf(SUMA_STDERR,"%s:\nSeed is %d %d %d\n", FuncName, i, j, k);
1230    }
1231    candlist = (DList*)SUMA_malloc(sizeof(DList));
1232    visited = (byte *)SUMA_calloc(nijk, sizeof(byte));
1233    if (!visited || !candlist) {
1234       SUMA_SL_Crit("Failed to allocate for visited or candlist");
1235       SUMA_RETURN(NULL);
1236    }
1237 
1238    if (usethisisin) {
1239       isin = usethisisin;
1240       SUMA_LH("Reusing isin");
1241    } else {
1242       isin = (byte *)SUMA_calloc(nijk, sizeof(byte));
1243       if (!isin) {
1244          SUMA_SL_Crit("Failed to allocate");
1245          SUMA_RETURN(NULL);
1246       }
1247       SUMA_LH("Fresh isin");
1248    }
1249 
1250    dothisvoxel = ijkseed;
1251    dlist_init(candlist, NULL);
1252 
1253    isin[dothisvoxel] = 1; ++(*N_in); /* Add voxel to cluster */
1254    visited[dothisvoxel] = 1;
1255    dlist_ins_next(candlist, dlist_tail(candlist),
1256                   (CVOID_CAST)dothisvoxel); /* Add voxel as next candidate*/
1257 
1258    while (dlist_size(candlist)) {
1259       /* find neighbors in its vicinity */
1260       dothiselm = dlist_head(candlist); dothisvoxel = (INT_CAST) dothiselm->data;
1261       N_n = SUMA_VoxelNeighbors (dothisvoxel, ni, nj, nk,
1262                                  SUMA_VOX_NEIGHB_FACE, nl);
1263       /*
1264          if (dothisvoxel == 5030 && LocalHead) {
1265             for (in=0; in<N_n; ++in) {
1266                fprintf(SUMA_STDERR,"%s: pre removal %d\n", FuncName, nl[in]);
1267             }
1268          }
1269       */
1270       /* remove node from candidate list */
1271       dlist_remove(candlist, dothiselm, (void *)(&dtmp)); /* Make sure dtmp has
1272             enough space to hold a pointer!
1273             An int will not hold a pointer on 64 bit MCs */
1274       /*
1275          if (dothisvoxel == 5030 && LocalHead) {
1276             for (in=0; in<N_n; ++in) {
1277                fprintf(SUMA_STDERR,"%s: post removal %d\n", FuncName, nl[in]);
1278             }
1279          }
1280       */
1281       /* search to see if any are to be assigned */
1282       /* if (dothisvoxel == 5030 && LocalHead)
1283          fprintf(SUMA_STDERR,"%s: dothisvoxel = %d\n", FuncName, dothisvoxel);*/
1284       for (in=0; in<N_n; ++in) {
1285          neighb = nl[in];
1286          /* if (dothisvoxel == 5030 && LocalHead)
1287                fprintf(SUMA_STDERR,
1288                        "   Working neighb %d, ijkmask[neighb] = %d\n",
1289                        neighb, ijkmask[neighb]);*/
1290          if (!ijkmask[neighb]) {
1291             /* if (dothisvoxel == 5030 && LocalHead)
1292                fprintf(SUMA_STDERR,"   neighb %d marked isin\n", neighb); */
1293             isin[neighb] = 1; ++(*N_in); /* Add voxel to cluster */
1294             /* mark it as a candidate if it has not been visited as a
1295                candidate before */
1296             if (!visited[neighb]) {
1297                /* if (dothisvoxel == 5030 && LocalHead)
1298                   fprintf( SUMA_STDERR,
1299                            "   neighb %d added to candidate list\n", neighb); */
1300                dlist_ins_next(candlist, dlist_tail(candlist),
1301                               (CVOID_CAST)neighb);
1302                visited[neighb] = 1;
1303             }
1304          } else {
1305          /*   if (dothisvoxel == 5030 && LocalHead)
1306                   fprintf(SUMA_STDERR,
1307                            "   neighb %d already in mask\n", neighb); */
1308          }
1309       }
1310    }
1311 
1312    if (visited) SUMA_free(visited); visited = NULL;
1313    if (candlist) { dlist_destroy(candlist);
1314                    SUMA_free(candlist); candlist  = NULL; }
1315 
1316 
1317    SUMA_RETURN(isin);
1318 }
1319 
1320 /*!
1321    \brief find voxels whose centers are inside the box with corners c1 and c2
1322    c1, c2 are in voxel index coordinates. c1 is the minimum coordinates point.
1323    c2 is the maximum coordinates point.
1324 */
SUMA_VoxelsInBox(int * voxelsijk,int * N_in,float * c1,float * c2)1325 SUMA_Boolean SUMA_VoxelsInBox(int *voxelsijk, int *N_in, float *c1, float *c2)
1326 {
1327    static char FuncName[]={"SUMA_VoxelsInBox"};
1328    int n3, i, j, k;
1329    int N_Allocated = 0;
1330 
1331    SUMA_ENTRY;
1332 
1333    if (!voxelsijk) {
1334       SUMA_SL_Err("NULL voxelsijk");
1335       SUMA_RETURN(NOPE);
1336    }
1337 
1338    if (*N_in != 0) { N_Allocated = *N_in; }
1339    *N_in = 0;
1340 
1341    #if 0
1342    for (k = SUMA_ROUND(c1[2]); k <= SUMA_ROUND(c2[2]); ++k) {
1343       for (j = SUMA_ROUND(c1[1]); j <= SUMA_ROUND(c2[1]); ++j) {
1344          for (i = SUMA_ROUND(c1[0]); i <= SUMA_ROUND(c2[0]); ++i) {
1345             n3 = 3*(*N_in);
1346             voxelsijk[n3] = i; voxelsijk[n3+1] = j; voxelsijk[n3+2] = k;
1347             ++(*N_in);
1348          }
1349       }
1350    }
1351    #else
1352    for (k = (int)(c1[2]); k <= SUMA_CEIL(c2[2]); ++k) {
1353       for (j = (int)(c1[1]); j <= SUMA_CEIL(c2[1]); ++j) {
1354          for (i = (int)(c1[0]); i <= SUMA_CEIL(c2[0]); ++i) {
1355             if (N_Allocated) {
1356                if (*N_in >= N_Allocated) {
1357                   fprintf( SUMA_STDERR,
1358                            "Error %s: More voxels inbox than allocated (%d)\n",
1359                            FuncName, N_Allocated);
1360                   SUMA_RETURN(NOPE);
1361                }
1362             }
1363             n3 = 3*(*N_in);
1364             voxelsijk[n3] = i; voxelsijk[n3+1] = j; voxelsijk[n3+2] = k;
1365             ++(*N_in);
1366          }
1367       }
1368    }
1369    #endif
1370    SUMA_RETURN(YUP);
1371 }
1372 
1373 
1374 /*!
1375    \brief Applies an affine transform the coordinates in NodeList
1376 
1377    \param NodeList (float *) a vector of node XYZ triplets. (N_Node x 3 long)
1378    \param N_Node (int) number of nodes in NodeList
1379    \param M (float **) the affine transform matrix.
1380                      Minimum size is 3 rows x 4 columns.
1381                      The top left 3x3 is mat
1382                      The right most column is the shift vector vec (3 elements)
1383    \param center (float *) If center is not null then
1384                      XYZnew = mat * (vec - center) + vec + center
1385                      else  XYZnew = mat * (vec ) + vec
1386    \return ans (SUMA_Boolean ) 1 OK, 0 not OK
1387 
1388    - COORDINATES IN NodeList are REPLACED with transformed ones.
1389 
1390 */
SUMA_ApplyAffine(float * NodeList,int N_Node,float M[][4],float * center)1391 SUMA_Boolean SUMA_ApplyAffine (float *NodeList, int N_Node, float M[][4], float *center)
1392 {
1393    static char FuncName[] = {"SUMA_ApplyAffine"};
1394    float **XYZo, **Mr, **XYZn, D[3];
1395    int i, i3, idbg = 0;
1396    SUMA_Boolean LocalHead = NOPE;
1397 
1398    SUMA_ENTRY;
1399 
1400    SUMA_S_Note("Use SUMA_Apply_Coord_xform instead");
1401 
1402    if (!NodeList || N_Node <=0) {
1403       SUMA_SL_Err("Bad Entries.\n");
1404       SUMA_RETURN(NOPE);
1405    }
1406 
1407    Mr = (float **)SUMA_allocate2D(3, 3, sizeof(float));
1408    XYZn = (float **)SUMA_allocate2D(3, 1, sizeof(float));
1409    XYZo = (float **)SUMA_allocate2D(3, 1, sizeof(float));
1410 
1411    SUMA_LH("Forming Mr");
1412    Mr[0][0] = M[0][0]; Mr[0][1] = M[0][1]; Mr[0][2] = M[0][2];
1413    Mr[1][0] = M[1][0]; Mr[1][1] = M[1][1]; Mr[1][2] = M[1][2];
1414    Mr[2][0] = M[2][0]; Mr[2][1] = M[2][1]; Mr[2][2] = M[2][2];
1415    D[0] = M[0][3]; D[1] = M[1][3]; D[2] = M[2][3];
1416 
1417    SUMA_LH("Transforming");
1418    if (LocalHead ) {
1419       i3 = 3*idbg;
1420       fprintf (SUMA_STDERR,"In: %f %f %f\n", NodeList[i3], NodeList[i3+1], NodeList[i3+2]);
1421    }
1422    for (i=0; i< N_Node; ++i) {
1423       i3 = 3 * i;
1424       if (!center) {
1425          XYZo[0][0] = NodeList[i3]; XYZo[1][0] = NodeList[i3+1]; XYZo[2][0] = NodeList[i3+2];
1426       } else {
1427          XYZo[0][0] = NodeList[i3] - center[0]; XYZo[1][0] = NodeList[i3+1] - center[1]; XYZo[2][0] = NodeList[i3+2] - center[2];
1428       }
1429 
1430       SUMA_MULT_MAT(Mr, XYZo, XYZn, 3, 3, 1, float,float,float);
1431 
1432       if (!center) {
1433          NodeList[i3] = XYZn[0][0]+D[0]; NodeList[i3+1] = XYZn[1][0]+D[1]; NodeList[i3+2] = XYZn[2][0]+D[2];
1434       } else {
1435          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];
1436       }
1437 
1438    }
1439    if (LocalHead ) {
1440       i3 = 3*idbg;
1441       fprintf (SUMA_STDERR,"Out: %f %f %f\n", NodeList[i3], NodeList[i3+1], NodeList[i3+2]);
1442    }
1443    SUMA_LH("Done");
1444 
1445    SUMA_free2D((char**)Mr, 3);
1446    SUMA_free2D((char**)XYZn, 3);
1447    SUMA_free2D((char**)XYZo, 3);
1448 
1449    SUMA_RETURN(YUP);
1450 }
1451 
SUMA_getoffsets(int n,SUMA_SurfaceObject * SO,float * Off,float lim)1452 SUMA_Boolean SUMA_getoffsets (int n, SUMA_SurfaceObject *SO, float *Off, float lim)
1453 {
1454    static char FuncName[]={"SUMA_getoffsets"};
1455    int i, ni, iseg;
1456    float Off_tmp;
1457    SUMA_Boolean Visit = NOPE;
1458    static SUMA_Boolean LocalHead = NOPE;
1459 
1460    SUMA_ENTRY;
1461 
1462    #if DoCheck
1463    if (!SO->FN || !SO->EL) {
1464       SUMA_SL_Err("SO->FN &/| SO->EL are NULL.\n");
1465       SUMA_RETURN(NOPE);
1466    }
1467    #endif
1468 
1469    #if DBG
1470    if (LocalHead) fprintf(SUMA_STDERR,"%s: Working node %d, %d neighbs. lim = %f\n",
1471                                     FuncName, n, SO->FN->N_Neighb[n], lim);
1472    #endif
1473 
1474    for (i=0; i < SO->FN->N_Neighb[n]; ++i) {
1475       ni = SO->FN->FirstNeighb[n][i]; /* for notational sanity */
1476       iseg = SUMA_FindEdge (SO->EL, n, SO->FN->FirstNeighb[n][i]);
1477       #if DoCheck
1478       if (iseg < 0) {
1479          SUMA_SL_Err("Failed to find segment");
1480          SUMA_RETURN(NOPE);
1481       }
1482       #endif
1483 
1484       Off_tmp = Off[n] + SO->EL->Le[iseg];   /* that is the distance from n (original n) to ni along
1485                                                 that particular path */
1486 
1487       Visit = NOPE;
1488       if (Off[ni] < 0 || Off_tmp < Off[ni]) { /* Distance improvement, visit/revist that node */
1489          if (Off_tmp < lim) { /* only record if less than lim */
1490             Visit = YUP;
1491             Off[ni] = Off_tmp;
1492          }
1493       }
1494 
1495       #if DBG
1496       if (LocalHead) fprintf(SUMA_STDERR,"%s: %d --> %d. Visit %d, Current %f, Old %f\n",
1497          FuncName, n, ni, Visit, Off_tmp, Off[ni]);
1498       #endif
1499 
1500       #if 0
1501          { int jnk; fprintf(SUMA_STDOUT,"Pausing ..."); jnk = getchar(); fprintf(SUMA_STDOUT,"\n"); }
1502       #endif
1503 
1504       if (Visit) { /* a new node has been reached with an offset less than limit, go down that road */
1505          if (!SUMA_getoffsets (ni, SO, Off, lim))  {
1506             SUMA_SL_Err("Failed in SUMA_getoffsets");
1507             SUMA_RETURN (NOPE);
1508          }
1509       }
1510    }
1511 
1512    SUMA_RETURN(YUP);
1513 }
1514 
1515 /*!
1516    \brief Allocate and initialize SUMA_GET_OFFSET_STRUCT* struct
1517    OffS = SUMA_Initialize_getoffsets (N_Node);
1518 
1519    \param N_Node(int) number of nodes forming mesh
1520    \return OffS (SUMA_GET_OFFSET_STRUCT *) allocate structure
1521            with initialized fields for zeroth order layer
1522 
1523    \sa SUMA_AddNodeToLayer
1524    \sa SUMA_Free_getoffsets
1525    \sa SUMA_Initialize_getoffsets
1526 */
1527 
SUMA_Initialize_getoffsets(int N_Node)1528 SUMA_GET_OFFSET_STRUCT *SUMA_Initialize_getoffsets (int N_Node)
1529 {
1530    static char FuncName[]={"SUMA_Initialize_getoffsets"};
1531    int i;
1532    SUMA_GET_OFFSET_STRUCT *OffS = NULL;
1533 
1534    SUMA_ENTRY;
1535 
1536    if (N_Node <= 0) {
1537       SUMA_SL_Err("Bad values for N_Node");
1538       SUMA_RETURN (OffS);
1539    }
1540 
1541    OffS = (SUMA_GET_OFFSET_STRUCT *)SUMA_malloc(sizeof(SUMA_GET_OFFSET_STRUCT));
1542    if (!OffS) {
1543       SUMA_SL_Err("Failed to allocate for OffS");
1544       SUMA_RETURN (OffS);
1545    }
1546 
1547    OffS->OffVect = (float *) SUMA_malloc(N_Node * sizeof(float));
1548    OffS->LayerVect = (int *) SUMA_malloc(N_Node * sizeof(int));
1549    OffS->N_Nodes = N_Node;
1550 
1551    if (!OffS->LayerVect || !OffS->OffVect) {
1552       SUMA_SL_Err("Failed to allocate for OffS->LayerVect &/| OffS->OffVect");
1553       SUMA_free(OffS);
1554       SUMA_RETURN (OffS);
1555    }
1556 
1557    /* initialize vectors */
1558    for (i=0; i< N_Node; ++i) {
1559       OffS->OffVect[i] = 0.0;
1560       OffS->LayerVect[i] = -1;
1561    }
1562 
1563    /* add a zeroth layer for node n */
1564    OffS->N_layers = 1;
1565    OffS->layers = (SUMA_NODE_NEIGHB_LAYER *) SUMA_malloc(OffS->N_layers * sizeof(SUMA_NODE_NEIGHB_LAYER));
1566    OffS->layers[0].N_AllocNodesInLayer = 1;
1567    OffS->layers[0].NodesInLayer = (int *) SUMA_malloc(OffS->layers[0].N_AllocNodesInLayer * sizeof(int));
1568    OffS->layers[0].N_NodesInLayer = 0;
1569 
1570    SUMA_RETURN (OffS);
1571 
1572 }
1573 
1574 /*!
1575    \brief Add node n to neighboring layer LayInd in OffS
1576    ans = SUMA_AddNodeToLayer (n, LayInd, OffS);
1577 
1578    \param n (int)
1579    \param LayInd (int)
1580    \param OffS (SUMA_GET_OFFSET_STRUCT *)
1581    \return YUP/NOPE (good/bad)
1582 
1583    - allocation is automatically taken care of
1584 
1585    \sa SUMA_Free_getoffsets
1586    \sa SUMA_Initialize_getoffsets
1587 */
SUMA_AddNodeToLayer(int n,int LayInd,SUMA_GET_OFFSET_STRUCT * OffS)1588 SUMA_Boolean SUMA_AddNodeToLayer (int n, int LayInd, SUMA_GET_OFFSET_STRUCT *OffS)
1589 {
1590    static char FuncName[]={"SUMA_AddNodeToLayer"};
1591    static SUMA_Boolean LocalHead = NOPE;
1592 
1593    SUMA_ENTRY;
1594 
1595    /* is this a new layer */
1596    if (LayInd > OffS->N_layers) { /* error */
1597       SUMA_SL_Err("LayInd > OffS->N_layers. This should not be!");
1598       SUMA_RETURN(NOPE);
1599    } else if (LayInd == OffS->N_layers) { /* need a new one */
1600       SUMA_LH("Adding layer");
1601       OffS->N_layers += 1;
1602       OffS->layers = (SUMA_NODE_NEIGHB_LAYER *) SUMA_realloc(OffS->layers, OffS->N_layers*sizeof(SUMA_NODE_NEIGHB_LAYER));
1603       OffS->layers[LayInd].N_AllocNodesInLayer = 200;
1604       OffS->layers[LayInd].NodesInLayer = (int *) SUMA_malloc(OffS->layers[LayInd].N_AllocNodesInLayer * sizeof(int));
1605       OffS->layers[LayInd].N_NodesInLayer = 0;
1606    }
1607 
1608    OffS->layers[LayInd].N_NodesInLayer += 1;
1609    /* do we need to reallocate for NodesInLayer ? */
1610    if (OffS->layers[LayInd].N_NodesInLayer ==  OffS->layers[LayInd].N_AllocNodesInLayer) { /* need more space */
1611       SUMA_LH("reallocating neighbors");
1612       OffS->layers[LayInd].N_AllocNodesInLayer += 200;
1613       OffS->layers[LayInd].NodesInLayer = (int *) SUMA_realloc (OffS->layers[LayInd].NodesInLayer, OffS->layers[LayInd].N_AllocNodesInLayer * sizeof(int));
1614    }
1615 
1616    OffS->layers[LayInd].NodesInLayer[OffS->layers[LayInd].N_NodesInLayer - 1] = n;
1617 
1618    SUMA_RETURN(YUP);
1619 }
1620 
1621 /*!
1622    \brief free memory associated with SUMA_GET_OFFSET_STRUCT * struct
1623 
1624    \param OffS (SUMA_GET_OFFSET_STRUCT *) Offset strcture
1625    \return NULL
1626 
1627    \sa SUMA_Recycle_getoffsets
1628    \sa SUMA_Initialize_getoffsets
1629 */
SUMA_Free_getoffsets(SUMA_GET_OFFSET_STRUCT * OffS)1630 SUMA_GET_OFFSET_STRUCT * SUMA_Free_getoffsets (SUMA_GET_OFFSET_STRUCT *OffS)
1631 {
1632    static char FuncName[]={"SUMA_Free_getoffsets"};
1633    int i = 0;
1634    static SUMA_Boolean LocalHead = NOPE;
1635 
1636    SUMA_ENTRY;
1637 
1638    if (!OffS) SUMA_RETURN(NULL);
1639 
1640    if (OffS->layers) {
1641       for (i=0; i< OffS->N_layers; ++i) if (OffS->layers[i].NodesInLayer) SUMA_free(OffS->layers[i].NodesInLayer);
1642       SUMA_free(OffS->layers);
1643    }
1644 
1645    if (OffS->OffVect) SUMA_free(OffS->OffVect);
1646    if (OffS->LayerVect) SUMA_free(OffS->LayerVect);
1647    SUMA_free(OffS); OffS = NULL;
1648 
1649    SUMA_RETURN(NULL);
1650 }
1651 
1652 /*!
1653    \brief reset the SUMA_GET_OFFSET_STRUCT after it has been used by a node
1654    \param OffS (SUMA_GET_OFFSET_STRUCT *) Offset structure that has node neighbor
1655                                           info and detail to be cleared
1656    \return (YUP/NOPE) success/failure
1657 
1658    - No memory is freed here
1659    - The used node layer indices are reset to -1
1660    - The number of nodes in each layer are reset to 0
1661 
1662    \sa SUMA_Free_getoffsets to free this structure once and for all
1663    \sa SUMA_Initialize_getoffsets
1664 */
SUMA_Recycle_getoffsets(SUMA_GET_OFFSET_STRUCT * OffS)1665 SUMA_Boolean SUMA_Recycle_getoffsets (SUMA_GET_OFFSET_STRUCT *OffS)
1666 {
1667    static char FuncName[]={"SUMA_Recycle_getoffsets"};
1668    int i, j;
1669    static SUMA_Boolean LocalHead = NOPE;
1670 
1671    SUMA_ENTRY;
1672 
1673    for (i=0; i < OffS->N_layers; ++i) {
1674       /* reset the layer index of used nodes in LayerVect */
1675       for (j=0; j < OffS->layers[i].N_NodesInLayer; ++j) {
1676          OffS->LayerVect[OffS->layers[i].NodesInLayer[j]] = -1;
1677       }
1678       /* reset number of nodes in each layer */
1679       OffS->layers[i].N_NodesInLayer = 0;
1680    }
1681 
1682    SUMA_RETURN(YUP);
1683 }
1684 
1685 /*!
1686    \brief calculates the length of the segments defined
1687    by a node and its first-order neighbors. The resulting
1688    matrix very closely resembles SO->FN->FirstNeighb
1689    DistFirstNeighb = SUMA_CalcNeighbDist (SO);
1690 
1691    \param SO (SUMA_SurfaceObject *) with FN field required
1692    \return DistFirstNeighb (float **) DistFirstNeighb[i][j] contains the
1693                                   length of the segment formed by nodes
1694                                   SO->FN->NodeId[i] and SO->FN->FirstNeighb[i][j]
1695 
1696    free it with: SUMA_free2D((char **)DistFirstNeighb, SO->FN->N_Node);
1697    This function was created to try and speed up SUMA_getoffsets2 but it proved
1698    useless.
1699    Sample code showing two ways of getting segment length:
1700    #if 1
1701          // calculate segment distances(a necessary horror)
1702          // this made no difference in speed
1703          DistFirstNeighb = SUMA_CalcNeighbDist (SO);
1704          if (!DistFirstNeighb) {
1705             SUMA_SL_Crit("Failed to allocate for DistFirstNeighb\n");
1706             exit(1);
1707          }
1708          { int n1, n2, iseg;
1709             n1 = 5; n2 = SO->FN->FirstNeighb[n1][2];
1710             iseg = SUMA_FindEdge(SO->EL, n1, n2);
1711             fprintf(SUMA_STDERR, "%s: Distance between nodes %d and %d:\n"
1712                                  "from DistFirstNeighb = %f\n"
1713                                  "from SO->EL->Le = %f\n", FuncName, n1, n2,
1714                                  DistFirstNeighb[n1][2], SO->EL->Le[iseg]);
1715             exit(1);
1716          }
1717    #endif
1718 */
1719 
SUMA_CalcNeighbDist(SUMA_SurfaceObject * SO)1720 float ** SUMA_CalcNeighbDist (SUMA_SurfaceObject *SO)
1721 {
1722    static char FuncName[]={"SUMA_CalcNeighbDist"};
1723    float **DistFirstNeighb=NULL, *a, *b;
1724    int i, j;
1725    static SUMA_Boolean LocalHead = NOPE;
1726 
1727    SUMA_ENTRY;
1728 
1729    if (!SO) { SUMA_RETURN(NULL); }
1730    if (!SO->FN) { SUMA_RETURN(NULL); }
1731 
1732    DistFirstNeighb = (float **)SUMA_allocate2D(SO->FN->N_Node,
1733                               SO->FN->N_Neighb_max, sizeof(float));
1734    if (!DistFirstNeighb) {
1735       SUMA_SL_Crit("Failed to allocate for DistFirstNeighb");
1736       SUMA_RETURN(NULL);
1737    }
1738    for (i=0; i < SO->FN->N_Node; ++i) {
1739       a = &(SO->NodeList[3*SO->FN->NodeId[i]]);
1740       for (j=0; j < SO->FN->N_Neighb[i]; ++j) {
1741          b = &(SO->NodeList[3*SO->FN->FirstNeighb[i][j]]);
1742          SUMA_SEG_LENGTH(a, b, DistFirstNeighb[i][j]);
1743          if (LocalHead) {
1744             if (SO->FN->NodeId[i] == 5 && SO->FN->FirstNeighb[i][j] == 133092) {
1745                fprintf (SUMA_STDERR, "%f %f %f\n%f %f %f\n%f\n",
1746                   SO->NodeList[3*SO->FN->NodeId[i]],
1747                   SO->NodeList[3*SO->FN->NodeId[i]+1],
1748                   SO->NodeList[3*SO->FN->NodeId[i]+2],
1749                   SO->NodeList[3*SO->FN->FirstNeighb[i][j]],
1750                   SO->NodeList[3*SO->FN->FirstNeighb[i][j]+1],
1751                   SO->NodeList[3*SO->FN->FirstNeighb[i][j]+2],
1752                   DistFirstNeighb[i][j]);
1753             }
1754          }
1755       }
1756    }
1757 
1758    SUMA_RETURN (DistFirstNeighb);
1759 }
1760 
1761 /*!
1762    \brief A function to calculate the geodesic distance of nodes
1763             connected to node n
1764            SUMA_getoffsets was the first incarnation but it was too slow.
1765     ans = SUMA_getoffsets2 (n, SO, lim, OffS, CoverThisNode, N_CoverThisNode)
1766 
1767    \param n (int) index of center node
1768    \param SO (SUMA_SurfaceObject *) structure containing surface object
1769    \param lim (float) maximum geodesic distance to travel
1770                      (ignored when CoverThisNode is used)
1771                      if lim is < 0 then the search stops when
1772                      layer (int)-lim is reached. The geodesic
1773                      distances are still computed in that case
1774                      but no distance limit applies here.
1775    \param OffS (SUMA_GET_OFFSET_STRUCT *) initialized structure to contain
1776           the nodes that neighbor n within lim mm
1777           or until all the nodes in CoverThisNode are used up
1778    \param CoverThisNode (int *) SO->N_Node mask vector such that
1779                                  if CoverThisNode[i] then node i
1780                                  has to be reached (supersedes lim)
1781                                  NULL if you don't want to use it.
1782    \param N_CoverThisNode (int) number of nodes to cover (where CoverThisNode = 1).
1783    \return ans (SUMA_Boolean) YUP = GOOD, NOPE = BAD
1784 
1785    \sa SUMA_AddNodeToLayer
1786    \sa SUMA_Free_getoffsets
1787    \sa SUMA_Initialize_getoffsets
1788    \sa SUMA_getoffsets_ll
1789 
1790 
1791 
1792 The following code was used to test different methods for calculating the segment length,
1793 none (except for Seg = constant) proved to be faster, probably because of memory access time.
1794 One of the options required the use of DistFirstNeighb which is calculated by function
1795 SUMA_CalcNeighbDist. It mirrors SO->EL->FirstNeighb
1796 
1797 static int SEG_METHOD;
1798 switch (SEG_METHOD) {
1799    case CALC_SEG:
1800       // this is the slow part, too many redundant computations.
1801       //cuts computation time by a factor > 3 if Seg was set to a constant
1802       //However, attempts at accessing pre-calculated segment lengths
1803       //proved to be slower.
1804       SUMA_SEG_LENGTH (a, b, Seg);
1805       break;
1806    case FIND_EDGE_MACRO:
1807       // this one's even slower, calculations have been made once but
1808       //function calls are costly (7.53 min)
1809       iseg = -1;
1810       if (n_k < n_jne) {SUMA_FIND_EDGE (SO->EL, n_k, n_jne, iseg);}
1811       else {SUMA_FIND_EDGE (SO->EL, n_jne, n_k, iseg);}
1812       if (iseg < 0) {
1813          SUMA_SL_Err("Segment not found.\nSetting Seg = 10000.0");
1814          Seg = 10000.0;
1815       } else Seg = SO->EL->Le[iseg];
1816       break;
1817    case FIND_EDGE:
1818       //this one's even slower, calculations have been made once but
1819       //function calls are costly
1820       iseg = SUMA_FindEdge (SO->EL, n_k, n_jne);
1821       Seg = SO->EL->Le[iseg];
1822       break;
1823 
1824    case DIST_FIRST_NEIGHB:
1825       // consumes memory but might be faster than previous 2 (5.22 min)
1826       Seg = DistFirstNeighb[n_jne][k];
1827       break;
1828    case CONST:
1829       // 1.7 min
1830       Seg = 1.0;
1831       break;
1832    default:
1833       SUMA_SL_Err("Bad option");
1834       break;
1835 }
1836 */
SUMA_getoffsets2(int n,SUMA_SurfaceObject * SO,float lim,SUMA_GET_OFFSET_STRUCT * OffS,int * CoverThisNode,int N_CoverThisNode)1837 SUMA_Boolean SUMA_getoffsets2 (  int n, SUMA_SurfaceObject *SO,
1838                                  float lim, SUMA_GET_OFFSET_STRUCT *OffS,
1839                                  int *CoverThisNode, int N_CoverThisNode)
1840 {
1841    static char FuncName[]={"SUMA_getoffsets2"};
1842    int LayInd, il, n_il, n_jne, k, n_prec = -1, n_k, jne, iseg=0, MaxLay;
1843    float Off_tmp, Seg, *a, *b, minSeg, SegPres;
1844       /*! *** SegPres added Jul 08 04, ZSS bug before ... */
1845    SUMA_Boolean Visit = NOPE;
1846    SUMA_Boolean AllDone = NOPE;
1847    static SUMA_Boolean LocalHead = NOPE;
1848 
1849    SUMA_ENTRY;
1850 
1851    if (!OffS || !SO->FN) {
1852       SUMA_SL_Err("NULL OffS, or NULL SO->FN");
1853       SUMA_RETURN(NOPE);
1854    }
1855 
1856    if (lim < 0) { /* Secret code for stopping based on layer number */
1857       MaxLay = (int) -lim; /* Maximum layer */
1858       lim = 100000.0;   /* Hayuge */
1859    } else MaxLay = SO->N_Node; /* a very large number */
1860 
1861    /* setup 0th layer */
1862    OffS->OffVect[n] = 0.0;   /* n is at a distance 0.0 from itself */
1863    OffS->LayerVect[n] = 0;   /* n is on the zeroth layer */
1864    OffS->layers[0].N_NodesInLayer = 1;
1865    OffS->layers[0].NodesInLayer[0] = n;
1866    if (CoverThisNode) {
1867       if (CoverThisNode[n]) {
1868          CoverThisNode[n] = 0; --N_CoverThisNode;
1869       }
1870    }
1871    LayInd = 1;  /* index of next layer to build */
1872    AllDone = NOPE;
1873    while (!AllDone && LayInd <= MaxLay) {
1874       AllDone = YUP; /* assume that this would be the last layer */
1875       for (il=0; il < OffS->layers[LayInd - 1].N_NodesInLayer; ++il) {
1876                                  /* go over all nodes in previous layer */
1877          n_il =  OffS->layers[LayInd - 1].NodesInLayer[il];
1878                               /* node from previous layer */
1879          for (jne=0; jne < SO->FN->N_Neighb[n_il]; ++jne) {
1880                            /* go over all the neighbours of node n_il */
1881             n_jne = SO->FN->FirstNeighb[n_il][jne];
1882                            /* node that is an immediate neighbor to n_il */
1883             if (OffS->LayerVect[n_jne] < 0) {
1884                            /* node is not assigned to a layer yet */
1885                OffS->LayerVect[n_jne] =  LayInd;
1886                            /* assign new layer index to node */
1887                OffS->OffVect[n_jne] = 0.0;
1888                            /* reset its distance from node n */
1889                SUMA_AddNodeToLayer (n_jne, LayInd, OffS);
1890                            /* add the node to the nodes in the layer */
1891                minSeg = 100000.0;
1892                n_prec = -1;
1893                Seg = 0.0;
1894                SegPres = 0.0;
1895                for (k=0; k < SO->FN->N_Neighb[n_jne]; ++k) {
1896                            /* calculate shortest distance of node to
1897                               any precursor */
1898                   n_k = SO->FN->FirstNeighb[n_jne][k];
1899                   if (OffS->LayerVect[n_k] == LayInd - 1) {
1900                            /* this neighbor is a part of the previous layer,
1901                               good */
1902                      if (n_prec < 0) n_prec = SO->FN->FirstNeighb[n_jne][0];
1903                      a = &(SO->NodeList[3*n_k]); b = &(SO->NodeList[3*n_jne]);
1904                      /* this is the slow part, too many redundant computations.
1905                         Computation time is cut by a factor > 2 if Seg was set
1906                         to a constant
1907                         However, attempts at accessing pre-calculated segment
1908                         lengths proved to be slower. See Comments in function
1909                         help*/
1910                      SUMA_SEG_LENGTH_SQ (a, b, Seg);
1911                      if (OffS->OffVect[n_prec] + Seg < minSeg) {
1912                         minSeg = Seg + OffS->OffVect[n_prec];
1913                         SegPres = Seg;
1914                         n_prec = n_k;
1915                      }
1916                   }
1917                }/* for k */
1918 
1919                if (n_prec < 0) { /* bad news */
1920                   SUMA_SL_Crit("No precursor found for node.");
1921                   OffS = SUMA_Free_getoffsets (OffS);
1922                   SUMA_RETURN(NOPE);
1923                } else {
1924                   OffS->OffVect[n_jne] = OffS->OffVect[n_prec] +
1925                                              sqrt(SegPres);
1926                   SegPres = 0.0;
1927                   if (!CoverThisNode) {
1928                      if (OffS->OffVect[n_jne] < lim) {
1929                         /* must go at least one more layer */
1930                         AllDone = NOPE;
1931                      }
1932                   } else {
1933                      if (CoverThisNode[n_jne]) {
1934                         CoverThisNode[n_jne] = 0;
1935                         --N_CoverThisNode;
1936                      }
1937                      if (N_CoverThisNode > 0) {
1938                         AllDone = NOPE;
1939                      }
1940                   }
1941                }
1942             } /* node not already in layer */
1943 
1944          } /* for jne */
1945 
1946       } /* for il */
1947       ++LayInd;
1948    } /* while AllDone */
1949 
1950    SUMA_RETURN(YUP);
1951 }
1952 
SUMA_Free_Offset_ll_Datum(void * data)1953 void SUMA_Free_Offset_ll_Datum(void *data)
1954 {
1955    static char FuncName[]={"SUMA_Free_Offset_ll_Datum"};
1956    SUMA_OFFSET_LL_DATUM *dt;
1957 
1958    SUMA_ENTRY;
1959 
1960    if (data) {
1961       dt = (SUMA_OFFSET_LL_DATUM *)data;
1962       SUMA_free(dt);
1963    }
1964 
1965    SUMA_RETURNe;
1966 }
1967 
SUMA_New_Offset_ll_Datum(int n,int layer)1968 SUMA_OFFSET_LL_DATUM *SUMA_New_Offset_ll_Datum(int n, int layer)
1969 {
1970    static char FuncName[]={"SUMA_New_Offset_ll_Datum"};
1971    SUMA_OFFSET_LL_DATUM * datum = NULL;
1972 
1973    SUMA_ENTRY;
1974 
1975    datum = (SUMA_OFFSET_LL_DATUM *)SUMA_malloc(sizeof(SUMA_OFFSET_LL_DATUM));
1976    datum->ni = n;
1977    datum->layer = layer;
1978    datum->off = -1.0;
1979 
1980    SUMA_RETURN(datum);
1981 }
1982 #define SUMA_BEGINNING_OF_LAYER(list, LayInd, Elm) {   \
1983    SUMA_OFFSET_LL_DATUM * m_dat = NULL; \
1984    DListElmt *m_Elm = NULL;   \
1985    do {  \
1986      if (m_Elm) m_Elm = m_Elm->next;   \
1987      else m_Elm =  dlist_head(list); \
1988      m_dat = (SUMA_OFFSET_LL_DATUM *)m_Elm->data; \
1989    } while(m_dat->layer != LayInd && m_Elm != dlist_tail(list));   \
1990    if (m_dat->layer != LayInd) Elm = NULL;  \
1991    else Elm = m_Elm; \
1992 }
1993 #define SUMA_FIND_ELMENT_FOR_NODE(list, n_jne, Elm){  \
1994    SUMA_OFFSET_LL_DATUM * m_dat = NULL; \
1995    DListElmt *m_Elm = NULL;   \
1996    do {  \
1997      if (m_Elm) m_Elm = m_Elm->next;   \
1998      else m_Elm =  dlist_head(list); \
1999      m_dat = (SUMA_OFFSET_LL_DATUM *)m_Elm->data; \
2000    } while(m_dat->ni != n_jne && m_Elm != dlist_tail(list));   \
2001    if (m_dat->ni != n_jne) Elm = NULL;  \
2002    else Elm = m_Elm; \
2003 }
SUMA_getoffsets_ll(int n,SUMA_SurfaceObject * SO,float lim,int * CoverThisNode,int N_CoverThisNode)2004 DList * SUMA_getoffsets_ll (  int n, SUMA_SurfaceObject *SO, float lim,
2005                               int *CoverThisNode, int N_CoverThisNode)
2006 {
2007    static char FuncName[]={"SUMA_getoffsets_ll"};
2008    int LayInd, il, n_il, n_jne, k, n_prec = -1, n_k, jne, iseg=0, MaxLay=0;
2009    float Off_tmp, Seg, *a, *b, minSeg, SegPres;
2010       /*! *** SegPres added Jul 08 04, ZSS bug before ... */
2011    SUMA_Boolean Visit = NOPE;
2012    SUMA_Boolean AllDone = NOPE;
2013    SUMA_OFFSET_LL_DATUM * n_dat = NULL, *dat = NULL, *dat_nk = NULL,
2014                         *dat_prec = NULL, *dat_ne=NULL;
2015    DList *list = NULL;
2016    DListElmt *elm = NULL, *elm_prec = NULL, *elm_ne=NULL, *elm_nk=NULL;
2017    static SUMA_Boolean LocalHead = NOPE;
2018 
2019    SUMA_ENTRY;
2020 
2021 
2022    /* create the list */
2023    SUMA_LH("Initializing list ...");
2024    list = (DList *)SUMA_malloc(sizeof(DList));
2025    dlist_init(list, SUMA_Free_Offset_ll_Datum);
2026 
2027    if (lim < 0) { /* Secret code for stopping based on layer number */
2028       MaxLay = (int) -lim; /* Maximum layer */
2029       lim = 100000.0;   /* Hayuge */
2030    } else MaxLay = SO->N_Node; /* a very large number */
2031 
2032    /* setup 0th layer */
2033    SUMA_LH("New OffsetDatum");
2034    n_dat = SUMA_New_Offset_ll_Datum(n, 0);
2035    n_dat->off = 0.0;   /* n is at a distance 0.0 from itself */
2036    dlist_ins_next(list, dlist_tail(list), (void*)n_dat);
2037 
2038    if (CoverThisNode) {
2039       if (CoverThisNode[n]) {
2040          CoverThisNode[n] = 0; --N_CoverThisNode;
2041       }
2042    }
2043    LayInd = 1;  /* index of next layer to build */
2044    AllDone = NOPE;
2045    while (!AllDone && LayInd <= MaxLay) {
2046       AllDone = YUP; /* assume that this would be the last layer */
2047       elm = NULL;
2048          do {
2049             if (!elm) { SUMA_BEGINNING_OF_LAYER(list, (LayInd-1), elm); }
2050             else elm = elm->next;
2051             if (!elm) {
2052                SUMA_SL_Err("Could not find beginning of layer!");
2053                SUMA_RETURN(NULL);
2054             }
2055             dat = (SUMA_OFFSET_LL_DATUM *)elm->data;
2056             if (dat->layer == LayInd -1) {
2057                n_il = dat->ni;
2058                for (jne=0; jne < SO->FN->N_Neighb[n_il]; ++jne) {
2059                         /* go over all the neighbours of node n_il */
2060                   n_jne = SO->FN->FirstNeighb[n_il][jne];
2061                         /* node that is an immediate neighbor to n_il */
2062                   SUMA_FIND_ELMENT_FOR_NODE(list, n_jne, elm_ne);
2063                   if (!elm_ne) { /* node not in any layer */
2064                      dat_ne = SUMA_New_Offset_ll_Datum(n_jne, LayInd);
2065                            /* create an element for it */
2066                      dat_ne->off = 0.0;
2067                      dlist_ins_next(list, dlist_tail(list), (void*)dat_ne);
2068                      minSeg = 100000.0;
2069                      n_prec = -1;
2070                      Seg = 0.0;
2071                      SegPres = 0.0;
2072                      for (k=0; k < SO->FN->N_Neighb[n_jne]; ++k) {
2073                            /* calculate shortest distance of node to
2074                               any precursor */
2075                         n_k = SO->FN->FirstNeighb[n_jne][k];
2076                         SUMA_FIND_ELMENT_FOR_NODE(list, n_k, elm_nk);
2077                         if (n_prec < 0 && elm_nk) {
2078                            n_prec = n_k; elm_prec = elm_nk;
2079                            dat_prec = (SUMA_OFFSET_LL_DATUM *)elm_prec->data;
2080                         }
2081                         if (elm_nk) {
2082                            dat_nk = (SUMA_OFFSET_LL_DATUM *)elm_nk->data;
2083                            if (dat_nk->layer == LayInd - 1) {
2084                                  /* this neighbor is a part of the
2085                                     previous layer, good */
2086                               a = &(SO->NodeList[3*n_k]);
2087                               b = &(SO->NodeList[3*n_jne]);
2088                                  /* this is the slow part, too many redundant
2089                                     computations. Computation time is cut by a
2090                                     factor > 2 if Seg was set to a constant
2091                                     However, attempts at accessing pre-calculated
2092                                     segment lengths proved to be slower.
2093                                     See Comments in function help*/
2094                               SUMA_SEG_LENGTH_SQ (a, b, Seg);
2095                               if (dat_prec->off + Seg < minSeg) {
2096                                  minSeg = Seg + dat_prec->off;
2097                                  SegPres = Seg;
2098                                  n_prec = n_k;
2099                                  elm_prec = elm_nk;
2100                                  dat_prec = dat_nk;
2101                               }
2102                            }
2103                         } /* if elm_nk */
2104                      }/* for k */
2105                      if (n_prec < 0) { /* bad news */
2106                         SUMA_SL_Crit("No precursor found for node.");
2107                         SUMA_RETURN(NULL);
2108                      } else {
2109                         dat_ne->off = dat_prec->off + sqrt(SegPres);
2110                         SegPres = 0.0;
2111                         if (!CoverThisNode) {
2112                            if (dat_ne->off < lim) {
2113                               /* must go at least one more layer */
2114                               AllDone = NOPE;
2115                            }
2116                         } else {
2117                            if (CoverThisNode[n_jne]) {
2118                               CoverThisNode[n_jne] = 0; --N_CoverThisNode;
2119                            }
2120                            if (N_CoverThisNode > 0) {
2121                               AllDone = NOPE;
2122                            }
2123                         }
2124                      }
2125                   } /* if elm_ne */
2126                } /* for jne */
2127             } /* dat->layer == LayInd */
2128          }  while (dat->layer == (LayInd-1) && elm != dlist_tail(list));
2129 
2130       ++LayInd;
2131    } /* while AllDone */
2132 
2133    SUMA_RETURN(list);
2134 }
2135 
2136 typedef struct {
2137    SUMA_SurfaceObject *SO;
2138    SUMA_SurfaceObject *SOref;
2139    SUMA_COMM_STRUCT *cs;
2140    double Vref;
2141    double Rref;
2142    double V;
2143    double R;
2144    float *tmpList;
2145 } SUMA_VolDiffDataStruct; /*!< a special struct for the functions to equate the
2146                                volume of two surfaces */
2147 typedef struct {
2148    SUMA_SurfaceObject *SO;
2149    SUMA_SurfaceObject *SOref;
2150    SUMA_COMM_STRUCT *cs;
2151    double Aref;
2152    double Rref;
2153    double A;
2154    double R;
2155    float *tmpList;
2156 } SUMA_AreaDiffDataStruct; /*!< a special struct for the functions to equate the
2157                                 area of two surfaces */
2158 
2159 /*!
2160    \brief Changes the coordinates of SO's nodes so that the new average radius of the surface
2161    is equal to r
2162 
2163    This function is an integral part of the function for equating the areas of 2 surfaces.
2164    Nodes are stretched by a fraction equal to:
2165       (Rref - r) / Rref * Un where Un is the distance of the node from the center of the surface
2166       Rref is the reference radius, r is the desired radius
2167    \param SO (SUMA_SurfaceObject *) Surface object, obviously
2168    \param r (double) (see above)
2169    \param Rref (double) (see above)
2170    \pram tmpList (float *) a pre-allocated vector to contain the new coordinates of the surface
2171    \return A (double) the area of the new surface (post streching)
2172    \sa SUMA_AreaDiff
2173 */
SUMA_NewAreaAtRadius(SUMA_SurfaceObject * SO,double r,double Rref,float * tmpList)2174 double SUMA_NewAreaAtRadius(SUMA_SurfaceObject *SO, double r,
2175                            double Rref, float *tmpList)
2176 {
2177    static char FuncName[]={"SUMA_NewAreaAtRadius"};
2178    double Dr, A=0.0,  Un, U[3], Dn, P2[2][3], c[3];
2179    float *fp;
2180    int i;
2181    SUMA_Boolean LocalHead = NOPE;
2182 
2183    SUMA_ENTRY;
2184 
2185    /* calculate Dr and normalize by the radius of SOref */
2186    Dr = ( r - Rref ) / Rref;
2187    /* Now loop over all the nodes in SO and add the deal */
2188    for (i=0; i<SO->N_Node; ++i) {
2189       /* change node coordinate of each node by Dr, along radial direction  */
2190       fp = &(SO->NodeList[3*i]);
2191       SUMA_UNIT_VEC(SO->Center, fp, U, Un);
2192       Dn = Dr*Un + Un;
2193       if (Un) {
2194          SUMA_COPY_VEC(SO->Center, c, 3, float, double);
2195          SUMA_POINT_AT_DISTANCE_NORM(U, c, Dn, P2);
2196          tmpList[3*i  ] = (float)P2[0][0];
2197          tmpList[3*i+1] = (float)P2[0][1];
2198          tmpList[3*i+2] = (float)P2[0][2];
2199       } else {
2200          SUMA_SL_Err("Identical points!\n"
2201                      "No coordinates modified");
2202          SUMA_RETURN(0);
2203       }
2204    }
2205 
2206 
2207    /* calculate the new Area */
2208    fp = SO->NodeList;/* save NodeList */
2209    SO->NodeList = tmpList; /* use new coordinates */
2210    A = fabs((double)SUMA_Mesh_Area(SO, NULL, -1));
2211    SO->NodeList = fp; fp = NULL;   /* make NodeList point to the original data */
2212 
2213    SUMA_LHv("Old: Rref=%.4f \n"
2214             "New:    r=%.4f, Area=%.4f\n", Rref, r, A);
2215    SUMA_RETURN(A);
2216 }
2217 /*!
2218    \brief Change the coordinates so that the new surface has a radius r
2219    \param SO: La surface
2220    \param r: Le radius
2221    \param Center: If not NULL then a 3x1 vector containing the Center of SO
2222                   Use this when SO->Center is not OK for some reason. Else, SO->Center
2223                   is used.
2224 */
SUMA_NewSurfaceRadius(SUMA_SurfaceObject * SO,double r,float * Center)2225 SUMA_Boolean SUMA_NewSurfaceRadius(SUMA_SurfaceObject *SO,
2226                                    double r, float *Center)
2227 {
2228    static char FuncName[]={"SUMA_NewSurfaceRadius"};
2229    double Un, U[3], Dn, P2[2][3], c[3];
2230    float *fp;
2231    int i;
2232    SUMA_Boolean LocalHead = NOPE;
2233 
2234    SUMA_ENTRY;
2235 
2236    if (!SO || !SO->NodeList) { SUMA_S_Err("Imbecile!"); SUMA_RETURN(NOPE); }
2237    if (!Center) Center = SO->Center;
2238 
2239    /* Now loop over all the nodes in SO and add the deal */
2240    for (i=0; i<SO->N_Node; ++i) {
2241       /* change node coordinate of each node by Dr, along radial direction  */
2242       fp = &(SO->NodeList[3*i]); SUMA_UNIT_VEC(Center, fp, U, Un);
2243       if (Un) {
2244          SUMA_COPY_VEC(Center, c, 3, float, double);
2245          SUMA_POINT_AT_DISTANCE_NORM(U, c, r, P2);
2246          SO->NodeList[3*i  ] = (float)P2[0][0];
2247          SO->NodeList[3*i+1] = (float)P2[0][1];
2248          SO->NodeList[3*i+2] = (float)P2[0][2];
2249       } else {
2250          SUMA_SL_Err("Identical points!\n"
2251                      "No coordinates modified");
2252       }
2253    }
2254 
2255    SUMA_RETURN(YUP);
2256 }
2257 
2258 /*!
2259    \brief Changes the coordinates of SO's nodes so that the new average radius of the surface
2260    is equal to r
2261 
2262    This function is an integral part of the function for equating the volumes of 2 surfaces.
2263    Nodes are stretched by a fraction equal to:
2264       (Rref - r) / Rref * Un where Un is the distance of the node from the center of the surface
2265       Rref is the reference radius, r is the desired radius
2266    \param SO (SUMA_SurfaceObject *) Surface object, obviously
2267    \param r (double) (see above)
2268    \param Rref (double) (see above)
2269    \pram tmpList (float *) a pre-allocated vector to contain the new coordinates of the surface
2270    \return V (double) the volume of the new surface (post streching)
2271    \sa SUMA_VolDiff
2272 */
SUMA_NewVolumeAtRadius(SUMA_SurfaceObject * SO,double r,double Rref,float * tmpList)2273 double SUMA_NewVolumeAtRadius(SUMA_SurfaceObject *SO, double r, double Rref, float *tmpList)
2274 {
2275    static char FuncName[]={"SUMA_NewVolumeAtRadius"};
2276    double Dr, V=0.0,  Un, U[3], Dn, P2[2][3], c[3];
2277    float *fp;
2278    int i;
2279    SUMA_Boolean LocalHead = NOPE;
2280 
2281    SUMA_ENTRY;
2282 
2283    /* calculate Dr and normalize by the radius of SOref */
2284    Dr = ( r - Rref ) / Rref;
2285 
2286    /* Now loop over all the nodes in SO and add the deal */
2287    for (i=0; i<SO->N_Node; ++i) {
2288       /* change node coordinate of each node by Dr, along radial direction  */
2289       fp = &(SO->NodeList[3*i]); SUMA_UNIT_VEC(SO->Center, fp, U, Un);
2290       Dn = Dr*Un + Un;
2291       if (Un) {
2292          SUMA_COPY_VEC(SO->Center, c, 3, float, double);
2293          SUMA_POINT_AT_DISTANCE_NORM(U, c, Dn, P2);
2294          tmpList[3*i] = (float)P2[0][0]; tmpList[3*i+1] = (float)P2[0][1]; tmpList[3*i+2] = (float)P2[0][2];
2295       } else {
2296          SUMA_SL_Err("Identical points!\n"
2297                      "No coordinates modified");
2298          SUMA_RETURN(0);
2299       }
2300    }
2301 
2302 
2303    /* calculate the new volume */
2304    fp = SO->NodeList;/* save NodeList */
2305    SO->NodeList = tmpList; /* use new coordinates */
2306    V = fabs((double)SUMA_Mesh_Volume(SO, NULL, -1, 1, NULL));
2307    SO->NodeList = fp; fp = NULL;   /* make NodeList point to the original data */
2308 
2309    SUMA_RETURN(V);
2310 }
2311 
SUMA_AreaDiff(double r,void * fvdata)2312 double SUMA_AreaDiff(double r, void *fvdata)
2313 {
2314    static char FuncName[]={"SUMA_AreaDiff"};
2315    double da, *fp, Dr, A;
2316    static int ncall=0;
2317    int i;
2318    static double Rref = 0.0, Aref = 0.0;
2319    SUMA_SurfaceObject *SO, *SOref;
2320    SUMA_COMM_STRUCT *cs=NULL;
2321    SUMA_AreaDiffDataStruct *fdata = (SUMA_AreaDiffDataStruct*)fvdata ;
2322    SUMA_Boolean LocalHead = NOPE;
2323 
2324    SUMA_ENTRY;
2325 
2326    if (!fdata) {
2327       SUMA_LH("Reset");
2328       Rref = 0.0; Aref = 0.0;
2329       ncall = 0;
2330       SUMA_RETURN(0.0);
2331    }
2332 
2333    SO = fdata->SO;
2334    SOref = fdata->SOref;
2335    cs = fdata->cs;
2336 
2337    if (!ncall) {
2338       SUMA_LH("Initializing, calculating Aref and Rref");
2339       Aref = fdata->Aref;
2340       Rref = fdata->Rref;
2341       if (LocalHead) {
2342          fprintf(SUMA_STDERR,"%s: Reference area = %f, radius = %f \n",
2343                         FuncName, Aref, Rref); }
2344       if (cs && cs->Send) { /* send the first monster
2345                      ( it's SOref "in SUMA" that's being modified on the fly)
2346                      That means only real time updates for isotopic surfaces
2347                      Someday change this to allow sending SO */
2348          if (!SUMA_SendToSuma (  SOref, cs, (void *)SO->NodeList,
2349                                  SUMA_NODE_XYZ, 1)) {
2350          SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCommunication halted.");
2351          }
2352       }
2353    }
2354 
2355    A = SUMA_NewAreaAtRadius(SO, r, Rref, fdata->tmpList);
2356    da = A - Aref; /* the area difference */
2357    if (LocalHead) {
2358       fprintf(SUMA_STDERR,
2359                "%s: Call %d, A = %f, Aref = %f, da = %f\n",
2360                FuncName,  ncall, A, Aref, da);
2361       fprintf(SUMA_STDERR, "SOref->idcode_str=%s\n", SOref->idcode_str);
2362    }
2363 
2364    /* need an update ? */
2365    if (cs && cs->Send) { /* send the update
2366                      (it's SOref "in SUMA" that's being modified on the fly) */
2367       if (!SUMA_SendToSuma (SOref, cs, (void *)fdata->tmpList,
2368                             SUMA_NODE_XYZ, 1)) {
2369       SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCommunication halted.");
2370       }
2371    }
2372 
2373    ++ncall;
2374 
2375    SUMA_RETURN(da);
2376 }
2377 
SUMA_VolDiff(double r,void * fvdata)2378 double SUMA_VolDiff(double r, void *fvdata)
2379 {
2380    static char FuncName[]={"SUMA_VolDiff"};
2381    double dv, *fp, Dr, V;
2382    static int ncall=0;
2383    int i;
2384    static double Rref = 0.0, Vref = 0.0;
2385    SUMA_SurfaceObject *SO, *SOref;
2386    SUMA_COMM_STRUCT *cs=NULL;
2387    SUMA_VolDiffDataStruct *fdata = (SUMA_VolDiffDataStruct*)fvdata ;
2388    SUMA_Boolean LocalHead = NOPE;
2389 
2390    SUMA_ENTRY;
2391 
2392    if (!fdata) {
2393       SUMA_LH("Reset");
2394       Rref = 0.0; Vref = 0.0;
2395       ncall = 0;
2396       SUMA_RETURN(0.0);
2397    }
2398 
2399    SO = fdata->SO;
2400    SOref = fdata->SOref;
2401    cs = fdata->cs;
2402 
2403    if (!ncall) {
2404       SUMA_LH("Initializing, calculating Vref and Rref");
2405       Vref = fdata->Vref;
2406       Rref = fdata->Rref;
2407       if (LocalHead) {
2408          fprintf(SUMA_STDERR,"%s: Reference volume = %f, radius = %f \n",
2409                   FuncName, Vref, Rref); }
2410       if (cs && cs->Send) { /* send the first monster
2411                (it's SOref "in SUMA" that's being modified on the fly)
2412                See comment in AreaDiff*/
2413          if (!SUMA_SendToSuma (SOref, cs, (void *)SO->NodeList,
2414                                  SUMA_NODE_XYZ, 1)) {
2415          SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCommunication halted.");
2416          }
2417       }
2418    }
2419 
2420    V = SUMA_NewVolumeAtRadius(SO, r, Rref, fdata->tmpList);
2421    dv = V-Vref; /* the volume difference */
2422 
2423    /* need an update ? */
2424    if (cs && cs->Send) { /* send the update
2425             (it's SOref "in SUMA" that's being modified on the fly) */
2426       if (!SUMA_SendToSuma (SOref, cs, (void *)fdata->tmpList,
2427                             SUMA_NODE_XYZ, 1)){
2428          SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCommunication halted.");
2429       }
2430    }
2431 
2432    ++ncall;
2433 
2434    SUMA_RETURN(dv);
2435 }
2436 
2437 /*! \brief Binary Zero Search, a function to find the zero of a function
2438    \param a (double) 1st point, f(a) < 0
2439    \param b (double) 2nd point, f(b) > 0 (actually all you need is that f(a)*f(b) is < 0
2440    \param *f (double )(double x, void *data) function to find the zero point (at the right x)
2441    \param fdata(void *)a pointer to the data that accompanies x as input to f
2442    \param Nitermax (int) the maximum number of iterations
2443    \param tol(double) the tolerance for convergence. Stop when ( |f(x)| < tol )
2444 */
SUMA_BinaryZeroSearch(double a,double b,double (* f)(double x,void * data),void * fdata,int Nitermax,double tol)2445 double SUMA_BinaryZeroSearch( double a, double b,
2446                               double(*f)(double x, void *data),
2447                               void *fdata, int Nitermax, double tol) {
2448    static char FuncName[]={"SUMA_BinaryZeroSearch"};
2449    int Niter;
2450    double x, fx;
2451    SUMA_Boolean done;
2452    SUMA_Boolean LocalHead = NOPE;
2453 
2454    SUMA_ENTRY;
2455 
2456    if (Nitermax < 0) Nitermax = 1000;
2457 
2458    x = 0.0;
2459    Niter = 0;
2460    done = NOPE;
2461    while(!done && Niter < Nitermax) {
2462       x = (a+b)/2.0;
2463       fx = (*f)(x, fdata);
2464       if (LocalHead)
2465          fprintf(SUMA_STDERR,"%s: %d\ta=%.4f\tb=%.4f\tx=%.4f\tfx=%.4f\n",
2466                              FuncName, Niter, a, b, x, fx);
2467       if (fx < 0) a = x;
2468       else b = x;
2469       if (fabs(fx) < tol) done = YUP;
2470       ++Niter;
2471    }
2472 
2473    /* Now do a cleanup call */
2474    fx = (*f)(x, NULL);
2475 
2476    if (!done) {
2477       SUMA_SL_Warn(  "Reached iteration limit\n"
2478                      "without converging.\n");
2479    }
2480 
2481    SUMA_RETURN(x);
2482 }
2483 
2484 
2485 /*
2486 #define FROM_THIS_NODE 0
2487 #define TO_THIS_NODE 10
2488 */
2489 /*!
2490    \brief a function to find two values a and b such that
2491    DA(a) is < 0 and DA(b) is > 0
2492    These two starting points are used for the optimization function
2493    SUMA_BinaryZeroSearch
2494 */
SUMA_GetAreaDiffRange(SUMA_AreaDiffDataStruct * fdata,double * ap,double * bp)2495 SUMA_Boolean SUMA_GetAreaDiffRange(
2496       SUMA_AreaDiffDataStruct *fdata, double *ap, double *bp)
2497 {
2498    static char FuncName[]={"SUMA_GetAreaDiffRange"};
2499    double a = 0.0, b = 0.0, nat=0, nbt=0, An, Bn;
2500    SUMA_Boolean LocalHead = NOPE;
2501 
2502    SUMA_ENTRY;
2503 
2504    /* decide on segment range */
2505    fdata->Aref = fabs((double)SUMA_Mesh_Area(fdata->SOref, NULL, -1));
2506    SUMA_SO_RADIUS(fdata->SOref, fdata->Rref);
2507    fdata->A = fabs((double)SUMA_Mesh_Area(fdata->SO, NULL, -1));
2508    SUMA_SO_RADIUS(fdata->SO, fdata->R);
2509 
2510    if (fdata->Aref > fdata->A) { /* current settings low end,
2511                                     acceptable for a */
2512       a = fdata->R;
2513       An = fdata->A;
2514       /* now find b such that area at b is larger than Aref */
2515       b = fdata->Rref;
2516       do {
2517          b *= 1.1;
2518          Bn = SUMA_NewAreaAtRadius(fdata->SO, b, fdata->Rref, fdata->tmpList);
2519          ++nbt;
2520       } while ( fdata->Aref > Bn && nbt < 200); /* stop when area at B is
2521                                                    larger than Aref */
2522    } else { /* current settings high end, acceptable for b */
2523       b = fdata->R;
2524       Bn = fdata->A;
2525       /* now find a such that area at a is less than Aref */
2526       a = fdata->Rref;
2527       do {
2528          a *= 0.9;
2529          An = SUMA_NewAreaAtRadius(fdata->SO, a, fdata->Rref, fdata->tmpList);
2530          ++nat;
2531       } while ( fdata->Aref < An && nat < 200); /* stop when area at A is
2532                                                    smaller than Aref */
2533 
2534    }
2535    *ap = a; *bp = b;
2536 
2537    if (nat >= 200 || nbt >= 200) {
2538       SUMA_SL_Err("Failed to find segment.");
2539       SUMA_RETURN(NOPE);
2540    }
2541 
2542    if (LocalHead) {
2543       fprintf (SUMA_STDERR,
2544          "%s:\nChosen range is [%f %f] with Areas [%f %f]\n"
2545          "             , reference Area is %f\n",
2546             FuncName, a, b, An, Bn, fdata->Aref);
2547    }
2548 
2549    SUMA_RETURN(YUP);
2550 }
2551 /*!
2552    \brief a function to find two values a and b such that
2553    DV(a) is < 0 and DV(b) is > 0
2554    These two starting points are used for the optimization function
2555    SUMA_BinaryZeroSearch
2556 */
SUMA_GetVolDiffRange(SUMA_VolDiffDataStruct * fdata,double * ap,double * bp)2557 SUMA_Boolean SUMA_GetVolDiffRange(SUMA_VolDiffDataStruct *fdata,
2558                                   double *ap, double *bp)
2559 {
2560    static char FuncName[]={"SUMA_GetVolDiffRange"};
2561    double a = 0.0, b = 0.0, nat=0, nbt=0;
2562    SUMA_Boolean LocalHead = NOPE;
2563 
2564    SUMA_ENTRY;
2565 
2566    /* decide on segment range */
2567    fdata->Vref = fabs((double)SUMA_Mesh_Volume(fdata->SOref, NULL, -1, 1, NULL));
2568    SUMA_SO_RADIUS(fdata->SOref, fdata->Rref);
2569    fdata->V = fabs((double)SUMA_Mesh_Volume(fdata->SO, NULL, -1, 1, NULL));
2570    SUMA_SO_RADIUS(fdata->SO, fdata->R);
2571 
2572    /* a very simple range setting. might very well fail at times */
2573    if (fdata->Vref > fdata->V) { /* current settings low end, acceptable for a */
2574       a = fdata->R;
2575       b = fdata->Rref;/* now find b such that volume at b is larger than Vref */
2576       do {
2577          SUMA_LH("Looking for b");
2578          b *= 1.1; ++nbt;
2579       } while ( fdata->Vref > SUMA_NewVolumeAtRadius(fdata->SO, b,
2580                               fdata->Rref, fdata->tmpList)
2581                && nbt < 200);/* stop when volume  at B is larger than Vref */
2582    }else{ /* current settings high end, acceptable for b */
2583       b = fdata->R;
2584       a = fdata->Rref;/* now find a such that volume at a is less than Vref */
2585       do {
2586          SUMA_LH("Looking for a");
2587          a *= 0.9; ++nat;
2588       } while ( fdata->Vref < SUMA_NewVolumeAtRadius(fdata->SO, a, fdata->Rref,
2589                                  fdata->tmpList) &&
2590                 nat < 200); /* stop when volume  at A is smaller than Vref */
2591    }
2592 
2593    *ap = a; *bp = b;
2594 
2595    if (nat >= 200 || nbt >= 200) {
2596       SUMA_SL_Err("Failed to find segment.");
2597       SUMA_RETURN(NOPE);
2598    }
2599 
2600    SUMA_RETURN(YUP);
2601 }
2602 
2603 /*!
2604    \brief inflates or deflates a surface to make the area of one
2605             surface (SO) equal to the area of another (SOref)
2606    \param SO: The surface to modify.
2607                SO's NodeList pointer is reallocated in the function!
2608    \param SOref: The reference surface
2609    \param tol (float): The acceptable difference between the two areas
2610    \param cs (SUMA_COMM_STRUCT *): The suma communication structure
2611 
2612    - This function does not update the normals and other coordinate
2613          related properties for SO.
2614    \sa SUMA_RECOMPUTE_NORMALS
2615 */
SUMA_EquateSurfaceAreas(SUMA_SurfaceObject * SO,SUMA_SurfaceObject * SOref,float tol,SUMA_COMM_STRUCT * cs)2616 SUMA_Boolean SUMA_EquateSurfaceAreas(
2617                   SUMA_SurfaceObject *SO,
2618                   SUMA_SurfaceObject *SOref,
2619                   float tol, SUMA_COMM_STRUCT *cs)
2620 {
2621    static char FuncName[]={"SUMA_EquateSurfaceAreas"};
2622    int iter, i, iter_max, ndiv;
2623    double a, b, d;
2624    SUMA_AreaDiffDataStruct fdata;
2625    SUMA_Boolean LocalHead = NOPE;
2626 
2627    SUMA_ENTRY;
2628 
2629    if (!SO || !SOref) { SUMA_SL_Err("NULL surfaces"); SUMA_RETURN(NOPE); }
2630    if (  (  SO->N_Node != SOref->N_Node
2631          || SO->N_FaceSet != SOref->N_FaceSet) && (cs && cs->Send)) {
2632       SUMA_S_Warn("Surfaces not isotopic, realtime updates now turned off");
2633       cs->Send = 0;
2634    }
2635 
2636    if (LocalHead) {
2637       fprintf(SUMA_STDERR, "%s:\n"
2638                            " SO    Center: %f, %f, %f\n"
2639                            " SOref Center: %f, %f, %f\n"
2640                            , FuncName,
2641                            SO->Center[0], SO->Center[1], SO->Center[2],
2642                            SOref->Center[0], SOref->Center[1],
2643                            SOref->Center[2]);
2644    }
2645 
2646    /* fill up fdata */
2647    fdata.SO = SO; fdata.SOref = SOref; fdata.cs = cs;
2648    fdata.tmpList = (float *)SUMA_malloc(
2649                               SO->NodeDim * SO->N_Node * sizeof(float));
2650    if (!fdata.tmpList) {
2651       SUMA_SL_Err("Failed to allocate");
2652       SUMA_RETURN(0);
2653    }
2654 
2655    if (!SUMA_GetAreaDiffRange(&fdata, &a, &b)) {
2656       SUMA_SL_Err("Failed to get range");
2657       SUMA_RETURN(NOPE);
2658    }
2659 
2660    if (LocalHead) {
2661       fprintf(SUMA_STDERR,"%s:\na = %f\tb=%f\n", FuncName, a, b);
2662    }
2663    SUMA_BinaryZeroSearch(a, b, SUMA_AreaDiff, &fdata, 500, tol);
2664 
2665    /* now make the new node list be SO's thingy*/
2666    SUMA_free(SO->NodeList); SO->NodeList = fdata.tmpList; fdata.tmpList = NULL;
2667 
2668    SUMA_RETURN(YUP);
2669 }
2670 
2671 /*!
2672    \brief inflates or deflates a surface to make the volume of one surface (SO) equal to the volume of another (SOref)
2673    \param SO: The surface to modify
2674    \param SOref: The reference surface
2675    \param tol (float): The acceptable difference between the two volumes
2676    \param cs (SUMA_COMM_STRUCT *): The suma communication structure
2677 
2678    - This function does not update the normals and other coordinate related properties for SO.
2679    \sa SUMA_RECOMPUTE_NORMALS
2680 */
SUMA_EquateSurfaceVolumes(SUMA_SurfaceObject * SO,SUMA_SurfaceObject * SOref,float tol,SUMA_COMM_STRUCT * cs)2681 SUMA_Boolean SUMA_EquateSurfaceVolumes(SUMA_SurfaceObject *SO,
2682                                        SUMA_SurfaceObject *SOref,
2683                                        float tol, SUMA_COMM_STRUCT *cs)
2684 {
2685    static char FuncName[]={"SUMA_EquateSurfaceVolumes"};
2686    int iter, i, iter_max, ndiv;
2687    double a, b, d;
2688    SUMA_VolDiffDataStruct fdata;
2689    SUMA_Boolean LocalHead = NOPE;
2690 
2691    SUMA_ENTRY;
2692 
2693    if (!SO || !SOref) { SUMA_SL_Err("NULL surfaces"); SUMA_RETURN(NOPE); }
2694    if ( (SO->N_Node != SOref->N_Node ||
2695          SO->N_FaceSet != SOref->N_FaceSet) && (cs && cs->Send)) {
2696        SUMA_S_Warn("Surfaces not isotopic, realtime updates now turned off");
2697        cs->Send = 0;
2698    }
2699 
2700    if (LocalHead) {
2701       fprintf(SUMA_STDERR, "%s:\n"
2702                            " SO    Center: %f, %f, %f\n"
2703                            " SOref Center: %f, %f, %f\n"
2704                            , FuncName,
2705                            SO->Center[0], SO->Center[1], SO->Center[2],
2706                            SOref->Center[0], SOref->Center[1], SOref->Center[2]);
2707    }
2708 
2709    /* fill up fdata */
2710    fdata.SO = SO; fdata.SOref = SOref; fdata.cs = cs;
2711    fdata.tmpList = (float *)SUMA_malloc(SO->NodeDim * SO->N_Node *
2712                                           sizeof(float));
2713    if (!fdata.tmpList) {
2714       SUMA_SL_Err("Failed to allocate");
2715       SUMA_RETURN(0);
2716    }
2717 
2718    if (!SUMA_GetVolDiffRange(&fdata, &a, &b)) {
2719       SUMA_SL_Err("Failed to get range");
2720       SUMA_RETURN(NOPE);
2721    }
2722 
2723    if (LocalHead) {
2724       fprintf(SUMA_STDERR,"%s:\na = %f\tb=%f\n", FuncName, a, b);
2725    }
2726    SUMA_BinaryZeroSearch(a, b, SUMA_VolDiff, &fdata, 500, tol);
2727 
2728    /* now make the new node list be SO's thingy*/
2729    SUMA_free(SO->NodeList); SO->NodeList = fdata.tmpList; fdata.tmpList = NULL;
2730 
2731    SUMA_RETURN(YUP);
2732 }
2733 
SUMA_EquateSurfaceCenters(SUMA_SurfaceObject * SO,SUMA_SurfaceObject * SOref,int recompute)2734 SUMA_Boolean SUMA_EquateSurfaceCenters (SUMA_SurfaceObject *SO,
2735                                         SUMA_SurfaceObject *SOref,
2736                                         int recompute)
2737 {
2738    static char FuncName[]={"SUMA_EquateSurfaceCenters"};
2739    float d[3];
2740    int i, i3;
2741    SUMA_Boolean LocalHead = NOPE;
2742 
2743    SUMA_ENTRY;
2744 
2745    if (!SO || !SOref) { SUMA_SL_Err("NULL surfaces"); SUMA_RETURN(NOPE); }
2746    if (recompute > 0) {/* recompute center of SO */
2747       SUMA_MIN_MAX_SUM_VECMAT_COL ( SO->NodeList, SO->N_Node,
2748                                     SO->NodeDim,  SO->MinDims,
2749                                     SO->MaxDims,  SO->Center );
2750       SO->Center[0] /= SO->N_Node;
2751       SO->Center[1] /= SO->N_Node;
2752       SO->Center[2] /= SO->N_Node;
2753    }
2754    if (recompute > 1) {/* recompute center of SOref */
2755       SUMA_MIN_MAX_SUM_VECMAT_COL ( SOref->NodeList, SOref->N_Node,
2756                                     SOref->NodeDim,  SOref->MinDims,
2757                                     SOref->MaxDims,  SOref->Center );
2758       SOref->Center[0] /= SOref->N_Node;
2759       SOref->Center[1] /= SOref->N_Node;
2760       SOref->Center[2] /= SOref->N_Node;
2761    }
2762    if (LocalHead) {
2763       fprintf(SUMA_STDERR, "%s:\n"
2764                            " SO    Center: %f, %f, %f\n"
2765                            " SOref Center: %f, %f, %f\n"
2766                            , FuncName,
2767                            SO->Center[0], SO->Center[1], SO->Center[2],
2768                            SOref->Center[0], SOref->Center[1], SOref->Center[2]);
2769    }
2770    for (i=0; i<3; ++i) d[i] = SO->Center[i] - SOref->Center[i];
2771    for (i=0; i<SO->N_Node; ++i) {
2772       i3 = SO->NodeDim*i;
2773       SO->NodeList[i3  ] -= d[0];
2774       SO->NodeList[i3+2] -= d[1];
2775       SO->NodeList[i3+3] -= d[2];
2776    }
2777    for (i=0; i<3; ++i) SO->Center[i] = SOref->Center[i];
2778 
2779    SUMA_RETURN(YUP);
2780 }
2781 
2782 /*!
2783    \brief stretch each node along the center--node direction such that the new distance is = radius
2784    \param SO The surface to be modified.
2785           Adjust node coordinates of SO so that
2786           Node i on SO is repositioned such
2787           that |c i| = radius
2788           c is the center of SO, calculated as the average coordinate.
2789    \param SOref reference SurfaceObject, used to communicate with SUMA
2790    \param radius , you know what.
2791    \param cs the famed communication structure
2792 
2793    \sa SUMA_ProjectToSphere
2794 */
2795 /*
2796 #define FROM_THIS_NODE 0
2797 #define TO_THIS_NODE 10
2798 */
SUMA_ProjectSurfaceToSphere(SUMA_SurfaceObject * SO,SUMA_SurfaceObject * SOref,float radius,SUMA_COMM_STRUCT * cs)2799 SUMA_Boolean SUMA_ProjectSurfaceToSphere(SUMA_SurfaceObject *SO,
2800                   SUMA_SurfaceObject *SOref ,float radius, SUMA_COMM_STRUCT *cs)
2801 {
2802    static char FuncName[]={"SUMA_ProjectSurfaceToSphere"};
2803    int i=0, j=0, cnt = 0, istrt, istp;
2804    struct timeval start_time, start_time_all;
2805    float etime_GetOffset, etime_GetOffset_all, ave_dist= 0.0, dj = 0.0, ave_dist_ref= 0.0, *a=NULL;
2806    float P2[2][3], U[3], Un;
2807    SUMA_Boolean LocalHead = NOPE;
2808 
2809    SUMA_ENTRY;
2810 
2811    if (!SO || (cs && !SOref)) { SUMA_SL_Err("NULL surface"); SUMA_RETURN(NOPE); }
2812 
2813    if (LocalHead) {
2814       fprintf(SUMA_STDERR, "%s:\n"
2815                            " SO    Center: %f, %f, %f\n"
2816                            " radius = %f\n", FuncName,
2817                            SO->Center[0], SO->Center[1], SO->Center[2],
2818                            radius);
2819    }
2820 
2821    #ifdef FROM_THIS_NODE
2822    istrt = FROM_THIS_NODE;
2823    istp = TO_THIS_NODE+1;
2824    #else
2825    istrt = 0;
2826    istp = SO->N_Node;
2827    #endif
2828    ave_dist_ref =  radius;
2829    for (i =istrt ; i<istp; ++i) {
2830       if (i == 0) {
2831          SUMA_etime(&start_time,0);
2832       }
2833       /* move node i to the reference average location
2834       Do not travel along normals, you should travel along
2835       radial direction Center-->node*/
2836       a = &(SO->NodeList[3*i]); SUMA_UNIT_VEC(SO->Center, a, U, Un);
2837       if (Un) {
2838          SUMA_POINT_AT_DISTANCE_NORM(U, SO->Center, ave_dist_ref, P2);
2839          SO->NodeList[3*i] = P2[0][0];
2840          SO->NodeList[3*i+1] = P2[0][1];
2841          SO->NodeList[3*i+2] = P2[0][2];
2842       } else {
2843             SUMA_SL_Err("Identical points!\n"
2844                         "No coordinates modified");
2845       }
2846 
2847       if (LocalHead) {
2848          if (! (i%999)) {
2849             a = &(SO->NodeList[3*i]);
2850             SUMA_SEG_LENGTH(a, SO->Center, dj);
2851             fprintf(SUMA_STDERR, "%s:\n"
2852                            "node i=%d, avg_dist_ref = %f\ncnt = %d\n"
2853                            "Check on P2: New dist =%f ?=? %f\n",
2854                            FuncName, i, ave_dist_ref, cnt, dj, ave_dist_ref);
2855             etime_GetOffset = SUMA_etime(&start_time,1);
2856             SUMA_S_Errv("Search to %f mm took %f seconds for %d nodes.\n"
2857                         "Projected completion time: %f minutes\n",
2858                   radius, etime_GetOffset, i+1,
2859                   etime_GetOffset * SO->N_Node / 60.0 / (i+1));
2860          }
2861       }
2862       if (! (i%99) && cs) {
2863          if (cs && cs->Send) { /* send the first monster
2864                   (it's SOref  "in SUMA" that's being modified on the fly*/
2865             if (!SUMA_SendToSuma (SOref, cs, (void *)SO->NodeList,
2866                                   SUMA_NODE_XYZ, 1)) {
2867             SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCommunication halted.");
2868             }
2869          }
2870       }
2871 
2872       #ifdef FROM_THIS_NODE
2873       {
2874          FILE *fid=NULL;
2875          char *outname=NULL, tmp[20];
2876          int ii;
2877          if (cs && cs->Send) { /* send the first monster (it's SOref that's being modified on the fly*/
2878             if (!SUMA_SendToSuma (SOref, cs, (void *)SO->NodeList, SUMA_NODE_XYZ, 1)) {
2879             SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCommunication halted.");
2880             }
2881          }
2882          sprintf(tmp,"offset_n%d", FROM_THIS_NODE);
2883          outname = SUMA_Extension("", ".1D", YUP);
2884          outname = SUMA_append_replace_string(outname, "offset.1D", "", 1);
2885          fid = fopen(outname, "w"); free(outname); outname = NULL;
2886          if (!fid) {
2887             SUMA_SL_Err("Could not open file for writing.\nCheck file permissions, disk space.\n");
2888          } else {
2889             fprintf (fid,"#Column 1 = Node index\n"
2890                          "#column 2 = Neighborhood layer\n"
2891                          "#Column 3 = Distance from node %d\n", 99);
2892             for (ii=0; ii<SO->N_Node; ++ii) {
2893                if (OffS->LayerVect[ii] >= 0) {
2894                   fprintf(fid,"%d\t%d\t%f\n", ii, OffS->LayerVect[ii], OffS->OffVect[ii]);
2895                }
2896             }
2897             fclose(fid);
2898          }
2899          { int jnk; fprintf(SUMA_STDOUT,"Pausing, next node is %d...", i+1); jnk = getchar(); fprintf(SUMA_STDOUT,"\n"); }
2900       }
2901       #endif
2902 
2903 
2904    }
2905    SO->isSphere = SUMA_GEOM_SPHERE;
2906    SO->SphereRadius = radius;
2907    SUMA_COPY_VEC(SO->Center, SO->SphereCenter, 3, float, float);
2908 
2909 
2910    SUMA_RETURN(YUP);
2911 }
2912 
2913 /*!
2914    \brief make the size of 2 surfaces match see help -match_size option in SurfSmooth
2915    \param SO The surface to be modified.
2916           Adjust node coordinates of SO so that
2917           it matches the original size.
2918           Node i on SO is repositioned such
2919           that |c i| = 1/N sum(|cr j|) where
2920           c and cr are the centers of SO and SOref, respectively.
2921           N is the number of nodes that are within max_off along
2922           the surface (geodesic) from node i.
2923           j is one of the nodes neighboring i.
2924    \param SOref The surface to be matched
2925    \param max_off geodesic neighborhood to search around i
2926    \param cs the famed communication structure
2927 */
2928 /*
2929 #define FROM_THIS_NODE 0
2930 #define TO_THIS_NODE 10
2931 */
SUMA_EquateSurfaceSize(SUMA_SurfaceObject * SO,SUMA_SurfaceObject * SOref,float max_off,SUMA_COMM_STRUCT * cs)2932 SUMA_Boolean SUMA_EquateSurfaceSize(SUMA_SurfaceObject *SO, SUMA_SurfaceObject *SOref, float max_off, SUMA_COMM_STRUCT *cs)
2933 {
2934    static char FuncName[]={"SUMA_EquateSurfaceSize"};
2935    int i=0, j=0, cnt = 0, istrt, istp;
2936    struct timeval start_time, start_time_all;
2937    float etime_GetOffset, etime_GetOffset_all, ave_dist= 0.0, dj = 0.0, ave_dist_ref= 0.0, *a=NULL;
2938    float P2[2][3], U[3], Un;
2939    SUMA_GET_OFFSET_STRUCT *OffS = NULL;
2940    SUMA_Boolean LocalHead = NOPE;
2941 
2942    SUMA_ENTRY;
2943 
2944    if (!SO || !SOref) { SUMA_SL_Err("NULL surfaces"); SUMA_RETURN(NOPE); }
2945    if (SO->N_Node != SOref->N_Node || SO->N_FaceSet != SOref->N_FaceSet) { SUMA_SL_Err("Surfaces not isotopic"); SUMA_RETURN(NOPE); }
2946 
2947    if (LocalHead) {
2948       fprintf(SUMA_STDERR, "%s:\n"
2949                            " SO    Center: %f, %f, %f\n"
2950                            " SOref Center: %f, %f, %f\n"
2951                            " max_off = %f\n", FuncName,
2952                            SO->Center[0], SO->Center[1], SO->Center[2],
2953                            SOref->Center[0], SOref->Center[1], SOref->Center[2],
2954                            max_off);
2955    }
2956 
2957    OffS = SUMA_Initialize_getoffsets (SOref->N_Node);
2958    #ifdef FROM_THIS_NODE
2959    istrt = FROM_THIS_NODE;
2960    istp = TO_THIS_NODE+1;
2961    #else
2962    istrt = 0;
2963    istp = SOref->N_Node;
2964    #endif
2965    for (i =istrt ; i<istp; ++i) {
2966       if (i == 0) {
2967          SUMA_etime(&start_time,0);
2968       }
2969       SUMA_getoffsets2 (i, SOref, max_off, OffS, NULL, 0);
2970       /* find average distance between nodes within offset and center of surface */
2971       a = &(SOref->NodeList[3*i]); SUMA_SEG_LENGTH(a, SOref->Center, ave_dist_ref);
2972       cnt = 1;
2973       #ifdef FROM_THIS_NODE
2974             fprintf(SUMA_STDERR, "%s: Considering the following %d neighbors to:\n"
2975                                  "i=%d; [%f, %f, %f]; d[%d] = %f\n", FuncName, OffS->N_Nodes,
2976                                     i, SOref->NodeList[3*i], SOref->NodeList[3*i+1], SOref->NodeList[3*i+2],
2977                                     cnt, ave_dist_ref);
2978       #endif
2979       for (j=0; j<OffS->N_Nodes; ++j)
2980       {
2981 
2982          if (i!=j && OffS->LayerVect[j] >= 0 && OffS->OffVect[j] <= max_off)
2983          {
2984 
2985             a = &(SOref->NodeList[3*j]); SUMA_SEG_LENGTH(a, SOref->Center, dj);
2986             ave_dist_ref += dj;
2987             ++cnt;
2988             #ifdef FROM_THIS_NODE
2989                fprintf(SUMA_STDERR, ""
2990                                  "j=%d; [%f, %f, %f]; d[%d] = %f\n",
2991                                     j, SOref->NodeList[3*j], SOref->NodeList[3*j+1], SOref->NodeList[3*j+2],
2992                                     cnt, dj);
2993             #endif
2994          }
2995       }
2996       ave_dist_ref /=  (float)cnt;
2997       /* move node i to the reference average location
2998       Do not travel along normals, you should travel along
2999       radial direction Center-->node*/
3000       a = &(SO->NodeList[3*i]); SUMA_UNIT_VEC(SO->Center, a, U, Un);
3001       if (Un) {
3002          SUMA_POINT_AT_DISTANCE_NORM(U, SO->Center, ave_dist_ref, P2);
3003          SO->NodeList[3*i] = P2[0][0]; SO->NodeList[3*i+1] = P2[0][1]; SO->NodeList[3*i+2] = P2[0][2];
3004       } else {
3005             SUMA_SL_Err("Identical points!\n"
3006                         "No coordinates modified");
3007       }
3008 
3009       if (LocalHead) {
3010          if (! (i%999)) {
3011             a = &(SO->NodeList[3*i]);
3012             SUMA_SEG_LENGTH(a, SOref->Center, dj);
3013             fprintf(SUMA_STDERR, "%s:\n"
3014                            "node i=%d, avg_dist_ref = %f\ncnt = %d\n"
3015                            "Check on P2: New dist =%f ?=? %f\n",
3016                            FuncName, i, ave_dist_ref, cnt, dj, ave_dist_ref);
3017             etime_GetOffset = SUMA_etime(&start_time,1);
3018             fprintf(SUMA_STDERR, "%s: Search to %f mm took %f seconds for %d nodes.\n"
3019                   "Projected completion time: %f minutes\n",
3020                   FuncName, max_off, etime_GetOffset, i+1,
3021                   etime_GetOffset * SO->N_Node / 60.0 / (i+1));
3022          }
3023       }
3024       if (! (i%99) && cs) {
3025          if (cs && cs->Send) { /* send the first monster (it's SOref  "in SUMA" that's being modified on the fly*/
3026             if (!SUMA_SendToSuma (SOref, cs, (void *)SO->NodeList, SUMA_NODE_XYZ, 1)) {
3027             SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCommunication halted.");
3028             }
3029          }
3030       }
3031 
3032       #ifdef FROM_THIS_NODE
3033       {
3034          FILE *fid=NULL;
3035          char *outname=NULL, tmp[20];
3036          int ii;
3037          if (cs && cs->Send) { /* send the first monster (it's SOref that's being modified on the fly*/
3038             if (!SUMA_SendToSuma (SOref, cs, (void *)SO->NodeList, SUMA_NODE_XYZ, 1)) {
3039             SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCommunication halted.");
3040             }
3041          }
3042          sprintf(tmp,"offset_n%d", FROM_THIS_NODE);
3043          outname = SUMA_Extension("", ".1D", YUP);
3044          outname = SUMA_append_replace_string(outname, "offset.1D", "", 1);
3045          fid = fopen(outname, "w"); free(outname); outname = NULL;
3046          if (!fid) {
3047             SUMA_SL_Err("Could not open file for writing.\nCheck file permissions, disk space.\n");
3048          } else {
3049             fprintf (fid,"#Column 1 = Node index\n"
3050                          "#column 2 = Neighborhood layer\n"
3051                          "#Column 3 = Distance from node %d\n", 99);
3052             for (ii=0; ii<SO->N_Node; ++ii) {
3053                if (OffS->LayerVect[ii] >= 0) {
3054                   fprintf(fid,"%d\t%d\t%f\n", ii, OffS->LayerVect[ii], OffS->OffVect[ii]);
3055                }
3056             }
3057             fclose(fid);
3058          }
3059          { int jnk; fprintf(SUMA_STDOUT,"Pausing, next node is %d...", i+1); jnk = getchar(); fprintf(SUMA_STDOUT,"\n"); }
3060       }
3061       #endif
3062 
3063       /* recycle offsets structure */
3064       SUMA_Recycle_getoffsets (OffS);
3065 
3066    }
3067 
3068    /* offsets are to be freed */
3069    SUMA_Free_getoffsets(OffS); OffS = NULL;
3070 
3071    SUMA_RETURN(YUP);
3072 }
3073 
3074 /*!
3075    \brief calculate the interpolation weights required to smooth data on the surface
3076    using M.K. Chung et al. Neuroimage 03
3077 
3078    \param SO (SUMA_SurfaceObject *) Surface object with valid SO->NodeList, SO->FaceSetList and SO->FN
3079    \return wgt (float **) 2D matrix of the same size as SO->FirstNeighb that contains the
3080                            weights to be applied to a node's neighbors in interpolation
3081                            Free the result with SUMA_free2D ((char **)wgt, SO->N_Node);
3082                            The weights are computed using the Finite Element method.
3083    \sa SUMA_Chung_Smooth
3084 */
SUMA_Chung_Smooth_Weights(SUMA_SurfaceObject * SO)3085 float ** SUMA_Chung_Smooth_Weights (SUMA_SurfaceObject *SO)
3086 {
3087    static char FuncName[]={"SUMA_Chung_Smooth_Weights"};
3088    float **wgt=NULL, *coord_nbr=NULL, *cotan=NULL, *tfp=NULL;
3089    float dv[3], p[3], q[3];
3090    float area, area_p, area_q, dot_p, dot_q;
3091    int i, j, k, n, j3p1, j3m1, n3, j3=0, nj, nj3;
3092    SUMA_Boolean LocalHead = NOPE;
3093 
3094    SUMA_ENTRY;
3095 
3096    if (!SO) {
3097       SUMA_SL_Err("Null SO");
3098       SUMA_RETURN(NULL);
3099    }
3100    if (!SO->FN) {
3101       SUMA_SL_Err("Null SO->FN");
3102       SUMA_RETURN(NULL);
3103    }
3104 
3105    /* in the demo mesh that Moo Chung gave me,
3106    Node 17 has the following neighbors in ccw order:
3107    231 230 250 261 239 236 - 231 230 250 261 239 236
3108    Set SUMA_SSidbg = 17 and turn on LocalHead to get a confirmation of this
3109    by this function*/
3110    /* implement the non-parametric weight estimation method */
3111    wgt = (float **)SUMA_allocate2D(SO->N_Node, SO->FN->N_Neighb_max, sizeof(float));  /* vector of node weights */
3112    coord_nbr = (float *)SUMA_malloc((SO->FN->N_Neighb_max + 2) * sizeof(float) * 3); /* vector of neighboring node coordinates */
3113    cotan = (float *)SUMA_malloc(SO->FN->N_Neighb_max * sizeof(float));
3114    if (!wgt || !coord_nbr || !cotan) {
3115       SUMA_SL_Crit("Failed to allocate for wgt &/|coord_nbr &/|cotan");
3116       SUMA_RETURN(NULL);
3117    }
3118 
3119    for (n=0; n < SO->N_Node; ++n) {
3120       n3 = 3 * n;
3121       /* translate the coordinates of the neighboring nodes to make n be the origin */
3122       for (j=0; j<SO->FN->N_Neighb[n]; ++j) {
3123          j3 = 3 * (j+1);
3124          nj = SO->FN->FirstNeighb[n][j]; nj3 = 3 * nj;
3125          coord_nbr[j3] = SO->NodeList[nj3] - SO->NodeList[n3];
3126          coord_nbr[j3+1] = SO->NodeList[nj3+1] - SO->NodeList[n3+1];
3127          coord_nbr[j3+2] = SO->NodeList[nj3+2] - SO->NodeList[n3+2];
3128       }  /* for j */
3129       /* padd with last neighbor at the very beginning and 1st neighbor at the end
3130          in matlab: coord_nbr = [coord_nbr(:,last_neighb) coord_nbr coord_nbr(:,first_neighb)];
3131       */
3132       for (k=0; k < 3; ++k) coord_nbr[k] = coord_nbr[j3+k];
3133       j3 = 3 * ( SO->FN->N_Neighb[n] + 1);
3134       for (k=0; k < 3; ++k) coord_nbr[j3+k] = coord_nbr[3+k];
3135       if (LocalHead && n == SUMA_SSidbg) { SUMA_disp_vect (coord_nbr, 3 * (SO->FN->N_Neighb[n] + 2)) ;  }
3136 
3137 
3138       area = 0.0;
3139       for (j=1; j<=SO->FN->N_Neighb[n]; ++j) {
3140          j3 = 3 * j; j3p1 = 3 * (j+1); j3m1 = 3 * (j-1);
3141          for (k=0; k < 3; ++k) dv[k] = coord_nbr[j3p1+k] - coord_nbr[j3+k];
3142          tfp = &(coord_nbr[j3p1]);
3143          dot_p = SUMA_MT_DOT (tfp, dv);
3144          SUMA_MT_CROSS(p, tfp, dv);
3145          for (k=0; k < 3; ++k) dv[k] = coord_nbr[j3m1+k] - coord_nbr[j3+k];
3146          tfp = &(coord_nbr[j3m1]);
3147          dot_q = SUMA_MT_DOT (tfp, dv);
3148          SUMA_MT_CROSS(q, tfp, dv);
3149 
3150          SUMA_NORM(area_p, p);
3151          SUMA_NORM(area_q, q);
3152 
3153          cotan[j-1] = dot_p/area_p + dot_q/area_q;
3154          area += area_p/2.0;
3155          if (LocalHead && n == SUMA_SSidbg) {
3156             fprintf (SUMA_STDERR,"[%d->%d] area_p, area_q = %f, %f\n",
3157                                   n, SO->FN->FirstNeighb[n][j-1],
3158                                   area_p / 2.0,  area_q / 2.0);
3159          }
3160       }
3161 
3162       for (j=0; j<SO->FN->N_Neighb[n]; ++j) {
3163          wgt[n][j] = cotan[j]/area;
3164       }
3165       if (LocalHead && n == SUMA_SSidbg) {
3166          fprintf (SUMA_STDERR,"%s: Weight Results for neighbors of %d (matlab node %d):\n",
3167                               FuncName, n, n+1);
3168          SUMA_disp_vect (wgt[n], SO->FN->N_Neighb[n]);
3169       }
3170    }  /* for n */
3171 
3172    /* free local variables */
3173    if (coord_nbr) SUMA_free(coord_nbr); coord_nbr = NULL;
3174    if (cotan) SUMA_free(cotan); cotan = NULL;
3175 
3176    SUMA_RETURN(wgt);
3177 }
3178 
3179 /*!
3180    \brief calculate the interpolation weights required to smooth data on the surface
3181    using M.K. Chung 's new method (see SUMA_Chung_Smooth_05 for refs)
3182 
3183    \param SO (SUMA_SurfaceObject *) Surface object with valid SO->NodeList, SO->FaceSetList and SO->FN
3184    \return wgt (float **) 2D matrix of the same size as SO->FirstNeighb that contains the
3185                            weights to be applied to a node's neighbors in interpolation
3186                            Free the result with SUMA_free2D ((char **)wgt, SO->N_Node);
3187                            The weights are computed using the Finite Element method.
3188    \sa SUMA_Chung_Smooth_05
3189    \sa Moo's hk_smooth.m
3190 */
3191 
SUMA_Chung_Smooth_Weights_05_Pre_07(SUMA_SurfaceObject * SO,float sigma)3192 float ** SUMA_Chung_Smooth_Weights_05_Pre_07 (SUMA_SurfaceObject *SO, float sigma)
3193 {
3194    static char FuncName[]={"SUMA_Chung_Smooth_Weights_05_Pre_07"};
3195    float **wgt=NULL, *dist=NULL, *kern=NULL, *tfp=NULL;
3196    float dx,dy,dz, skern;
3197    int j, n, n3, nj, nj3, skern_warn = 0;
3198    int *n_troub=NULL;
3199    SUMA_Boolean LocalHead = NOPE;
3200 
3201    SUMA_ENTRY;
3202 
3203    if (!SO) {
3204       SUMA_SL_Err("Null SO");
3205       SUMA_RETURN(NULL);
3206    }
3207    if (!SO->FN) {
3208       SUMA_SL_Err("Null SO->FN");
3209       SUMA_RETURN(NULL);
3210    }
3211    if (sigma < 0.00001) sigma = 1.0;
3212 
3213    /* in the demo mesh that Moo Chung gave me,
3214    Node 17 has the following neighbors in ccw order:
3215    231 230 250 261 239 236 - 231 230 250 261 239 236
3216    Set SUMA_SSidbg = 17 and turn on LocalHead to get a confirmation of this
3217    by this function*/
3218    /* implement the non-parametric weight estimation method */
3219    wgt  = (float **)SUMA_allocate2D(SO->N_Node, (SO->FN->N_Neighb_max+1), sizeof(float));  /* vector of node weights */
3220    dist = (float *)SUMA_malloc((SO->FN->N_Neighb_max + 1 )* sizeof(float));
3221    kern = (float *)SUMA_malloc((SO->FN->N_Neighb_max + 1 )* sizeof(float));
3222    if (!wgt || !dist || !kern) {
3223       SUMA_SL_Crit("Failed to allocate for wgt &/|dist &/|kern");
3224       SUMA_RETURN(NULL);
3225    }
3226 
3227    for (n=0; n < SO->N_Node; ++n) {
3228       n3 = 3 * n;
3229       /* Distances from neighboring nodes to n  */
3230       for (j=0; j<SO->FN->N_Neighb[n]; ++j) {
3231          nj = SO->FN->FirstNeighb[n][j]; nj3 = 3 * nj;
3232          dx = SO->NodeList[nj3  ] - SO->NodeList[n3  ];
3233          dy = SO->NodeList[nj3+1] - SO->NodeList[n3+1];
3234          dz = SO->NodeList[nj3+2] - SO->NodeList[n3+2];
3235          dist[j+1] = (dx*dx+dy*dy+dz*dz);
3236       }  /* for j */
3237       /* padd with 0 at the very beginning, distance from n to itself */
3238       dist[0] =  0.0;
3239       if (LocalHead && n == SUMA_SSidbg) {
3240          SUMA_S_Note("Showing neighbors vector followed by distance vector");
3241          SUMA_disp_dvect (SO->FN->FirstNeighb[n], (SO->FN->N_Neighb[n]));
3242          SUMA_disp_vect (dist, (SO->FN->N_Neighb[n] + 1)) ;
3243       }
3244 
3245       /* calculate numerator of the kernel and the sum for all distances*/
3246       skern = 0.0;
3247       for (j=0; j<=SO->FN->N_Neighb[n]; ++j) {
3248          kern[j] = SUMA_CHUNG_KERNEL_NUMER(dist[j],sigma);
3249          if (LocalHead && n == SUMA_SSidbg) {
3250             fprintf (SUMA_STDERR,"%s: Neighb %d of %d: dist %f sigma %f: %g\n",
3251                            FuncName, SO->FN->FirstNeighb[n][j], n,
3252                            dist[j], sigma, SUMA_CHUNG_KERNEL_NUMER(dist[j],sigma));
3253          }
3254          skern += kern[j];
3255       }
3256 
3257       if (skern < 1.0f+1e-8) {
3258          if (!skern_warn) {
3259             n_troub = (int *)SUMA_malloc(sizeof(int)*SO->N_Node);
3260             SUMA_S_Warnv(  "   Weights sum < 1.0f+1e-8 at node %d\n"
3261                         "   Mesh may be too coarse for kernel\n"
3262                         "   bandwidth of %f in float precision.\n"
3263                         "   Consider decreasing your number of iterations. \n"
3264                         "   Future similar warnings are muted, but \n"
3265                         "   a count is issued at the end.\n", n, sigma);
3266          }
3267          if (n_troub) n_troub[skern_warn] = n;
3268          ++skern_warn;
3269       }
3270 
3271       /* now calculate the weights */
3272       for (j=0; j<=SO->FN->N_Neighb[n]; ++j) {
3273          wgt[n][j] = kern[j]/skern;
3274       }
3275 
3276       if (LocalHead && n == SUMA_SSidbg) {
3277          fprintf (SUMA_STDERR,"%s: Weight Results for neighbors of %d (matlab node %d):\nskern=%f",
3278                               FuncName, n, n+1, skern);
3279          SUMA_disp_vect (wgt[n], SO->FN->N_Neighb[n]+1);
3280       }
3281    }  /* for n */
3282 
3283    /* free local variables */
3284    if (kern) SUMA_free(kern); kern = NULL;
3285    if (dist) SUMA_free(dist); dist = NULL;
3286 
3287    if (skern_warn) {
3288       SUMA_S_Warnv("    %d precision warnings out of %d nodes forming surface (%.5f %%).\n",
3289                        skern_warn, SO->N_Node, (float)skern_warn/(float)SO->N_Node*100.0);
3290       if (n_troub) {
3291          char *s = SUMA_ShowMeSome((void*)n_troub, SUMA_int,
3292                                     skern_warn, SUMA_MIN_PAIR(20, skern_warn),
3293                                     "Nodes with possible precision problems:\n   ");
3294          fprintf(SUMA_STDERR,"%s\n", s);
3295          SUMA_free(s); s= NULL;
3296          SUMA_free(n_troub); n_troub = NULL;
3297       }
3298    }
3299    SUMA_RETURN(wgt);
3300 }
3301 
SUMA_Chung_Smooth_Weights_05_single(SUMA_SurfaceObject * SO,float sigma)3302 float ** SUMA_Chung_Smooth_Weights_05_single (SUMA_SurfaceObject *SO, float sigma)
3303 {
3304    static char FuncName[]={"SUMA_Chung_Smooth_Weights_05_single"};
3305    float **wgt=NULL, *dist=NULL, *kern=NULL, *tfp=NULL;
3306    float dx,dy,dz, skern;
3307    int j, n, n3, nj, nj3, skern_warn = 0;
3308    int *n_troub=NULL;
3309    SUMA_Boolean LocalHead = NOPE;
3310 
3311    SUMA_ENTRY;
3312 
3313    if (!SO) {
3314       SUMA_SL_Err("Null SO");
3315       SUMA_RETURN(NULL);
3316    }
3317    if (!SO->FN) {
3318       SUMA_SL_Err("Null SO->FN");
3319       SUMA_RETURN(NULL);
3320    }
3321    if (sigma < 0.00001) sigma = 1.0;
3322 
3323    /* in the demo mesh that Moo Chung gave me,
3324    Node 17 has the following neighbors in ccw order:
3325    231 230 250 261 239 236 - 231 230 250 261 239 236
3326    Set SUMA_SSidbg = 17 and turn on LocalHead to get a confirmation of this
3327    by this function*/
3328    /* implement the non-parametric weight estimation method */
3329    wgt  = (float **)SUMA_allocate2D(SO->N_Node, (SO->FN->N_Neighb_max+1), sizeof(float));  /* vector of node weights */
3330    dist = (float *)SUMA_malloc((SO->FN->N_Neighb_max + 1 )* sizeof(float));
3331    kern = (float *)SUMA_malloc((SO->FN->N_Neighb_max + 1 )* sizeof(float));
3332    if (!wgt || !dist || !kern) {
3333       SUMA_SL_Crit("Failed to allocate for wgt &/|dist &/|kern");
3334       SUMA_RETURN(NULL);
3335    }
3336 
3337    for (n=0; n < SO->N_Node; ++n) {
3338       n3 = 3 * n;
3339       /* Distances from neighboring nodes to n  */
3340       for (j=0; j<SO->FN->N_Neighb[n]; ++j) {
3341          nj = SO->FN->FirstNeighb[n][j]; nj3 = 3 * nj;
3342          dx = SO->NodeList[nj3  ] - SO->NodeList[n3  ];
3343          dy = SO->NodeList[nj3+1] - SO->NodeList[n3+1];
3344          dz = SO->NodeList[nj3+2] - SO->NodeList[n3+2];
3345          dist[j+1] = (dx*dx+dy*dy+dz*dz);
3346       }  /* for j */
3347       /* padd with 0 at the very beginning, distance from n to itself */
3348       dist[0] =  0.0;
3349       if (LocalHead && n == SUMA_SSidbg) {
3350          SUMA_S_Note("Showing neighbors vector followed by distance vector");
3351          SUMA_disp_dvect (SO->FN->FirstNeighb[n], (SO->FN->N_Neighb[n]));
3352          SUMA_disp_vect (dist, (SO->FN->N_Neighb[n] + 1)) ;
3353       }
3354 
3355       /* calculate numerator of the kernel and the sum for all distances*/
3356       skern = 0.0;
3357       for (j=0; j<=SO->FN->N_Neighb[n]; ++j) {
3358          kern[j] = SUMA_CHUNG_KERNEL_NUMER(dist[j],sigma);
3359          if (LocalHead && n == SUMA_SSidbg) {
3360             fprintf (SUMA_STDERR,"%s: Neighb %d of %d: dist %f sigma %f: %g\n",
3361                            FuncName, SO->FN->FirstNeighb[n][j], n,
3362                            dist[j], sigma, SUMA_CHUNG_KERNEL_NUMER(dist[j],sigma));
3363          }
3364          skern += kern[j];
3365       }
3366 
3367       if (skern < 1.0f+1e-8) {
3368          if (!skern_warn) {
3369             n_troub = (int *)SUMA_malloc(sizeof(int)*SO->N_Node);
3370             SUMA_S_Warnv(  "   Weights sum < 1.0f+1e-8 at node %d\n"
3371                         "   Mesh may be too coarse for kernel\n"
3372                         "   bandwidth of %f in float precision.\n"
3373                         "   Consider decreasing your number of iterations. \n"
3374                         "   Future similar warnings are muted, but \n"
3375                         "   a count is issued at the end.\n", n, sigma);
3376          }
3377          if (n_troub) n_troub[skern_warn] = n;
3378          ++skern_warn;
3379       }
3380 
3381       /* now calculate the weights */
3382       for (j=0; j<=SO->FN->N_Neighb[n]; ++j) {
3383          wgt[n][j] = kern[j]/skern;
3384       }
3385 
3386       if (LocalHead && n == SUMA_SSidbg) {
3387          fprintf (SUMA_STDERR,"%s: Weight Results for neighbors of %d (matlab node %d):\nskern=%f",
3388                               FuncName, n, n+1, skern);
3389          SUMA_disp_vect (wgt[n], SO->FN->N_Neighb[n]+1);
3390       }
3391    }  /* for n */
3392 
3393    /* free local variables */
3394    if (kern) SUMA_free(kern); kern = NULL;
3395    if (dist) SUMA_free(dist); dist = NULL;
3396 
3397    if (skern_warn) {
3398       SUMA_S_Warnv("    %d precision warnings out of %d nodes forming surface (%.5f %%).\n",
3399                        skern_warn, SO->N_Node, (float)skern_warn/(float)SO->N_Node*100.0);
3400       if (n_troub) {
3401          char *s = SUMA_ShowMeSome((void*)n_troub, SUMA_int,
3402                                     skern_warn, SUMA_MIN_PAIR(20, skern_warn),
3403                                     "Nodes with possible precision problems:\n   ");
3404          fprintf(SUMA_STDERR,"%s\n", s);
3405          SUMA_free(s); s= NULL;
3406          SUMA_free(n_troub); n_troub = NULL;
3407       }
3408    }
3409    SUMA_RETURN(wgt);
3410 }
3411 
3412 
SUMA_Chung_Smooth_Weights_07(SUMA_SurfaceObject * SO,double sigma)3413 double ** SUMA_Chung_Smooth_Weights_07 (SUMA_SurfaceObject *SO, double sigma)
3414 {
3415    static char FuncName[]={"SUMA_Chung_Smooth_Weights_07"};
3416    double **wgt=NULL, *dist=NULL, *kern=NULL, *tfp=NULL;
3417    double dx,dy,dz, skern;
3418    int j, n, n3, nj, nj3, skern_warn = 0;
3419    int *n_troub=NULL;
3420    SUMA_Boolean LocalHead = NOPE;
3421 
3422    SUMA_ENTRY;
3423 
3424    if (!SO) {
3425       SUMA_SL_Err("Null SO");
3426       SUMA_RETURN(NULL);
3427    }
3428    if (!SO->FN) {
3429       SUMA_SL_Err("Null SO->FN");
3430       SUMA_RETURN(NULL);
3431    }
3432    if (sigma < 0.00001) sigma = 1.0;
3433 
3434    /* in the demo mesh that Moo Chung gave me,
3435    Node 17 has the following neighbors in ccw order:
3436    231 230 250 261 239 236 - 231 230 250 261 239 236
3437    Set SUMA_SSidbg = 17 and turn on LocalHead to get a confirmation of this
3438    by this function*/
3439    /* implement the non-parametric weight estimation method */
3440    wgt  = (double **)SUMA_malloc(SO->N_Node * sizeof(double*));  /* vector of node weights */
3441    dist = (double *)SUMA_malloc((SO->FN->N_Neighb_max + 1 )* sizeof(double));
3442    kern = (double *)SUMA_malloc((SO->FN->N_Neighb_max + 1 )* sizeof(double));
3443    if (!wgt || !dist || !kern) {
3444       SUMA_SL_Crit("Failed to allocate for wgt &/|dist &/|kern");
3445       SUMA_RETURN(NULL);
3446    }
3447 
3448    for (n=0; n < SO->N_Node; ++n) {
3449       n3 = 3 * n;
3450       /* Distances from neighboring nodes to n  */
3451       for (j=0; j<SO->FN->N_Neighb[n]; ++j) {
3452          nj = SO->FN->FirstNeighb[n][j]; nj3 = 3 * nj;
3453          dx = SO->NodeList[nj3  ] - SO->NodeList[n3  ];
3454          dy = SO->NodeList[nj3+1] - SO->NodeList[n3+1];
3455          dz = SO->NodeList[nj3+2] - SO->NodeList[n3+2];
3456          dist[j+1] = (dx*dx+dy*dy+dz*dz);
3457       }  /* for j */
3458       /* padd with 0 at the very beginning, distance from n to itself */
3459       dist[0] =  0.0;
3460       if (LocalHead && n == SUMA_SSidbg) {
3461          SUMA_S_Note("Showing neighbors vector followed by distance^2 vector");
3462          SUMA_disp_dvect (SO->FN->FirstNeighb[n], (SO->FN->N_Neighb[n]));
3463          SUMA_disp_doubvect (dist, (SO->FN->N_Neighb[n] + 1)) ;
3464       }
3465 
3466       /* calculate numerator of the kernel and the sum for all distances*/
3467       skern = 0.0;
3468       for (j=0; j<=SO->FN->N_Neighb[n]; ++j) {
3469          kern[j] = SUMA_CHUNG_KERNEL_NUMER(dist[j],sigma);
3470          if (LocalHead && n == SUMA_SSidbg) {
3471             fprintf (SUMA_STDERR,"%s: Neighb %d of %d: dist^2 %f sigma %f: %g\n",
3472                            FuncName, SO->FN->FirstNeighb[n][j], n,
3473                            dist[j], sigma, SUMA_CHUNG_KERNEL_NUMER(dist[j],sigma));
3474          }
3475          skern += kern[j];
3476       }
3477 
3478       if (skern < 1.0f+1e-8) {
3479          if (!skern_warn) {
3480             n_troub = (int *)SUMA_malloc(sizeof(int)*SO->N_Node);
3481             SUMA_S_Warnv(  "   Weights sum < 1.0f+1e-8 at node %d\n"
3482                         "   Mesh may be too coarse for kernel\n"
3483                         "   bandwidth of %f in float precision.\n"
3484                         "   Consider decreasing your number of iterations. \n"
3485                         "   Future similar warnings are muted, but \n"
3486                         "   a count is issued at the end.\n", n, sigma);
3487          }
3488          if (n_troub) n_troub[skern_warn] = n;
3489          ++skern_warn;
3490       }
3491 
3492       /* now calculate the weights */
3493       wgt[n] = (double *)SUMA_malloc(sizeof(double)*(SO->FN->N_Neighb[n]+1));
3494       for (j=0; j<=SO->FN->N_Neighb[n]; ++j) {
3495          wgt[n][j] = kern[j]/skern;
3496       }
3497 
3498       if (LocalHead && n == SUMA_SSidbg) {
3499          fprintf (SUMA_STDERR,"%s: Weight Results for neighbors of %d (matlab node %d):\nskern=%f",
3500                               FuncName, n, n+1, skern);
3501          SUMA_disp_doubvect (wgt[n], SO->FN->N_Neighb[n]+1);
3502       }
3503    }  /* for n */
3504 
3505    /* free local variables */
3506    if (kern) SUMA_free(kern); kern = NULL;
3507    if (dist) SUMA_free(dist); dist = NULL;
3508 
3509    if (skern_warn) {
3510       SUMA_S_Warnv("    %d precision warnings out of %d nodes forming surface (%.5f %%).\n",
3511                        skern_warn, SO->N_Node, (float)skern_warn/(float)SO->N_Node*100.0);
3512       if (n_troub) {
3513          char *s = SUMA_ShowMeSome((void*)n_troub, SUMA_int,
3514                                     skern_warn, SUMA_MIN_PAIR(20, skern_warn),
3515                                     "Nodes with possible precision problems:\n   ");
3516          fprintf(SUMA_STDERR,"%s\n", s);
3517          SUMA_free(s); s= NULL;
3518          SUMA_free(n_troub); n_troub = NULL;
3519       }
3520    }
3521    SUMA_RETURN(wgt);
3522 }
3523 
3524 static byte SUMA_Taubin_Weights=SUMA_SMOOTH_NOT_SET;
SUMA_Get_Taubin_Weights(void)3525 byte SUMA_Get_Taubin_Weights(void)
3526 {
3527    return(SUMA_Taubin_Weights);
3528 }
3529 
SUMA_Set_Taubin_Weights(SUMA_TAUBIN_SMOOTH_OPTIONS tb)3530 void SUMA_Set_Taubin_Weights(SUMA_TAUBIN_SMOOTH_OPTIONS tb)
3531 {
3532    SUMA_Taubin_Weights = tb;
3533 }
3534 
3535 /*!
3536    \brief calculate the Fujiwara interpolation weights. Compensates for irregular edge lengths, trying
3537    to better preserve geometry in instances where mesh is irregular
3538    (Taubin G, Eurographics 2000)
3539 
3540    \param SO (SUMA_SurfaceObject *) Surface object with valid SO->NodeList, SO->FaceSetList and SO->FN
3541    \param NewNodeList (float *) a node list to use instead of the one in SO. Useful when you're calling
3542                                 the function repeatedly. Set to NULL if you want to use SO->NodeList
3543    \param UseThisWeight (float ***) Pointer to a weight matrix that is to be used instead of reallocating a new
3544                                     one. If UseThisWeight == NULL, a new wgt is returned at each time.
3545                                     If *UseThisWeight == NULL, a new wgt is allocated and stored in *UseThisWeight
3546                                     so the next time you call the function with UseThisWeight, a new wgt needs
3547                                     not be created.
3548    \return wgt (float **) 2D matrix of the same size as SO->FirstNeighb that contains the
3549                            weights to be applied to a node's neighbors in interpolation
3550                            Free the result with SUMA_free2D ((char **)wgt, SO->N_Node);
3551 
3552    \sa SUMA_Taubin_Smooth
3553 */
SUMA_Taubin_Fujiwara_Smooth_Weights(SUMA_SurfaceObject * SO,float * NewNodeList,float *** UseThisWeight)3554 float ** SUMA_Taubin_Fujiwara_Smooth_Weights (SUMA_SurfaceObject *SO, float *NewNodeList, float ***UseThisWeight)
3555 {
3556    static char FuncName[]={"SUMA_Taubin_Fujiwara_Smooth_Weights"};
3557    float **wgt=NULL, *coord_nbr=NULL, *cotan=NULL, *tfp=NULL, *nl = NULL;
3558    float dv[3], p[3], q[3];
3559    double cij=0.0;
3560    float area, area_p, area_q, dot_p, dot_q;
3561    int i, j, k, n, j3p1, j3m1, n3, j3=0, nj, nj3;
3562    SUMA_Boolean LocalHead = NOPE;
3563 
3564    SUMA_ENTRY;
3565 
3566    if (!SO) {
3567       SUMA_SL_Err("Null SO");
3568       SUMA_RETURN(NULL);
3569    }
3570    if (!SO->FN) {
3571       SUMA_SL_Err("Null SO->FN");
3572       SUMA_RETURN(NULL);
3573    }
3574    if (NewNodeList) {
3575       nl = NewNodeList;
3576    } else {
3577       nl = SO->NodeList;
3578    }
3579 
3580    SUMA_SL_Note("FUJIWARA!!!!");
3581    /* implement the Fujiwara weight estimation method */
3582    wgt = NULL;
3583    if (UseThisWeight) {
3584       wgt = *UseThisWeight;
3585    }
3586    if (!wgt) {
3587       wgt = (float **)SUMA_allocate2D(SO->N_Node, SO->FN->N_Neighb_max, sizeof(float));  /* vector of node weights */
3588       if (UseThisWeight) { /* store it for use later on */
3589          *UseThisWeight = wgt;
3590       }
3591    }
3592    if (!wgt) {
3593       SUMA_SL_Crit("Failed to allocate for wgt &/|coord_nbr &/|cotan");
3594       SUMA_RETURN(NULL);
3595    }
3596 
3597    for (n=0; n < SO->N_Node; ++n) {
3598       n3 = 3 * n;
3599       area = 0.0;
3600       for (j=0; j<SO->FN->N_Neighb[n]; ++j) {
3601          j3 = 3 * (j);
3602          nj = SO->FN->FirstNeighb[n][j]; nj3 = 3 * nj;
3603          SUMA_SEG_LENGTH( (&(nl[nj3])), (&(nl[n3])), cij);
3604          if (cij > 0.00001) {
3605             cij = 1/cij;
3606          } else {
3607             cij = 0.0;
3608          }
3609          area += (float)cij;
3610          wgt[n][j] = (float)cij;
3611       }  /* for j */
3612       if (area) {
3613          for (j=0; j<SO->FN->N_Neighb[n]; ++j) { wgt[n][j] /= area; }
3614       }
3615 
3616 
3617       if (LocalHead && n == SUMA_SSidbg) {
3618          fprintf (SUMA_STDERR,"%s: Weight Results for neighbors of %d (matlab node %d):\n",
3619                               FuncName, n, n+1);
3620          SUMA_disp_vect (wgt[n], SO->FN->N_Neighb[n]);
3621       }
3622    }  /* for n */
3623 
3624    SUMA_RETURN(wgt);
3625 }
3626 
3627 /*!
3628    \brief calculate the Desbrun interpolation weights, results in zero tangential component to smoothing
3629    (Taubin G, Eurographics 2000)
3630 
3631    \param SO (SUMA_SurfaceObject *) Surface object with valid SO->NodeList, SO->FaceSetList and SO->FN
3632    \param NewNodeList (float *) a node list to use instead of the one in SO. Useful when you're calling
3633                                 the function repeatedly. Set to NULL if you want to use SO->NodeList
3634    \param UseThisWeight (float ***) Pointer to a weight matrix that is to be used instead of reallocating a new
3635                                     one. If UseThisWeight == NULL, a new wgt is returned at each time.
3636                                     If *UseThisWeight == NULL, a new wgt is allocated and stored in *UseThisWeight
3637                                     so the next time you call the function with UseThisWeight, a new wgt needs
3638                                     not be created.
3639    \return wgt (float **) 2D matrix of the same size as SO->FirstNeighb that contains the
3640                            weights to be applied to a node's neighbors in interpolation
3641                            Free the result with SUMA_free2D ((char **)wgt, SO->N_Node);
3642 
3643    \sa SUMA_Taubin_Smooth
3644 */
SUMA_Taubin_Desbrun_Smooth_Weights(SUMA_SurfaceObject * SO,float * NewNodeList,float *** UseThisWeight)3645 float ** SUMA_Taubin_Desbrun_Smooth_Weights (SUMA_SurfaceObject *SO,
3646                            float *NewNodeList, float ***UseThisWeight)
3647 {
3648    static char FuncName[]={"SUMA_Taubin_Desbrun_Smooth_Weights"};
3649    float **wgt=NULL, *coord_nbr=NULL, *cotan=NULL, *tfp=NULL, *nl = NULL;
3650    float dv[3], p[3], q[3];
3651    double cij=0.0, cot1, cot2, Uc[3], U1[3], U2[3], Ucn, U1n, U2n;
3652    float area, area_p, area_q, dot_p, dot_q;
3653    int N_inci_alloc=100;
3654    int i, j, k, n, j3p1, j3m1, n3, j3=0, nj, nj3, nk_1, nk_2,
3655        N_inci, inci[N_inci_alloc];
3656    SUMA_Boolean LocalHead = NOPE;
3657 
3658    SUMA_ENTRY;
3659 
3660    if (!SO) {
3661       SUMA_SL_Err("Null SO");
3662       SUMA_RETURN(NULL);
3663    }
3664    if (!SO->FN) {
3665       SUMA_SL_Err("Null SO->FN");
3666       SUMA_RETURN(NULL);
3667    }
3668    if (NewNodeList) {
3669       nl = NewNodeList;
3670    } else {
3671       nl = SO->NodeList;
3672    }
3673 
3674    /* implement the Desbrun weight estimation method */
3675    wgt = NULL;
3676    if (UseThisWeight) {
3677       wgt = *UseThisWeight;
3678    }
3679    if (!wgt) {
3680       wgt = (float **)SUMA_allocate2D(SO->N_Node, SO->FN->N_Neighb_max,
3681                                    sizeof(float));  /* vector of node weights */
3682       if (UseThisWeight) { /* store it for use later on */
3683          *UseThisWeight = wgt;
3684       }
3685    }
3686    if (!wgt) {
3687       SUMA_SL_Crit("Failed to allocate for wgt");
3688       SUMA_RETURN(NULL);
3689    }
3690 
3691    for (n=0; n < SO->N_Node; ++n) {
3692       n3 = 3 * n;
3693       area = 0.0;
3694       for (j=0; j<SO->FN->N_Neighb[n]; ++j) {
3695          j3 = 3 * (j);
3696          nj = SO->FN->FirstNeighb[n][j]; nj3 = 3 * nj;
3697          /* find the incident triangle to n nj */
3698          N_inci = 0;
3699          if (!SUMA_Get_Incident(n, nj, SO->EL, inci, &N_inci, 0, 0)) {
3700             SUMA_SL_Err("Failed to find incident triangles!\n");
3701             SUMA_RETURN(NULL);
3702          }else if (N_inci > 2) {
3703             SUMA_SL_Warn("Non 2-manifold surface!");
3704             if (N_inci > N_inci_alloc) {
3705                SUMA_SL_Crit("Bad allocation for inci.");
3706                SUMA_RETURN(NULL);
3707             }
3708          }
3709 
3710          /* calculate cij = cot1 + cot2 */
3711 
3712          cot1= cot2 = cij = 0.0;
3713          /* find the third node from each of the triangles */
3714          SUMA_THIRD_TRIANGLE_NODE(nj,n,inci[0],SO->FaceSetList,nk_1);
3715          /* find the cotangent of the opposing angles at nk_1 and nk_2 */
3716          /* form vectors */
3717          SUMA_UNIT_VEC((&(nl[3*nk_1])), (&(nl[3*n ])), U1, U1n);
3718          SUMA_UNIT_VEC((&(nl[3*nk_1])), (&(nl[3*nj])), U2, U2n);
3719          SUMA_MT_CROSS(Uc, U1, U2);
3720          Ucn = sqrt(Uc[0]*Uc[0]+Uc[1]*Uc[1]+Uc[2]*Uc[2]);
3721          /* cotangent = dot / cross product*/
3722          if (Ucn > 0.00000000001) cot1 = SUMA_MT_DOT(U1,U2)/Ucn;
3723          if (N_inci > 1) {
3724             SUMA_THIRD_TRIANGLE_NODE(nj,n,inci[1],SO->FaceSetList,nk_2);
3725             /* form vectors */
3726             SUMA_UNIT_VEC((&(nl[3*nk_2])), (&(nl[3*n ])), U1, U1n);
3727             SUMA_UNIT_VEC((&(nl[3*nk_2])), (&(nl[3*nj])), U2, U2n);
3728             SUMA_MT_CROSS(Uc, U1, U2);
3729             Ucn = sqrt(Uc[0]*Uc[0]+Uc[1]*Uc[1]+Uc[2]*Uc[2]);
3730             /* cotangent = dot / cross product*/
3731             if (Ucn > 0.00000000001) cot2 = SUMA_MT_DOT(U1,U2)/Ucn;
3732          }
3733          if (LocalHead && n == SUMA_SSidbg) {
3734             int kk;
3735             fprintf (SUMA_STDERR,"%s: Edge [%d %d]:   third nodes:\n",
3736                               FuncName, n, nj);
3737             for (kk=0; kk<N_inci; ++kk) {
3738                fprintf (SUMA_STDERR,"   %d", inci[kk]);
3739             }
3740             fprintf (SUMA_STDERR,"\n");
3741             fprintf (SUMA_STDERR,"   cot: %f   %f\n", cot1, cot2);
3742          }
3743          cij = SUMA_MAX_PAIR((cot1 + cot2), 0.05);
3744             /* give a minimum of 5% weight to each node */
3745 
3746          area += (float)cij;
3747          wgt[n][j] = (float)cij;
3748       }  /* for j */
3749       if (area) {
3750          for (j=0; j<SO->FN->N_Neighb[n]; ++j) { wgt[n][j] /= area; }
3751       }
3752 
3753 
3754       if (LocalHead && n == SUMA_SSidbg) {
3755          fprintf (SUMA_STDERR,
3756                   "%s: Weight Results for neighbors of %d (matlab node %d):\n",
3757                               FuncName, n, n+1);
3758          SUMA_disp_vect (wgt[n], SO->FN->N_Neighb[n]);
3759       }
3760    }  /* for n */
3761 
3762    SUMA_RETURN(wgt);
3763 }
3764 /*!
3765    \brief Show the transfer function (f(k)) for the Taubin
3766    smoothing algorithm for a combination of scaling factors
3767    and number of iterations
3768 
3769    \param l (float)  postive scaling factor
3770    \param m (float)  negative scaling factor
3771                     (for avoiding the shrinkage of surfaces)
3772             The band-pass frequency (kbp) is 1/m + 1/l
3773             f(kbp) = 1
3774    \param N_iter (int) number of iterations
3775    \param Out (FILE *) pointer to output file. If NULL,
3776                   output is to stdout
3777    \return ans (SUMA_Boolean) YUP, no problems
3778                               NOPE, yes problems
3779 
3780    The output is of the form:
3781    k f(k)
3782 
3783    where k is the normalized frequency and
3784    f(k) is the value of the transfer function at k
3785 
3786    \sa figure 4 in Geometric Signal Processing on
3787    Polygonal Meshes (Taubin G, Eurographics 2000)
3788    \sa SUMA_Taubin_Smooth
3789    \sa SUMA_Taubin_Smooth_Coef
3790 */
SUMA_Taubin_Smooth_TransferFunc(float l,float m,int N,FILE * Out)3791 SUMA_Boolean  SUMA_Taubin_Smooth_TransferFunc (float l, float m, int N, FILE *Out)
3792 {
3793    static char FuncName[]={"SUMA_Taubin_Smooth_TransferFunc"};
3794    FILE *Outp = NULL;
3795    int i, imax = 100;
3796    float fk, k;
3797    SUMA_Boolean LocalHead = NOPE;
3798 
3799    SUMA_ENTRY;
3800 
3801    if (N % 2) {
3802       SUMA_SL_Err("N_iter must be even");
3803       SUMA_RETURN(NOPE);
3804    }
3805 
3806    if (!Out) Outp = stdout;
3807    else Outp = Out;
3808 
3809    k = 0.0;
3810    for (i=0; i< imax; ++i) {
3811       fk = pow( ( ( 1-m*k ) * ( 1-l*k ) ) , N / 2 );
3812       fprintf (Outp,"%f %f\n", k, fk);
3813       k += (float)i/(float)imax;
3814    }
3815 
3816 
3817    SUMA_RETURN(YUP);
3818 }
3819 /*!
3820    \brief Calculates Mu(m) and Lambda(l) smoothing coefficients
3821    based on Taubin's smoothing algorithm in Geometric Signal
3822    Processing on Polygonal Meshes (Eurographics 2000)
3823 
3824    \param k (float) the pass-band frequency (typically 0.1)
3825    \param *l (float) what will be the postive scaling factor
3826    \param *m (float) what will be the negative scaling factor
3827                      (for avoiding the shrinkage of surfaces)
3828    \return ans (SUMA_Boolean) YUP, good solution found
3829                               NOPE, solution could not be found
3830 
3831    k, l and m are related by the following equations:
3832 
3833    k = 1/l + 1/m                 (eq 8)
3834    0 = 1 - 3(l+m) + 5lm          (eq 9)
3835 
3836    the solutions must verify the following:
3837    l > 0
3838    m < -l < 0
3839 
3840    \sa SUMA_Taubin_Smooth_TransferFunc
3841    \sa SUMA_Taubin_Smooth
3842 
3843 */
SUMA_Taubin_Smooth_Coef(float k,float * l,float * m)3844 SUMA_Boolean SUMA_Taubin_Smooth_Coef (float k, float *l, float *m)
3845 {
3846    static char FuncName[]={"SUMA_Taubin_Smooth_Coef"};
3847    int i;
3848    float ls[2], delta;
3849    SUMA_Boolean Done = NOPE;
3850    SUMA_Boolean LocalHead = NOPE;
3851 
3852    SUMA_ENTRY;
3853 
3854    if (k < 0) { SUMA_SL_Err("k < 0"); SUMA_RETURN(NOPE); }
3855 
3856    /* l1 and l2 are solutions of the quadratic equation:
3857       (5 - 3 k) l^2 + k l - 1 = 0 */
3858    delta = ( k * k - 12.0 * k + 20 );
3859    if (delta < 0) { SUMA_SL_Err("Delta is < 0 for specified k"); SUMA_RETURN(NOPE); }
3860 
3861    ls[0] = ( -k + sqrt(delta) ) / ( 10 - 6 * k );
3862    ls[1] = ( -k - sqrt(delta) ) / ( 10 - 6 * k );
3863    if (ls[0] < 0 && ls[1] < 0) { SUMA_SL_Err("No positive solution for l"); SUMA_RETURN(NOPE); }
3864 
3865    if (ls[1] > ls[0]) { /* swap them */
3866       *l = ls[0]; ls[0] = ls[1]; ls[1] = *l;
3867    }
3868 
3869    Done = NOPE;
3870    i = 0;
3871    while (!Done && i < 2) {
3872       /* calculate mu */
3873       *l = ls[i];
3874       *m = *l / ( k * *l - 1.0 );
3875       if (*m < 0) Done = YUP;
3876       ++i;
3877    }
3878 
3879    if (!Done) { SUMA_SL_Err("No good solutions found."); SUMA_RETURN(NOPE); }
3880 
3881    if ( ! ( ( *m < -1.0 * *l ) && ( -1.0 * *l < 0 ) ) ) {
3882       SUMA_SL_Err("Solution did not meet m < -l < 0"); SUMA_RETURN(NOPE);
3883    }
3884 
3885    SUMA_RETURN(YUP);
3886 }
3887 
3888 /* A convenience function that uses SUMA_Taubin_Smooth() */
SUMA_Taubin_Smooth_SO(SUMA_SurfaceObject * SO,SUMA_TAUBIN_SMOOTH_OPTIONS smopt,float kpb,byte * nmask,byte strict_mask,int Niter)3889 int SUMA_Taubin_Smooth_SO(  SUMA_SurfaceObject *SO,
3890                             SUMA_TAUBIN_SMOOTH_OPTIONS smopt,
3891                             float kpb, byte *nmask, byte strict_mask,
3892                             int Niter)
3893 {
3894    static char FuncName[]={"SUMA_Taubin_Smooth_SO"};
3895    float **wgt=NULL, l=0.0, m=0.0, *dsmooth=NULL;
3896 
3897    SUMA_ENTRY;
3898 
3899    if (!SO || !SO->NodeList) {
3900       SUMA_S_Err("NULL input");
3901       SUMA_RETURN(NOPE);
3902    }
3903 
3904    if (Niter == 0) Niter = 100;
3905    if (kpb == 0.0f) kpb = 0.1;
3906 
3907    if (!SUMA_Taubin_Smooth_Coef (kpb, &l, &m)) {
3908       SUMA_S_Err("Failed to find smoothing coefficients");
3909       SUMA_RETURN(NOPE);
3910    }
3911 
3912    switch (smopt) {
3913       case SUMA_SMOOTH_NOT_SET:
3914       case SUMA_EQUAL:
3915          SUMA_Set_Taubin_Weights(SUMA_EQUAL);
3916          break;
3917       case SUMA_FUJIWARA:
3918          SUMA_Set_Taubin_Weights(smopt);
3919          if (!(wgt = SUMA_Taubin_Fujiwara_Smooth_Weights(SO, NULL, NULL))) {
3920             SUMA_S_Err("Failed to compute Fujiwara weights.");
3921             SUMA_RETURN(NOPE);
3922          }
3923          break;
3924       case SUMA_DESBRUN:
3925          SUMA_Set_Taubin_Weights(smopt);
3926          if (!(wgt = SUMA_Taubin_Desbrun_Smooth_Weights(SO, NULL, NULL))) {
3927             SUMA_S_Err("Failed to compute Desbrun weights.");
3928             SUMA_RETURN(NOPE);
3929          }
3930          break;
3931       default:
3932          SUMA_S_Err("Willis!");
3933          SUMA_RETURN(NOPE);
3934          break;
3935    }
3936 
3937 
3938    if (!(dsmooth = SUMA_Taubin_Smooth (SO, wgt,
3939              l, m, SO->NodeList,
3940              Niter, SO->NodeDim, SUMA_ROW_MAJOR,
3941              NULL, NULL, nmask, strict_mask))) {
3942       SUMA_S_Err("Failed to Taubin smooth");
3943       SUMA_RETURN(NOPE);
3944    }
3945 
3946    SUMA_free(SO->NodeList); SO->NodeList = dsmooth; dsmooth = NULL;
3947 
3948    SUMA_RECOMPUTE_NORMALS_and_AREAS(SO);
3949    SUMA_DIM_CENTER(SO);
3950 
3951    if (wgt) SUMA_free2D ((char **)wgt, SO->N_Node); wgt = NULL;
3952 
3953    SUMA_RETURN(YUP);
3954 }
3955 
SUMA_NN_GeomSmooth_SO(SUMA_SurfaceObject * SO,byte * nmask,byte strict_mask,int Niter)3956 int SUMA_NN_GeomSmooth_SO(   SUMA_SurfaceObject *SO,
3957                          byte *nmask, byte strict_mask,
3958                          int Niter)
3959 {
3960    static char FuncName[]={"SUMA_NN_GeomSmooth_SO"};
3961    float *dsmooth=NULL;
3962 
3963    SUMA_ENTRY;
3964 
3965    if (!SO || !SO->NodeList) {
3966       SUMA_S_Err("NULL input");
3967       SUMA_RETURN(NOPE);
3968    }
3969 
3970    if (Niter == 0) Niter = 100;
3971 
3972    if (!(dsmooth = SUMA_NN_GeomSmooth( SO, Niter, SO->NodeList,
3973                                        SO->NodeDim, SUMA_ROW_MAJOR,
3974                                        NULL, NULL,
3975                                        nmask, strict_mask))) {
3976       SUMA_S_Err("Failed to NN smooth");
3977       SUMA_RETURN(NOPE);
3978    }
3979 
3980    SUMA_free(SO->NodeList); SO->NodeList = dsmooth; dsmooth = NULL;
3981 
3982    SUMA_RECOMPUTE_NORMALS_and_AREAS(SO);
3983    SUMA_DIM_CENTER(SO);
3984 
3985    SUMA_RETURN(YUP);
3986 }
3987 
SUMA_NN_GeomSmooth2_SO(SUMA_SurfaceObject * SO,byte * nmask,byte strict_mask,int Niter,int anchor_each,SUMA_SurfaceObject * SOe,float * altdir,float * altw)3988 int SUMA_NN_GeomSmooth2_SO(   SUMA_SurfaceObject *SO,
3989                          byte *nmask, byte strict_mask,
3990                          int Niter, int anchor_each,
3991                               SUMA_SurfaceObject *SOe,
3992                          float *altdir, float *altw)
3993 {
3994    static char FuncName[]={"SUMA_NN_GeomSmooth2_SO"};
3995    float *dsmooth=NULL;
3996 
3997    SUMA_ENTRY;
3998 
3999    if (!SO || !SO->NodeList) {
4000       SUMA_S_Err("NULL input");
4001       SUMA_RETURN(NOPE);
4002    }
4003 
4004    if (Niter == 0) Niter = 100;
4005 
4006    if (!(dsmooth = SUMA_NN_GeomSmooth2( SO, Niter, SO->NodeList,
4007                                        SO->NodeDim, SUMA_ROW_MAJOR,
4008                                        NULL, NULL,
4009                                        nmask, anchor_each, SOe,
4010                                        altdir, altw))) {
4011       SUMA_S_Err("Failed to NN smooth");
4012       SUMA_RETURN(NOPE);
4013    }
4014 
4015    SUMA_free(SO->NodeList); SO->NodeList = dsmooth; dsmooth = NULL;
4016 
4017    SUMA_RECOMPUTE_NORMALS_and_AREAS(SO);
4018    SUMA_DIM_CENTER(SO);
4019 
4020    SUMA_RETURN(YUP);
4021 }
4022 
SUMA_NN_GeomSmooth3_SO(SUMA_SurfaceObject * SO,byte * nmask,byte strict_mask,int Niter,int anchor_each,SUMA_SurfaceObject * SOe,float * altw,THD_3dim_dataset * voxelize,SUMA_COMM_STRUCT * cs)4023 int SUMA_NN_GeomSmooth3_SO(   SUMA_SurfaceObject *SO,
4024                          byte *nmask, byte strict_mask,
4025                          int Niter, int anchor_each,
4026                          SUMA_SurfaceObject *SOe,
4027                          float *altw, THD_3dim_dataset *voxelize,
4028                          SUMA_COMM_STRUCT *cs)
4029 {
4030    static char FuncName[]={"SUMA_NN_GeomSmooth3_SO"};
4031    float *dsmooth=NULL;
4032 
4033    SUMA_ENTRY;
4034 
4035    if (!SO || !SO->NodeList) {
4036       SUMA_S_Err("NULL input");
4037       SUMA_RETURN(NOPE);
4038    }
4039 
4040    if (Niter == 0) Niter = 100;
4041 
4042    if (!(dsmooth = SUMA_NN_GeomSmooth3( SO, Niter, SO->NodeList,
4043                                        SO->NodeDim, SUMA_ROW_MAJOR,
4044                                        NULL, cs,
4045                                        nmask, anchor_each, SOe,
4046                                        altw, voxelize))) {
4047       SUMA_S_Err("Failed to NN smooth");
4048       SUMA_RETURN(NOPE);
4049    }
4050 
4051    SUMA_free(SO->NodeList); SO->NodeList = dsmooth; dsmooth = NULL;
4052 
4053    SUMA_RECOMPUTE_NORMALS_and_AREAS(SO);
4054    SUMA_DIM_CENTER(SO);
4055 
4056    SUMA_RETURN(YUP);
4057 }
4058 
4059 /*!
4060    \brief performs smoothing based on Taubin's smoothing
4061    algorithm in Geometric Signal Processing on Polygonal
4062    Meshes (Eurographics 2000)
4063 
4064    fout =  SUMA_Taubin_Smooth (SUMA_SurfaceObject *SO, float **wgt,
4065                             float lambda, float mu, float *fin,
4066                             int N_iter, int vpn,  d_order,
4067                             float *fout_user, SUMA_COMM_STRUCT *cs);
4068 
4069    \param SO (SUMA_SurfaceObject *SO) The surface, with NodeList, FaceSetList
4070                                        and FN fields present
4071    \param wgt (float **) interpolation weights for each node.
4072                          The dimentions of wgt are equal to those of
4073                          SO->FN->FirstNeighb
4074                          These weights may need to be re-evaluated for
4075                          each iteration
4076                          For equal weights (1/SO->FN->N_FirstNeighb[n]),
4077                          just pass NULL
4078    \param lambda (float) postive scaling factor
4079    \param mu (float) negative scaling factor
4080    \param fin (float *) vector containing node data. The length of this vector
4081                         is vpn x SO->N_Node , where vpn is the number of values
4082                         per node.
4083    \param N_iter (int)  number of iterations
4084                         (same weights are used in each iteration)
4085    \param vpn (int) number of values per node in fin
4086    \param d_order (SUMA_INDEXING_ORDER) Indicates how multiple values per node
4087                         are stored in fin
4088          SUMA_ROW_MAJOR: The data in fin is stored in  Row Major  order.
4089             The ith value (start at 0) for node n is at index fin[vpn*n+i]
4090          SUMA_COLUMN_MAJOR: The data in fin is stored in  Column Major  order.
4091             The ith (start at 0) value for node n is at index
4092             fin[n+SO->N_Node*i]; etc...
4093    \param fout_user (float *) a pointer to the vector where the smoothed version
4094          of fin will reside.
4095          You can pass NULL and the function will do the allocation for you and
4096          return the pointer to the smoothed data in fout.
4097          If you already have space allocated for the result, then pass the
4098          pointer in fout_user and save on allocation time and space. In that
4099          case, fout is equal to fout_user.
4100          Either way, you are responsible for freeing memory pointed to by fout.
4101          DO NOT PASS fout_user = fin
4102    \param cs (SUMA_COMM_STRUCT *) See SUMA_Chung_Smooth
4103    \param nmask (byte *) NULL == filter all nodes
4104           else filter node n if nmask[n]
4105    \return fout (float *)
4106       A pointer to the smoothed data (vpn * SO->N_Node values).
4107       You will have to free the memory allocated for fout yourself.
4108 */
SUMA_Taubin_Smooth(SUMA_SurfaceObject * SO,float ** wgt,float lambda,float mu,float * fin_orig,int N_iter,int vpn,SUMA_INDEXING_ORDER d_order,float * fout_final_user,SUMA_COMM_STRUCT * cs,byte * nmask,byte strict_mask)4109 float * SUMA_Taubin_Smooth (SUMA_SurfaceObject *SO, float **wgt,
4110                             float lambda, float mu, float *fin_orig,
4111                             int N_iter, int vpn, SUMA_INDEXING_ORDER d_order,
4112                             float *fout_final_user, SUMA_COMM_STRUCT *cs,
4113                             byte *nmask, byte strict_mask)
4114 {
4115    static char FuncName[]={"SUMA_Taubin_Smooth"};
4116    float *fout_final=NULL, *fbuf=NULL, *fin=NULL, *fout=NULL,
4117          *fin_next=NULL, *ftmp=NULL;
4118    float fp, dfp, fpj;
4119    int i, n , k, j, niter, vnk, n_offset, DoThis, nj=-1, nnei=-1;
4120    SUMA_Boolean LocalHead = NOPE;
4121 
4122    SUMA_ENTRY;
4123 
4124    if (N_iter % 2) {
4125       SUMA_SL_Err("N_iter must be an even number\n");
4126       SUMA_RETURN(NULL);
4127    }
4128 
4129    if (!SO || !fin_orig) {
4130       SUMA_SL_Err("NULL SO or fin_orig\n");
4131       SUMA_RETURN(NULL);
4132    }
4133    if (!SO->FN) { /* try to compute, perhaps oversight */
4134       SUMA_SurfaceMetrics(SO, "EdgeList|MemberFace", NULL);
4135    }
4136    if (!SO->FN) {
4137       SUMA_SL_Err("NULL SO->FN\n");
4138       SUMA_RETURN(NULL);
4139    }
4140 
4141    if (vpn < 1) {
4142       SUMA_SL_Err("vpn < 1\n");
4143       SUMA_RETURN(NULL);
4144    }
4145 
4146    if (fout_final_user == fin_orig) {
4147       SUMA_SL_Err("fout_final_user == fin_orig");
4148       SUMA_RETURN(NULL);
4149    }
4150 
4151    if (!fout_final_user) { /* allocate for output */
4152       fout_final = (float *)SUMA_calloc(SO->N_Node * vpn, sizeof(float));
4153       if (!fout_final) {
4154          SUMA_SL_Crit("Failed to allocate for fout_final\n");
4155          SUMA_RETURN(NULL);
4156       }
4157    }else {
4158       fout_final = fout_final_user; /* pre-allocated */
4159    }
4160 
4161    if (d_order == SUMA_COLUMN_MAJOR) {
4162          SUMA_SL_Warn("SUMA_COLUMN_MAJOR has not been thoroughly tested.");
4163    }
4164 
4165    if (cs && cs->Send) {
4166       if(vpn != 3) {
4167          SUMA_SL_Warn(  "It does not look like you are smoothing coordinates!\n"
4168                         "Communication halted.");
4169          cs->Send = NOPE;
4170       }
4171       if (d_order == SUMA_COLUMN_MAJOR) {
4172          SUMA_SL_Warn("Talking with SUMA_COLUMN_MAJOR has not been tested.");
4173       }
4174    }
4175 
4176    /* allocate for buffer */
4177    fbuf = (float *)SUMA_calloc(SO->N_Node * vpn, sizeof(float));
4178    if (!fbuf) {
4179       SUMA_SL_Crit("Failed to allocate for fbuf\n");
4180       SUMA_RETURN(NULL);
4181    }
4182 
4183    if (LocalHead) {
4184       fprintf (SUMA_STDERR,
4185                "%s: Mu = %f, Lambda = %f\n"
4186                "Should have M(%f)< -L(%f) < 0\nN_iter=%d\n",
4187          FuncName, mu, lambda, mu, -lambda, N_iter);
4188    }
4189 
4190    if (cs && cs->Send) { /* send the first monster */
4191       if (!SUMA_SendToSuma (SO, cs, (void *)fin_orig, SUMA_NODE_XYZ, 1)) {
4192          SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCommunication halted.");
4193       }
4194    }
4195 
4196    fin_next = fin_orig;
4197    switch (d_order) {
4198       case SUMA_COLUMN_MAJOR:
4199          for (niter=0; niter < N_iter; ++niter) {
4200             if ( niter % 2 ) { /* odd */
4201                fin = fin_next; /* input from previous output buffer */
4202                fout = fout_final; /* results go into final vector */
4203                fin_next = fout_final; /*  in the next iteration,
4204                                           the input is from fout_final */
4205             } else { /* even */
4206                /* input data is in fin_new */
4207                fin = fin_next;
4208                fout = fbuf; /* results go into buffer */
4209                fin_next = fbuf; /*  in the next iteration,
4210                                     the input is from the buffer */
4211                if (wgt && niter) {
4212                   /* recalculate the weights */
4213                   if (SUMA_Taubin_Weights == SUMA_FUJIWARA) {
4214                      SUMA_Taubin_Fujiwara_Smooth_Weights(SO, fin, &wgt);
4215                   } else if (SUMA_Taubin_Weights == SUMA_DESBRUN) {
4216                      SUMA_Taubin_Desbrun_Smooth_Weights(SO, fin, &wgt);
4217                   } else {
4218                      SUMA_SL_Err("Weights not set");
4219                   }
4220                }
4221             }
4222             for (k=0; k < vpn; ++k) {
4223                n_offset = k * SO->N_Node;  /* offset of kth node value in fin */
4224                for (n=0; n < SO->N_Node; ++n) {
4225                   DoThis = 1;
4226                   if (nmask && !nmask[n]) DoThis = 0;
4227                   vnk = n+n_offset;
4228                   if (DoThis) {
4229                      fp = fin[vnk]; /* kth value at node n */
4230                      dfp = 0.0;
4231                      if (nmask) {
4232                         nnei = 0;
4233                         for (j=0; j < SO->FN->N_Neighb[n]; ++j) {
4234                                                 /* calculating the laplacian */
4235                               nj = SO->FN->FirstNeighb[n][j];
4236                               if (nmask[nj] || !strict_mask){ /* consider only
4237                                     neighbors that are in mask if strict_mask
4238                                     is 1*/
4239                                  fpj = fin[nj+n_offset]; /* value at jth neighbor                                                             of n */
4240                                  if (wgt) dfp += wgt[n][j] * (fpj - fp);
4241                                  else { dfp += (fpj - fp); ++nnei; }/* will apply                                                            equal weight later */
4242                               }
4243                         }/* for j*/
4244                      } else {
4245                         nnei = SO->FN->N_Neighb[n];
4246                         for (j=0; j < SO->FN->N_Neighb[n]; ++j) { /* calculating
4247                                                                  the laplacian */
4248                               fpj = fin[SO->FN->FirstNeighb[n][j]+n_offset];
4249                                                  /* value at jth neighbor of n */
4250                               if (wgt) dfp += wgt[n][j] * (fpj - fp);
4251                               else dfp += (fpj - fp); /* will apply equal
4252                                                          weight later      */
4253                         }/* for j*/
4254                      }
4255                      if (niter%2) { /* odd */
4256                         if (wgt) fout[vnk] = fin[vnk] + mu * dfp;
4257                         else fout[vnk] = fin[vnk] + mu * dfp / (float)nnei;
4258                                           /* apply equal weight factor here */
4259                      }else{ /* even */
4260                        if (wgt) fout[vnk] = fin[vnk] + lambda * dfp;
4261                        else fout[vnk] = fin[vnk] + lambda * dfp / (float)nnei;
4262                                           /* apply equal weight factor here */
4263                      }
4264                   } else {
4265                      fout[vnk] = fin[vnk];
4266                   }
4267                }/* for n */
4268             }/* for k */
4269             if (cs && cs->Send) {
4270                /* SUMA_SendToSuma does not deal with such COLUMN_MAJOR order.
4271                   Must flip things here, boooooring */
4272                if (!niter) { /* allocate for buffer */
4273                   ftmp = (float *) SUMA_malloc(3*SO->N_Node*sizeof(float));
4274                   if (!ftmp) {
4275                      SUMA_SL_Err("Failed to allocate. Communication Off.\n");
4276                      cs->Send = NOPE;
4277                   }
4278                }
4279                if (ftmp) {
4280                   for (i=0; i<SO->N_Node; ++i) {
4281                      ftmp[3*i] = fout[i]; ftmp[3*i+1] = fout[i+SO->N_Node];
4282                      ftmp[3*i+2] = fout[i+2*SO->N_Node];
4283                   }
4284                   if (!SUMA_SendToSuma (  SO, cs, (void *)ftmp,
4285                                           SUMA_NODE_XYZ, 1) ) {
4286                      SUMA_SL_Warn("Failed in SUMA_SendToSuma\n"
4287                                   "Communication halted.");
4288                   }
4289                }
4290                if (niter == N_iter -1) { /* free the buffer */
4291                   if (ftmp) { SUMA_free(ftmp); ftmp = NULL; }
4292                }
4293             }
4294          }/* for niter */
4295          break;
4296       case SUMA_ROW_MAJOR:
4297          for (niter=0; niter < N_iter; ++niter) {
4298             if ( niter % 2 ) { /* odd */
4299                fin = fin_next; /* input from previous output buffer */
4300                fout = fout_final; /* results go into final vector */
4301                fin_next = fout_final; /*  in the next iteration,
4302                                           the input is from fout_final */
4303             } else { /* even */
4304                /* input data is in fin_new */
4305                fin = fin_next;
4306                fout = fbuf; /* results go into buffer */
4307                fin_next = fbuf; /* in the next iteration,
4308                                    the input is from the buffer */
4309                if (wgt && niter ) {
4310                   /* recalculate the weights */
4311                   if (SUMA_Taubin_Weights == SUMA_FUJIWARA) {
4312                      SUMA_Taubin_Fujiwara_Smooth_Weights(SO, fin, &wgt);
4313                   } else if (SUMA_Taubin_Weights == SUMA_DESBRUN) {
4314                      SUMA_Taubin_Desbrun_Smooth_Weights(SO, fin, &wgt);
4315                   } else {
4316                      SUMA_SL_Err("Weights not set");
4317                   }
4318                }
4319             }
4320             for (n=0; n < SO->N_Node; ++n) {
4321                DoThis = 1;
4322                if (nmask && !nmask[n]) DoThis = 0;
4323                vnk = n * vpn; /* index of 1st value at node n */
4324                for (k=0; k < vpn; ++k) {
4325                   if (DoThis) {
4326                      fp = fin[vnk]; /* kth value at node n */
4327                      dfp = 0.0;
4328                      if (nmask) {
4329                         nnei = 0;
4330                         for (j=0; j < SO->FN->N_Neighb[n]; ++j) { /* calculating
4331                                                                the laplacian */
4332                            nj = SO->FN->FirstNeighb[n][j];
4333                            if (nmask[nj] || !strict_mask) { /* consider only
4334                                  neighbors that are in mask if strict_mask is 1*/
4335 
4336                               fpj = fin[nj*vpn+k]; /* value at jth neighbor
4337                                                       of n */
4338                               if (wgt) dfp += wgt[n][j] * (fpj - fp);
4339                               else { dfp += (fpj - fp); ++nnei;} /* will apply
4340                                                            equal weight later */
4341                            }
4342                         }/* for j*/
4343                      } else {
4344                         nnei = SO->FN->N_Neighb[n];
4345                         for (j=0; j < SO->FN->N_Neighb[n]; ++j) { /* calculating
4346                                                                the laplacian */
4347                            fpj = fin[SO->FN->FirstNeighb[n][j]*vpn+k]; /* value
4348                                                          at jth neighbor of n */
4349                            if (wgt) dfp += wgt[n][j] * (fpj - fp);
4350                            else dfp += (fpj - fp); /* will apply equal weight
4351                                                       later */
4352                         }/* for j*/
4353                      }
4354                      if (niter%2) { /* odd */
4355                         if (wgt) fout[vnk] = fin[vnk] + mu * dfp;
4356                         else fout[vnk] = fin[vnk] + mu * dfp / (float)nnei;
4357                                              /* apply equal weight factor here */
4358                      }else{ /* even */
4359                        if (wgt) fout[vnk] = fin[vnk] + lambda * dfp;
4360                        else fout[vnk] = fin[vnk] + lambda * dfp / (float)nnei;
4361                                              /* apply equal weight factor here */
4362                      }
4363                   } else {
4364                      fout[vnk] = fin[vnk];
4365                   }
4366                   ++vnk; /* index of next value at node n */
4367                } /* for k */
4368             }/* for n */
4369             if (cs && cs->Send) {
4370                if (!SUMA_SendToSuma (SO, cs, (void *)fout, SUMA_NODE_XYZ, 1)) {
4371                   SUMA_SL_Warn("Failed in SUMA_SendToSuma\n"
4372                                "Communication halted.");
4373                }
4374             }
4375          }/* for niter */
4376          break;
4377       default:
4378          SUMA_SL_Err("Bad Major, very bad.\n");
4379          SUMA_RETURN(NULL);
4380          break;
4381    }
4382 
4383    if (fbuf) SUMA_free(fbuf); fbuf = NULL;
4384 
4385    SUMA_RETURN(fout);
4386 }
4387 
4388 static int OffsetDebugNode = -1; /* debugging variable for Offset computing functions */
SUMA_Set_OffsetDebugNode(int d)4389 void SUMA_Set_OffsetDebugNode (int d)
4390 {
4391    OffsetDebugNode = d;
4392    return;
4393 }
4394 
4395 
4396 /*!
4397    \brief a function to turn the often cumbersome SUMA_GET_OFFSET_STRUCT
4398    to a more friendly SUMA_OFFSET_STRUCT
4399 */
4400 
SUMA_GetOffset2Offset(SUMA_GET_OFFSET_STRUCT * GOS,SUMA_OFFSET_STRUCT * OS)4401 SUMA_Boolean SUMA_GetOffset2Offset (SUMA_GET_OFFSET_STRUCT *GOS,
4402                                     SUMA_OFFSET_STRUCT *OS)
4403 {
4404    static char FuncName[]={"SUMA_GetOffset2Offset"};
4405    int il, jl, noffs;
4406    SUMA_Boolean LocalHead = NOPE;
4407 
4408    SUMA_ENTRY;
4409 
4410    if (!GOS || !OS) {
4411       SUMA_SL_Err("NULL input"); SUMA_RETURN(NOPE);
4412    }
4413 
4414    OS->N_Neighb = 0;
4415    for (il=1; il<GOS->N_layers; ++il) {
4416       OS->N_Neighb += GOS->layers[il].N_NodesInLayer;
4417    }
4418    OS->Neighb_ind = (int *)SUMA_malloc(OS->N_Neighb * sizeof(int));
4419    OS->Neighb_dist = (float *)SUMA_malloc(OS->N_Neighb * sizeof(float));
4420    if (!OS->Neighb_ind || !OS->Neighb_dist) {
4421       SUMA_SL_Crit("Failed to allocate.");
4422       SUMA_RETURN(NOPE);
4423    }
4424 
4425    noffs = 0;
4426    for (il=1; il<GOS->N_layers; ++il) {
4427       for (jl=0; jl<GOS->layers[il].N_NodesInLayer; ++jl) {
4428          OS->Neighb_ind[noffs] = GOS->layers[il].NodesInLayer[jl];
4429          OS->Neighb_dist[noffs] = GOS->OffVect[OS->Neighb_ind[noffs]];
4430          ++noffs;
4431       }
4432    }
4433 
4434    SUMA_RETURN(YUP);
4435 }
4436 
SUMA_GetOffset2bytemask(SUMA_GET_OFFSET_STRUCT * GOS,byte * thismask)4437 byte *SUMA_GetOffset2bytemask (SUMA_GET_OFFSET_STRUCT *GOS, byte *thismask)
4438 {
4439    static char FuncName[]={"SUMA_GetOffset2bytemask"};
4440    int il, jl;
4441    byte *nmask=NULL;
4442    SUMA_ENTRY;
4443 
4444    if (!GOS) {
4445       SUMA_S_Err("NULL input");
4446       SUMA_RETURN(thismask);
4447    }
4448 
4449    if (thismask) {
4450       nmask=thismask;
4451    } else {
4452       nmask=(byte *)SUMA_malloc(GOS->N_Nodes*sizeof(byte));
4453    }
4454    memset(nmask, 0, GOS->N_Nodes* sizeof(byte));
4455    for (il=0; il<GOS->N_layers; ++il) {
4456       for (jl=0; jl<GOS->layers[il].N_NodesInLayer; ++jl) {
4457          nmask[GOS->layers[il].NodesInLayer[jl]]=1;
4458       }
4459    }
4460 
4461    SUMA_RETURN(nmask);
4462 }
4463 
SUMA_ShowOffset_Info(SUMA_GET_OFFSET_STRUCT * OffS,int detail)4464 char * SUMA_ShowOffset_Info (SUMA_GET_OFFSET_STRUCT *OffS, int detail)
4465 {
4466    static char FuncName[]={"SUMA_ShowOffset_Info"};
4467    SUMA_STRING *SS = NULL;
4468    int ii, *ltmp=NULL, *imap = NULL;
4469    char *s=NULL;
4470 
4471    SUMA_ENTRY;
4472 
4473    SS = SUMA_StringAppend (NULL, NULL);
4474 
4475    if (!OffS) {
4476       SS = SUMA_StringAppend (SS,"#NULL offset structure.\n");
4477    } else {
4478       SS = SUMA_StringAppend_va (SS,"#Node Offsets (graph distance) from node %d\n", OffS->layers[0].NodesInLayer[0]);
4479       SS = SUMA_StringAppend_va (SS,"#Column 0 = Node index\n"
4480                                     "#column 1 = Neighborhood layer\n"
4481                                     "#Column 2 = Distance from node %d\n", OffS->layers[0].NodesInLayer[0]);
4482       ltmp = (int *)SUMA_malloc(OffS->N_Nodes*sizeof(int)); /* make a copy to avoid disturbinh OffS's contents*/
4483       if (!ltmp) {
4484          SUMA_SL_Crit("Failed to allocate for ltmp");
4485          SUMA_RETURN(NULL);
4486       }
4487       for (ii=0; ii<OffS->N_Nodes; ++ii) ltmp[ii] = OffS->LayerVect[ii];
4488       imap = SUMA_z_dqsort(ltmp,OffS->N_Nodes);
4489       for (ii=0; ii<OffS->N_Nodes; ++ii) {
4490          if (OffS->LayerVect[imap[ii]] >= 0) {
4491             SS = SUMA_StringAppend_va (SS,"%6d\t%6d\t%f\n", imap[ii], OffS->LayerVect[imap[ii]], OffS->OffVect[imap[ii]]);
4492          }
4493       }
4494    }
4495    if (ltmp) SUMA_free(ltmp); ltmp = NULL;
4496    if (imap) SUMA_free(imap); imap = NULL;
4497    SUMA_SS2S(SS,s);
4498 
4499    SUMA_RETURN(s);
4500 }
4501 
SUMA_ShowOffset_ll_Info(DList * list,int detail)4502 char * SUMA_ShowOffset_ll_Info (DList *list, int detail)
4503 {
4504    static char FuncName[]={"SUMA_ShowOffset_ll_Info"};
4505    SUMA_STRING *SS = NULL;
4506    DListElmt *elm = NULL;
4507    SUMA_OFFSET_LL_DATUM *dat=NULL;
4508    int ii;
4509    char *s=NULL;
4510 
4511    SUMA_ENTRY;
4512 
4513    SS = SUMA_StringAppend (NULL, NULL);
4514 
4515    if (!list) {
4516       SS = SUMA_StringAppend (SS,"#NULL offset list.\n");
4517    } else {
4518       do {
4519          if (!elm) elm = dlist_head(list);
4520          else elm = elm->next;
4521          dat = (SUMA_OFFSET_LL_DATUM *)elm->data;
4522          if (elm == dlist_head(list)) {
4523             SS = SUMA_StringAppend_va (SS,"#Node Offsets (graph distance) from node %d\n", dat->ni);
4524             SS = SUMA_StringAppend_va (SS,"#Column 0 = Node index\n"
4525                                        "#column 1 = Neighborhood layer\n"
4526                                        "#Column 2 = Distance from node %d\n", dat->ni);
4527          }
4528          SS = SUMA_StringAppend_va (SS,"%6d\t%6d\t%f\n", dat->ni, dat->layer, dat->off);
4529       } while (elm != dlist_tail(list));
4530    }
4531    SUMA_SS2S(SS,s);
4532 
4533    SUMA_RETURN(s);
4534 }
4535 
4536 /*!
4537    For each node past layer 0, determine the propagation location prop_loc of that node. The
4538    propagation direction is given by the vector formed by vector: node-->prop_loc
4539    Here's how the function works:
4540    For each node njl in layer il,
4541       find nodes surrounding it and store njl and its neighbors in n_sur
4542       calculate the 1st principal component of the matrix formed by the coordinates of these nodes
4543       Form the plane that has the 1st principal component as a normal and that passes by njl
4544       Intersect the plane with those triangles that are not in the previous layer (i.e. intersect with triangles going forward)
4545       One triangle should intersect the plane and the intersection point (other than njl of course) is
4546       called the propagation location of node njl.
4547 */
4548 #define LOC_DBG 1
SUMA_OffsetLayerPropagationLocation(SUMA_SurfaceObject * SO,SUMA_GET_OFFSET_STRUCT * OffS,float * PropLoc)4549 int SUMA_OffsetLayerPropagationLocation(SUMA_SurfaceObject *SO, SUMA_GET_OFFSET_STRUCT *OffS, float *PropLoc)
4550 {
4551    static char FuncName[]={"SUMA_OffsetLayerPropagationLocation"};
4552    int n_sur[50], iseg, njl, njjll, jjll, in_sur, hit, itgood, it, il0, nil0, n2, n3, i;
4553    int N_Incident, N_IncidentMax=100, Incident[N_IncidentMax];
4554    int noffs = 0, il, jl, ip;
4555    float data_mat[50*3], *p1, *p2;
4556    double trace, pc_vec[9], pc_eig[3], Eq[4], pinter[3], pc0[3];
4557    SUMA_Boolean LocalHead = NOPE;
4558 
4559    SUMA_ENTRY;
4560 
4561    if (SO->NodeDim != 3) {
4562       SUMA_S_Err("Need 3 dims for surf coords.");
4563       SUMA_RETURN(0);
4564    }
4565    if (!SO->EL) {
4566       SUMA_S_Err("Need SO->EL.");
4567       SUMA_RETURN(0);
4568    }
4569    i = OffS->layers[0].NodesInLayer[0];
4570    if (i==OffsetDebugNode) {
4571       LocalHead = YUP;
4572       SUMA_LHv("Debug mode for central node %d (must have LOC_DBG defined as 1): \n", i);
4573    }
4574    noffs = 0;
4575    for (il=1; il<OffS->N_layers; ++il) { /* for each layer */
4576       for (jl=0; jl<OffS->layers[il].N_NodesInLayer; ++jl) { /* for each node in layer */
4577          PropLoc[3*noffs  ] = PropLoc[3*noffs+1] = PropLoc[3*noffs+2] = 0.0 ;
4578          njl = OffS->layers[il].NodesInLayer[jl];
4579          in_sur = 0;
4580          n_sur[in_sur] = njl; ++in_sur;
4581          /* SUMA_LHv("get nodes in layer %d, connected to node %d\n", il, njl); */
4582          for (jjll=0; jjll<OffS->layers[il].N_NodesInLayer; ++jjll) {
4583             njjll = OffS->layers[il].NodesInLayer[jjll];
4584             if (njjll != njl) {
4585                iseg = -1;
4586                /* SUMA_LHv("Looking for edge: %d %d\n", njjll, njl); */
4587                if (njjll < njl) {SUMA_FIND_EDGE (SO->EL, njjll, njl, iseg);}
4588                else {SUMA_FIND_EDGE (SO->EL, njl, njjll, iseg);}
4589                /* SUMA_LHv("iseg is %d\n", iseg); */
4590                if (iseg < 0) {
4591                   /* SUMA_LH("Segment not found."); */
4592                } else {
4593                   /* segment found, accept node in neighborhood */
4594                   if (in_sur > 49) {
4595                      SUMA_S_Crit("Hard limit exceeded! Fix it.");
4596                      exit(1);
4597                   }
4598                   n_sur[in_sur] = njjll; ++in_sur;
4599                }
4600             }
4601          }
4602          if (in_sur < 2) {
4603             /* Node juts out with no nodes connected to it to meet the
4604             layer's distance criterion. This node is going nowhere*/
4605             fprintf(SUMA_STDERR, "Warning %s: \n"
4606                                  "in_sur is %d\n"
4607                                  "Central node: %d\n"
4608                                  "Layer %d, node %d.\n"
4609                                  "%d nodes in this layer are:\n",
4610                                  FuncName, in_sur, i, il, n_sur[0], OffS->layers[il].N_NodesInLayer);
4611             for (ip=0; ip<OffS->layers[il].N_NodesInLayer; ++ip) fprintf(SUMA_STDERR, "%d   ", OffS->layers[il].NodesInLayer[ip]);
4612             fprintf(SUMA_STDERR, "\nPropLoc[%d] is set to 0s.\n", noffs);
4613             goto NEW_OFFS;
4614          } else if (in_sur == 2) {
4615             /* Node has only one neighbor in layer.
4616             This happens when other neighboring nodes were part of the
4617             previous layer. In this case, the direction of this node is towards its only
4618             neighbor */
4619             PropLoc[3*noffs+0] = SO->NodeList[3*n_sur[1]  ];
4620             PropLoc[3*noffs+1] = SO->NodeList[3*n_sur[1]+1];
4621             PropLoc[3*noffs+2] = SO->NodeList[3*n_sur[1]+2];
4622             SUMA_LHv("Storing lazy hit (noffs=%d)\n"
4623                      "PropLoc[%d]=%f %f %f\n", noffs, noffs,
4624                      PropLoc[3*noffs+0], PropLoc[3*noffs+1], PropLoc[3*noffs+2]);
4625             goto NEW_OFFS;
4626          } else if (in_sur > 6) {
4627             fprintf(SUMA_STDERR, "Warning %s: \n"
4628                                  "in_sur is %d\n"
4629                                  "Although it is common for it \n"
4630                                  "to be slightly larger than 3.\n"
4631                                  "the value observed seems excessive.\n"
4632                                  "check mesh at nodes below\n"
4633                                  "Central node: %d\n"
4634                                  "Layer %d, node %d.\n",
4635                                  FuncName, in_sur, i, il, n_sur[0]);
4636          }
4637          /* Now we have in n_sur, the indces of nodes that would enter the PCA */
4638          for (ip=0; ip<in_sur; ++ip) { /* load the coords */
4639                data_mat[ip  ] = SO->NodeList[3*n_sur[ip]  ];
4640                data_mat[ip+3] = SO->NodeList[3*n_sur[ip]+1];
4641                data_mat[ip+6] = SO->NodeList[3*n_sur[ip]+2];
4642          }
4643 
4644          #if LOC_DBG
4645          SUMA_LHv("Calculating pca of nodes %d %d %d:\n"
4646                   "[%f %f %f\n%f %f %f\n%f %f %f]\n",
4647                   n_sur[0], n_sur[1], n_sur[2],
4648                   SO->NodeList[3*n_sur[0]],SO->NodeList[3*n_sur[0]+1],SO->NodeList[3*n_sur[0]+2],
4649                   SO->NodeList[3*n_sur[1]],SO->NodeList[3*n_sur[1]+1],SO->NodeList[3*n_sur[1]+2],
4650                   SO->NodeList[3*n_sur[2]],SO->NodeList[3*n_sur[2]+1],SO->NodeList[3*n_sur[2]+2]);
4651          #endif
4652 
4653          if ((trace = pca_fast3 (data_mat, in_sur, 1, pc_vec, pc_eig)) < 0) {
4654             fprintf(SUMA_STDERR, "Warning %s:\n"
4655                                  "Failed calculating PC at\n"
4656                                  "Central node: %d\n"
4657                                  "Layer %d, nodes for pca: %d %d %d\n",
4658                                  FuncName, i, il, n_sur[0], n_sur[1], n_sur[2]);
4659             goto NEW_OFFS;
4660          }
4661          /* Have PC, will travel */
4662          /* Find equation of plane passing by node n_sur[0] and having the PC for a normal */
4663          pc0[0] = pc_vec[0]; pc0[1] = pc_vec[3]; pc0[2] = pc_vec[6];
4664          p1 = &(SO->NodeList[3*n_sur[0]]);
4665          SUMA_LHv("   Forming plane with normal [%f %f %f], passing by point [%f %f %f]\n",
4666             pc0[0], pc0[1], pc0[2], p1[0], p1[1], p1[2]);
4667          SUMA_PLANE_NORMAL_POINT(pc0, p1, Eq);
4668          /* Then find the triangle that is      incident to node n_sur[0]
4669                                           AND   not part of the previous layer
4670                                           AND   intersects the plane Eq*/
4671          N_Incident = N_IncidentMax; /* pass limit to SUMA_Get_NodeIncident */
4672          if (!SUMA_Get_NodeIncident(n_sur[0], SO, Incident, &N_Incident)) {
4673             SUMA_S_Err("Failed to get incident triangles.");
4674             goto NEW_OFFS;
4675          }
4676          if (!N_Incident) {
4677             SUMA_S_Err("No incident triangles");
4678             goto NEW_OFFS;
4679          }
4680          /* of those triangles, which ones have a node in the previous layer? */
4681          SUMA_LHv("   Searching for acceptable triangles out of a total of %d\n", N_Incident);
4682          itgood = 0;
4683          for (it=0; it<N_Incident; ++it) {
4684             hit = 0;
4685             for (il0=0; il0<OffS->layers[il-1].N_NodesInLayer;++il0) {
4686                nil0 = OffS->layers[il-1].NodesInLayer[il0];
4687                if (  SO->FaceSetList[3*Incident[it]  ] == nil0 ||
4688                      SO->FaceSetList[3*Incident[it]+1] == nil0 ||
4689                      SO->FaceSetList[3*Incident[it]+2] == nil0) {
4690                   hit = 1;
4691                   break;
4692                }
4693             }
4694             if (!hit) {
4695                Incident[itgood] = Incident[it];
4696                ++itgood;
4697             }
4698          }
4699          N_Incident = itgood;
4700          if (LocalHead) {
4701             fprintf (SUMA_STDERR,"%s:\n"
4702                                  "    Which of remaining %d triangles intersect the plane [%.2f %.2f %.2f %.2f]\n"
4703                                  "    Triangles:\n",
4704                                  FuncName, N_Incident,Eq[0], Eq[1], Eq[2], Eq[3]);
4705             for (it=0; it<N_Incident; ++it) {
4706                fprintf (SUMA_STDERR,"   %d", Incident[it]);
4707             }
4708             fprintf (SUMA_STDERR,"\n");
4709          }
4710          /* Now, which of these good triangles intersect the plane ?*/
4711          hit = 0;
4712          for (it=0; it<N_Incident; ++it) {
4713             SUMA_LHv("      Checking Triangle %d\n", Incident[it]);
4714             SUMA_TWO_OTHER_TRIANGLE_NODES(n_sur[0],Incident[it],SO->FaceSetList,n2,n3);
4715             if (n2 < 0 || n3 < 0) {
4716                SUMA_S_Err("Failed to find other nodes in triangle");
4717                goto NEW_OFFS;
4718             }
4719             p1 = &(SO->NodeList[3*n2]);
4720             p2 = &(SO->NodeList[3*n3]);
4721             SUMA_LHv("Calling SUMA_SEGMENT_PLANE_INTERSECT, nodes of triangle are %d %d %d\n", n_sur[0], n2, n3);
4722             SUMA_SEGMENT_PLANE_INTERSECT(p1, p2, Eq, hit, pinter);
4723             if (hit) {  /* OK, have hit, and the propagation direction */
4724                PropLoc[3*noffs+0] = pinter[0];
4725                PropLoc[3*noffs+1] = pinter[1];
4726                PropLoc[3*noffs+2] = pinter[2];
4727                SUMA_LHv("Storing hit (noffs=%d)\n"
4728                         "PropLoc[%d]=%f %f %f\n", noffs, noffs,
4729                         PropLoc[3*noffs+0], PropLoc[3*noffs+1], PropLoc[3*noffs+2]);
4730                break;
4731             }
4732          }
4733          if (!hit) {
4734             SUMA_LHv("             Failed to get hit (noffs=%d)\n", noffs);
4735          }
4736 
4737          NEW_OFFS:
4738          SUMA_LHv("Done with noffs=%d\n", noffs);
4739          ++noffs;
4740       }  /* for each node in layer */
4741    }  /* for each layer */
4742    SUMA_RETURN(1);
4743 }
4744 
4745 /*!
4746    \brief creates a vector of node neighbors structures such that:
4747    OffS = SUMA_FormNeighbOffset ( SUMA_SurfaceObject *SO, float OffsetLim,
4748                                  const char *Opts, byte *nmask, float FWHM);
4749 
4750    \param OffS (SUMA_OFFSET_STRUCT *) SO->Node x 1 vector of structures
4751          OffS[i] is a structure containing node neighbors of node i
4752          OffS[i].N_Neighb (int) number of neighbors of node i
4753          OffS[i].Neighb_ind (int *) OffS[i].N_Neighb x 1 vector containing
4754                                     nodes neighboring node i
4755          OffS[i].Neighb_dist (float *) OffS[i].N_Neighb x 1 vector containing
4756                               node distances from node i.
4757                               These are the shortest distances ON THE GRAPH.
4758                               The distances might be larger than OffsetLim
4759                               because the child function SUMA_getoffsets2
4760                               does so.
4761          OffS[i].Prop_Direc (float *) OffS[i].N_Neighb x 3 vector containing
4762                                     contour propagation directions at each node,
4763                                     in layers 1 and above. This is only created
4764                                     if Opts has "DoProp" in it.
4765 
4766    \param OffsetLim (float) maximal inclusive neighbor distance. In reality, some
4767                            nodes may be farther apart. But all nodes closer than
4768                            OffsetLim to each node will be included
4769    \param Opts (const char *) if contains the string "DoProp" then the function
4770                               also calculates the propagation directions.
4771                               Default is NULL, no extras.
4772    \param nmask (byte *) a mask vector, if non null, then only nodes n,
4773                where nmask[n] == 1 are processed
4774    \param FWHM (float) if > 0, then the distances x are changed in
4775                Neighb_dist are changed to
4776                   G(x) = 1/(sqrt(2pi)Sig)*exp(-(x*x)/(2*Sig*Sig))
4777               where Sig is calculated from FWHM by Sig = FWHM /   2.354820;
4778    - NOTE: This function will chew up a lot of memory, real quick.
4779             An approximate equation for the size needed for OffS:
4780                (mean_N_Neighb_per_Node * 8 + 12) * SO->N_Node Bytes
4781                With OffsetLim = 10 and a FreeSurfer surface of 150'000 nodes,
4782                the average number of neighbors per node is ~800.
4783                Which means 962MB for the returned structure.
4784      When memory usage is this high, you should consider using SUMA_getoffsets2
4785          repeatedly for each node, as is done in this function.
4786 
4787 */
4788 
SUMA_FormNeighbOffset(SUMA_SurfaceObject * SO,float OffsetLim,const char * opts,byte * nmask,float FWHM)4789 SUMA_OFFSET_STRUCT *SUMA_FormNeighbOffset (
4790       SUMA_SurfaceObject *SO, float OffsetLim,
4791       const char *opts, byte *nmask, float FWHM)
4792 {
4793    static char FuncName[]={"SUMA_FormNeighbOffset"};
4794    int i, ii, il, jl, ip, noffs,  DoProp = 0, iproc, N_mask;
4795    SUMA_GET_OFFSET_STRUCT *OffS = NULL;
4796    struct  timeval start_time;
4797    double scl=0.0, ds2=0.0, sig, d;
4798    float etime_GetOffset, mean_N_Neighb, dist, dist_norm;
4799    SUMA_OFFSET_STRUCT *OffS_out=NULL;
4800    SUMA_Boolean LocalHead = NOPE;
4801 
4802    SUMA_ENTRY;
4803    if (!SO) { SUMA_SL_Err("NULL SO"); SUMA_RETURN(NULL); }
4804    if (!SO->FN || !SO->EL) {
4805       /* do it here */
4806       if (  !SO->EL &&
4807             !(SO->EL = SUMA_Make_Edge_List_eng (SO->FaceSetList, SO->N_FaceSet,
4808                                  SO->N_Node, SO->NodeList, 0, SO->idcode_str))) {
4809          SUMA_S_Err("Failed to create Edge_List");
4810          SUMA_RETURN(NULL);
4811       }
4812       if (!SO->FN &&
4813           !(SO->FN = SUMA_Build_FirstNeighb( SO->EL,
4814                                              SO->N_Node, SO->idcode_str, 1)) ) {
4815          SUMA_S_Err("Failed to create FirstNeighb");
4816          SUMA_RETURN(NULL);
4817       }
4818    }
4819 
4820    if (SUMA_iswordin(opts,"DoProp") == 1) {
4821       SUMA_LH("Propagation Directions Will Be Computed");
4822       DoProp = 1;
4823    }
4824 
4825    /* calculate sigma */
4826    if (FWHM > 0.0) {
4827       sig = FWHM /   2.354820;
4828       ds2 = 2*sig*sig;
4829       scl = iSQ_2PI/sig;
4830    } else sig = -1.0;
4831    /* calculate the offset limit, if allowed */
4832    if (OffsetLim < 0.0) {
4833       if (sig > 0.0) {
4834          OffsetLim = 3.5*sig;
4835       } else {
4836          SUMA_S_Errv("Have OffsetLim =%f and no FWHM (%f) "
4837                      "from which to estimate it.\n", OffsetLim, FWHM);
4838          SUMA_RETURN(NULL);
4839       }
4840    }
4841    SUMA_LHv("OffsetLim set to %f\nSigma set to %f\n", OffsetLim, sig);
4842 
4843    N_mask=0;
4844    if (nmask) {
4845       for (i=0; i<SO->N_Node; ++i) if (nmask[i]) ++N_mask;
4846    } else N_mask = SO->N_Node;
4847 
4848    OffS_out = (SUMA_OFFSET_STRUCT *)
4849                   SUMA_malloc(SO->N_Node * sizeof(SUMA_OFFSET_STRUCT));
4850 
4851    SUMA_etime(&start_time,0);
4852 
4853 
4854    OffS = SUMA_Initialize_getoffsets (SO->N_Node);
4855    mean_N_Neighb = 0;
4856    dist_norm = 1.1 * OffsetLim;
4857    iproc = 0;
4858    for (i=0; i < SO->N_Node; ++i) {
4859       OffS_out[i].N_Neighb = 0;
4860       OffS_out[i].Neighb_ind = NULL;
4861       OffS_out[i].Neighb_dist = NULL;
4862       OffS_out[i].Neighb_PropLoc = NULL;
4863       if (!nmask || nmask[i]) {
4864          SUMA_getoffsets2 (i, SO, OffsetLim, OffS, NULL, 0);
4865          /* Now store all the relevant info in OffS in OffS_out[i] */
4866          for (il=1; il<OffS->N_layers; ++il) {
4867             OffS_out[i].N_Neighb += OffS->layers[il].N_NodesInLayer;
4868          }
4869          OffS_out[i].Neighb_ind =
4870             (int *)SUMA_malloc(OffS_out[i].N_Neighb * sizeof(int));
4871          OffS_out[i].Neighb_dist =
4872             (float *)SUMA_malloc(OffS_out[i].N_Neighb * sizeof(float));
4873          if (DoProp) {
4874             OffS_out[i].Neighb_PropLoc =
4875                (float*)SUMA_malloc(OffS_out[i].N_Neighb * sizeof(float)*3);
4876          }
4877          mean_N_Neighb += OffS_out[i].N_Neighb;
4878          noffs = 0;
4879          for (il=1; il<OffS->N_layers; ++il) {
4880             for (jl=0; jl<OffS->layers[il].N_NodesInLayer; ++jl) {
4881                OffS_out[i].Neighb_ind[noffs] = OffS->layers[il].NodesInLayer[jl];
4882                #if 1
4883                /* don't play fancy with the weights here */
4884                d = OffS->OffVect[OffS_out[i].Neighb_ind[noffs]];
4885                if (sig > 0.0) {
4886                   OffS_out[i].Neighb_dist[noffs] = scl * exp(-(d*d)/(ds2));
4887                }  else {
4888                   OffS_out[i].Neighb_dist[noffs] = d;
4889                }
4890                #else
4891                dist = OffS->OffVect[OffS_out[i].Neighb_ind[noffs]];
4892                if (dist > OffsetLim) OffS_out[i].Neighb_dist[noffs] = 0;
4893                else OffS_out[i].Neighb_dist[noffs] = (dist );
4894                #endif
4895                ++noffs;
4896             }
4897          }
4898 
4899          if (DoProp) {
4900             SUMA_LH("Going to SUMA_OffsetLayerPropagationLocation\n");
4901             if (!SUMA_OffsetLayerPropagationLocation(SO, OffS,
4902                                           OffS_out[i].Neighb_PropLoc)) {
4903                SUMA_S_Err("Failed to calculation propagation location.");
4904             }
4905             SUMA_LH("Done with SUMA_OffsetLayerPropagationLocation\n");
4906          }
4907 
4908          /* Show me the offsets for one node*/
4909          if (0) {
4910             if (i == OffsetDebugNode) {
4911                FILE *fid=NULL;
4912                char *outname=NULL;
4913                outname = SUMA_Extension("SomethingOffset", ".1D", YUP);
4914                outname = SUMA_append_replace_string(outname, "offset.1D", "", 1);
4915                fid = fopen(outname, "w"); free(outname); outname = NULL;
4916                if (!fid) {
4917                   SUMA_SL_Err("Could not open file for writing.\n"
4918                               "Check file permissions, disk space.\n");
4919                } else {
4920                   fprintf (fid,"#Column 1 = Node index\n"
4921                                "#column 2 = Neighborhood layer\n"
4922                                "#Column 3 = Distance from node %d\n",
4923                                OffsetDebugNode);
4924                   for (ii=0; ii<SO->N_Node; ++ii) {
4925                      if (OffS->LayerVect[ii] >= 0) {
4926                         fprintf(fid,"%d\t%d\t%f\n",
4927                                  ii, OffS->LayerVect[ii], OffS->OffVect[ii]);
4928                      }
4929                   }
4930                   fclose(fid);
4931                }
4932             }
4933          }
4934          ++iproc;
4935          if (iproc == 100) {
4936             etime_GetOffset = SUMA_etime(&start_time,1);
4937             fprintf(SUMA_STDERR,
4938                      "%s: Search to %f mm took %f seconds for %d nodes.\n"
4939                      "Projected completion time: %f minutes\n"
4940                      "Projected memory need for structure %f MB\n",
4941                      FuncName, OffsetLim, etime_GetOffset, iproc,
4942                      etime_GetOffset * N_mask / 60.0 / (float)(iproc),
4943                      (mean_N_Neighb / (iproc) * 8 + 12)* N_mask/1000000.0);
4944          }
4945          SUMA_Recycle_getoffsets (OffS);
4946       } /* if node in mask */
4947    }
4948    SUMA_Free_getoffsets(OffS); OffS = NULL;
4949 
4950    etime_GetOffset = SUMA_etime(&start_time,1);
4951    fprintf(SUMA_STDERR, "%s: Search to %f mm took %f seconds for %d nodes.\n"
4952                         "Mean number of neighbors per node: %f\n",
4953                         FuncName, OffsetLim, etime_GetOffset, SO->N_Node,
4954                         mean_N_Neighb / SO->N_Node);
4955 
4956    SUMA_RETURN(OffS_out);
4957 }
4958 
4959 /*!
4960    Return a byte mask of the nodes neighboring node 'node'.
4961 
4962    \param SO: The surface
4963    \param node: The node whose neighbors you seek
4964    \param maxlay: The maximum layer of neighbors.
4965    \param N_inmask: The total number of neighboring nodes
4966    \return mask: if (mask[k]) --> node k is a neighboring node
4967                                of node 'node' with a neighborhood
4968                                order of mask[k] . Note that
4969                                neighborhood orders max out at 255
4970 
4971       NOTE  that mask[node] is set to 0 , in keeping with the convention
4972             of getoffset* functions.
4973 */
SUMA_NodeNeighborMask(SUMA_SurfaceObject * SO,int node,int maxlay,int * N_inmask)4974 byte *SUMA_NodeNeighborMask(SUMA_SurfaceObject *SO, int node,
4975                             int maxlay, int *N_inmask)
4976 {
4977    static char FuncName[]={"SUMA_NodeNeighborMask"};
4978    int noffs=0, il, jl, ii, inode=-1;
4979    SUMA_GET_OFFSET_STRUCT *OffS = NULL;
4980    byte *mask=NULL;
4981    SUMA_Boolean LocalHead = NOPE;
4982 
4983    SUMA_ENTRY;
4984    if (!SO) { SUMA_SL_Err("NULL SO"); SUMA_RETURN(NULL); }
4985    if (!SO->FN || !SO->EL) {
4986       /* do it here */
4987       if (  !SO->EL &&
4988             !(SO->EL = SUMA_Make_Edge_List_eng (SO->FaceSetList, SO->N_FaceSet,
4989                                  SO->N_Node, SO->NodeList, 0, SO->idcode_str))) {
4990          SUMA_S_Err("Failed to create Edge_List");
4991          SUMA_RETURN(NULL);
4992       }
4993       if (!SO->FN &&
4994           !(SO->FN = SUMA_Build_FirstNeighb( SO->EL,
4995                                              SO->N_Node, SO->idcode_str, 1)) ) {
4996          SUMA_S_Err("Failed to create FirstNeighb");
4997          SUMA_RETURN(NULL);
4998       }
4999    }
5000 
5001    mask = (byte *)SUMA_calloc(SO->N_Node, sizeof(byte));
5002    if (maxlay > 1) {
5003       OffS = SUMA_Initialize_getoffsets (SO->N_Node);
5004       SUMA_getoffsets2 (node, SO, -maxlay, OffS, NULL, 0);
5005       /* Now store all the relevant info in OffS in output*/
5006       #if 0
5007       noffs = 0;
5008       for (il=1; il<OffS->N_layers; ++il) {
5009          for (jl=0; jl<OffS->layers[il].N_NodesInLayer; ++jl) {
5010             mask[OffS->layers[il].NodesInLayer[jl]]=(il<256 ? il:255);
5011             ++noffs;
5012          }
5013       }
5014       #else
5015       noffs = 0;
5016       for (ii=0; ii<SO->N_Node; ++ii) {
5017          if (OffS->LayerVect[ii] > 0) {
5018             mask[ii]=(OffS->LayerVect[ii]<256 ? OffS->LayerVect[ii]:255);
5019             ++noffs;
5020          }
5021       }
5022       #endif
5023       /* Show me the offsets for one node*/
5024       if (LocalHead) {
5025             FILE *fid=NULL;
5026             char *outname=NULL, stmp[256];
5027             snprintf(stmp, 250, "S_%s.%d", SO->Label, node);
5028             outname = SUMA_append_replace_string(outname, ".offset.1D", "", 1);
5029             fid = fopen(outname, "w");
5030             if (!fid) {
5031                SUMA_SL_Err("Could not open file for writing.\n"
5032                            "Check file permissions, disk space.\n");
5033             } else {
5034                fprintf (fid,"#Column 1 = Node index of surface %s\n"
5035                             "#column 2 = Neighborhood layer from node %d\n"
5036                             "#Column 3 = Distance from node %d\n",
5037                             SO->Label, node, node);
5038                for (il=0; il<SO->N_Node; ++il) {
5039                   if (OffS->LayerVect[il] >= 0) {
5040                      fprintf(fid,"%d\t%d\t%f\n",
5041                               il, OffS->LayerVect[il], OffS->OffVect[il]);
5042                   }
5043                }
5044                fclose(fid);
5045             }
5046             SUMA_LHv("Debug file %s written to disk.\n", outname);
5047             free(outname); outname = NULL;
5048       }
5049       SUMA_Recycle_getoffsets (OffS);
5050       SUMA_Free_getoffsets(OffS); OffS = NULL;
5051    } else {
5052       if (SO->FN->NodeId[node]==node) inode = node;
5053       else {
5054          inode = -1;
5055          for (ii=0; ii<SO->N_Node && inode < 0; ++ii) {
5056             if (SO->FN->NodeId[ii] == node) inode = ii;
5057          }
5058          if (inode < 0) {
5059             SUMA_S_Errv("Node %d's entry not found!\n",node);
5060             SUMA_free(mask); mask=NULL;
5061             SUMA_RETURN(NULL);
5062          }
5063       }
5064       for (ii=0; ii<SO->FN->N_Neighb[inode]; ++ii) {
5065          mask[SO->FN->FirstNeighb[node][ii]] = 1; ++noffs;
5066       }
5067    }
5068 
5069    SUMA_LHv("Got %d nodes at maxlay %d\n", noffs, maxlay);
5070    if (N_inmask) *N_inmask = noffs;
5071 
5072    SUMA_RETURN(mask);
5073 }
5074 
5075 /*!
5076    \brief frees what is created by SUMA_FormNeighbOffset
5077 
5078    \sa SUMA_FormNeighbOffset
5079 */
SUMA_free_NeighbOffset(SUMA_SurfaceObject * SO,SUMA_OFFSET_STRUCT * OffS_out)5080 SUMA_OFFSET_STRUCT * SUMA_free_NeighbOffset (SUMA_SurfaceObject *SO,
5081                                              SUMA_OFFSET_STRUCT *OffS_out)
5082 {
5083    static char FuncName[]={"SUMA_free_NeighbOffset"};
5084    int i;
5085    SUMA_ENTRY;
5086 
5087    if (!SO) {
5088       SUMA_S_Err("NULL SO!");
5089       SUMA_RETURN(NULL);
5090    }
5091    if (!OffS_out) SUMA_RETURN(NULL);
5092    for (i=0; i < SO->N_Node; ++i) {
5093       OffS_out[i].N_Neighb = 0;
5094       if (OffS_out[i].Neighb_dist) SUMA_free(OffS_out[i].Neighb_dist); OffS_out[i].Neighb_dist = NULL;
5095       if (OffS_out[i].Neighb_ind) SUMA_free(OffS_out[i].Neighb_ind); OffS_out[i].Neighb_ind = NULL;
5096       if (OffS_out[i].Neighb_PropLoc) SUMA_free(OffS_out[i].Neighb_PropLoc); OffS_out[i].Neighb_PropLoc = NULL;
5097    }
5098    SUMA_free(OffS_out);
5099    SUMA_RETURN(NULL);
5100 }
5101 
5102 /*!
5103    \brief A filtering function that is based on brute force estimates of node neighbor distance
5104    matrix.
5105    It is not finished because it makes no use of the neighbor distances to properly weigh the interpolation.
5106    It ends up being too slow because of the high memory load for computing OffS_out
5107 */
SUMA_Offset_GeomSmooth(SUMA_SurfaceObject * SO,int N_iter,float OffsetLim,float * fin_orig,int vpn,SUMA_INDEXING_ORDER d_order,float * fout_final_user,SUMA_COMM_STRUCT * cs)5108 float *SUMA_Offset_GeomSmooth( SUMA_SurfaceObject *SO, int N_iter, float OffsetLim, float *fin_orig,
5109                               int vpn, SUMA_INDEXING_ORDER d_order, float *fout_final_user,
5110                               SUMA_COMM_STRUCT *cs)
5111 {
5112 
5113    static char FuncName[]= {"SUMA_Offset_GeomSmooth"};
5114    float *fout_final=NULL, *fbuf=NULL, *fin_next=NULL, *fin=NULL, *fout=NULL;
5115    int niter=0, i, il,jl, j, ii,  noffs;
5116    struct  timeval start_time;
5117    float etime_GetOffset, weight_tot;
5118    SUMA_OFFSET_STRUCT *OffS_out=NULL;
5119    SUMA_Boolean LocalHead = NOPE;
5120 
5121    SUMA_ENTRY;
5122    if (!SO) { SUMA_SL_Err("NULL SO"); SUMA_RETURN(NULL); }
5123    if (!SO->FN) {
5124       SUMA_SL_Err("NULL SO->FN");
5125       SUMA_RETURN(NULL);
5126    }
5127    if (N_iter % 2) {
5128       SUMA_SL_Err("N_iter must be an even number\n");
5129       SUMA_RETURN(NULL);
5130    }
5131    if (vpn < 1) {
5132       SUMA_SL_Err("vpn < 1\n");
5133       SUMA_RETURN(NULL);
5134    }
5135 
5136    if (fout_final_user == fin_orig) {
5137       SUMA_SL_Err("fout_final_user == fin_orig");
5138       SUMA_RETURN(NULL);
5139    }
5140 
5141    if (!fout_final_user) { /* allocate for output */
5142       fout_final = (float *)SUMA_calloc(SO->N_Node * vpn, sizeof(float));
5143       if (!fout_final) {
5144          SUMA_SL_Crit("Failed to allocate for fout_final\n");
5145          SUMA_RETURN(NULL);
5146       }
5147    }else {
5148       fout_final = fout_final_user; /* pre-allocated */
5149    }
5150 
5151    /* allocate for buffer */
5152    fbuf = (float *)SUMA_calloc(SO->N_Node * vpn, sizeof(float));
5153    if (!fbuf) {
5154       SUMA_SL_Crit("Failed to allocate for fbuf\n");
5155       SUMA_RETURN(NULL);
5156    }
5157 
5158 
5159    if (cs && cs->Send) { /* send the first monster */
5160       if (!SUMA_SendToSuma (SO, cs, (void *)fin_orig, SUMA_NODE_XYZ, 1)) {
5161          SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCommunication halted.");
5162       }
5163    }
5164    SUMA_LH("Calculating OffS_out ...");
5165    OffS_out = SUMA_FormNeighbOffset (SO, OffsetLim, NULL, NULL, -1.0);
5166    fin_next = fin_orig;
5167    switch (d_order) {
5168       case SUMA_ROW_MAJOR:
5169          for (niter=0; niter < N_iter; ++niter) {
5170             if ( niter % 2 ) { /* odd */
5171                fin = fin_next; /* input from previous output buffer */
5172                fout = fout_final; /* results go into final vector */
5173                fin_next = fout_final; /* in the next iteration, the input is from fout_final */
5174             } else { /* even */
5175                /* input data is in fin_new */
5176                fin = fin_next;
5177                fout = fbuf; /* results go into buffer */
5178                fin_next = fbuf; /* in the next iteration, the input is from the buffer */
5179             }
5180 
5181             SUMA_etime(&start_time,0);
5182 
5183             for (i=0; i < SO->N_Node; ++i) {
5184                for (j=0; j < vpn; ++j) {
5185                   /* do the averaging using OffS_out NO ATTENTION IS GIVEN TO PROPER WEIGHING YET!*/
5186                   fout[i*vpn+j] = fin[i*vpn+j];
5187                   for (il=0; il<OffS_out[i].N_Neighb; ++il) {
5188                      fout[i*vpn+j] += fin[OffS_out[i].Neighb_ind[il]*vpn+j];
5189                   }
5190                   fout[i*vpn+j] /= (OffS_out[i].N_Neighb+1);
5191                }
5192             }
5193 
5194             etime_GetOffset = SUMA_etime(&start_time,1);
5195             fprintf( SUMA_STDERR,
5196                      "%s: Smoothing at dist %f took %f seconds for %d nodes.\n",
5197                            FuncName, OffsetLim, etime_GetOffset, SO->N_Node);
5198 
5199             if (cs && cs->Send) {
5200                if (!SUMA_SendToSuma (SO, cs, (void *)fout, SUMA_NODE_XYZ, 1)) {
5201                   SUMA_SL_Warn("Failed in SUMA_SendToSuma\n"
5202                                "Communication halted.");
5203                }
5204             }
5205          }
5206          break;
5207       case SUMA_COLUMN_MAJOR:
5208          SUMA_SL_Err("Column Major not implemented");
5209          SUMA_RETURN(NULL);
5210          break;
5211       default:
5212          SUMA_SL_Err("Bad Major, very bad.\n");
5213          SUMA_RETURN(NULL);
5214          break;
5215    }
5216 
5217    if (fbuf) SUMA_free(fbuf); fbuf = NULL;
5218 
5219    /* Have to free OffS_out */
5220    OffS_out = SUMA_free_NeighbOffset (SO, OffS_out);
5221 
5222    SUMA_RETURN(fout);
5223 }
5224 
SUMA_NN_GeomSmooth(SUMA_SurfaceObject * SO,int N_iter,float * fin_orig,int vpn,SUMA_INDEXING_ORDER d_order,float * fout_final_user,SUMA_COMM_STRUCT * cs,byte * nmask,byte strict_mask)5225 float *SUMA_NN_GeomSmooth( SUMA_SurfaceObject *SO, int N_iter, float *fin_orig,
5226                            int vpn, SUMA_INDEXING_ORDER d_order,
5227                            float *fout_final_user,
5228                            SUMA_COMM_STRUCT *cs, byte *nmask, byte strict_mask)
5229 {
5230    static char FuncName[]= {"SUMA_NN_GeomSmooth"};
5231    float *fout_final=NULL, *fbuf=NULL, *fin_next=NULL, *fin=NULL, *fout=NULL;
5232    int niter=0;
5233    float *xyzmask=NULL;
5234 
5235    SUMA_ENTRY;
5236 
5237    if (!SO) { SUMA_SL_Err("NULL SO"); SUMA_RETURN(NULL); }
5238    if (!SO->FN) {
5239       SUMA_SL_Err("NULL SO->FN");
5240       SUMA_RETURN(NULL);
5241    }
5242    if (N_iter % 2) {
5243       SUMA_SL_Err("N_iter must be an even number\n");
5244       SUMA_RETURN(NULL);
5245    }
5246    if (vpn < 1) {
5247       SUMA_SL_Err("vpn < 1\n");
5248       SUMA_RETURN(NULL);
5249    }
5250 
5251    if (fout_final_user == fin_orig) {
5252       SUMA_SL_Err("fout_final_user == fin_orig");
5253       SUMA_RETURN(NULL);
5254    }
5255 
5256    if (!fout_final_user) { /* allocate for output */
5257       fout_final = (float *)SUMA_calloc(SO->N_Node * vpn, sizeof(float));
5258       if (!fout_final) {
5259          SUMA_SL_Crit("Failed to allocate for fout_final\n");
5260          SUMA_RETURN(NULL);
5261       }
5262    }else {
5263       fout_final = fout_final_user; /* pre-allocated */
5264    }
5265 
5266    /* allocate for buffer */
5267    fbuf = (float *)SUMA_calloc(SO->N_Node * vpn, sizeof(float));
5268    if (!fbuf) {
5269       SUMA_SL_Crit("Failed to allocate for fbuf\n");
5270       SUMA_RETURN(NULL);
5271    }
5272 
5273 
5274    if (cs && cs->Send) { /* send the first monster */
5275       if (!SUMA_SendToSuma (SO, cs, (void *)fin_orig, SUMA_NODE_XYZ, 1)) {
5276          SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCommunication halted.");
5277       }
5278    }
5279 
5280    fin_next = fin_orig;
5281    switch (d_order) {
5282       case SUMA_ROW_MAJOR:
5283          for (niter=0; niter < N_iter; ++niter) {
5284             if ( niter % 2 ) { /* odd */
5285                fin = fin_next; /* input from previous output buffer */
5286                fout = fout_final; /* results go into final vector */
5287                fin_next = fout_final; /* in the next iteration, the input is
5288                                          from fout_final */
5289             } else { /* even */
5290                /* input data is in fin_new */
5291                fin = fin_next;
5292                fout = fbuf; /* results go into buffer */
5293                fin_next = fbuf; /* in the next iteration, the input is from
5294                                    the buffer */
5295             }
5296             fout = SUMA_SmoothAttr_Neighb ( fin, vpn*SO->N_Node,
5297                                       fout, SO->FN, vpn, nmask, strict_mask);
5298             if (cs && cs->Send) {
5299                if (!SUMA_SendToSuma (SO, cs, (void *)fout, SUMA_NODE_XYZ, 1)) {
5300                   SUMA_SL_Warn("Failed in SUMA_SendToSuma\n"
5301                                "Communication halted.");
5302                }
5303             }
5304          }
5305          break;
5306       case SUMA_COLUMN_MAJOR:
5307          SUMA_SL_Err("Column Major not implemented");
5308          SUMA_RETURN(NULL);
5309          break;
5310       default:
5311          SUMA_SL_Err("Bad Major, very bad.\n");
5312          SUMA_RETURN(NULL);
5313          break;
5314    }
5315 
5316    if (fbuf) SUMA_free(fbuf); fbuf = NULL;
5317 
5318    SUMA_RETURN(fout);
5319 }
5320 
SUMA_NN_GeomSmooth2(SUMA_SurfaceObject * SO,int N_iter,float * fin_orig,int vpn,SUMA_INDEXING_ORDER d_order,float * fout_final_user,SUMA_COMM_STRUCT * cs,byte * nmask,int MaskEnforce,SUMA_SurfaceObject * SOe,float * anchor_loc,float * anchor_wght)5321 float *SUMA_NN_GeomSmooth2( SUMA_SurfaceObject *SO, int N_iter, float *fin_orig,
5322                            int vpn, SUMA_INDEXING_ORDER d_order,
5323                            float *fout_final_user,
5324                            SUMA_COMM_STRUCT *cs, byte *nmask, int MaskEnforce,
5325                            SUMA_SurfaceObject *SOe,
5326                            float *anchor_loc, float *anchor_wght)
5327 {
5328    static char FuncName[]= {"SUMA_NN_GeomSmooth2"};
5329    float *fout_final=NULL, *fbuf=NULL, *fin_next=NULL, *fin=NULL, *fout=NULL;
5330    float *fin_initial=NULL, danch[3]={0.0, 0.0, 0.0};
5331    int niter=0, ii, jj, id;
5332    float P0[3], P1[3], P2[3], N0[3], *PP=NULL;
5333    float Points[2][3]={ {0.0, 0.0, 0.0}, {0.0, 0.0, 0.0} } ;
5334    float *xyzmask=NULL, frc_mv=0.1;
5335    SUMA_MT_INTERSECT_TRIANGLE *MTI = NULL;
5336    SUMA_SURF_NORM SN;
5337    SUMA_Boolean LocalHead = NOPE;
5338 
5339    SUMA_ENTRY;
5340 
5341 
5342    if (!SO || !SOe) { SUMA_SL_Err("NULL SO"); SUMA_RETURN(NULL); }
5343    if (!SO->FN) {
5344       SUMA_SL_Err("NULL SO->FN");
5345       SUMA_RETURN(NULL);
5346    }
5347    if (N_iter % 2) {
5348       SUMA_SL_Err("N_iter must be an even number\n");
5349       SUMA_RETURN(NULL);
5350    }
5351    if (vpn < 1) {
5352       SUMA_SL_Err("vpn < 1\n");
5353       SUMA_RETURN(NULL);
5354    }
5355    if (MaskEnforce < 2) MaskEnforce = 1;
5356 
5357    SUMA_LHv("NN_Geom2, N_iter %d, MaskEnforce %d, nmask %p\n",
5358             N_iter,  MaskEnforce, nmask);
5359 
5360    if (fout_final_user == fin_orig) {
5361       SUMA_SL_Err("fout_final_user == fin_orig");
5362       SUMA_RETURN(NULL);
5363    }
5364 
5365    if (anchor_loc) {
5366       if (!anchor_wght) {
5367          SUMA_S_Err("If anchor_loc, then need anchor_wght");
5368          SUMA_RETURN(NULL);
5369       }
5370    }
5371 
5372    if (!fout_final_user) { /* allocate for output */
5373       fout_final = (float *)SUMA_calloc(SO->N_Node * vpn, sizeof(float));
5374       if (!fout_final) {
5375          SUMA_SL_Crit("Failed to allocate for fout_final\n");
5376          SUMA_RETURN(NULL);
5377       }
5378    }else {
5379       fout_final = fout_final_user; /* pre-allocated */
5380    }
5381 
5382    if (MaskEnforce > 1) {
5383       if (!(fin_initial=(float *)SUMA_calloc(SO->N_Node * vpn, sizeof(float)))) {
5384          SUMA_SL_Crit("Failed to allocate for fin_initial\n");
5385          SUMA_RETURN(NULL);
5386       }
5387       memcpy(fin_initial, fin_orig, SO->N_Node * vpn * sizeof(float));
5388    }
5389 
5390 
5391    /* allocate for buffer */
5392    fbuf = (float *)SUMA_calloc(SO->N_Node * vpn, sizeof(float));
5393    if (!fbuf) {
5394       SUMA_SL_Crit("Failed to allocate for fbuf\n");
5395       SUMA_RETURN(NULL);
5396    }
5397 
5398 
5399    if (cs && cs->Send) { /* send the first monster */
5400       if (!SUMA_SendToSuma (SO, cs, (void *)fin_orig, SUMA_NODE_XYZ, 1)) {
5401          SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCommunication halted.");
5402       }
5403    }
5404 
5405    memset(&SN, 0, sizeof(SUMA_SURF_NORM));
5406    fin_next = fin_orig;
5407    switch (d_order) {
5408       case SUMA_ROW_MAJOR:
5409          for (niter=0; niter < N_iter; ++niter) {
5410             frc_mv = (float)(niter+1.0)/N_iter;
5411             if ( niter % 2 ) { /* odd */
5412                fin = fin_next; /* input from previous output buffer */
5413                fout = fout_final; /* results go into final vector */
5414                fin_next = fout_final; /* in the next iteration, the input is
5415                                          from fout_final */
5416             } else { /* even */
5417                /* input data is in fin_new */
5418                fin = fin_next;
5419                fout = fbuf; /* results go into buffer */
5420                fin_next = fbuf; /* in the next iteration, the input is from
5421                                    the buffer */
5422             }
5423             if (vpn != 3) { SUMA_S_Err("No no no"); SUMA_RETURN(NULL); }
5424             fout = SUMA_SmoothAttr_Neighb_wght ( fin, vpn*SO->N_Node,
5425                               anchor_wght, fout, SO->FN, vpn, NULL, 0);
5426             if (fin_initial && !((niter+1) % MaskEnforce)) {
5427                SUMA_LHv("Facelift %d (%d)\n", niter, MaskEnforce);
5428                /* recompute normals */
5429                if (SN.FaceNormList) SUMA_free(SN.FaceNormList);
5430                if (SN.NodeNormList) SUMA_free(SN.NodeNormList);
5431                SN = SUMA_SurfNorm(fout, SO->N_Node,
5432                                   SO->FaceSetList, SO->N_FaceSet );
5433                for (ii=0; ii<SO->N_Node; ++ii) {
5434                   id = vpn*ii;
5435                   if (nmask && !nmask[ii]) {
5436                      for (jj=0; jj<vpn; ++jj)
5437                         fout[id+jj] = fin_initial[id+jj];
5438                   } else {
5439                      /* move node along proj dir until it hits enclosing surf*/
5440                      P0[0] = fout[id];
5441                      P0[1] = fout[id+1];
5442                      P0[2] = fout[id+2];
5443                      /* compute the proj direction */
5444                      if (anchor_loc) {
5445                         danch[0] = anchor_loc[id  ] - P0[0];
5446                         danch[1] = anchor_loc[id+1] - P0[1];
5447                         danch[2] = anchor_loc[id+2] - P0[2];
5448                         N0[0] = (1.0-anchor_wght[ii])*SN.NodeNormList[id  ]
5449                                                  + anchor_wght[ii]*danch[0];
5450                         N0[1] = (1.0-anchor_wght[ii])*SN.NodeNormList[id+1]
5451                                                  + anchor_wght[ii]*danch[1];
5452                         N0[2] = (1.0-anchor_wght[ii])*SN.NodeNormList[id+2]
5453                                                  + anchor_wght[ii]*danch[2];
5454                      } else { /* just normals */
5455                         N0[0] = SN.NodeNormList[id  ];
5456                         N0[1] = SN.NodeNormList[id+1];
5457                         N0[2] = SN.NodeNormList[id+2];
5458                      }
5459                      SUMA_POINT_AT_DISTANCE(N0, P0, 90000, Points);
5460                      P1[0] = Points[0][0];
5461                      P1[1] = Points[0][1];
5462                      P1[2] = Points[0][2];
5463                      P2[0] = Points[1][0];
5464                      P2[1] = Points[1][1];
5465                      P2[2] = Points[1][2];
5466                      /* now determine the distance along normal */
5467                      MTI = SUMA_MT_intersect_triangle(P0, P1,
5468                                              SOe->NodeList, SOe->N_Node,
5469                                              SOe->FaceSetList, SOe->N_FaceSet,
5470                                              MTI, 1);
5471                      if (MTI->N_poshits ==0) {
5472                         /* go backwards VERY inefficient, consider writing
5473                            intersection function that keeps separate min/max */
5474                         MTI = SUMA_MT_intersect_triangle(P0, P2,
5475                                              SOe->NodeList, SOe->N_Node,
5476                                              SOe->FaceSetList, SOe->N_FaceSet,
5477                                              MTI, 1);
5478                      }
5479                      if (MTI->N_poshits ==0) { /* Nothing */
5480                         SUMA_S_Warnv("No hits for node %d\n", ii);
5481                      } else {
5482                         for (jj=0; jj<vpn; ++jj)
5483                            fout[id+jj] = frc_mv*MTI->P[jj] +
5484                                          (1.0-frc_mv)*fout[id+jj];
5485                      }
5486                   }
5487                }
5488             }
5489 
5490             if (cs && cs->Send) {
5491                if (!SUMA_SendToSuma (SO, cs, (void *)fout, SUMA_NODE_XYZ, 1)) {
5492                   SUMA_SL_Warn("Failed in SUMA_SendToSuma\n"
5493                                "Communication halted.");
5494                }
5495             }
5496          }
5497          if (MTI) MTI = SUMA_Free_MT_intersect_triangle(MTI);
5498          break;
5499       case SUMA_COLUMN_MAJOR:
5500          SUMA_SL_Err("Column Major not implemented");
5501          SUMA_RETURN(NULL);
5502          break;
5503       default:
5504          SUMA_SL_Err("Bad Major, very bad.\n");
5505          SUMA_RETURN(NULL);
5506          break;
5507    }
5508 
5509    if (fbuf) SUMA_free(fbuf); fbuf = NULL;
5510    if (fin_initial) SUMA_free(fin_initial); fin_initial=NULL;
5511    SUMA_RETURN(fout);
5512 }
5513 
SUMA_NN_GeomSmooth3(SUMA_SurfaceObject * SO,int N_iter,float * fin_orig,int vpn,SUMA_INDEXING_ORDER d_order,float * fout_final_user,SUMA_COMM_STRUCT * cs,byte * nmask,int MaskEnforce,SUMA_SurfaceObject * SOe,float * anchor_wght,THD_3dim_dataset * voxelize)5514 float *SUMA_NN_GeomSmooth3( SUMA_SurfaceObject *SO, int N_iter, float *fin_orig,
5515                            int vpn, SUMA_INDEXING_ORDER d_order,
5516                            float *fout_final_user,
5517                            SUMA_COMM_STRUCT *cs, byte *nmask, int MaskEnforce,
5518                            SUMA_SurfaceObject *SOe,
5519                            float *anchor_wght, THD_3dim_dataset *voxelize)
5520 {
5521    static char FuncName[]= {"SUMA_NN_GeomSmooth3"};
5522    float *fout_final=NULL, *fbuf=NULL, *fin_next=NULL, *fin=NULL, *fout=NULL;
5523    float *fin_initial=NULL, danch[3]={0.0, 0.0, 0.0};
5524    int niter=0, ii, jj, id;
5525    float P0[3], P1[3], P2[3], N0[3], *PP=NULL;
5526    float Points[2][3]={ {0.0, 0.0, 0.0}, {0.0, 0.0, 0.0} } ;
5527    float *xyzmask=NULL, frc_mv=0.1;
5528    SUMA_MT_INTERSECT_TRIANGLE *MTI = NULL;
5529    SUMA_SURF_NORM SN;
5530    float expan = 0.0, expan_init = 0.01, sdist = 0.0;
5531    SUMA_Boolean LocalHead = NOPE;
5532 
5533    SUMA_ENTRY;
5534 
5535 
5536    if (!SO || !SOe) { SUMA_SL_Err("NULL SO"); SUMA_RETURN(NULL); }
5537    if (!SO->FN) {
5538       SUMA_SL_Err("NULL SO->FN");
5539       SUMA_RETURN(NULL);
5540    }
5541    if (N_iter % 2) {
5542       SUMA_SL_Err("N_iter must be an even number\n");
5543       SUMA_RETURN(NULL);
5544    }
5545    if (vpn < 1) {
5546       SUMA_SL_Err("vpn < 1\n");
5547       SUMA_RETURN(NULL);
5548    }
5549    if (MaskEnforce < 2) MaskEnforce = 1;
5550 
5551    SUMA_LHv("NN_Geom3, N_iter %d, MaskEnforce %d, nmask %p\n",
5552             N_iter,  MaskEnforce, nmask);
5553 
5554    if (fout_final_user == fin_orig) {
5555       SUMA_SL_Err("fout_final_user == fin_orig");
5556       SUMA_RETURN(NULL);
5557    }
5558 
5559 
5560    if (!fout_final_user) { /* allocate for output */
5561       fout_final = (float *)SUMA_calloc(SO->N_Node * vpn, sizeof(float));
5562       if (!fout_final) {
5563          SUMA_SL_Crit("Failed to allocate for fout_final\n");
5564          SUMA_RETURN(NULL);
5565       }
5566    }else {
5567       fout_final = fout_final_user; /* pre-allocated */
5568    }
5569 
5570    if (MaskEnforce > 1) {
5571       if (!(fin_initial=(float *)SUMA_calloc(SO->N_Node * vpn, sizeof(float)))) {
5572          SUMA_SL_Crit("Failed to allocate for fin_initial\n");
5573          SUMA_RETURN(NULL);
5574       }
5575       memcpy(fin_initial, fin_orig, SO->N_Node * vpn * sizeof(float));
5576    }
5577 
5578 
5579    /* allocate for buffer */
5580    fbuf = (float *)SUMA_calloc(SO->N_Node * vpn, sizeof(float));
5581    if (!fbuf) {
5582       SUMA_SL_Crit("Failed to allocate for fbuf\n");
5583       SUMA_RETURN(NULL);
5584    }
5585 
5586 
5587    if (cs && cs->Send) { /* send the first monster */
5588       if (!SUMA_SendToSuma (SO, cs, (void *)fin_orig, SUMA_NODE_XYZ, 1)) {
5589          SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCommunication halted.");
5590       }
5591    }
5592 
5593    if (LocalHead) {
5594       SUMA_S_Warn("Ad-hoc constraints on expansion, deal with this.\n");
5595    }
5596    memset(&SN, 0, sizeof(SUMA_SURF_NORM));
5597    fin_next = fin_orig;
5598    switch (d_order) {
5599       case SUMA_ROW_MAJOR:
5600          for (niter=0; niter < N_iter; ++niter) {
5601             frc_mv = (float)(niter+1.0)/N_iter;
5602             if ( niter % 2 ) { /* odd */
5603                fin = fin_next; /* input from previous output buffer */
5604                fout = fout_final; /* results go into final vector */
5605                fin_next = fout_final; /* in the next iteration, the input is
5606                                          from fout_final */
5607             } else { /* even */
5608                /* input data is in fin_new */
5609                fin = fin_next;
5610                fout = fbuf; /* results go into buffer */
5611                fin_next = fbuf; /* in the next iteration, the input is from
5612                                    the buffer */
5613             }
5614             if (vpn != 3) { SUMA_S_Err("No no no"); SUMA_RETURN(NULL); }
5615             fout = SUMA_SmoothAttr_Neighb_wght ( fin, vpn*SO->N_Node,
5616                                        NULL, fout, SO->FN, vpn, NULL, 0);
5617             /* recompute normals and expand along the normal*/
5618             if (SN.FaceNormList) SUMA_free(SN.FaceNormList);
5619             if (SN.NodeNormList) SUMA_free(SN.NodeNormList);
5620             SN = SUMA_SurfNorm(fout, SO->N_Node,
5621                                SO->FaceSetList, SO->N_FaceSet );
5622             for (ii=0; niter > 400 && ii<SO->N_Node; ++ii) {
5623                id = vpn*ii;
5624                expan = expan_init;
5625                sdist = 0.0;
5626                if (!nmask || (nmask && !nmask[ii])) {
5627                   /* modulate expansion by position in volume of
5628                   voxelization of an enclosing surface */
5629                   if (voxelize) {
5630                      sdist = THD_get_voxel_dicom(voxelize,
5631                                          fout[id], fout[id+1], fout[id+2],0);
5632                      if (sdist <= 0.0) {
5633                         expan = 0.0;
5634                      } else {
5635                         if (sdist < expan) expan = sdist/2.0;
5636                      }
5637                      if (expan > 0.0 && expan < 0.01) expan = 0.01;
5638                   }
5639                   for (jj=0; jj<vpn; ++jj)
5640                      fout[id+jj] += expan * SN.NodeNormList[id+jj];
5641                }
5642             }
5643 
5644             if (anchor_wght) {
5645                SUMA_LH("Anchoring...");
5646                /* allow move based on weight */
5647                for (ii=0; ii<SO->N_Node; ++ii) {
5648                   id = vpn*ii;
5649                   if (!nmask || (nmask && !nmask[ii])) {
5650                      for (jj=0; jj<vpn; ++jj)
5651                         fout[id+jj] = anchor_wght[ii]*fin_initial[id+jj] +
5652                                       (1.0-anchor_wght[ii])*fout[id+jj];
5653                   }
5654                }
5655             }
5656 
5657 
5658             if (fin_initial && !((niter+1) % MaskEnforce)) {
5659                if (cs && cs->Send) {
5660                   if (!SUMA_SendToSuma (SO, cs, (void *)fout,
5661                                         SUMA_NODE_XYZ, 1)) {
5662                      SUMA_SL_Warn("Failed in SUMA_SendToSuma\n"
5663                                   "Communication halted.");
5664                   }
5665                }
5666                SUMA_LHv("Facelift %d (%d)\n", niter, MaskEnforce);
5667                if (LocalHead) {
5668                   char sbuf[256];
5669                   THD_force_ok_overwrite(1) ;
5670                   sprintf(sbuf,"exp_prelift.%03d",niter);
5671                   SUMA_Save_Surface_Object_Wrap(sbuf, NULL, SO,
5672                                           SUMA_GIFTI, SUMA_ASCII, NULL);
5673                }
5674                /* recompute normals */
5675                if (SN.FaceNormList) SUMA_free(SN.FaceNormList);
5676                if (SN.NodeNormList) SUMA_free(SN.NodeNormList);
5677                SN = SUMA_SurfNorm(fout, SO->N_Node,
5678                                   SO->FaceSetList, SO->N_FaceSet );
5679                #if 0
5680                /* Slows things down but might come in handy */
5681                SN.NodeNormList = SUMA_SmoothAttr_Neighb_Rec (SN.NodeNormList,
5682                                                 SO->N_Node*3, SN.NodeNormList,
5683                                                 SO->FN, 3, 6, NULL, 0);
5684                #endif
5685                for (ii=0; ii<SO->N_Node; ++ii) {
5686                   id = vpn*ii;
5687                   if (nmask && !nmask[ii]) {
5688                      for (jj=0; jj<vpn; ++jj)
5689                         fout[id+jj] = fin_initial[id+jj];
5690                   } else {
5691                      /* move node along proj dir until it hits enclosing surf*/
5692                      P0[0] = fout[id];
5693                      P0[1] = fout[id+1];
5694                      P0[2] = fout[id+2];
5695                      /* compute the proj direction */
5696                      N0[0] = SN.NodeNormList[id  ];
5697                      N0[1] = SN.NodeNormList[id+1];
5698                      N0[2] = SN.NodeNormList[id+2];
5699 
5700                      SUMA_POINT_AT_DISTANCE(N0, P0, 90000, Points);
5701                      P1[0] = Points[0][0];
5702                      P1[1] = Points[0][1];
5703                      P1[2] = Points[0][2];
5704                      P2[0] = Points[1][0];
5705                      P2[1] = Points[1][1];
5706                      P2[2] = Points[1][2];
5707                      if (ii==SUMA_SSidbg) {
5708                         SUMA_S_Notev(
5709                "Node %d, iter %d at: %f %f %f, normal %f %f %f\n"
5710                "expanding potential along: [%f %f %f] --> [%f %f %f] or \n"
5711                "                           [%f %f %f] --> [%f %f %f]",
5712                         ii, niter, fout[id], fout[id+1], fout[id+2],
5713                         N0[0], N0[1], N0[2],
5714                         P0[0], P0[1], P0[2], P1[0], P1[1], P1[2],
5715                         P0[0], P0[1], P0[2], P2[0], P2[1], P2[2]);
5716                      }
5717                      /* now determine the distance along normal */
5718                      MTI = SUMA_MT_intersect_triangle(P0, P1,
5719                                              SOe->NodeList, SOe->N_Node,
5720                                              SOe->FaceSetList, SOe->N_FaceSet,
5721                                              MTI, 1);
5722                      if (ii==SUMA_SSidbg) {
5723                         SUMA_Show_MT_intersect_triangle(MTI, NULL,
5724                                           "Forward intersection result\n");
5725                      }
5726                      if (MTI->N_poshits ==0) { /* go backwards
5727                                 Very inefficient, consider new intersection
5728                                 function that keeps minima from both directions*/
5729                         if (ii==SUMA_SSidbg){
5730                            SUMA_S_Warnv("No hit for node %d, going backwards!\n",
5731                                     ii);
5732                         }
5733                         MTI = SUMA_MT_intersect_triangle(P0, P2,
5734                                              SOe->NodeList, SOe->N_Node,
5735                                              SOe->FaceSetList, SOe->N_FaceSet,
5736                                              MTI,1);
5737                         if (ii==SUMA_SSidbg) {
5738                            SUMA_S_Note("Reverse intersection result");
5739                            SUMA_Show_MT_intersect_triangle(MTI, NULL,
5740                                                 "Reverse intersection result\n");
5741                         }
5742                      }
5743                      if (MTI->N_poshits ==0) { /* Nothing */
5744                         SUMA_S_Warnv("No hits for node %d\n", ii);
5745                      } else {
5746                         if (ii==SUMA_SSidbg) {
5747                            SUMA_S_Notev(
5748                         "Node %d, hit at %f %f %f, expansion fraction: %f\n",
5749                               ii, MTI->P[0], MTI->P[1], MTI->P[2], frc_mv);
5750                         }
5751                         for (jj=0; jj<vpn; ++jj)
5752                            fout[id+jj] = frc_mv*MTI->P[jj] +
5753                                          (1.0-frc_mv)*fout[id+jj];
5754                      }
5755                   }
5756                }
5757                if (LocalHead) {
5758                   char sbuf[256];
5759                   THD_force_ok_overwrite(1) ;
5760                   sprintf(sbuf,"exp_postlift.%03d",niter);
5761                   SUMA_Save_Surface_Object_Wrap(sbuf, NULL, SO,
5762                                           SUMA_GIFTI, SUMA_ASCII, NULL);
5763                }
5764                if (cs && cs->Send) {
5765                   if (!SUMA_SendToSuma (SO, cs, (void *)fout,
5766                                         SUMA_NODE_XYZ, 1)) {
5767                      SUMA_SL_Warn("Failed in SUMA_SendToSuma\n"
5768                                   "Communication halted.");
5769                   }
5770                }
5771             } else {
5772                if (cs && cs->Send) {
5773                   if (!SUMA_SendToSuma (SO, cs, (void *)fout,
5774                                         SUMA_NODE_XYZ, 1)) {
5775                      SUMA_SL_Warn("Failed in SUMA_SendToSuma\n"
5776                                   "Communication halted.");
5777                   }
5778                }
5779             }
5780          }
5781          if (MTI) MTI = SUMA_Free_MT_intersect_triangle(MTI);
5782          break;
5783       case SUMA_COLUMN_MAJOR:
5784          SUMA_SL_Err("Column Major not implemented");
5785          SUMA_RETURN(NULL);
5786          break;
5787       default:
5788          SUMA_SL_Err("Bad Major, very bad.\n");
5789          SUMA_RETURN(NULL);
5790          break;
5791    }
5792 
5793    if (fbuf) SUMA_free(fbuf); fbuf = NULL;
5794    if (fin_initial) SUMA_free(fin_initial); fin_initial=NULL;
5795 
5796    SUMA_RETURN(fout);
5797 }
5798 
5799 /*!
5800    \brief Filter data defined on the surface using M.K. Chung et al.'s method (Neuroimage 03)
5801    dm_smooth = SUMA_Chung_Smooth (SO, wgt, N_iter, FWHM, fin, vpn, d_order, fout_user, SUMA_COMM_STRUCT *cs);
5802 
5803    \param SO (SUMA_SurfaceObject *) Surface object with valid
5804                                     SO->NodeList, SO->FaceSetList and SO->FN
5805    \param wgt (float **) interpolation weights for each node.
5806                            These weights are obtained from SUMA_Chung_Smooth_Weights
5807    \param N_iter (int) number of smoothing iterations (must be even, > 1)
5808    \param FWHM (float) Full Width at Half Maximum of equivalent Gaussian filter
5809    \param fin (float *) vector containing node data. The length of this vector
5810                         is vpn x SO->N_Node , where vpn is the number of values
5811                         per node.
5812    \param vpn (int) the numberof values per node in fin
5813    \param d_order (SUMA_INDEXING_ORDER) Indicates how multiple values per node are stored in fin
5814                         SUMA_ROW_MAJOR: The data in fin is stored in *** Row Major *** order.
5815                         The ith value (start at 0) for node n is at index fin[vpn*n+i]
5816                         SUMA_COLUMN_MAJOR: The data in fin is stored in *** Column Major *** order.
5817                         The ith (start at 0) value for node n is at index fin[n+SO->N_Node*i];
5818                         etc...
5819    \param fout_user (float *) a pointer to the vector where the smoothed version of fin will reside
5820                         You can pass NULL and the function will do the allocation for you and return
5821                         the pointer to the smoothed data in dm_smooth.
5822                         If you already have space allocated for the result, then pass the pointer
5823                         in fout_user and save on allocation time and space. In that case, dm_smooth
5824                         is equal to fout_user.
5825                         Either way, you are responsible for freeing memory pointed to by dm_smooth
5826                         DO NOT PASS fout_user = fin
5827    \param cs (SUMA_COMM_STRUCT *) A pointer to the structure containing info for taking to SUMA
5828 
5829    \return dm_smooth (float *) A pointer to the smoothed data (vpn * SO->N_Node values).
5830                         You will have to free the memory allocated for dm_smooth yourself.
5831 
5832    \sa SUMA_Chung_Smooth_Weights
5833 
5834 */
5835 
5836 
SUMA_Chung_Smooth(SUMA_SurfaceObject * SO,float ** wgt,int N_iter,float FWHM,float * fin_orig,int vpn,SUMA_INDEXING_ORDER d_order,float * fout_final_user,SUMA_COMM_STRUCT * cs,byte * nmask,byte strict_mask)5837 float * SUMA_Chung_Smooth (SUMA_SurfaceObject *SO, float **wgt,
5838                            int N_iter, float FWHM, float *fin_orig,
5839                            int vpn, SUMA_INDEXING_ORDER d_order,
5840                            float *fout_final_user,
5841                            SUMA_COMM_STRUCT *cs, byte *nmask, byte strict_mask)
5842 {
5843    static char FuncName[]={"SUMA_Chung_Smooth"};
5844    float *fout_final = NULL, *fbuf=NULL, *fin=NULL, *fout=NULL, *fin_next = NULL;
5845    float delta_time, fp, dfp, fpj, minfn=0.0, maxfn=0.0;
5846    int n , k, j, niter, vnk, os, jj, nj=-1;
5847    SUMA_Boolean LocalHead = NOPE;
5848 
5849    SUMA_ENTRY;
5850 
5851    if (N_iter % 2) {
5852       SUMA_SL_Err("N_iter must be an even number\n");
5853       SUMA_RETURN(NULL);
5854    }
5855 
5856    if (!SO || !wgt || !fin_orig) {
5857       SUMA_SL_Err("NULL SO or wgt or fin_orig\n");
5858       SUMA_RETURN(NULL);
5859    }
5860    if (!SO->FN) {
5861       SUMA_SL_Err("NULL SO->FN\n");
5862       SUMA_RETURN(NULL);
5863    }
5864 
5865    if (vpn < 1) {
5866       SUMA_SL_Err("vpn < 1\n");
5867       SUMA_RETURN(NULL);
5868    }
5869 
5870    if (fout_final_user == fin_orig) {
5871       SUMA_SL_Err("fout_final_user == fin_orig");
5872       SUMA_RETURN(NULL);
5873    }
5874 
5875    if (!fout_final_user) { /* allocate for output */
5876       fout_final = (float *)SUMA_calloc(SO->N_Node * vpn, sizeof(float));
5877       if (!fout_final) {
5878          SUMA_SL_Crit("Failed to allocate for fout_final\n");
5879          SUMA_RETURN(NULL);
5880       }
5881    }else {
5882       fout_final = fout_final_user; /* pre-allocated */
5883    }
5884 
5885    /* allocate for buffer */
5886    fbuf = (float *)SUMA_calloc(SO->N_Node * vpn, sizeof(float));
5887    if (!fbuf) {
5888       SUMA_SL_Crit("Failed to allocate for fbuf\n");
5889       SUMA_RETURN(NULL);
5890    }
5891 
5892 
5893    if (cs && cs->Send) { /* send the first monster */
5894       if (!SUMA_SendToSuma (SO, cs, (void *)fin_orig, SUMA_NODE_RGBAb, 1)) {
5895          SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCommunication halted.");
5896       }
5897    }
5898 
5899    fin_next = fin_orig;
5900    delta_time= (FWHM * FWHM)/(16*N_iter*log(2));
5901    switch (d_order) {
5902       case SUMA_COLUMN_MAJOR:
5903          for (niter=0; niter < N_iter; ++niter) {
5904             if ( niter % 2 ) { /* odd */
5905                fin = fin_next; /* input from previous output buffer */
5906                fout = fout_final; /* results go into final vector */
5907                fin_next = fout_final; /* in the next iteration, the input is from fout_final */
5908             } else { /* even */
5909                /* input data is in fin_new */
5910                fin = fin_next;
5911                fout = fbuf; /* results go into buffer */
5912                fin_next = fbuf; /* in the next iteration, the input is from the buffer */
5913             }
5914             for (k=0; k < vpn; ++k) {
5915                os = SO->N_Node*k;   /* node value indexing offset */
5916                for (n=0; n < SO->N_Node; ++n) {
5917                   vnk = n+os; /* index of kth value at node n */
5918                   fp = fin[vnk]; /* kth value at node n */
5919                   dfp = 0.0;
5920                   if (!nmask) {
5921                      if (SO->FN->N_Neighb[n]) minfn = maxfn = fin[SO->FN->FirstNeighb[n][0]+os];
5922                      for (j=0; j < SO->FN->N_Neighb[n]; ++j) {
5923                         fpj = fin[SO->FN->FirstNeighb[n][j]+os]; /* value at jth neighbor of n */
5924                         if (fpj < minfn) minfn = fpj;
5925                         if (fpj > maxfn) maxfn = fpj;
5926                         dfp += wgt[n][j] * (fpj - fp);
5927                      }/* for j*/
5928                      fout[vnk] = fin[vnk] + delta_time * dfp;
5929                      if (fout[vnk] < minfn) fout[vnk] = minfn;
5930                      if (fout[vnk] > maxfn) fout[vnk] = maxfn;
5931                   } else { /* masking potential */
5932                      if (nmask[n]) {
5933                         if (SO->FN->N_Neighb[n]) {
5934                            jj = 0;
5935                            if (strict_mask) { /* consider only neighbors that are in mask */
5936                               do {
5937                                  minfn = maxfn = fin[SO->FN->FirstNeighb[n][jj]+os]; ++jj;
5938                               } while (!nmask[SO->FN->FirstNeighb[n][jj]] && jj < SO->FN->N_Neighb[n]);
5939                            } else { /* consider all neighbors */
5940                               minfn = maxfn = fin[SO->FN->FirstNeighb[n][jj]+os];
5941                            }
5942                         }
5943                         for (j=0; j < SO->FN->N_Neighb[n]; ++j) {
5944                            nj = SO->FN->FirstNeighb[n][j];
5945                            if (nmask[nj] || !strict_mask) { /* consider only neighbors that are in mask if strict_mask is 1*/
5946                               fpj = fin[nj+os]; /* value at jth neighbor of n */
5947                               if (fpj < minfn) minfn = fpj;
5948                               if (fpj > maxfn) maxfn = fpj;
5949                               dfp += wgt[n][j] * (fpj - fp);
5950                            }
5951                         }/* for j*/
5952                         fout[vnk] = fin[vnk] + delta_time * dfp;
5953                         if (fout[vnk] < minfn) fout[vnk] = minfn;
5954                         if (fout[vnk] > maxfn) fout[vnk] = maxfn;
5955                      } else {
5956                         fout[vnk] = fin[vnk];
5957                      }
5958                   }
5959                }/* for n */
5960 
5961                if (cs && cs->Send) {
5962                   if (!SUMA_SendToSuma (SO, cs, (void *)fout, SUMA_NODE_RGBAb, 1)) {
5963                      SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCommunication halted.");
5964                   }
5965                }
5966             } /* for k */
5967          }/* for niter */
5968          break;
5969       case SUMA_ROW_MAJOR:
5970          SUMA_SL_Err("Row Major not implemented");
5971          SUMA_RETURN(NULL);
5972          break;
5973       default:
5974          SUMA_SL_Err("Bad Major, very bad.\n");
5975          SUMA_RETURN(NULL);
5976          break;
5977    }
5978 
5979    if (fbuf) SUMA_free(fbuf); fbuf = NULL;
5980 
5981    SUMA_RETURN(fout);
5982 }
5983 
5984 
5985 /*!
5986    \brief A version of SUMA_Chung_Smooth that works on SUMA_DSET rather than
5987    a float array.
5988    NOTE: dset is changed on output (blurred values) but idcode
5989    remains unchanged
5990 */
SUMA_Chung_Smooth_dset(SUMA_SurfaceObject * SO,float ** wgt,int N_iter,float FWHM,SUMA_DSET * dset,SUMA_COMM_STRUCT * cs,byte * nmask,byte strict_mask)5991 SUMA_Boolean SUMA_Chung_Smooth_dset (SUMA_SurfaceObject *SO, float **wgt,
5992                            int N_iter, float FWHM, SUMA_DSET *dset,
5993                            SUMA_COMM_STRUCT *cs, byte *nmask, byte strict_mask)
5994 {
5995    static char FuncName[]={"SUMA_Chung_Smooth_dset"};
5996    float *fout_final = NULL, *fbuf=NULL, *fin=NULL, *fout=NULL, *fin_next = NULL, *fin_orig = NULL;
5997    float delta_time, fp, dfp, fpj, minfn=0.0, maxfn=0.0;
5998    int n , k, j, niter, jj, *icols=NULL, N_icols, N_nmask, nj=-1;
5999    byte *bfull=NULL;
6000    SUMA_Boolean LocalHead = NOPE;
6001 
6002    SUMA_ENTRY;
6003 
6004    if (N_iter % 2) {
6005       SUMA_SL_Err("N_iter must be an even number\n");
6006       SUMA_RETURN(NOPE);
6007    }
6008 
6009    if (!SO || !wgt || !dset) {
6010       SUMA_SL_Err("NULL SO or wgt or dset\n");
6011       SUMA_RETURN(NOPE);
6012    }
6013 
6014    if (!SO->FN) {
6015       SUMA_SL_Err("NULL SO->FN\n");
6016       SUMA_RETURN(NOPE);
6017    }
6018 
6019    /* what columns can we process ?*/
6020    icols = SUMA_FindNumericDataDsetCols(dset, &N_icols);
6021 
6022    if (N_icols <= 0) {
6023       SUMA_SL_Err("No approriate data columns in dset");
6024       SUMA_RETURN(NOPE);
6025    }
6026 
6027    /* allocate for buffer and output */
6028    fbuf = (float *)SUMA_calloc(SO->N_Node, sizeof(float));
6029    fout_final = (float *)SUMA_calloc(SO->N_Node, sizeof(float));
6030    if (!fbuf || !fout_final) {
6031       SUMA_SL_Crit("Failed to allocate for fbuf and fout_final\n");
6032       SUMA_RETURN(NOPE);
6033    }
6034 
6035    if (cs->Send && N_icols > 1) {
6036       SUMA_S_Warn("Only 1st data column will be sent to SUMA in talk_mode.");
6037    }
6038 
6039    delta_time= (FWHM * FWHM)/(16*N_iter*log(2));
6040 
6041    /* Begin filtering operation for each column */
6042    for (k=0; k < N_icols; ++k) {
6043       /* get a float copy of the data column */
6044       fin_orig = SUMA_DsetCol2Float (dset, icols[k], 1);
6045       if (!fin_orig) {
6046          SUMA_SL_Crit("Failed to get copy of column. Woe to thee!");
6047          SUMA_RETURN(NOPE);
6048       }
6049       /* make sure column is not sparse, one value per node */
6050       if (k==0) {
6051          SUMA_LH( "Special case k = 0, going to SUMA_MakeSparseColumnFullSorted");
6052          bfull = NULL;
6053          if (!SUMA_MakeSparseColumnFullSorted(&fin_orig, SDSET_VECFILLED(dset), 0.0, &bfull, dset, SO->N_Node)) {
6054             SUMA_S_Err("Failed to get full column vector");
6055             SUMA_RETURN(NOPE);
6056          }
6057          if (bfull) {
6058             SUMA_LH( "Something was filled in SUMA_MakeSparseColumnFullSorted\n" );
6059             /* something was filled in good old SUMA_MakeSparseColumnFullSorted */
6060             if (nmask) {   /* combine bfull with nmask */
6061                SUMA_LH( "Merging masks\n" );
6062                for (jj=0; jj < SO->N_Node; ++jj) { if (nmask[jj] && !bfull[jj]) nmask[jj] = 0; }
6063             } else { nmask = bfull; }
6064          }
6065          if (nmask) {
6066             N_nmask = 0;
6067             for (n=0; n<SO->N_Node; ++n) { if (nmask[n]) ++ N_nmask; }
6068             SUMA_LHv("Blurring with node mask (%d nodes in mask)\n", N_nmask);
6069             if (!N_nmask) {
6070                SUMA_S_Warn("Empty mask, nothing to do");
6071                goto CLEANUP;
6072             }
6073          }
6074       } else {
6075          SUMA_LH( "going to SUMA_MakeSparseColumnFullSorted");
6076          if (!SUMA_MakeSparseColumnFullSorted(&fin_orig, SDSET_VECFILLED(dset), 0.0, NULL, dset, SO->N_Node)) {
6077             SUMA_S_Err("Failed to get full column vector");
6078             SUMA_RETURN(NOPE);
6079          }
6080          /* no need for reworking nmask and bfull for each column...*/
6081       }
6082 
6083       if (cs->Send && k == 0) { /* send the first monster */
6084          if (!SUMA_SendToSuma (SO, cs, (void *)fin_orig, SUMA_NODE_RGBAb, 1)) {
6085             SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCommunication halted.");
6086          }
6087       }
6088       /* filter this column for each of the iterations */
6089       fin_next = fin_orig;
6090       for (niter=0; niter < N_iter; ++niter) {
6091          SUMA_LHv("niter %d\n", niter);
6092          if ( niter % 2 ) { /* odd */
6093             fin = fin_next; /* input from previous output buffer */
6094             fout = fout_final; /* results go into final vector */
6095             fin_next = fout_final; /* in the next iteration, the input is from fout_final */
6096          } else { /* even */
6097             /* input data is in fin_new */
6098             fin = fin_next;
6099             fout = fbuf; /* results go into buffer */
6100             fin_next = fbuf; /* in the next iteration, the input is from the buffer */
6101          }
6102          /* filter iteration for each node in data column k*/
6103          if (!nmask) {
6104             for (n=0; n < SO->N_Node; ++n) {
6105                /*SUMA_LHv("node %d\n", n);*/
6106                fp = fin[n]; /* kth value at node n */
6107                dfp = 0.0;
6108                if (SO->FN->N_Neighb[n]) minfn = maxfn = fin[SO->FN->FirstNeighb[n][0]];
6109                for (j=0; j < SO->FN->N_Neighb[n]; ++j) {
6110                   fpj = fin[SO->FN->FirstNeighb[n][j]]; /* value at jth neighbor of n */
6111                   if (fpj < minfn) minfn = fpj;
6112                   if (fpj > maxfn) maxfn = fpj;
6113                   dfp += wgt[n][j] * (fpj - fp);
6114                }/* for j*/
6115                fout[n] = fin[n] + delta_time * dfp;
6116                if (fout[n] < minfn) fout[n] = minfn;
6117                if (fout[n] > maxfn) fout[n] = maxfn;
6118             }/* for n */
6119          } else {  /* masking potential */
6120             for (n=0; n < SO->N_Node; ++n) {
6121                /*SUMA_LHv("node %d\n", n);*/
6122                fp = fin[n]; /* kth value at node n */
6123                dfp = 0.0;
6124                if (nmask[n]) {
6125                   if (SO->FN->N_Neighb[n]) {
6126                      jj = 0;
6127                      if (strict_mask) { /* consider only neighbors that are in mask */
6128                         do {
6129                            minfn = maxfn = fin[SO->FN->FirstNeighb[n][jj]]; ++jj;
6130                         } while (!nmask[SO->FN->FirstNeighb[n][jj]] && jj < SO->FN->N_Neighb[n]);
6131                      } else { /* consider all neighbors */
6132                         minfn = maxfn = fin[SO->FN->FirstNeighb[n][jj]];
6133                      }
6134                   }
6135                   for (j=0; j < SO->FN->N_Neighb[n]; ++j) {
6136                      nj = SO->FN->FirstNeighb[n][j];
6137                      if (nmask[nj] || !strict_mask) { /* consider only neighbors that are in mask if strict_mask is 1*/
6138                         fpj = fin[nj]; /* value at jth neighbor of n */
6139                         if (fpj < minfn) minfn = fpj;
6140                         if (fpj > maxfn) maxfn = fpj;
6141                         dfp += wgt[n][j] * (fpj - fp);
6142                      }
6143                   }/* for j*/
6144                   fout[n] = fin[n] + delta_time * dfp;
6145                   if (fout[n] < minfn) fout[n] = minfn;
6146                   if (fout[n] > maxfn) fout[n] = maxfn;
6147                } else {
6148                   fout[n] = fin[n];
6149                }
6150             }/* for n */
6151          } /* masking potential */
6152 
6153          if (cs->Send && k == 0) {
6154             if (!SUMA_SendToSuma (SO, cs, (void *)fout, SUMA_NODE_RGBAb, 1)) {
6155                SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCommunication halted.");
6156             }
6157          }
6158 
6159       } /* for niter */
6160 
6161       if (fin_orig) SUMA_free(fin_orig); fin_orig = NULL;
6162 
6163       /* Now we need to shove the filtered data back into the dset */
6164       if (!SUMA_Float2DsetCol (dset, icols[k], fout_final, 1, nmask)) {
6165          SUMA_S_Err("Failed to update dset's values");
6166          SUMA_RETURN(NOPE);
6167       }
6168 
6169    } /* for each col */
6170 
6171    CLEANUP:
6172    if (fin_orig) SUMA_free(fin_orig); fin_orig = NULL; /* just in case, this one's still alive from a GOTO */
6173    /* Pre Dec 06 stupidity: if (bfull == nmask) { if (nmask) SUMA_free(nmask); nmask = NULL; bfull = NULL; } */
6174    if (bfull) SUMA_free(bfull); bfull = NULL;
6175    if (fbuf) SUMA_free(fbuf); fbuf = NULL;
6176    if (fout_final) SUMA_free(fout_final); fout_final = NULL;
6177 
6178    SUMA_RETURN(YUP);
6179 }
6180 
6181 /*!
6182    \brief Filter data defined on the surface using M.K. Chung et al.'s 2005 method
6183     [1] Chung, M.K., Robbins,S., Dalton, K.M., Davidson, R.J., Evans, A.C. (2004)
6184         Cortical thickness analysis in autism via heat kernel smoothing. NeuroImage, submitted.
6185         http://www.stat.wisc.edu/~mchung/papers/ni_heatkernel.pdf
6186 
6187     [2] Chung, M.K. (2004) Heat kernel smoothing and its application to cortical manifolds.
6188         Technical Report 1090. Department of Statististics, Universisty of Wisconsin-Madison.
6189         http://www.stat.wisc.edu/~mchung/papers/heatkernel_tech.pdf
6190 
6191    dm_smooth = SUMA_Chung_Smooth_05 (SO, wgt, N_iter, FWHM, fin, vpn, d_order, fout_user, SUMA_COMM_STRUCT *cs);
6192 
6193    \param SO (SUMA_SurfaceObject *) Surface object with valid
6194                                     SO->NodeList, SO->FaceSetList and SO->FN
6195    \param wgt (float **) interpolation weights for each node.
6196                            These weights are obtained from SUMA_Chung_Smooth_Weights
6197    \param N_iter (int) number of smoothing iterations (must be even, > 1)
6198    \param FWHM (float) Full Width at Half Maximum of equivalent Gaussian filter
6199    \param fin (float *) vector containing node data. The length of this vector
6200                         is vpn x SO->N_Node , where vpn is the number of values
6201                         per node.
6202    \param vpn (int) the numberof values per node in fin
6203    \param d_order (SUMA_INDEXING_ORDER) Indicates how multiple values per node are stored in fin
6204                         SUMA_ROW_MAJOR: The data in fin is stored in *** Row Major *** order.
6205                         The ith value (start at 0) for node n is at index fin[vpn*n+i]
6206                         SUMA_COLUMN_MAJOR: The data in fin is stored in *** Column Major *** order.
6207                         The ith (start at 0) value for node n is at index fin[n+SO->N_Node*i];
6208                         etc...
6209    \param fout_user (float *) a pointer to the vector where the smoothed version of fin will reside
6210                         You can pass NULL and the function will do the allocation for you and return
6211                         the pointer to the smoothed data in dm_smooth.
6212                         If you already have space allocated for the result, then pass the pointer
6213                         in fout_user and save on allocation time and space. In that case, dm_smooth
6214                         is equal to fout_user.
6215                         Either way, you are responsible for freeing memory pointed to by dm_smooth
6216                         DO NOT PASS fout_user = fin
6217    \param cs (SUMA_COMM_STRUCT *) A pointer to the structure containing info for taking to SUMA
6218 
6219    \return dm_smooth (float *) A pointer to the smoothed data (vpn * SO->N_Node values).
6220                         You will have to free the memory allocated for dm_smooth yourself.
6221 
6222    \sa SUMA_Chung_Smooth_Weights_05
6223 
6224 */
6225 
6226 
SUMA_Chung_Smooth_05(SUMA_SurfaceObject * SO,float ** wgt,int N_iter,float FWHM,float * fin_orig,int vpn,SUMA_INDEXING_ORDER d_order,float * fout_final_user,SUMA_COMM_STRUCT * cs,byte * nmask,byte strict_mask)6227 float * SUMA_Chung_Smooth_05 (SUMA_SurfaceObject *SO, float **wgt,
6228                            int N_iter, float FWHM, float *fin_orig,
6229                            int vpn, SUMA_INDEXING_ORDER d_order, float *fout_final_user,
6230                            SUMA_COMM_STRUCT *cs, byte *nmask, byte strict_mask)
6231 {
6232    static char FuncName[]={"SUMA_Chung_Smooth_05"};
6233    float *fout_final = NULL, *fbuf=NULL, *fin=NULL, *fout=NULL, *fin_next = NULL;
6234    float fp, dfp, fpj, dfps = 0.0;
6235    int n , k, j, niter, vnk, os, jj, nj;
6236    SUMA_Boolean LocalHead = NOPE;
6237 
6238    SUMA_ENTRY;
6239 
6240    if (N_iter % 2) {
6241       SUMA_SL_Err("N_iter must be an even number\n");
6242       SUMA_RETURN(NULL);
6243    }
6244 
6245    if (!SO || !wgt || !fin_orig) {
6246       SUMA_SL_Err("NULL SO or wgt or fin_orig\n");
6247       SUMA_RETURN(NULL);
6248    }
6249    if (!SO->FN) {
6250       SUMA_SL_Err("NULL SO->FN\n");
6251       SUMA_RETURN(NULL);
6252    }
6253 
6254    if (vpn < 1) {
6255       SUMA_SL_Err("vpn < 1\n");
6256       SUMA_RETURN(NULL);
6257    }
6258 
6259    if (fout_final_user == fin_orig) {
6260       SUMA_SL_Err("fout_final_user == fin_orig");
6261       SUMA_RETURN(NULL);
6262    }
6263 
6264    if (!fout_final_user) { /* allocate for output */
6265       fout_final = (float *)SUMA_calloc(SO->N_Node * vpn, sizeof(float));
6266       if (!fout_final) {
6267          SUMA_SL_Crit("Failed to allocate for fout_final\n");
6268          SUMA_RETURN(NULL);
6269       }
6270    }else {
6271       fout_final = fout_final_user; /* pre-allocated */
6272    }
6273 
6274    /* allocate for buffer */
6275    fbuf = (float *)SUMA_calloc(SO->N_Node * vpn, sizeof(float));
6276    if (!fbuf) {
6277       SUMA_SL_Crit("Failed to allocate for fbuf\n");
6278       SUMA_RETURN(NULL);
6279    }
6280 
6281 
6282    if (cs && cs->Send) { /* send the first monster */
6283       if (!SUMA_SendToSuma (SO, cs, (void *)fin_orig, SUMA_NODE_RGBAb, 1)) {
6284          SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCommunication halted.");
6285       }
6286    }
6287 
6288    fin_next = fin_orig;
6289    switch (d_order) {
6290       case SUMA_COLUMN_MAJOR:
6291          for (niter=0; niter < N_iter; ++niter) {
6292             if ( niter % 2 ) { /* odd */
6293                fin = fin_next; /* input from previous output buffer */
6294                fout = fout_final; /* results go into final vector */
6295                fin_next = fout_final; /* in the next iteration, the input is from fout_final */
6296             } else { /* even */
6297                /* input data is in fin_new */
6298                fin = fin_next;
6299                fout = fbuf; /* results go into buffer */
6300                fin_next = fbuf; /* in the next iteration, the input is from the buffer */
6301             }
6302             for (k=0; k < vpn; ++k) {
6303                os = SO->N_Node*k;   /* node value indexing offset */
6304                for (n=0; n < SO->N_Node; ++n) {
6305                   vnk = n+os; /* index of kth value at node n */
6306                   fp = fin[vnk]; /* kth value at node n */
6307                   dfp = 0.0;
6308                   if (!nmask) {
6309                      for (j=0; j < SO->FN->N_Neighb[n]; ++j) {
6310                         fpj = fin[SO->FN->FirstNeighb[n][j]+os]; /* value at jth neighbor of n */
6311                         dfp += wgt[n][j+1] * (fpj);
6312                      }/* for j*/
6313                      fout[vnk] = fin[vnk] * wgt[n][0] +  dfp;
6314                   } else { /* masking potential */
6315                      if (nmask[n]) {
6316                         dfps = wgt[n][0];
6317                         for (j=0; j < SO->FN->N_Neighb[n]; ++j) {
6318                            {
6319                               nj = SO->FN->FirstNeighb[n][j];
6320                               if (nmask[nj] || !strict_mask) { /* consider only neighbors that are in mask if strict_mask is 1*/
6321                                  fpj = fin[nj+os]; /* value at jth neighbor of n */
6322                                  dfp += wgt[n][j+1] * fpj;
6323                                  dfps += wgt[n][j+1];
6324                               }
6325                            }
6326                         }/* for j*/
6327                         fout[vnk] = (fin[vnk] * wgt[n][0] +  dfp)/dfps;
6328                      } else {
6329                         fout[vnk] = fin[vnk];
6330                      }
6331                   }
6332                }/* for n */
6333 
6334                if (cs && cs->Send) {
6335                   if (!SUMA_SendToSuma (SO, cs, (void *)fout, SUMA_NODE_RGBAb, 1)) {
6336                      SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCommunication halted.");
6337                   }
6338                }
6339             } /* for k */
6340          }/* for niter */
6341          break;
6342       case SUMA_ROW_MAJOR:
6343          SUMA_SL_Err("Row Major not implemented");
6344          SUMA_RETURN(NULL);
6345          break;
6346       default:
6347          SUMA_SL_Err("Bad Major, very bad.\n");
6348          SUMA_RETURN(NULL);
6349          break;
6350    }
6351 
6352    if (fbuf) SUMA_free(fbuf); fbuf = NULL;
6353 
6354    SUMA_RETURN(fout);
6355 }
6356 
6357 /*!
6358    A version of SUMA_Chung_Smooth_05 that works with SUMA_DSETs
6359    NOTE: dset is changed on output (blurred values) but idcode
6360    remains unchanged
6361 */
SUMA_Chung_Smooth_05_single_dset(SUMA_SurfaceObject * SO,float ** wgt,int N_iter,float FWHM,SUMA_DSET * dset,SUMA_COMM_STRUCT * cs,byte * nmask,byte strict_mask)6362 SUMA_Boolean SUMA_Chung_Smooth_05_single_dset (SUMA_SurfaceObject *SO, float **wgt,
6363                            int N_iter, float FWHM, SUMA_DSET *dset,
6364                            SUMA_COMM_STRUCT *cs, byte *nmask, byte strict_mask)
6365 {
6366    static char FuncName[]={"SUMA_Chung_Smooth_05_signle_dset"};
6367    float *fout_final = NULL, *fbuf=NULL, *fin=NULL, *fout=NULL, *fin_next = NULL, *fin_orig = NULL;
6368    float fp, dfp, fpj, minfn=0.0, maxfn=0.0, dfps = 0.0;
6369    int n , k, j, niter, jj, nj, *icols=NULL, N_icols, N_nmask, kth_buf;
6370    byte *bfull=NULL;
6371    SUMA_Boolean LocalHead = NOPE;
6372 
6373    SUMA_ENTRY;
6374 
6375    /*
6376    if (N_iter % 2) {
6377       SUMA_SL_Err("N_iter must be an even number\n");
6378       SUMA_RETURN(NOPE);
6379    }
6380    */
6381 
6382    if (!SO || !wgt || !dset) {
6383       SUMA_SL_Err("NULL SO or wgt or dset\n");
6384       SUMA_RETURN(NOPE);
6385    }
6386 
6387    if (!SO->FN) {
6388       SUMA_SL_Err("NULL SO->FN\n");
6389       SUMA_RETURN(NOPE);
6390    }
6391 
6392    /* what columns can we process ?*/
6393    icols = SUMA_FindNumericDataDsetCols(dset, &N_icols);
6394 
6395    if (N_icols <= 0) {
6396       SUMA_SL_Err("No approriate data columns in dset");
6397       SUMA_RETURN(NOPE);
6398    }
6399    SUMA_LHv("Have %d columns to process.\n", N_icols);
6400 
6401    /* make a copy of nmask if need be */
6402    if (nmask) {
6403       bfull = (byte *)SUMA_malloc(sizeof(byte)*SO->N_Node);
6404       memcpy((void *)bfull, (void *)nmask, sizeof(byte)*SO->N_Node);
6405    }
6406 
6407    /* allocate for buffer and output */
6408    fbuf = (float *)SUMA_calloc(SO->N_Node, sizeof(float));
6409    fout_final = (float *)SUMA_calloc(SO->N_Node, sizeof(float));
6410    if (!fbuf || !fout_final) {
6411       SUMA_SL_Crit("Failed to allocate for fbuf and fout_final\n");
6412       SUMA_RETURN(NOPE);
6413    }
6414    SUMA_LH("Done with buffer allocation");
6415 
6416    if (cs && cs->Send && N_icols > 1) {
6417       SUMA_S_Warn("Only 1st data column will be sent to SUMA in talk_mode.");
6418    }
6419 
6420 
6421    /* Begin filtering operation for each column */
6422    for (k=0; k < N_icols; ++k) {
6423       SUMA_LHv("Filtering column %d\n",icols[k]);
6424       if (k==0) {
6425          fin_orig = SUMA_DsetCol2FloatFullSortedColumn (dset, icols[k], &bfull, 0.0, SO->N_Node, &N_nmask, YUP);
6426       } else {
6427          fin_orig = SUMA_DsetCol2FloatFullSortedColumn (dset, icols[k], &bfull, 0.0, SO->N_Node, &N_nmask, NOPE);
6428       }
6429 
6430       if (cs && cs->Send && k == 0) { /* send the first monster */
6431          if (!SUMA_SendToSuma (SO, cs, (void *)fin_orig, SUMA_NODE_RGBAb, 1)) {
6432             SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCommunication halted.");
6433          }
6434       }
6435 
6436       /* filter this column for each of the iterations */
6437       fin_next = fin_orig;
6438       for (niter=0; niter < N_iter; ++niter) {
6439          SUMA_LHv("niter %d\n", niter);
6440          if ( niter % 2 ) { /* odd */
6441             fin = fin_next; /* input from previous output buffer */
6442             fout = fout_final; /* results go into final vector */
6443             fin_next = fout_final; /* in the next iteration, the input is from fout_final */
6444          } else { /* even */
6445             /* input data is in fin_new */
6446             fin = fin_next;
6447             fout = fbuf; /* results go into buffer */
6448             fin_next = fbuf; /* in the next iteration, the input is from the buffer */
6449          }
6450          /* filter iteration for each node in data column k*/
6451          if (!bfull) {
6452             for (n=0; n < SO->N_Node; ++n) {
6453                /*SUMA_LHv("node %d\n", n);*/
6454                fp = fin[n]; /* kth value at node n */
6455                dfp = 0.0;
6456                for (j=0; j < SO->FN->N_Neighb[n]; ++j) {
6457                   fpj = fin[SO->FN->FirstNeighb[n][j]]; /* value at jth neighbor of n */
6458                   dfp += wgt[n][j+1] * (fpj);
6459                }/* for j*/
6460                fout[n] = fin[n] * wgt[n][0] +  dfp;
6461             }/* for n */
6462          } else {  /* masking potential */
6463             for (n=0; n < SO->N_Node; ++n) {
6464                /*SUMA_LHv("node %d\n", n);*/
6465                fp = fin[n]; /* kth value at node n */
6466                dfp = 0.0;
6467                if (bfull[n]) {
6468                   dfps = wgt[n][0];
6469                   for (j=0; j < SO->FN->N_Neighb[n]; ++j) {
6470                      {
6471                         nj = SO->FN->FirstNeighb[n][j];
6472                         if (bfull[nj] || !strict_mask) { /* consider only neighbors that are in mask if strict_mask is 1*/
6473                            fpj = fin[nj]; /* value at jth neighbor of n */
6474                            dfp += wgt[n][j+1] * fpj;
6475                            dfps += wgt[n][j+1];
6476                         }
6477                      }
6478                   }/* for j*/
6479                   fout[n] = (fin[n] * wgt[n][0] +  dfp)/dfps;
6480                } else {
6481                   fout[n] = fin[n];
6482                }
6483             }/* for n */
6484          } /* masking potential */
6485 
6486          if (cs && cs->Send && k == 0) {
6487             kth_buf = cs->kth;
6488             if (niter == N_iter -1) {
6489                cs->kth = 1;   /* send the last iteration no matter what */
6490             }
6491             if (!SUMA_SendToSuma (SO, cs, (void *)fout, SUMA_NODE_RGBAb, 1)) {
6492                SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCommunication halted.");
6493             }
6494             cs->kth = kth_buf;
6495          }
6496 
6497       } /* for niter */
6498 
6499       if (fin_orig) SUMA_free(fin_orig); fin_orig = NULL;
6500 
6501 
6502       if (N_iter % 2 ) {/* if Niter is odd, then copy contents into fout final from fbuf */
6503          SUMA_LHv("Copying buffer content, N_iter = %d\n", N_iter);
6504          memcpy((void*)fout_final, (void *)fbuf, SO->N_Node*sizeof(float));
6505       }
6506 
6507       /* Now we need to shove the filtered data back into the dset */
6508       if (!SUMA_Float2DsetCol (dset, icols[k], fout_final, 1, bfull)) {
6509          SUMA_S_Err("Failed to update dset's values");
6510          SUMA_RETURN(NOPE);
6511       }
6512 
6513    } /* for each col */
6514 
6515    /* CLEANUP: */
6516    if (fin_orig) SUMA_free(fin_orig); fin_orig = NULL;
6517       /* just in case, this one's still alive from a GOTO */
6518    if (bfull) SUMA_free(bfull); bfull = NULL;
6519    if (fbuf) SUMA_free(fbuf); fbuf = NULL;
6520    if (fout_final) SUMA_free(fout_final); fout_final = NULL;
6521 
6522    SUMA_RETURN(YUP);
6523 }
6524 
6525 /*!
6526    A version of SUMA_Chung_Smooth_05 that works with SUMA_DSETs
6527    NOTE: dset is changed on output (blurred values) but idcode
6528    remains unchanged
6529 */
SUMA_Chung_Smooth_05_Pre_07_dset(SUMA_SurfaceObject * SO,float ** wgt,int N_iter,float FWHM,SUMA_DSET * dset,SUMA_COMM_STRUCT * cs,byte * nmask,byte strict_mask)6530 SUMA_Boolean SUMA_Chung_Smooth_05_Pre_07_dset (SUMA_SurfaceObject *SO, float **wgt,
6531                            int N_iter, float FWHM, SUMA_DSET *dset,
6532                            SUMA_COMM_STRUCT *cs, byte *nmask, byte strict_mask)
6533 {
6534    static char FuncName[]={"SUMA_Chung_Smooth_05_Pre_07_dset"};
6535    float *fout_final = NULL, *fbuf=NULL, *fin=NULL, *fout=NULL, *fin_next = NULL, *fin_orig = NULL;
6536    float delta_time, fp, dfp, fpj, minfn=0.0, maxfn=0.0, dfps = 0.0;
6537    int n , k, j, niter, jj, nj, *icols=NULL, N_icols, N_nmask, kth_buf;
6538    byte *bfull=NULL;
6539    SUMA_Boolean LocalHead = NOPE;
6540 
6541    SUMA_ENTRY;
6542 
6543    /*
6544    if (N_iter % 2) {
6545       SUMA_SL_Err("N_iter must be an even number\n");
6546       SUMA_RETURN(NOPE);
6547    }
6548    */
6549 
6550    if (!SO || !wgt || !dset) {
6551       SUMA_SL_Err("NULL SO or wgt or dset\n");
6552       SUMA_RETURN(NOPE);
6553    }
6554 
6555    if (!SO->FN) {
6556       SUMA_SL_Err("NULL SO->FN\n");
6557       SUMA_RETURN(NOPE);
6558    }
6559 
6560    /* what columns can we process ?*/
6561    icols = SUMA_FindNumericDataDsetCols(dset, &N_icols);
6562 
6563    if (N_icols <= 0) {
6564       SUMA_SL_Err("No approriate data columns in dset");
6565       SUMA_RETURN(NOPE);
6566    }
6567 
6568    /* allocate for buffer and output */
6569    fbuf = (float *)SUMA_calloc(SO->N_Node, sizeof(float));
6570    fout_final = (float *)SUMA_calloc(SO->N_Node, sizeof(float));
6571    if (!fbuf || !fout_final) {
6572       SUMA_SL_Crit("Failed to allocate for fbuf and fout_final\n");
6573       SUMA_RETURN(NOPE);
6574    }
6575 
6576    if (cs->Send && N_icols > 1) {
6577       SUMA_S_Warn("Only 1st data column will be sent to SUMA in talk_mode.");
6578    }
6579 
6580    delta_time= (FWHM * FWHM)/(16*N_iter*log(2));
6581 
6582    /* Begin filtering operation for each column */
6583    for (k=0; k < N_icols; ++k) {
6584       /* get a float copy of the data column */
6585       fin_orig = SUMA_DsetCol2Float (dset, icols[k], 1);
6586       if (!fin_orig) {
6587          SUMA_SL_Crit("Failed to get copy of column. Woe to thee!");
6588          SUMA_RETURN(NOPE);
6589       }
6590       /* make sure column is not sparse, one value per node */
6591       if (k==0) {
6592          SUMA_LH( "Special case k = 0, going to SUMA_MakeSparseColumnFullSorted");
6593          bfull = NULL;
6594          if (!SUMA_MakeSparseColumnFullSorted(&fin_orig, SDSET_VECFILLED(dset), 0.0, &bfull, dset, SO->N_Node)) {
6595             SUMA_S_Err("Failed to get full column vector");
6596             SUMA_RETURN(NOPE);
6597          }
6598          if (bfull) {
6599             SUMA_LH( "Something was filled in SUMA_MakeSparseColumnFullSorted\n" );
6600             /* something was filled in good old SUMA_MakeSparseColumnFullSorted */
6601             if (nmask) {   /* combine bfull with nmask */
6602                SUMA_LH( "Merging masks\n" );
6603                for (jj=0; jj < SO->N_Node; ++jj) { if (nmask[jj] && !bfull[jj]) nmask[jj] = 0; }
6604             } else { nmask = bfull; }
6605          }
6606          if (nmask) {
6607             N_nmask = 0;
6608             for (n=0; n<SO->N_Node; ++n) { if (nmask[n]) ++ N_nmask; }
6609             SUMA_LHv("Blurring with node mask (%d nodes in mask)\n", N_nmask);
6610             if (!N_nmask) {
6611                SUMA_S_Warn("Empty mask, nothing to do");
6612                goto CLEANUP;
6613             }
6614          }
6615       } else {
6616          SUMA_LH( "going to SUMA_MakeSparseColumnFullSorted");
6617          if (!SUMA_MakeSparseColumnFullSorted(&fin_orig, SDSET_VECFILLED(dset), 0.0, NULL, dset, SO->N_Node)) {
6618             SUMA_S_Err("Failed to get full column vector");
6619             SUMA_RETURN(NOPE);
6620          }
6621          /* no need for reworking nmask and bfull for each column...*/
6622       }
6623 
6624       if (cs->Send && k == 0) { /* send the first monster */
6625          if (!SUMA_SendToSuma (SO, cs, (void *)fin_orig, SUMA_NODE_RGBAb, 1)) {
6626             SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCommunication halted.");
6627          }
6628       }
6629 
6630       /* filter this column for each of the iterations */
6631       fin_next = fin_orig;
6632       for (niter=0; niter < N_iter; ++niter) {
6633          SUMA_LHv("niter %d\n", niter);
6634          if ( niter % 2 ) { /* odd */
6635             fin = fin_next; /* input from previous output buffer */
6636             fout = fout_final; /* results go into final vector */
6637             fin_next = fout_final; /* in the next iteration, the input is from fout_final */
6638          } else { /* even */
6639             /* input data is in fin_new */
6640             fin = fin_next;
6641             fout = fbuf; /* results go into buffer */
6642             fin_next = fbuf; /* in the next iteration, the input is from the buffer */
6643          }
6644          /* filter iteration for each node in data column k*/
6645          if (!nmask) {
6646             for (n=0; n < SO->N_Node; ++n) {
6647                /*SUMA_LHv("node %d\n", n);*/
6648                fp = fin[n]; /* kth value at node n */
6649                dfp = 0.0;
6650                for (j=0; j < SO->FN->N_Neighb[n]; ++j) {
6651                   fpj = fin[SO->FN->FirstNeighb[n][j]]; /* value at jth neighbor of n */
6652                   dfp += wgt[n][j+1] * (fpj);
6653                }/* for j*/
6654                fout[n] = fin[n] * wgt[n][0] +  dfp;
6655             }/* for n */
6656          } else {  /* masking potential */
6657             for (n=0; n < SO->N_Node; ++n) {
6658                /*SUMA_LHv("node %d\n", n);*/
6659                fp = fin[n]; /* kth value at node n */
6660                dfp = 0.0;
6661                if (nmask[n]) {
6662                   dfps = wgt[n][0];
6663                   for (j=0; j < SO->FN->N_Neighb[n]; ++j) {
6664                      {
6665                         nj = SO->FN->FirstNeighb[n][j];
6666                         if (nmask[nj] || !strict_mask) { /* consider only neighbors that are in mask if strict_mask is 1*/
6667                            fpj = fin[nj]; /* value at jth neighbor of n */
6668                            dfp += wgt[n][j+1] * fpj;
6669                            dfps += wgt[n][j+1];
6670                         }
6671                      }
6672                   }/* for j*/
6673                   fout[n] = (fin[n] * wgt[n][0] +  dfp)/dfps;
6674                } else {
6675                   fout[n] = fin[n];
6676                }
6677             }/* for n */
6678          } /* masking potential */
6679 
6680          if (cs->Send && k == 0) {
6681             kth_buf = cs->kth;
6682             if (niter == N_iter -1) {
6683                cs->kth = 1;   /* send the last iteration no matter what */
6684             }
6685             if (!SUMA_SendToSuma (SO, cs, (void *)fout, SUMA_NODE_RGBAb, 1)) {
6686                SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCommunication halted.");
6687             }
6688             cs->kth = kth_buf;
6689          }
6690 
6691       } /* for niter */
6692 
6693       if (fin_orig) SUMA_free(fin_orig); fin_orig = NULL;
6694 
6695 
6696       if (N_iter % 2 ) {/* if Niter is odd, then copy contents into fout final from fbuf */
6697          SUMA_LHv("Copying buffer content, N_iter = %d\n", N_iter);
6698          memcpy((void*)fout_final, (void *)fbuf, SO->N_Node*sizeof(float));
6699       }
6700 
6701       /* Now we need to shove the filtered data back into the dset */
6702       if (!SUMA_Float2DsetCol (dset, icols[k], fout_final, SO->N_Node, nmask)) {
6703          SUMA_S_Err("Failed to update dset's values");
6704          SUMA_RETURN(NOPE);
6705       }
6706 
6707    } /* for each col */
6708 
6709    CLEANUP:
6710    if (fin_orig) SUMA_free(fin_orig); fin_orig = NULL; /* just in case, this one's still alive from a GOTO */
6711    /* Pre Dec 06 stupidity: if (bfull == nmask) { if (nmask) SUMA_free(nmask); nmask = NULL; bfull = NULL; } */
6712    if (bfull) SUMA_free(bfull); bfull = NULL;
6713    if (fbuf) SUMA_free(fbuf); fbuf = NULL;
6714    if (fout_final) SUMA_free(fout_final); fout_final = NULL;
6715 
6716    SUMA_RETURN(YUP);
6717 }
6718 
6719 /*!
6720    NOTE: In this function, IF N_iter is set to -1, EVERY column is
6721    filtered separately until it reaches the FWHM. This means columns
6722    can be filtered with differing numbers of iterations, something
6723    which is not always desirable!
6724 */
SUMA_Chung_Smooth_07_dset(SUMA_SurfaceObject * SO,double ** wgt,int * N_iter,double * FWHMp,SUMA_DSET * dset,SUMA_COMM_STRUCT * cs,byte * nmask,byte strict_mask)6725 SUMA_Boolean SUMA_Chung_Smooth_07_dset (SUMA_SurfaceObject *SO, double **wgt,
6726                            int *N_iter, double *FWHMp, SUMA_DSET *dset,
6727                            SUMA_COMM_STRUCT *cs, byte *nmask, byte strict_mask)
6728 {
6729    static char FuncName[]={"SUMA_Chung_Smooth_07_dset"};
6730    double   *fout_final = NULL, *fbuf=NULL, *fin=NULL,
6731             *fout=NULL, *fin_next = NULL, *fin_orig = NULL, FWHM;
6732    double fp, dfp, fpj, minfn=0.0, maxfn=0.0, dfps=0.0;
6733    float *fin_float=NULL, *fsend=NULL, *fwhmg=NULL;
6734    int n , k, j, niter=-1, jj, nj,
6735       *icols=NULL, N_icols, N_nmask, kth_buf, niter_alloc;
6736    byte *bfull=NULL, stop = 0;
6737    char stmp[100];
6738    SUMA_Boolean LocalHead = NOPE;
6739 
6740    SUMA_ENTRY;
6741 
6742    FWHM = *FWHMp;
6743 
6744    if (*N_iter > 0 && FWHM > 0) {
6745       SUMA_S_Err("Can't specify both of FWHM and N_iter");
6746       SUMA_RETURN(NOPE);
6747    }
6748 
6749    if (!SO || !wgt || !dset) {
6750       SUMA_SL_Err("NULL SO or wgt or dset\n");
6751       SUMA_RETURN(NOPE);
6752    }
6753 
6754    if (!SO->FN) {
6755       SUMA_SL_Err("NULL SO->FN\n");
6756       SUMA_RETURN(NOPE);
6757    }
6758 
6759    /* what columns can we process ?*/
6760    icols = SUMA_FindNumericDataDsetCols(dset, &N_icols);
6761 
6762    if (N_icols <= 0) {
6763       SUMA_SL_Err("No approriate data columns in dset");
6764       SUMA_RETURN(NOPE);
6765    }
6766    SUMA_LHv("Have %d columns to process.\n", N_icols);
6767    if (*N_iter < 0 && N_icols > 1) {
6768       SUMA_S_Note(
6769          "Each column will be blurred until it reaches the desired FWHM.\n"
6770          "Therefore it is possible (make that likely) that different columns\n"
6771          "are blurred by a differing number of iterations.\n");
6772    }
6773 
6774    /* make a copy of nmask if need be */
6775    if (nmask) {
6776       bfull = (byte *)SUMA_malloc(sizeof(byte)*SO->N_Node);
6777       memcpy((void *)bfull, (void *)nmask, sizeof(byte)*SO->N_Node);
6778    }
6779 
6780    /* allocate for buffer and output */
6781    niter_alloc = 500;
6782    fwhmg = (float*)SUMA_calloc(niter_alloc, sizeof(float));
6783    fbuf = (double *)SUMA_calloc(SO->N_Node, sizeof(double));
6784    fin_orig = (double *)SUMA_calloc(SO->N_Node, sizeof(double));
6785    fout_final = (double *)SUMA_calloc(SO->N_Node, sizeof(double));
6786    if (!fbuf || !fout_final || !fin_orig || !fwhmg) {
6787       SUMA_SL_Crit("Failed to allocate for fbuf and fout_final\n");
6788       SUMA_RETURN(NOPE);
6789    }
6790    SUMA_LH("Done with buffer allocation");
6791 
6792    if (cs && cs->Send && N_icols > 1) {
6793       SUMA_S_Warn("Only 1st data column will be sent to SUMA in talk_mode.");
6794    }
6795 
6796 
6797    /* Begin filtering operation for each column */
6798    for (k=0; k < N_icols; ++k) {
6799       SUMA_LHv(
6800          "Filtering column %d, N_iter=%d, FWHM=%f\n",icols[k], *N_iter, FWHM);
6801       if (k==0) {
6802          fin_float = SUMA_DsetCol2FloatFullSortedColumn (
6803             dset, icols[k], &bfull, 0.0, SO->N_Node, &N_nmask, YUP);
6804       } else {
6805          fin_float = SUMA_DsetCol2FloatFullSortedColumn (
6806             dset, icols[k], &bfull, 0.0, SO->N_Node, &N_nmask, NOPE);
6807       }
6808       /* copy the float to double array */
6809       for (n=0; n<SO->N_Node; ++n) fin_orig[n] = (double)fin_float[n];
6810 
6811       if (cs && cs->Send && k == 0) { /* send the first monster */
6812          /* Must do this stupid copy */
6813          if (!SUMA_SendToSuma (SO, cs, (void *)fin_float, SUMA_NODE_RGBAb, 1)) {
6814             SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCommunication halted.");
6815          }
6816       }
6817 
6818       /* filter this column for each of the iterations */
6819       fin_next = fin_orig;
6820       niter = 0; stop = 0;
6821       if (*N_iter < 0) {
6822          fwhmg[niter] = SUMA_estimate_FWHM_1dif( SO, fin_float, bfull, 1);
6823          if (fwhmg[niter] > FWHM) stop = 1;
6824       } else {
6825          if (niter >= *N_iter) stop = 1;
6826       }
6827 
6828       while (!stop) {
6829          SUMA_LHv("niter %d\n", niter);
6830          if ( niter % 2 ) { /* odd */
6831             fin = fin_next; /* input from previous output buffer */
6832             fout = fout_final; /* results go into final vector */
6833             fin_next = fout_final; /*  in the next iteration,
6834                                        the input is from fout_final */
6835          } else { /* even */
6836             /* input data is in fin_new */
6837             fin = fin_next;
6838             fout = fbuf; /* results go into buffer */
6839             fin_next = fbuf; /*  in the next iteration,
6840                                  the input is from the buffer */
6841          }
6842          /* filter iteration for each node in data column k*/
6843          if (!bfull) {
6844             for (n=0; n < SO->N_Node; ++n) {
6845                fp = fin[n]; /* kth value at node n */
6846                dfp = 0.0;
6847                for (j=0; j < SO->FN->N_Neighb[n]; ++j) {
6848                   /* value at jth neighbor of n */
6849                   fpj = fin[SO->FN->FirstNeighb[n][j]];
6850                   dfp += wgt[n][j+1] * (fpj);
6851                }/* for j*/
6852                fout[n] = fin[n] * wgt[n][0] +  dfp;
6853                #if 0
6854                   if ((LocalHead && n == SUMA_SSidbg))
6855                      SUMA_LHv("node %d, fin %g, fout %g\n", n, fin[n], fout[n]);
6856                #endif
6857             }/* for n */
6858          } else {  /* masking potential */
6859             for (n=0; n < SO->N_Node; ++n) {
6860                fp = fin[n]; /* kth value at node n */
6861                dfp = 0.0;
6862                if (bfull[n]) {
6863                   dfps = wgt[n][0];
6864                   for (j=0; j < SO->FN->N_Neighb[n]; ++j) {
6865                      {
6866                         nj = SO->FN->FirstNeighb[n][j];
6867                         if (bfull[nj] || !strict_mask) {
6868                            /* consider only neighbors that are
6869                               in mask if strict_mask is 1*/
6870                            fpj = fin[nj]; /* value at jth neighbor of n */
6871                            dfp += wgt[n][j+1] * fpj;
6872                            dfps += wgt[n][j+1];
6873                         }
6874                      }
6875                   }/* for j*/
6876                   fout[n] = (fin[n] * wgt[n][0] +  dfp)/dfps;
6877                } else {
6878                   fout[n] = fin[n];
6879                }
6880                #if 0
6881                   if ((LocalHead && n == SUMA_SSidbg))
6882                      SUMA_LHv("node %d, fin %g, fout %g\n", n, fin[n], fout[n]);
6883                #endif
6884             }/* for n */
6885          } /* masking potential */
6886          if (cs && cs->Send && k == 0) { /*  IF YOU CHANGE THIS CONDITION,
6887                                              CHANGE IT IN next block! */
6888             /* Must do this stupid copy */
6889             if (!fsend) {
6890                fsend = (float*)SUMA_malloc(sizeof(float)*SO->N_Node); }
6891             for (n=0; n < SO->N_Node; ++n) { fsend[n] = (float)fout[n]; }
6892             kth_buf = cs->kth;
6893             if (niter == *N_iter -1) {
6894                cs->kth = 1;   /* send the last iteration no matter what */
6895             }
6896             if (!SUMA_SendToSuma (SO, cs, (void *)fsend, SUMA_NODE_RGBAb, 1)) {
6897                SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCommunication halted.");
6898             }
6899             cs->kth = kth_buf;
6900          }
6901          if (*N_iter < 0) {
6902             /* base on fwhm */
6903             if (!fsend) {
6904                fsend = (float*)SUMA_malloc(sizeof(float)*SO->N_Node); }
6905             if (!(cs && cs->Send && k == 0)) {/* Else fsend is setup above */
6906                for (n=0; n < SO->N_Node; ++n) { fsend[n] = (float)fout[n]; }
6907             }
6908             if (niter >= niter_alloc-1) {
6909                fwhmg = SUMA_realloc(fwhmg,sizeof(float)*(niter_alloc+500));
6910                niter_alloc = niter_alloc+500;
6911             }
6912             ++niter;
6913             fwhmg[niter] = SUMA_estimate_FWHM_1dif( SO, fsend, bfull, 1);
6914             if (fwhmg[niter] > FWHM) stop = 1;
6915             fprintf(SUMA_STDERR,"   iteration %d, fwhmg = %f\n",
6916                      niter, fwhmg[niter]);
6917          } else {
6918             ++niter;
6919             if (niter >= *N_iter) stop = 1;
6920          }
6921       } /* for niter */
6922 
6923       if ( (niter % 2) ) {/*  if niter is odd, then copy
6924                               contents into fout final from fbuf */
6925          SUMA_LHv("Copying buffer content, N_iter = %d\n", *N_iter);
6926          memcpy((void*)fout_final, (void *)fbuf, SO->N_Node*sizeof(double));
6927       }
6928 
6929       /* Now we need to shove the filtered data back into the dset */
6930       for (n=0; n<SO->N_Node; ++n) fin_float[n] = (float)fout_final[n];
6931       if (LocalHead &&  SUMA_SSidbg >= 0 && SUMA_SSidbg <= SO->N_Node) {
6932          SUMA_LHv("fin_float[%d] = %f,  fout_final[%d] = %f\n",
6933                SUMA_SSidbg, fin_float[SUMA_SSidbg],
6934                SUMA_SSidbg, fout_final[SUMA_SSidbg]);
6935       }
6936       if (!SUMA_Float2DsetCol (dset, icols[k], fin_float, 1, bfull)) {
6937          SUMA_S_Err("Failed to update dset's values");
6938          SUMA_RETURN(NOPE);
6939       }
6940       if (LocalHead &&  SUMA_SSidbg >= 0 && SUMA_SSidbg <= SO->N_Node) {
6941          SUMA_LHv("In Dset, node %d=%f\n",
6942                SUMA_SSidbg,
6943                SUMA_GetDsetNodeValInCol2(dset,icols[k],
6944                                           SUMA_SSidbg, SO->N_Node-1));
6945       }
6946       if (fin_float) SUMA_free(fin_float); fin_float = NULL;
6947 
6948       if (niter == 0) {
6949          if (*N_iter < 0) {
6950             SUMA_S_Notev(  "Data column %d had a FWHM of %f,"
6951                            "which is greater than the requested FHWM of %f\n"
6952                            "No filtering done there.\n",
6953                            icols[k], fwhmg[niter], (float)FWHM);
6954          }
6955       }
6956       #if 0
6957          if (*N_iter < 0) {
6958             FILE *foutiter=NULL;
6959             char fname[500];
6960             SUMA_S_Note("Kill me");
6961             sprintf(fname, "FWHM_vs_iteration_Col_%d_Le+%.3f",
6962                      icols[k], SO->EL->AvgLe);
6963             foutiter = fopen(fname, "w");
6964             fprintf(foutiter,"#iteration     fwhm  Col %d AvgLe = %f\n",
6965                      icols[k], SO->EL->AvgLe);
6966             for (n=0; n<=*N_iter; ++n) {
6967                fprintf(foutiter, "%d   %f \n", n, fwhmg[n]);
6968             }
6969             fclose (foutiter); foutiter = NULL;
6970          }
6971          /* recover the value FROM the dset of that node in question */
6972          if (LocalHead && SUMA_SSidbg >= 0 && SUMA_SSidbg <= SO->N_Node) {
6973             int nnn = -1;
6974             nnn = SUMA_GetNodeRow_FromNodeIndex_s(
6975                         dset, SUMA_SSidbg,
6976                         SO->N_Node-1);
6977             SUMA_LHv("Value at node %d (row %d), column %d: %f\n",
6978                SUMA_SSidbg, nnn, icols[k],
6979                SUMA_GetDsetValInCol2( dset,icols[k], nnn ) );
6980          }
6981       #endif
6982    } /* for each col */
6983 
6984    /* record values for posterity */
6985    *N_iter = niter;
6986    *FWHMp = FWHM;
6987 
6988    /* CLEANUP: */
6989    if (fwhmg) SUMA_free(fwhmg); fwhmg=NULL;
6990    if (fsend) SUMA_free(fsend); fsend=NULL;
6991    if (fin_orig) SUMA_free(fin_orig); fin_orig = NULL;
6992    if (fin_float) SUMA_free(fin_float); fin_float = NULL; /* just in case,
6993                                                             this one's still
6994                                                             alive from a GOTO */
6995    if (bfull) SUMA_free(bfull); bfull = NULL;
6996    if (fbuf) SUMA_free(fbuf); fbuf = NULL;
6997    if (fout_final) SUMA_free(fout_final); fout_final = NULL;
6998 
6999    SUMA_RETURN(YUP);
7000 }
7001 
SUMA_WriteSmoothingRecord(SUMA_SurfaceObject * SO,float * fwhmg,int Niter,double * sigma,int cnst_sig,char * prefix)7002 SUMA_Boolean SUMA_WriteSmoothingRecord (  SUMA_SurfaceObject *SO,
7003                                           float *fwhmg, int Niter,
7004                                           double *sigma, int cnst_sig,
7005                                           char *prefix)
7006 {
7007    static char FuncName[]={"SUMA_WriteSmoothingRecord"};
7008    FILE *foutiter=NULL;
7009    int n;
7010    SUMA_Boolean LocalHead = NOPE;
7011 
7012    SUMA_ENTRY;
7013 
7014    if (!SO || !SO->EL || !fwhmg) {
7015       SUMA_S_Err("NULL input");
7016       SUMA_RETURN(NOPE);
7017    }
7018    SUMA_LHv("Initial FWHM of %f\n"
7019             "FWHM %f reached after %d iterations.\n", fwhmg[0], fwhmg[Niter], Niter);
7020    {
7021       char fname[500];
7022       snprintf(fname, 450*sizeof(char), "%s.1D.smrec",  prefix ? prefix:"anonyme" );
7023       SUMA_S_Notev("Writing FWHM progression history to %s ...\n", fname);
7024       foutiter = fopen(fname, "w");
7025       fprintf(foutiter, "#History of FWHM versus iteration number.\n"
7026                         "#Surface %s had average segment length of %f \n"
7027                         "#Initial FWHM of %f\n"
7028                         "#Col.0 : iteration number\n"
7029                         "#Col.1 : estimated fwhm\n"
7030                         "#Col.2 : kernel bandwidth (sigma)\n"
7031                         , SO->Label, SO->EL->AvgLe, fwhmg[0]);
7032       for (n=0; n<=Niter; ++n) {
7033          fprintf(foutiter, "%d   %f   %f\n", n, fwhmg[n], cnst_sig ? *sigma:sigma[n]);
7034       }
7035       fclose (foutiter); foutiter = NULL;
7036    }
7037 
7038    SUMA_RETURN(YUP);
7039 }
7040 
7041 /*!
7042    This function repeatedly blurs a dataset until the FWHM of the dataset
7043    just surpasses FWHM.
7044    SO: Surface object over which data are defined
7045    wgt: The weights for the immediate neighbors
7046    N_iter: Pointer to an integer showing the number of iterations that ended
7047            up being used
7048    FWHM: The desired FWHM
7049    dset: The dataset to be filtred
7050    nmask: The nodes to which the analysis is restricted
7051    strict_mask: Use only values from nodes inside mask (should be 1)
7052    FWHM_mixmode: Choose from "arit" or "geom" for the arithmetic and geometric
7053                  mean, respectively. When one has multi-sub-bricks for
7054                  input the FWHM of all sub-bricks is either the arithmetic
7055                  mean or the geometric mean.
7056    Note that this implementation is rather slow. But life is hard, because
7057    all columns are to be processed at each iteration. Rather than iterating
7058    on each column, successively.
7059 
7060 */
SUMA_Chung_Smooth_07_toFWHM_dset(SUMA_SurfaceObject * SO,double ** wgt,int * N_iter,double * FWHMp,SUMA_DSET * dset,byte * nmask,byte strict_mask,char * FWHM_mixmode,float ** fwhmgp)7061 SUMA_Boolean SUMA_Chung_Smooth_07_toFWHM_dset (
7062                            SUMA_SurfaceObject *SO, double **wgt,
7063                            int *N_iter, double *FWHMp, SUMA_DSET *dset,
7064                            byte *nmask, byte strict_mask,
7065                            char *FWHM_mixmode, float **fwhmgp)
7066 {
7067    static char FuncName[]={"SUMA_Chung_Smooth_07_toFWHM_dset"};
7068    double fp, dfp, fpj, minfn=0.0, maxfn=0.0, FWHM, dfps=0.0;
7069    float *fin_float=NULL, *fwhmg=NULL, *fwhmv=NULL,
7070          *fout_final = NULL, *fin=NULL, *fout=NULL;
7071    int   n , k, j, niter, jj, nj, *icols=NULL,
7072          N_icols, N_nmask, kth_buf, niter_alloc, N;
7073    int   NITERMAX = 3000;
7074    char  LogFWHM_hist[]={""}; /* for now don't save history.
7075                                  to turn it back on, use something like:
7076                                  {"FWHM_vs_iteration"}; */
7077    byte *bfull=NULL, stop = 0;
7078    SUMA_Boolean LocalHead = NOPE;
7079 
7080    SUMA_ENTRY;
7081 
7082    FWHM = *FWHMp;
7083 
7084    if (*N_iter > 0 && FWHM > 0) {
7085       SUMA_S_Err("Too much information. One or the other please");
7086       SUMA_RETURN(NOPE);
7087    }
7088    if (!SO || !wgt || !dset) {
7089       SUMA_SL_Err("NULL SO or wgt or dset\n");
7090       SUMA_RETURN(NOPE);
7091    }
7092 
7093    if (!SO->FN) {
7094       SUMA_SL_Err("NULL SO->FN\n");
7095       SUMA_RETURN(NOPE);
7096    }
7097 
7098    /* what columns can we process ?*/
7099    icols = SUMA_FindNumericDataDsetCols(dset, &N_icols);
7100 
7101    if (N_icols <= 0) {
7102       SUMA_SL_Err("No approriate data columns in dset");
7103       SUMA_RETURN(NOPE);
7104    }
7105    SUMA_LHv("Have %d columns to process.\n", N_icols);
7106 
7107    /* make a copy of nmask if need be */
7108    if (nmask) {
7109       bfull = (byte *)SUMA_malloc(sizeof(byte)*SO->N_Node);
7110       memcpy((void *)bfull, (void *)nmask, sizeof(byte)*SO->N_Node);
7111    }
7112 
7113    /* allocate for buffer and output */
7114    niter_alloc = 500;
7115    fwhmg = (float*)SUMA_calloc(niter_alloc, sizeof(float));
7116    fout_final = (float *)SUMA_calloc(SO->N_Node, sizeof(float));
7117    if (!fout_final  || !fwhmg) {
7118       SUMA_SL_Crit("Failed to allocate for fbuf and fout_final\n");
7119       SUMA_RETURN(NOPE);
7120    }
7121    SUMA_LH("Done with buffer allocation");
7122 
7123 
7124    /* estimate the FWHM of the dataset */
7125    if (!strict_mask) {
7126       SUMA_S_Warn("Strict mask is always applied for FWHM estimation.");
7127    }
7128 
7129    /* iterations have not begun */
7130    stop = 0;
7131    niter = 0;
7132    while (!stop) {
7133       if (*N_iter < 0) { /* function knows best */
7134          if (niter >= niter_alloc) {
7135             fwhmg = SUMA_realloc(fwhmg,sizeof(float)*(niter_alloc+500));
7136             niter_alloc = niter_alloc+500;
7137          }
7138          fwhmg[niter] = 0.0; N = 0;
7139          if (!(fwhmv = SUMA_estimate_dset_FWHM_1dif(  SO, dset,
7140                                                 icols, N_icols, nmask,
7141                                                 1, NULL))) {
7142             SUMA_S_Err("Rien ne va plus");
7143             SUMA_RETURN(NOPE);
7144          }
7145          SUMA_FWHM_MEAN(fwhmv, N_icols, fwhmg[niter], FWHM_mixmode, N);
7146          if (N <= 0) {
7147             SUMA_S_Err("Failed to get mean fwhm");
7148             SUMA_RETURN(NOPE);
7149          }
7150          if (fwhmg[niter] > FWHM) stop = 1;
7151          else if (niter > NITERMAX) {
7152             SUMA_S_Warnv(
7153                   "More than %d iterations failed to reach target of %f.\n"
7154                   "Stopping at fwhm of %f.\n"
7155                   "Consider increasing kernel bandwidth\n",
7156                   niter-1,FWHM, fwhmg[niter]);
7157             stop =1;
7158          }
7159       } else { /* caller is the boss */
7160          if (niter >= *N_iter) stop = 1;
7161       }
7162       if (!stop) {
7163          if (*N_iter < 0) {
7164             SUMA_LHv("Beginning iteration %d, fwhm = %f; target %f\n",
7165                       niter, fwhmg[niter], FWHM); }
7166          else { SUMA_LHv("Iteration %d/%d\n", niter+1,*N_iter);}
7167          /* Dull report */
7168          if (!LocalHead && !(niter % 10))
7169             fprintf( SUMA_STDERR,
7170                      "Iteration %d, fwhm = %f; target %f\n",
7171                       niter, fwhmg[niter], FWHM);
7172          /* Begin filtering operation for each column */
7173          for (k=0; k < N_icols; ++k) {
7174             /*SUMA_LHv("Filtering column %d\n",icols[k]); */
7175             if (k==0) {
7176                fin_float =
7177                   SUMA_DsetCol2FloatFullSortedColumn (
7178                      dset, icols[k], &bfull, 0.0, SO->N_Node, &N_nmask, YUP);
7179             } else {
7180                fin_float =
7181                   SUMA_DsetCol2FloatFullSortedColumn (
7182                      dset, icols[k], &bfull, 0.0, SO->N_Node, &N_nmask, NOPE);
7183             }
7184 
7185 
7186             /* filter this column for each of the iterations */
7187             fin = fin_float;
7188             fout = fout_final;
7189             /* filter iteration for each node in data column k*/
7190             if (!bfull) {
7191                for (n=0; n < SO->N_Node; ++n) {
7192                   fp = fin[n]; /* kth value at node n */
7193                   dfp = 0.0;
7194                   for (j=0; j < SO->FN->N_Neighb[n]; ++j) {
7195                      fpj = (double)fin[SO->FN->FirstNeighb[n][j]];
7196                                           /* value at jth neighbor of n */
7197                      dfp += wgt[n][j+1] * (fpj);
7198                   }/* for j*/
7199                   fout[n] = (float)((double)fin[n] * wgt[n][0] +  dfp);
7200                   #if 0
7201                      if (LocalHead && n == SUMA_SSidbg)
7202                         SUMA_LHv("node %d, fin %g, fout %g\n",
7203                                  n, fin[n], fout[n]);
7204                   #endif
7205                }/* for n */
7206             } else {  /* masking potential */
7207                for (n=0; n < SO->N_Node; ++n) {
7208                   /*SUMA_LHv("node %d\n", n);*/
7209                   fp = fin[n]; /* kth value at node n */
7210                   dfp = 0.0;
7211                   if (bfull[n]) {
7212                      dfps = wgt[n][0];
7213                      for (j=0; j < SO->FN->N_Neighb[n]; ++j) {
7214                         {
7215                            nj = SO->FN->FirstNeighb[n][j];
7216                            if (bfull[nj] || !strict_mask) {
7217                                  /* consider only neighbors that are in mask
7218                                     if strict_mask is 1*/
7219                               fpj = (double)fin[nj];
7220                                           /* value at jth neighb. of n */
7221                               dfp += wgt[n][j+1] * fpj;
7222                               dfps += wgt[n][j+1];
7223                            }
7224                         }
7225                      }/* for j*/
7226                      fout[n] = (float)(((double)fin[n] * wgt[n][0] +  dfp)/dfps);
7227                   } else {
7228                      fout[n] = fin[n];
7229                   }
7230                }/* for n */
7231             } /* masking potential */
7232 
7233 
7234             /* Now we need to shove the filtered data back into the dset */
7235             if (!SUMA_Float2DsetCol (dset, icols[k], fout, 1, bfull)) {
7236                SUMA_S_Err("Failed to update dset's values");
7237                SUMA_RETURN(NOPE);
7238             }
7239             /* Need to free fin_float now */
7240             SUMA_free(fin_float); fin_float = NULL;
7241          }/* repeat for each column */
7242 
7243          if (fwhmv) SUMA_free(fwhmv); fwhmv = NULL;
7244          ++niter; /* go back for another pass */
7245       }
7246    }
7247 
7248    if (*N_iter < 0) {
7249       SUMA_LHv("Initial FWHM of %f\n"
7250                "FWHM %f reached after %d iterations.\n", fwhmg[0], fwhmg[niter], niter);
7251       /*
7252       for (k=1; k<=niter; ++k) {
7253          fprintf(SUMA_STDERR,"     FWHM[%d]= %f\n", k-1, fwhmg[k]);
7254       }
7255       */
7256       if (LogFWHM_hist[0] != '\0') {
7257          {
7258             FILE *foutiter=NULL;
7259             char fname[500];
7260             sprintf(fname, "%s_Le+%.3f",  LogFWHM_hist, SO->EL->AvgLe);
7261             SUMA_S_Notev("Writing FWHM progression history to %s ...\n", fname);
7262             foutiter = fopen(fname, "w");
7263             fprintf(foutiter,"#iteration     fwhm  @ AvgLe = %f\n#Initial FWHM of %f\n", SO->EL->AvgLe, fwhmg[0]);
7264             for (n=0; n<=niter; ++n) {
7265                fprintf(foutiter, "%d   %f \n", n, fwhmg[n]);
7266             }
7267             fclose (foutiter); foutiter = NULL;
7268          }
7269      }
7270    }
7271 
7272    /* set the number of iterations (does nothing if user passed *N_iter > 0)*/
7273    *N_iter = niter;
7274    *FWHMp = FWHM;
7275 
7276    /* CLEANUP: */
7277    if (fwhmv) SUMA_free(fwhmv); fwhmv = NULL;
7278    if (!fwhmgp) {
7279       if (fwhmg) SUMA_free(fwhmg); fwhmg=NULL;
7280    } else { /* save it for the trip back home */
7281       *fwhmgp = fwhmg; fwhmg = NULL;
7282    }
7283    if (fin_float) SUMA_free(fin_float); fin_float = NULL;
7284       /* just in case, this one's still alive from a GOTO */
7285    if (bfull) SUMA_free(bfull); bfull = NULL;
7286    if (fout_final) SUMA_free(fout_final); fout_final = NULL;
7287 
7288    SUMA_RETURN(YUP);
7289 }
7290 
SUMA_Chung_Smooth_05_N_iter(double fwhm,double AvgLe,double * sigmap)7291 int SUMA_Chung_Smooth_05_N_iter (double fwhm, double AvgLe, double *sigmap)
7292 {
7293    static char FuncName[]={"SUMA_Chung_Smooth_05_N_iter"};
7294    double sequiv;
7295    int N_iter = -1;
7296 
7297    SUMA_ENTRY;
7298    SUMA_S_Err("Bad news in tennis shoes. Don't use me no more.");
7299    SUMA_RETURN(-1);
7300 
7301    /* make a suggestion */
7302    *sigmap = sqrt(-(AvgLe*AvgLe)/(2*log(0.1))); /* making the average SUMA_CHUNG_KERNEL_NUMER be 10 percent */
7303    /* have sigma and fwhm, what is N? */
7304    sequiv  = fwhm * 0.42466090;
7305    N_iter = SUMA_POW2(sequiv/(*sigmap));
7306    if (N_iter % 2) ++N_iter;
7307    if (N_iter < 4) N_iter = 4; /* need a few iterations */
7308    /* now reset sigma based on number of iterations */
7309    *sigmap = sequiv / sqrt(N_iter);
7310 
7311    SUMA_RETURN(N_iter);
7312 }
7313 
7314 
7315 /* see logistic.m and ilogistic.m, which are complimentary to FWHM_Beta2.m*/
SUMA_logistic(double * beta,double x)7316 double SUMA_logistic (double *beta, double x)
7317 {
7318    return(   beta[0] +
7319                   beta[1]*( ( 1+beta[2]*exp(-(x/beta[3])) ) / ( 1+beta[4]*exp(-(x/beta[3]))) ) );
7320 }
7321 
SUMA_ilogistic(double * beta,double a)7322 double SUMA_ilogistic (double *beta, double a)
7323 {
7324    static char FuncName[]={"SUMA_ilogistic"};
7325    double ll = (beta[0]+beta[1]-a) / (a*beta[4]-beta[1]*beta[2]-beta[0]*beta[4]);
7326    if (ll <= 0.0f) { fprintf(SUMA_STDERR,"Error SUMA_ilogistic:\n log of <= 0 value.\nReturning 0.0\n");return(0.0); }
7327    return(   -beta[3] * log( ll )  );
7328 }
7329 
7330 /* A function to guess the appropriate sigma (kernel std) from
7331 the desired (suggested) number of iterations (default 100) and suitable for
7332 a particular mesh
7333 beta is a vector of parameters for a logistic function that fits the function
7334 relating kernel width. beta is determined by the script FWHM_Beta2.m, see also FWHM_GetSig.m
7335 Make sure that changes there are reflected here and vice versa.
7336 */
7337 
7338 static double betadefault[]={ 0.2826  ,  1.2059 , -10.9964  ,  0.1691 ,  23.2139   };
7339 
SUMA_SigForFWHM(float AvgLe,double dfwhm,int * niterest,double * beta)7340 double SUMA_SigForFWHM(float AvgLe, double dfwhm, int *niterest, double *beta)
7341 {
7342    static char FuncName[]={"SUMA_SigForFWHM"};
7343    double wt, avg_wt;
7344    double Delta, Sig2, Sigma;
7345    int niter = -1;
7346    double SigmaLim[]={0.5, 11}; /*  Lower than 0.5 causes non linear slopes of FWHM versus Niter
7347                                     (see bunch with low Sigma/AvgLe in Figures 1 and especially 4)
7348                                     I do not have values higher than 11. */
7349    double DeltaLim[]={0.2, 1.5}; /* Low enough to allow Sigmas as low as 0.5,
7350                                     high is take care of below */
7351    SUMA_Boolean LocalHead=NOPE;
7352 
7353    SUMA_ENTRY;
7354 
7355    if (niterest) niter = *niterest;
7356    else niter = -1;
7357    if (dfwhm <= 0) { SUMA_S_Err("dfwhm is <=0 !"); SUMA_RETURN(-1.0); }
7358    if (niter <= 0) niter = 100;
7359    if (!beta) beta = betadefault;
7360    Sigma = -1;
7361 
7362    if (dfwhm/AvgLe < 2) {
7363       SUMA_S_Errv(
7364    "FWHM desired (%.3f) is too small relative to "
7365    "average intersegment length (AvgLe = %.3f).\n"
7366    "Expecting a ration of FWHM/AvgLe >= 2.0\n"
7367    "The automatic sigma selection is poor for this FWHM/AvgLe of %f.\n"
7368    "You can set sigma manually instead.\n"
7369    , dfwhm, AvgLe, dfwhm/AvgLe);
7370       SUMA_RETURN(-1.0);
7371    }
7372 
7373    /* upper limit for Delta, not that critical */
7374    DeltaLim[1] = SUMA_logistic(beta, SigmaLim[1]);
7375 
7376    /* What is the Delta needed? */
7377    Delta = AvgLe * dfwhm / sqrt(niter);
7378 
7379    /* within lims? */
7380    if (Delta > DeltaLim[1]) {
7381       Delta = DeltaLim[1];
7382       niter = SUMA_POW2((AvgLe * dfwhm / Delta));  /* VERY UNRELIABLE */
7383       SUMA_S_Notev("Large Delta, niter (wild) guess %d\n", niter);
7384       Sigma = dfwhm / AvgLe / 10.0;    /* sigma would be 1/10 of fwhm  (recall this Sigma is already normalized by avgle) */
7385    } else if ( Delta < DeltaLim[0]) {
7386       Delta = DeltaLim[0];
7387       niter = SUMA_POW2((AvgLe * dfwhm / Delta));
7388       SUMA_S_Notev("Low Delta, niter guess %d\n", niter);
7389       Sigma = SUMA_ilogistic(beta, Delta);
7390    } else {
7391       Sigma = SUMA_ilogistic(beta, Delta);
7392    }
7393 
7394    /* some more safeguards */
7395    if (Sigma < SigmaLim[0] && dfwhm > 3.0*AvgLe) { /*  That fwhm condition is mostly to keep the number of iterations largish when fwhm is small */
7396       Sigma = SigmaLim[0];
7397       Delta = SUMA_logistic(beta, Sigma);
7398       niter = SUMA_POW2((AvgLe * dfwhm / Delta));
7399       SUMA_S_Notev("Low Sigma/AvgLe, increased it to %.3f\n"
7400                    "Expected niter now: %d\n", Sigma, niter);
7401    }
7402 
7403    if (niterest) *niterest = niter;
7404 
7405    if (LocalHead) {
7406       fprintf (SUMA_STDERR,"For FWHM of = %f\n"
7407                         " kernel bandwidth per iteration= %f\n"
7408                         " kernel FWHM per iteration     = %f\n"
7409                         " N_iter (guess) = %d\n", dfwhm, Sigma*AvgLe, Sigma*AvgLe/0.42466090, niter);
7410    }
7411    avg_wt = SUMA_CHUNG_KERNEL_NUMER((AvgLe*AvgLe),Sigma*AvgLe);
7412    fprintf(SUMA_STDERR, "Kernel Bandwidth / Average Edge Distance = %f/%f = %f\n"
7413                         "   Corresponding Kernel Numerator = %g\n",
7414                            Sigma, AvgLe, Sigma*AvgLe, avg_wt);
7415 
7416    SUMA_RETURN(Sigma);
7417 }
7418 
7419 
7420 /*!
7421    Function to apply successive blurring operations so that no two neighboring nodes have the same
7422    value. The blurring is done on all columns of the data but the testing is done on column indexed
7423    icol
7424    If MaskZeros is set to 1 then nodes having a value of 0 are not considered
7425 */
SUMA_FixNN_Oversampling(SUMA_SurfaceObject * SO,SUMA_DSET * dset,byte * nmask,int icol,SUMA_Boolean MaskZeros)7426 SUMA_Boolean SUMA_FixNN_Oversampling ( SUMA_SurfaceObject *SO, SUMA_DSET *dset,
7427                                        byte *nmask,
7428                                        int icol, SUMA_Boolean MaskZeros)
7429 {
7430    static char FuncName[]={"SUMA_FixNN_Oversampling"};
7431    char stmp[100];
7432    float *c;
7433    double dfwhm, **wgt=NULL;
7434    double sigma, fwhmttt;
7435    byte *bfull = NULL, *blur_zone=NULL, *blur_zone_2=NULL;
7436    int N_nmask, N_zeros, ipass=0, N_iter, n, j, nj, nn;
7437    SUMA_Boolean SameZone=YUP; /* if YUP, then keep blurring the first zone detected.
7438                                  if NOPE, then zone changes with each pass (results in many interpolation artifacts at edges
7439                                          of shrinking zone...*/
7440    SUMA_Boolean LocalHead = NOPE;
7441 
7442    SUMA_ENTRY;
7443 
7444    if (!SO->EL || !SO->FN) {
7445       if (!SUMA_SurfaceMetrics(SO, "EdgeList", NULL)) {
7446          SUMA_S_Err("This elevator has not been inspected in 3 months!\nThis makes me SICK!\n");
7447          SUMA_RETURN(NOPE);
7448       }
7449    }
7450 
7451 
7452    if (nmask) {
7453       SUMA_LH("Copying mask");
7454       bfull = (byte*)SUMA_malloc(sizeof(byte)*SO->N_Node);
7455       memcpy((void*)bfull, (void *)nmask, sizeof(byte)*SO->N_Node);
7456    }
7457 
7458    SUMA_LH("Blur parameters");
7459    dfwhm = (double)SO->EL->AvgLe;
7460    N_iter = 20;
7461    sigma = SUMA_SigForFWHM( SO->EL->AvgLe, dfwhm, NULL, NULL);
7462    if (sigma == -1.0f) {
7463       SUMA_S_Err("Failed to select sigma\n");
7464       SUMA_RETURN(NOPE);
7465    } else {
7466       sigma = sigma * SO->EL->AvgLe;
7467    }
7468 
7469    ipass = 0;
7470    do {
7471       if (!blur_zone) { blur_zone = (byte *)SUMA_calloc(SO->N_Node, sizeof(byte)); }
7472       SUMA_LHv("Going to get 'c', pass %d\n", ipass);
7473       /* get the column of interest, nice and plump */
7474       c = SUMA_DsetCol2FloatFullSortedColumn (dset, icol, &bfull, 0.0, SO->N_Node, &N_nmask, YUP);
7475       /* Now for each node in the mask, see if you have similar values*/
7476       SUMA_LHv("Looking for zeros bfull: %p     c: %p.\n", bfull, c);
7477       N_zeros = 0;
7478       for (n=0; n<SO->N_Node; ++n) {
7479          if (!bfull || bfull[n]) {
7480             if (!MaskZeros || c[n] != 0.0f) {
7481                for (j=0; j<SO->FN->N_Neighb[n]; ++j) {
7482                   nj = SO->FN->FirstNeighb[n][j];
7483                   if (c[n] == c[nj]) {
7484                      blur_zone[n] = 1;
7485                      blur_zone[nj] = 1;
7486                      ++N_zeros;
7487                   }
7488                }
7489             }
7490          }
7491       }
7492 
7493       /* fatten blur_zone */
7494       if (!blur_zone_2) { blur_zone_2 = (byte *)SUMA_calloc(SO->N_Node, sizeof(byte)); }
7495       for (n=0; n<SO->N_Node; ++n) {
7496          if (blur_zone[n]) {
7497             for (j=0; j<SO->FN->N_Neighb[n]; ++j) {
7498                nj = SO->FN->FirstNeighb[n][j];
7499                if (!bfull || bfull[nj]) {
7500                   if (!blur_zone[nj] && (!MaskZeros || c[nj] != 0.0f)) blur_zone_2[nj] = 1;
7501                }
7502             }
7503          }
7504       }
7505 
7506       for (n=0; n<SO->N_Node; ++n) { if (blur_zone_2[n]) blur_zone[n] = 1; }
7507       if (!SameZone) { SUMA_free(blur_zone_2); blur_zone_2 = NULL; }
7508 
7509       if (N_zeros) {
7510          SUMA_LHv("Have %d redundancies at pass %d (%d nodes in mask).\n"
7511                   " We must reduce redundancies.\n"
7512                   " Have addtional blur fwhm of %f, sigma %g, Niter %d\n",
7513                   N_zeros, ipass, N_nmask, dfwhm, sigma, N_iter);
7514          if (!wgt) {
7515             wgt = SUMA_Chung_Smooth_Weights_07(SO, sigma);
7516             if (!wgt) {
7517                SUMA_SL_Err("Failed to compute weights.\n");
7518                SUMA_RETURN(NOPE);
7519             }
7520          }
7521          nn = 1; fwhmttt = -1.0;
7522          if (!SUMA_Chung_Smooth_07_dset ( SO, wgt,
7523                                        &nn, &fwhmttt,
7524                                        dset, NULL, blur_zone, 1)) {
7525             SUMA_S_Err("Failed in  SUMA_Chung_Smooth_07_dset");
7526             SUMA_RETURN(NOPE);
7527          }
7528       }
7529       if (LocalHead) {
7530          sprintf(stmp,"junk_blurzone_pass%d.1D.dset",ipass);
7531          SUMA_WRITE_ARRAY_1D(blur_zone, SO->N_Node, 1, stmp);
7532       }
7533       SUMA_free(c); c = NULL;
7534       if (!SameZone) { SUMA_free(blur_zone); blur_zone = NULL; }
7535       ++ipass;
7536    } while (N_zeros > 0.01 *  N_nmask);
7537 
7538    if (wgt) SUMA_free2D ((char **)wgt, SO->N_Node); wgt = NULL;
7539    if (bfull) bfull = NULL;
7540    SUMA_RETURN(YUP);
7541 }
7542 
SUMA_New_PC_XYZ_Proj(void)7543 SUMA_PC_XYZ_PROJ *SUMA_New_PC_XYZ_Proj(void)
7544 {
7545    static char FuncName[]={"SUMA_New_PC_XYZ_Proj"};
7546    SUMA_PC_XYZ_PROJ *pcp=NULL;
7547 
7548    SUMA_ENTRY;
7549 
7550    pcp = (SUMA_PC_XYZ_PROJ *)SUMA_calloc(1,sizeof(SUMA_PC_XYZ_PROJ));
7551    pcp->RotMat[0][0] = pcp->RotMat[1][1] =
7552                        pcp->RotMat[2][2] = pcp->RotMat[3][3] = 1.0;
7553    pcp->target[0] = '\0';
7554 
7555    pcp->lowest_node = pcp->highest_node = -1;
7556 
7557    SUMA_RETURN(pcp);
7558 }
7559 
SUMA_Free_PC_XYZ_Proj(SUMA_PC_XYZ_PROJ * pcp)7560 SUMA_PC_XYZ_PROJ *SUMA_Free_PC_XYZ_Proj(SUMA_PC_XYZ_PROJ *pcp)
7561 {
7562    static char FuncName[]={"SUMA_Free_PC_XYZ_Proj"};
7563 
7564    SUMA_ENTRY;
7565 
7566    if (!pcp) SUMA_RETURN(NULL);
7567    SUMA_ifree(pcp->xyzp);
7568    SUMA_ifree(pcp);
7569 
7570    SUMA_RETURN(pcp);
7571 }
7572 
SUMA_Write_PC_XYZ_Proj(SUMA_PC_XYZ_PROJ * pcp,char * prefix)7573 SUMA_Boolean SUMA_Write_PC_XYZ_Proj(SUMA_PC_XYZ_PROJ *pcp, char *prefix)
7574 {
7575    static char FuncName[]={"SUMA_Write_PC_XYZ_Proj"};
7576    FILE *fout=NULL;
7577    char *s1=NULL, *s2=NULL;
7578    int i;
7579 
7580    SUMA_ENTRY;
7581 
7582    if (!prefix) prefix = FuncName;
7583    if (!strcmp(prefix,"stdout")) {
7584       fout = stdout;
7585    } else if (!strcmp(prefix,"stderr")) {
7586       fout = stderr;
7587    } else {
7588       s2 = SUMA_ModifyName(prefix, "append",".xyzp", NULL);
7589       s1 = SUMA_Extension(s2,".1D.coord",NOPE); SUMA_ifree(s2);
7590       if (!THD_ok_overwrite() && SUMA_filexists(s1)) {
7591          SUMA_S_Err(
7592             "%s exists already, will not overwrite without -overwrite!",s1);
7593          SUMA_ifree(s1); SUMA_RETURN(NOPE);
7594       }
7595 
7596       if (!(fout = fopen(s1,"w"))) {
7597          SUMA_S_Err("Failed to open %s for writing", s1);
7598          SUMA_RETURN(NOPE);
7599       }
7600    }
7601 
7602    if (pcp->target[0] == 'p') {
7603       fprintf(fout,"#Projection onto %s [%f %f %f %f]\n",
7604                   pcp->target, pcp->target_params[0], pcp->target_params[1],
7605                   pcp->target_params[2], pcp->target_params[3]);
7606    } else if (pcp->target[0] == 'l') {
7607       fprintf(fout,"#Projection onto %s [%f %f %f]\n",
7608                   pcp->target, pcp->target_params[0], pcp->target_params[1],
7609                   pcp->target_params[2]);
7610    } else if (pcp->target[0] == '\0') {
7611       fprintf(fout,"#No projection\n");
7612    } else {
7613       fprintf(fout,"#Target %s not known\n", pcp->target);
7614    }
7615 
7616    fprintf(fout,"#\n#Transformation\n");
7617    fprintf(fout,"#   %.5f   %.5f   %.5f   %.5f\n"
7618                 "#   %.5f   %.5f   %.5f   %.5f\n"
7619                 "#   %.5f   %.5f   %.5f   %.5f\n",
7620                 pcp->RotMat[0][0], pcp->RotMat[0][1],
7621                                    pcp->RotMat[0][2], pcp->RotMat[0][3],
7622                 pcp->RotMat[1][0], pcp->RotMat[1][1],
7623                                    pcp->RotMat[1][2], pcp->RotMat[1][3],
7624                 pcp->RotMat[2][0], pcp->RotMat[2][1],
7625                                    pcp->RotMat[2][2], pcp->RotMat[2][3]);
7626 
7627    fprintf(fout,"#\n"
7628                 "#Eigen values, Eigen vectors, and closest cardinal direction\n"
7629                 "#   %f      %f   %f   %f      %c\n"
7630                 "#   %f      %f   %f   %f      %c\n"
7631                 "#   %f      %f   %f   %f      %c\n",
7632              pcp->eig[0], pcp->PC[0], pcp->PC[1], pcp->PC[2], pcp->closest[0],
7633              pcp->eig[1], pcp->PC[3], pcp->PC[4], pcp->PC[5], pcp->closest[1],
7634              pcp->eig[2], pcp->PC[6], pcp->PC[7], pcp->PC[8], pcp->closest[2]);
7635 
7636    fprintf(fout,"#\n#%d points, CM %f %f %f\n",
7637                 pcp->N_xyz, pcp->avg[0], pcp->avg[1], pcp->avg[2]);
7638 
7639    if (pcp->lowest_node >= 0) {
7640       fprintf(fout,"#\n#Min, Max extrema coordinates on projection line\n"
7641                 "#%d    %f %f %f%s\n"
7642                 "#%d    %f %f %f%s\n",
7643                 pcp->lowest_node,
7644                    pcp->lowest_proj[0],
7645                       pcp->lowest_proj[1], pcp->lowest_proj[2],
7646                    pcp->lowest_node < 0 ? "   #(Not computed)":"",
7647                 pcp->highest_node,
7648                    pcp->highest_proj[0],
7649                       pcp->highest_proj[1], pcp->highest_proj[2],
7650                    pcp->highest_node < 0 ? "   #(Not computed)":"");
7651    } else {
7652       fprintf(fout,"#\n#No extrema along line computed.\n");
7653    }
7654    fprintf(fout,"#\n#Projection coordinates (including any applied rotation)\n"
7655                 "#   Col. 0: X\n"
7656                 "#   Col. 1: Y\n"
7657                 "#   Col. 0: Z\n");
7658    if (pcp->xyzp) {
7659       for (i=0; i<pcp->N_xyz; ++i) {
7660          fprintf(fout, "%f   %f   %f\n",
7661                         pcp->xyzp[3*i], pcp->xyzp[3*i+1], pcp->xyzp[3*i+2]);
7662       }
7663    }
7664 
7665    if (fout != stdout && fout != stderr) fclose(fout); fout = NULL;
7666    SUMA_ifree(s1);
7667 
7668    /* Now write the eigen vectors into a DO */
7669    if (strcmp(prefix,"stdout") && strcmp(prefix,"stderr")) {
7670       s2 = SUMA_ModifyName(prefix, "append",".eigvec", NULL);
7671       s1 = SUMA_Extension(s2,".1D.do",NOPE); SUMA_ifree(s2);
7672       if (!THD_ok_overwrite() && SUMA_filexists(s1)) {
7673          SUMA_S_Err(
7674             "%s exists already, will not overwrite without -overwrite!",s1);
7675          SUMA_ifree(s1); SUMA_RETURN(NOPE);
7676       }
7677 
7678       if (!(fout = fopen(s1,"w"))) {
7679          SUMA_S_Err("Failed to open %s for writing", s1);
7680          SUMA_RETURN(NOPE);
7681       }
7682       fprintf(fout,"#oriented_segments\n"
7683                    "%f %f %f    %f %f %f    %f %f %f %f\n"
7684                    "%f %f %f    %f %f %f    %f %f %f %f\n"
7685                    "%f %f %f    %f %f %f    %f %f %f %f\n",
7686          pcp->avg[0], pcp->avg[1], pcp->avg[2],
7687             pcp->avg[0]+pcp->PC[0]*pcp->eig[0]/2.0,
7688             pcp->avg[1]+pcp->PC[1]*pcp->eig[0]/2.0,
7689             pcp->avg[2]+pcp->PC[2]*pcp->eig[0]/2.0,
7690                1.0, 0.0, 0.0, 1.0,
7691          pcp->avg[0], pcp->avg[1], pcp->avg[2],
7692             pcp->avg[0]+pcp->PC[3]*pcp->eig[1]/2.0,
7693             pcp->avg[1]+pcp->PC[4]*pcp->eig[1]/2.0,
7694             pcp->avg[2]+pcp->PC[5]*pcp->eig[1]/2.0,
7695                0.0, 1.0, 0.0, 1.0,
7696          pcp->avg[0], pcp->avg[1], pcp->avg[2],
7697             pcp->avg[0]+pcp->PC[6]*pcp->eig[2]/2.0,
7698             pcp->avg[1]+pcp->PC[7]*pcp->eig[2]/2.0,
7699             pcp->avg[2]+pcp->PC[8]*pcp->eig[2]/2.0,
7700                0.0, 0.0, 1.0, 1.0);
7701       fclose(fout); fout = NULL;
7702       SUMA_ifree(s1);
7703    }
7704 
7705    SUMA_RETURN(YUP);
7706 }
7707 
7708 /*!
7709    \brief Project coordinates along a plane prependicular to one
7710    of the principal components.
7711 
7712    \param xyz (float *): vector of coordinate triplets
7713    \param N_xyz (int) : number of coordinate triplets (nodes)
7714    \param iref (int): When projecting onto a plane, make plane pass through
7715                       node iref at xyz[3*iref], xyz[3*iref+1], xyz[3*iref+2]
7716 
7717                       When projecting onto a direction, the projections are
7718                       offset by xyz[3*iref], xyz[3*iref+1], xyz[3*iref+2]
7719 
7720                       If iref == -1, the function chooses iref = 0 if projecting
7721                       to a plane. A negative iref is ignored if projecting to a
7722                       direction.
7723 
7724                       iref is ignored when xyzref is not null.
7725 
7726    \param xyzref (float *): Instead of using coordinates of node iref,
7727                             use coordinates from xyzref.
7728                             When projecting onto a line, xyzref is an
7729                             offset applied to projected coordinates.
7730                             When projecting onto a plane, that plane is
7731                             made to pass through xyzref
7732 
7733    \param compnum (SUMA_PC_PROJ): Choose  the projection plane's normal
7734                               or the projection direction line:
7735 
7736               NOTE: enum SUMA_PC_PROJ now replaces the integer codes
7737 
7738                               Plane projections along various normals:
7739                                   0 normal is 1st principal vector
7740                                   1 normal is  2nd principal vector
7741                                   2 normal is  3rd principal vector
7742                                   3 normal is  component closest to Z axis
7743                                   4 normal is  component closest to Y axis
7744                                   5 normal is  component closest to X axis
7745                               Line projections:
7746                                   6 project along 1st principal vector
7747                                   7 project along 2nd principal vector
7748                                   8 project along 3rd principal vector
7749                                   9 project along component closest to Z axis
7750                                  10 project along component closest to Y axis
7751                                  11 project along component closest to X axis
7752 
7753               use NO_PRJ for no projections, or rotations, just return after PC.
7754 
7755    \param rotate (SUMA_PC_ROT): Rotate projection plane or direction as follows:
7756                            ROT_2_Z rotate projection plane so it becomes
7757                         parallel to XY plane,
7758                            ROT_2_Y for XZ plane
7759                            ROT_2_X for YZ plane
7760                         If projecting to lines, then
7761                            ROT_2_Z rotates line to Z axis
7762                            ROT_2_Y rotates line to Y axis
7763                            ROT_2_X rotates line to X axis
7764    \param remean (int): if non-zero, then add the mean of each column back to
7765                         xyz after it got removed by the PC decomposition
7766 */
7767 
SUMA_Project_Coords_PCA(float * xyz,int N_xyz,int iref,float * xyzref,SUMA_PC_PROJ compnum,SUMA_PC_ROT rotate,int remean)7768 SUMA_PC_XYZ_PROJ *SUMA_Project_Coords_PCA (float *xyz, int N_xyz, int iref,
7769                                 float *xyzref,
7770                                 SUMA_PC_PROJ compnum, SUMA_PC_ROT rotate,
7771                                 int remean)
7772 {
7773    static char FuncName[]={"SUMA_Project_Coords_PCA"};
7774    int i, i3, lineproj=0;
7775    double trace, pc_vec[9], pc_eig[3], Eq[4], pc0[3], proj[3], rdot, avg[3];
7776    float *xyzp=NULL, fv[3], **fm=NULL, *p1, avgf[3],
7777          pcf0[3], pp[3], ddx[3], ddy[3], ddz[3], dx, dy, dz, pc_md[3];
7778    char pc_m[3];
7779    SUMA_PC_XYZ_PROJ *pcp=NULL;
7780    SUMA_Boolean LocalHead = NOPE;
7781 
7782    SUMA_ENTRY;
7783 
7784    SUMA_LH("Xforming, iref=%d, xref=%p, remean=%d", iref, xyzref, remean);
7785 
7786    if (compnum < NO_PRJ || compnum >= N_PRJ) {
7787       SUMA_S_Errv("Bad compnum %d, range [%d %d]\n",
7788                   compnum, NO_PRJ, N_PRJ-1);
7789       SUMA_RETURN(NULL);
7790    }
7791 
7792    if (compnum >= E1_DIR_PRJ) lineproj = 1;
7793    else lineproj = 0;
7794 
7795    if (xyzref) {
7796       if (iref >= 0) {
7797          SUMA_S_Warn("iref ignored when xyzref is not NULL");
7798          iref = -1;
7799       }
7800    } else {
7801       if (iref < 0 && !lineproj) iref = 0;
7802       if (iref >= N_xyz) {
7803          SUMA_S_Errv("Bad iref %d, range [%d %d]\n", iref, 0, N_xyz-1);
7804          SUMA_RETURN(NULL);
7805       }
7806    }
7807 
7808    /* Need to go column major */
7809    xyzp = (float *)SUMA_calloc(N_xyz*3, sizeof(float));
7810    avg[0] = avg[1] = avg[2] = 0.0;
7811    for (i=0; i<N_xyz; ++i) {
7812       xyzp[i]         = xyz[3*i  ]; avg[0] += xyz[3*i  ];
7813       xyzp[i+N_xyz]   = xyz[3*i+1]; avg[1] += xyz[3*i+1];
7814       xyzp[i+2*N_xyz] = xyz[3*i+2]; avg[2] += xyz[3*i+2];
7815    }
7816    avg[0] /= N_xyz; avg[1] /= N_xyz; avg[2] /= N_xyz;
7817 
7818    if ((trace = pca_fast3(xyzp, N_xyz, 1, pc_vec, pc_eig)) < 0){
7819       SUMA_S_Err("Failed calculating PC\n");
7820       SUMA_free(xyzp); xyzp=NULL;
7821       SUMA_RETURN(NULL);
7822    }
7823 
7824    /* relate directions to principal directions */
7825    ddx[0] = 1.0; ddx[1] = 0.0; ddx[2] = 0.0;
7826    ddy[0] = 0.0; ddy[1] = 1.0; ddy[2] = 0.0;
7827    ddz[0] = 0.0; ddz[1] = 0.0; ddz[2] = 1.0;
7828 
7829    pp[0] = pc_vec[0]; pp[1] = pc_vec[3]; pp[2] = pc_vec[6];
7830    dx = SUMA_ABS(SUMA_MT_DOT(pp,ddx));
7831    dy = SUMA_ABS(SUMA_MT_DOT(pp,ddy));
7832    dz = SUMA_ABS(SUMA_MT_DOT(pp,ddz));
7833         if (dx >= dy && dx >= dz) { pc_m[0]='X'; pc_md[0]=SUMA_MT_DOT(pp,ddx); }
7834    else if (dy >= dx && dy >= dz) { pc_m[0]='Y'; pc_md[0]=SUMA_MT_DOT(pp,ddy); }
7835    else { pc_m[0]='Z'; pc_md[0]=SUMA_MT_DOT(pp,ddz); }
7836 
7837    pp[0] = pc_vec[1]; pp[1] = pc_vec[4]; pp[2] = pc_vec[7];
7838    dx = SUMA_ABS(SUMA_MT_DOT(pp,ddx));
7839    dy = SUMA_ABS(SUMA_MT_DOT(pp,ddy));
7840    dz = SUMA_ABS(SUMA_MT_DOT(pp,ddz));
7841         if (dy >= dx && dy >= dz) { pc_m[1]='Y'; pc_md[1]=SUMA_MT_DOT(pp,ddy); }
7842    else if (dx >= dy && dx >= dz) { pc_m[1]='X'; pc_md[1]=SUMA_MT_DOT(pp,ddx); }
7843    else { pc_m[1]='Z'; pc_md[1]=SUMA_MT_DOT(pp,ddz); }
7844 
7845    pp[0] = pc_vec[2]; pp[1] = pc_vec[5]; pp[2] = pc_vec[8];
7846         if (pc_m[0] != 'X' && pc_m[1] != 'X') {
7847          pc_m[2]='X'; pc_md[2]=SUMA_MT_DOT(pp,ddx); }
7848    else if (pc_m[0] != 'Y' && pc_m[1] != 'Y') {
7849       pc_m[2]='Y'; pc_md[2]=SUMA_MT_DOT(pp,ddy); }
7850    else {
7851       pc_m[2] = 'Z'; pc_md[2]=SUMA_MT_DOT(pp,ddz); }
7852 
7853    SUMA_LHv("PCA results:\n"
7854             "Eig[0]=%f     pc[0]=[%f %f %f] closest to %c axis (dot %f)\n"
7855             "Eig[1]=%f     pc[1]=[%f %f %f] closest to %c axis (dot %f)\n"
7856             "Eig[2]=%f     pc[2]=[%f %f %f] closest to %c axis (dot %f)\n"
7857             "Avg coord: %f %f %f\n",
7858             pc_eig[0], pc_vec[0], pc_vec[3], pc_vec[6], pc_m[0], pc_md[0],
7859             pc_eig[1], pc_vec[1], pc_vec[4], pc_vec[7], pc_m[1], pc_md[1],
7860             pc_eig[2], pc_vec[2], pc_vec[5], pc_vec[8], pc_m[2], pc_md[2],
7861             avg[0], avg[1], avg[2]);
7862    #if 0
7863       /* This does not work well for getting proper depth signs
7864          See comment in SUMA_NodeDepth where flipit is used */
7865    /* flip so that we are along the cardinals. Signs don't matter anyway */
7866    if (pc_md[0] < 0) {
7867       SUMA_LH("Flipping pc[0]");
7868       pc_vec[0] = -pc_vec[0]; pc_vec[3] = -pc_vec[3]; pc_vec[6] = -pc_vec[6];
7869    }
7870    if (pc_md[1] < 0) {
7871       SUMA_LH("Flipping pc[1]");
7872       pc_vec[1] = -pc_vec[1]; pc_vec[4] = -pc_vec[4]; pc_vec[7] = -pc_vec[7];
7873    }
7874    if (pc_md[2] < 0) {
7875       SUMA_LH("Flipping pc[2]");
7876       pc_vec[2] = -pc_vec[2]; pc_vec[5] = -pc_vec[5]; pc_vec[8] = -pc_vec[8];
7877    }
7878    #endif
7879    /* prep for output */
7880    pcp = SUMA_New_PC_XYZ_Proj();
7881    SUMA_COPY_VEC(avg, pcp->avg, 3, double, double);
7882    SUMA_COPY_VEC(pc_eig, pcp->eig, 3, double, double);
7883    SUMA_COPY_VEC(pc_m, pcp->closest, 3, char, char);
7884    pcp->PC[0] = pc_vec[0];
7885    pcp->PC[1] = pc_vec[3];
7886    pcp->PC[2] = pc_vec[6];
7887    pcp->PC[3] = pc_vec[1];
7888    pcp->PC[4] = pc_vec[4];
7889    pcp->PC[5] = pc_vec[7];
7890    pcp->PC[6] = pc_vec[2];
7891    pcp->PC[7] = pc_vec[5];
7892    pcp->PC[8] = pc_vec[8];
7893    pcp->xyzp = xyzp;
7894    pcp->N_xyz = N_xyz;
7895 
7896    /* need to transpose xyzp again for the remainder  */
7897    if (!remean) avg[0] = avg[1] = avg[2] = 0.0;
7898 
7899    {
7900       float *xxx=(float *)SUMA_calloc(N_xyz*3, sizeof(float));
7901       memcpy(xxx, xyzp,3*N_xyz*sizeof(float));
7902       for (i=0; i<N_xyz; ++i) {
7903          xyzp[3*i  ] = xxx[i        ]+avg[0];
7904          xyzp[3*i+1] = xxx[i+  N_xyz]+avg[1];
7905          xyzp[3*i+2] = xxx[i+2*N_xyz]+avg[2];
7906       }
7907       SUMA_free(xxx); xxx=NULL;
7908    }
7909    if (LocalHead) {
7910       SUMA_WRITE_ARRAY_1D(xyzp,3*N_xyz,3,"DBGafterpc.1D");
7911    }
7912    if (compnum == NO_PRJ) SUMA_RETURN(pcp);
7913 
7914 
7915    if (compnum != E1_PLN_PRJ && compnum != E2_PLN_PRJ &&
7916        compnum != E3_PLN_PRJ && compnum != E1_DIR_PRJ &&
7917        compnum != E2_DIR_PRJ && compnum != E3_DIR_PRJ ) {
7918              if (compnum == EZ_PLN_PRJ || compnum == EZ_DIR_PRJ) {
7919          /* find direction closest to Z */
7920                if (pc_m[0] == 'Z') compnum = E1_PLN_PRJ;
7921          else  if (pc_m[1] == 'Z') compnum = E2_PLN_PRJ;
7922          else  if (pc_m[2] == 'Z') compnum = E3_PLN_PRJ;
7923       } else if (compnum == EY_PLN_PRJ || compnum == EY_DIR_PRJ) {
7924          /* find direction closest to Y */
7925                if (pc_m[0] == 'Y') compnum = E1_PLN_PRJ;
7926          else  if (pc_m[1] == 'Y') compnum = E2_PLN_PRJ;
7927          else  if (pc_m[2] == 'Y') compnum = E3_PLN_PRJ;
7928       } else if (compnum == EX_PLN_PRJ || compnum == EX_DIR_PRJ) {
7929          /* find direction closest to X */
7930                if (pc_m[0] == 'X') compnum = E1_PLN_PRJ;
7931          else  if (pc_m[1] == 'X') compnum = E2_PLN_PRJ;
7932          else  if (pc_m[2] == 'X') compnum = E3_PLN_PRJ;
7933       } else {
7934          SUMA_S_Errv("Bad value for compnum %d, setting to 0\n",
7935                      compnum);
7936          compnum = 0;
7937       }
7938    } else if (compnum == E1_DIR_PRJ || compnum == E2_DIR_PRJ ||
7939               compnum == E3_DIR_PRJ) {
7940       switch (compnum) {
7941          case E1_DIR_PRJ:
7942             compnum = E1_PLN_PRJ;
7943             break;
7944          case E2_DIR_PRJ:
7945             compnum = E2_PLN_PRJ;
7946             break;
7947          case E3_DIR_PRJ:
7948             compnum = E3_PLN_PRJ;
7949             break;
7950          default:
7951             SUMA_S_Err("Should not be here with %d\nLeaky return.", compnum);
7952             SUMA_RETURN(NULL);
7953       }
7954    }
7955 
7956    if (compnum < 0 || compnum > 2) {
7957       SUMA_S_Err("Not at this stage, compnum (%d) must now be between 0 and 2",
7958                   compnum);
7959       SUMA_RETURN(SUMA_Free_PC_XYZ_Proj(pcp));
7960    }
7961    if (!lineproj) {
7962       /* Find equation of plane passing by node iref or xyzref and having the PC
7963          for a normal */
7964       pc0[0] = pc_vec[compnum  ];
7965       pc0[1] = pc_vec[compnum+3];
7966       pc0[2] = pc_vec[compnum+6];
7967       for (i=0; i<3;++i) pcf0[i]=pc0[i];
7968 
7969       if (xyzref) p1 = xyzref;
7970       else p1 = &(xyz[3*iref]);
7971       SUMA_LHv("   Forming plane with normal [%f %f %f], \n"
7972                "   passing by point xyzref [%f %f %f]\n",
7973          pc0[0], pc0[1], pc0[2], p1[0], p1[1], p1[2]);
7974       SUMA_PLANE_NORMAL_POINT(pc0, p1, Eq);
7975 
7976       /* project all points to the plane */
7977       for (i=0; i<N_xyz; ++i) {
7978          i3=3*i;
7979          proj[0] = xyzp[i3];proj[1] = xyzp[i3+1];proj[2] = xyzp[i3+2];
7980          SUMA_PROJECT_ONTO_PLANE(Eq, proj, (xyzp+3*i));
7981       }
7982       if (iref) {
7983          SUMA_LHv(" After plane projection\n"
7984                   "izero now: [%f %f %f]\n"
7985                   "iref  now: [%f %f %f] %d\n",
7986                   xyzp[0], xyzp[1], xyzp[2],
7987                   xyzp[iref*3], xyzp[iref*3+1], xyzp[iref*3+2], iref);
7988       } else {
7989          SUMA_LHv(" After plane projection\n"
7990                   "izero now: [%f %f %f]\n",
7991                   xyzp[0], xyzp[1], xyzp[2]);
7992       }
7993       sprintf(pcp->target,"plane");
7994       SUMA_COPY_VEC(Eq, pcp->target_params, 4, double, double);
7995    } else {
7996       if (iref >= 0) xyzref = &(xyz[3*iref]);
7997       if (!xyzref && remean) {
7998          avgf[0] = avg[0]; avgf[1] = avg[1]; avgf[2] = avg[2];
7999          xyzref = avgf;
8000       }
8001       pc0[0] = pc_vec[compnum  ];
8002       pc0[1] = pc_vec[compnum+3];
8003       pc0[2] = pc_vec[compnum+6];
8004       SUMA_LH( "Projecting onto direction [%f %f %f]", pc0[0], pc0[1], pc0[2]);
8005       for (i=0; i<3;++i) pcf0[i]=pc0[i];
8006       for (i=0; i<N_xyz; ++i) {
8007          i3=3*i;
8008          proj[0] = xyzp[i3]-avg[0];
8009          proj[1] = xyzp[i3+1]-avg[1];
8010          proj[2] = xyzp[i3+2]-avg[2];
8011          SUMA_PROJECT_ONTO_DIR(pc0, proj, (xyzp+3*i));
8012 
8013       }
8014 
8015       if (xyzref) {
8016          SUMA_LH( "Offseting projections by [%f %f %f]",
8017                   xyzref[0], xyzref[1], xyzref[2] );
8018          for (i=0; i<N_xyz; ++i) {
8019             i3=3*i;
8020             xyzp[i3  ] += xyzref[0];
8021             xyzp[i3+1] += xyzref[1];
8022             xyzp[i3+2] += xyzref[2];
8023          }
8024       }
8025 
8026       if (iref>=0) {
8027          SUMA_LHv(" After line projection\n"
8028                   "izero now: [%f %f %f]\n"
8029                   "iref  now: [%f %f %f]\n",
8030                   xyzp[0], xyzp[1], xyzp[2],
8031                   xyzp[iref*3], xyzp[iref*3+1], xyzp[iref*3+2]);
8032       } else {
8033          SUMA_LHv(" After line projection\n"
8034                   "izero now: [%f %f %f]\n",
8035                   xyzp[0], xyzp[1], xyzp[2]);
8036       }
8037       sprintf(pcp->target,"line");
8038       SUMA_COPY_VEC(pc0, pcp->target_params, 3, double, double);
8039       pcp->target_params[3] = 0.0;
8040    }
8041    if (rotate > NO_ROT) {
8042       /* Now rotate all projected points*/
8043       switch (rotate) {
8044          case ROT_2_Z:
8045             fv[0]=0.0; fv[1]=0.0; fv[2]=1.0; /* Z axis */
8046             break;
8047          case ROT_2_Y:
8048             fv[0]=0.0; fv[1]=1.0; fv[2]=0.0; /* Y axis */
8049             break;
8050          case ROT_2_X:
8051             fv[0]=1.0; fv[1]=0.0; fv[2]=0.0; /* X axis */
8052             break;
8053          default:
8054             SUMA_S_Errv("Bad rotation parameter %d\n", rotate);
8055             SUMA_RETURN(SUMA_Free_PC_XYZ_Proj(pcp));
8056       }
8057       rdot = SUMA_SIGN(SUMA_MT_DOT(pcf0, fv));
8058       SUMA_LHv("Dot product before rotation: %f\n",
8059                SUMA_MT_DOT(pcf0, fv));
8060       fm = (float **)SUMA_allocate2D(4,4,sizeof(float));
8061       if (!SUMA_FromToRotation (pcf0, fv,  fm)) {
8062             SUMA_S_Err("Failed to get rotation");
8063          SUMA_free2D((char **)fm, 4); fm = NULL;
8064          SUMA_RETURN(SUMA_Free_PC_XYZ_Proj(pcp));
8065       }
8066       proj[0] = pcf0[0]*fm[0][0] +
8067                 pcf0[1]*fm[0][1] +
8068                 pcf0[2]*fm[0][2] ;
8069       proj[1] = pcf0[0]*fm[1][0] +
8070                 pcf0[1]*fm[1][1] +
8071                 pcf0[2]*fm[1][2] ;
8072       proj[2] = pcf0[0]*fm[2][0] +
8073                 pcf0[1]*fm[2][1] +
8074                 pcf0[2]*fm[2][2] ;
8075       SUMA_LHv("Dot product after rotation: %f\n",
8076                SUMA_MT_DOT(proj, fv));
8077       /* Apply rotation to all points */
8078       for (i=0; i<N_xyz; ++i) {
8079          i3=3*i;
8080          proj[0] = xyzp[i3  ]*fm[0][0] +
8081                    xyzp[i3+1]*fm[0][1] +
8082                    xyzp[i3+2]*fm[0][2] ;
8083          proj[1] = xyzp[i3  ]*fm[1][0] +
8084                    xyzp[i3+1]*fm[1][1] +
8085                    xyzp[i3+2]*fm[1][2] ;
8086          proj[2] = xyzp[i3  ]*fm[2][0] +
8087                    xyzp[i3+1]*fm[2][1] +
8088                    xyzp[i3+2]*fm[2][2] ;
8089          xyzp[i3  ] = proj[0];
8090          xyzp[i3+1] = proj[1];
8091          xyzp[i3+2] = proj[2];
8092       }
8093 
8094       for (i=0; i<4; ++i) {
8095          for (i3=0; i3<4; ++i3) {
8096             pcp->RotMat[i][i3] = fm[i][i3];
8097          }
8098       }
8099       pcp->RotMat[3][3] = 1.0;
8100 
8101       SUMA_LHv("After rotate %d\n"
8102                "izero now: [%f %f %f]\n"
8103                "iref  now: [%f %f %f]\n"
8104                "initial izero still: [%f %f %f]\n",
8105                rotate,
8106                xyzp[0], xyzp[1], xyzp[2],
8107                iref >= 0 ? xyzp[iref*3]:-1.0,
8108                iref >= 0 ? xyzp[iref*3+1]:-1.0,
8109                iref >= 0 ? xyzp[iref*3+2]:-1.0,
8110                xyz[0], xyz[1], xyz[2]);
8111 
8112       if (fm) SUMA_free2D((char **)fm, 2); fm = NULL;
8113    }
8114 
8115    SUMA_RETURN(pcp);
8116 }
8117 
SUMA_VoxelDepth(THD_3dim_dataset * dset,SUMA_PC_PROJ prjdir,float ** dpth,float thr,byte ** cmaskp,int applymask)8118 int SUMA_VoxelDepth(THD_3dim_dataset *dset,
8119                     SUMA_PC_PROJ prjdir , float **dpth,
8120                     float thr, byte **cmaskp, int applymask)
8121 {
8122    static char FuncName[]={"SUMA_VoxelDepth"};
8123    float *sdpth=NULL, *xyz=NULL;
8124    int ii, jj, kk, nn, vv, nvox=0;
8125    byte *cmask=NULL, *scmask=NULL;
8126    int N_inmask = -1;
8127    THD_fvec3 mm, di;
8128    SUMA_Boolean LocalHead = NOPE;
8129 
8130    SUMA_ENTRY;
8131 
8132    if (!dset) {
8133       SUMA_S_Err("NULL input");
8134       SUMA_RETURN(-1);
8135    }
8136    if (dpth && *dpth) {
8137       SUMA_S_Err("If passing dpth, *dpth must be NULL");
8138       SUMA_RETURN(-1);
8139    }
8140    if (cmaskp && *cmaskp) {
8141       SUMA_S_Err("If passing cmaskp, *cmaskp must be NULL");
8142       SUMA_RETURN(-1);
8143    }
8144 
8145    if (prjdir != EZ_DIR_PRJ &&
8146        prjdir != E1_DIR_PRJ) {
8147       SUMA_S_Err("Not tested for prjdir = %d", prjdir);
8148       SUMA_RETURN(-1);
8149    }
8150 
8151    /* get xyz of all non-zero voxels in dset */
8152    if (!(cmask = THD_makemask( dset , 0 , 1.0, -1.0 ))) {
8153       SUMA_S_Err("Failed to get mask");
8154       SUMA_RETURN(-1);
8155    }
8156    for (nvox=0, ii=0; ii<DSET_NVOX(dset); ++ii) {
8157       if (cmask[ii]) ++nvox;
8158    }
8159    if (!(xyz = (float *)SUMA_calloc(3*nvox, sizeof(float)))) {
8160       SUMA_S_Errv("Failed to allocate for %d floats\n",
8161                   3*nvox);
8162       free(cmask);
8163       SUMA_RETURN(-1);
8164    }
8165    vv=0; nn = 0;
8166    for (kk=0; kk<DSET_NZ(dset); ++kk) {
8167    for (jj=0; jj<DSET_NY(dset); ++jj) {
8168    for (ii=0; ii<DSET_NX(dset); ++ii) {
8169       if (cmask[vv]) {
8170          mm.xyz[0] = DSET_XORG(dset)+ii*DSET_DX(dset);
8171          mm.xyz[1] = DSET_YORG(dset)+jj*DSET_DY(dset);
8172          mm.xyz[2] = DSET_ZORG(dset)+kk*DSET_DZ(dset);
8173          di = SUMA_THD_3dmm_to_dicomm( dset->daxes->xxorient,
8174                                        dset->daxes->yyorient,
8175                                        dset->daxes->zzorient, mm);
8176          xyz[3*nn  ] = di.xyz[0];
8177          xyz[3*nn+1] = di.xyz[1];
8178          xyz[3*nn+2] = di.xyz[2];
8179          ++nn;
8180       }
8181       ++vv;
8182    } } }
8183 
8184    SUMA_LHv("Calling depth function, thr %f\n", thr);
8185    N_inmask = SUMA_NodeDepth(xyz, nvox, prjdir,
8186                              dpth ? &sdpth:NULL,
8187                              thr, &scmask, NULL, NULL);
8188    SUMA_LHv("%d / %d voxels met threshold\n", N_inmask, nvox);
8189    SUMA_free(xyz); xyz = NULL;
8190 
8191    /* Does the user want voxel depths back ? */
8192    if (dpth) {
8193       SUMA_LH("Returning depth values");
8194       *dpth = (float *)SUMA_calloc(DSET_NVOX(dset), sizeof(float));
8195       vv=0; nn = 0;
8196       for (kk=0; kk<DSET_NZ(dset); ++kk) {
8197       for (jj=0; jj<DSET_NY(dset); ++jj) {
8198       for (ii=0; ii<DSET_NX(dset); ++ii) {
8199          if (cmask[vv]) {
8200             *(*dpth+vv)=sdpth[nn]; ++nn;
8201          }
8202          ++vv;
8203       } } }
8204    }
8205    /* Does the user want mask back ? */
8206    if (cmaskp) {
8207       SUMA_LH("Returning byte mask");
8208       *cmaskp = (byte *)SUMA_calloc(DSET_NVOX(dset), sizeof(byte));
8209       vv=0; nn = 0;
8210       for (kk=0; kk<DSET_NZ(dset); ++kk) {
8211       for (jj=0; jj<DSET_NY(dset); ++jj) {
8212       for (ii=0; ii<DSET_NX(dset); ++ii) {
8213          if (cmask[vv]) {
8214             *(*cmaskp+vv)=scmask[nn]; ++nn;
8215          }
8216          ++vv;
8217       } } }
8218    }
8219    /* Apply the mask ? */
8220    if (applymask) {
8221       SUMA_LH("Applying mask");
8222       switch (DSET_BRICK_TYPE(dset,0)) {
8223          case MRI_byte:
8224             {  byte *bv = (byte *)DSET_ARRAY(dset,0) ;
8225                vv=0; nn = 0;
8226                for (kk=0; kk<DSET_NZ(dset); ++kk) {
8227                for (jj=0; jj<DSET_NY(dset); ++jj) {
8228                for (ii=0; ii<DSET_NX(dset); ++ii) {
8229                   if (cmask[vv]) {
8230                      if (!scmask[nn]) bv[vv] = 0;
8231                      ++nn;
8232                   }
8233                   ++vv;
8234                } } }
8235             }
8236             break;
8237          case MRI_short:
8238             {  short *sv = (short *)DSET_ARRAY(dset,0) ;
8239                vv=0; nn = 0;
8240                for (kk=0; kk<DSET_NZ(dset); ++kk) {
8241                for (jj=0; jj<DSET_NY(dset); ++jj) {
8242                for (ii=0; ii<DSET_NX(dset); ++ii) {
8243                   if (cmask[vv]) {
8244                      if (!scmask[nn]) sv[vv] = 0;
8245                      ++nn;
8246                   }
8247                   ++vv;
8248                } } }
8249             }
8250             break;
8251          case MRI_float:
8252             {  float *fv = (float *)DSET_ARRAY(dset,0) ;
8253                vv=0; nn = 0;
8254                for (kk=0; kk<DSET_NZ(dset); ++kk) {
8255                for (jj=0; jj<DSET_NY(dset); ++jj) {
8256                for (ii=0; ii<DSET_NX(dset); ++ii) {
8257                   if (cmask[vv]) {
8258                      if (!scmask[nn]) fv[vv] = 0;
8259                      ++nn;
8260                   }
8261                   ++vv;
8262                } } }
8263             }
8264             break;
8265          default:
8266             SUMA_S_Errv("Dset type %d not supported\n", DSET_BRICK_TYPE(dset,0));
8267             break;
8268       }
8269    }
8270 
8271    SUMA_LH("Liberte");
8272    if (cmask) free(cmask); cmask = NULL;
8273    if (scmask) SUMA_free(scmask);
8274    if (sdpth) SUMA_free(sdpth);
8275    SUMA_RETURN(N_inmask);
8276 }
8277 
8278 
SUMA_VoxelDepth_Z(THD_3dim_dataset * dset,byte * cmasku,float ** dpth,float thr,byte ** cmaskp,int applymask,float peakperc,float * ztop)8279 int SUMA_VoxelDepth_Z(THD_3dim_dataset *dset, byte *cmasku,
8280                     float **dpth,
8281                     float thr, byte **cmaskp, int applymask,
8282                     float peakperc, float *ztop)
8283 {
8284    static char FuncName[]={"SUMA_VoxelDepth_Z"};
8285    float *zs=NULL, *z=NULL;
8286    int ii, jj, kk, nn, vv, nvox=0, itop=0;
8287    byte *cmask=NULL;
8288    int N_inmask = -1;
8289    THD_fvec3 mm, di;
8290    SUMA_Boolean LocalHead = NOPE;
8291 
8292    SUMA_ENTRY;
8293 
8294    if (!dset) {
8295       SUMA_S_Err("NULL input");
8296       SUMA_RETURN(-1);
8297    }
8298    if (dpth && *dpth) {
8299       SUMA_S_Err("If passing dpth, *dpth must be NULL");
8300       SUMA_RETURN(-1);
8301    }
8302    if (cmaskp && *cmaskp) {
8303       SUMA_S_Err("If passing cmaskp, *cmaskp must be NULL");
8304       SUMA_RETURN(-1);
8305    }
8306 
8307    /* get xyz of all non-zero voxels in dset */
8308    if (!cmasku) {
8309       if (!(cmask = THD_makemask( dset , 0 , 1.0, -1.0 ))) {
8310          SUMA_S_Err("Failed to get mask");
8311          SUMA_RETURN(-1);
8312       }
8313    } else {
8314       cmask = cmasku;
8315    }
8316    for (nvox=0, ii=0; ii<DSET_NVOX(dset); ++ii) {
8317       if (cmask[ii]) ++nvox;
8318    }
8319 
8320    if (!(z = (float *)SUMA_calloc(nvox, sizeof(float)))) {
8321       SUMA_S_Errv("Failed to allocate for %d floats\n",
8322                   nvox);
8323       if (!cmasku) free(cmask);
8324       SUMA_RETURN(-1);
8325    }
8326    if (!(zs = (float *)SUMA_calloc(nvox, sizeof(float)))) {
8327       SUMA_S_Errv("Failed to allocate for %d floats\n",
8328                   nvox);
8329       if (!cmasku) free(cmask);
8330       SUMA_RETURN(-1);
8331    }
8332    vv=0; nn = 0;
8333    for (kk=0; kk<DSET_NZ(dset); ++kk) {
8334    for (jj=0; jj<DSET_NY(dset); ++jj) {
8335    for (ii=0; ii<DSET_NX(dset); ++ii) {
8336       if (cmask[vv]) {
8337          mm.xyz[0] = DSET_XORG(dset)+ii*DSET_DX(dset);
8338          mm.xyz[1] = DSET_YORG(dset)+jj*DSET_DY(dset);
8339          mm.xyz[2] = DSET_ZORG(dset)+kk*DSET_DZ(dset);
8340          di = SUMA_THD_3dmm_to_dicomm( dset->daxes->xxorient,
8341                                        dset->daxes->yyorient,
8342                                        dset->daxes->zzorient, mm);
8343          z[nn] = zs[nn] = di.xyz[2];
8344          ++nn;
8345       }
8346       ++vv;
8347    } } }
8348 
8349    /* sort the depths */
8350    qsort(zs, nvox, sizeof(float),
8351          (int(*) (const void *, const void *)) SUMA_compare_float);
8352    /* find the top Z value */
8353    itop = (nvox-1)*((100-peakperc)/100.0);
8354    if (itop > nvox-1) {
8355       itop = nvox-1;
8356    } else if (itop < 0) {
8357       itop = 0;
8358    }
8359    SUMA_LHv("Top Z selection at %f %% is %fmm\n", peakperc, zs[itop]);
8360    if (ztop) *ztop = zs[itop];
8361 
8362    /* Does the user want voxel depths back ? */
8363    if (dpth) {
8364       SUMA_LH("Returning depth values");
8365       *dpth = (float *)SUMA_calloc(DSET_NVOX(dset), sizeof(float));
8366       vv=0; nn = 0;
8367       for (kk=0; kk<DSET_NZ(dset); ++kk) {
8368       for (jj=0; jj<DSET_NY(dset); ++jj) {
8369       for (ii=0; ii<DSET_NX(dset); ++ii) {
8370          if (cmask[vv]) {
8371             *(*dpth+vv)=zs[itop]-z[nn]; ++nn;
8372          }
8373          ++vv;
8374       } } }
8375    }
8376    /* Does the user want mask back ? */
8377    if (cmaskp) {
8378       SUMA_LH("Returning byte mask");
8379       *cmaskp = (byte *)SUMA_calloc(DSET_NVOX(dset), sizeof(byte));
8380       vv=0; nn = 0;
8381       for (kk=0; kk<DSET_NZ(dset); ++kk) {
8382       for (jj=0; jj<DSET_NY(dset); ++jj) {
8383       for (ii=0; ii<DSET_NX(dset); ++ii) {
8384          if (cmask[vv]) {
8385             if (zs[itop]-z[nn] <= thr) {
8386                *(*cmaskp+vv) = 1;
8387             }
8388             ++nn;
8389          }
8390          ++vv;
8391       } } }
8392    }
8393    /* Apply the mask ? */
8394    if (applymask) {
8395       SUMA_LH("Applying mask");
8396       switch (DSET_BRICK_TYPE(dset,0)) {
8397          case MRI_byte:
8398             {  byte *bv = (byte *)DSET_ARRAY(dset,0) ;
8399                vv=0; nn = 0;
8400                for (kk=0; kk<DSET_NZ(dset); ++kk) {
8401                for (jj=0; jj<DSET_NY(dset); ++jj) {
8402                for (ii=0; ii<DSET_NX(dset); ++ii) {
8403                   if (cmask[vv]) {
8404                      if (zs[itop]-z[nn] > thr) bv[vv] = 0;
8405                      ++nn;
8406                   }
8407                   ++vv;
8408                } } }
8409             }
8410             break;
8411          case MRI_short:
8412             {  short *sv = (short *)DSET_ARRAY(dset,0) ;
8413                vv=0; nn = 0;
8414                for (kk=0; kk<DSET_NZ(dset); ++kk) {
8415                for (jj=0; jj<DSET_NY(dset); ++jj) {
8416                for (ii=0; ii<DSET_NX(dset); ++ii) {
8417                   if (cmask[vv]) {
8418                      if (zs[itop]-z[nn] > thr)  sv[vv] = 0;
8419                      ++nn;
8420                   }
8421                   ++vv;
8422                } } }
8423             }
8424             break;
8425          case MRI_float:
8426             {  float *fv = (float *)DSET_ARRAY(dset,0) ;
8427                vv=0; nn = 0;
8428                for (kk=0; kk<DSET_NZ(dset); ++kk) {
8429                for (jj=0; jj<DSET_NY(dset); ++jj) {
8430                for (ii=0; ii<DSET_NX(dset); ++ii) {
8431                   if (cmask[vv]) {
8432                      if (zs[itop]-z[nn] > thr)  fv[vv] = 0;
8433                      ++nn;
8434                   }
8435                   ++vv;
8436                } } }
8437             }
8438             break;
8439          default:
8440             SUMA_S_Errv("Dset type %d not supported\n", DSET_BRICK_TYPE(dset,0));
8441             break;
8442       }
8443    }
8444 
8445    SUMA_LH("Liberte");
8446    if (!cmasku && cmask) free(cmask); cmask = NULL;
8447    if (z) SUMA_free(z); z= NULL;
8448    if (zs) SUMA_free(zs); zs= NULL;
8449    SUMA_RETURN(N_inmask);
8450 }
8451 
SUMA_VoxelPlaneCut(THD_3dim_dataset * dset,float * Eq,byte ** cmaskp,int applymask)8452 int SUMA_VoxelPlaneCut(THD_3dim_dataset *dset, float *Eq,
8453                        byte **cmaskp, int applymask)
8454 {
8455    static char FuncName[]={"SUMA_VoxelPlaneCut"};
8456    int ii, jj, kk, nn, vv;
8457    byte *cmask=NULL;
8458    int N_inmask = -1;
8459    THD_fvec3 mm, di;
8460    float dist;
8461    SUMA_Boolean LocalHead = NOPE;
8462 
8463    SUMA_ENTRY;
8464 
8465    if (!dset) {
8466       SUMA_S_Err("NULL input");
8467       SUMA_RETURN(-1);
8468    }
8469    if (cmaskp && *cmaskp) {
8470       SUMA_S_Err("If passing cmaskp, *cmaskp must be NULL");
8471       SUMA_RETURN(-1);
8472    }
8473 
8474    /* get xyz of all non-zero voxels in dset */
8475    if (!(cmask = THD_makemask( dset , 0 , 1.0, -1.0 ))) {
8476       SUMA_S_Err("Failed to get mask");
8477       SUMA_RETURN(-1);
8478    }
8479 
8480    /* Change plane equation to fit orientation */
8481    di.xyz[0] = Eq[0];
8482    di.xyz[1] = Eq[1];
8483    di.xyz[2] = Eq[2];
8484    mm = SUMA_THD_dicomm_to_3dmm( dset->daxes->xxorient,
8485                                        dset->daxes->yyorient,
8486                                        dset->daxes->zzorient, di);
8487    Eq[0] = mm.xyz[0];
8488    Eq[1] = mm.xyz[1];
8489    Eq[2] = mm.xyz[2];
8490 
8491    vv=0; nn = 0, N_inmask=0;
8492    for (kk=0; kk<DSET_NZ(dset); ++kk) {
8493    for (jj=0; jj<DSET_NY(dset); ++jj) {
8494    for (ii=0; ii<DSET_NX(dset); ++ii) {
8495       if (cmask[vv]) {
8496          mm.xyz[0] = DSET_XORG(dset)+ii*DSET_DX(dset);
8497          mm.xyz[1] = DSET_YORG(dset)+jj*DSET_DY(dset);
8498          mm.xyz[2] = DSET_ZORG(dset)+kk*DSET_DZ(dset);
8499          dist = Eq[0]*mm.xyz[0]+Eq[1]*mm.xyz[1]+Eq[2]*mm.xyz[2]-Eq[3];
8500          if (dist < 0.0) {
8501             cmask[vv] = 0;
8502          } else {
8503             ++N_inmask;
8504          }
8505          ++nn;
8506       }
8507       ++vv;
8508    } } }
8509 
8510    SUMA_LHv("%d / %d nozero voxels pass the cut test\n", N_inmask, nn);
8511 
8512    /* Apply the mask ? */
8513    if (applymask) {
8514       SUMA_LH("Applying mask");
8515       THD_applydsetmask( dset ,  cmask );
8516    }
8517 
8518    /* Does the user want mask back ? */
8519    if (cmaskp) {
8520       SUMA_LH("Returning byte mask");
8521       *cmaskp = cmask; cmask = NULL;
8522    }
8523 
8524    SUMA_LH("Liberte");
8525    if (cmask) free(cmask); cmask = NULL;
8526 
8527    SUMA_RETURN(N_inmask);
8528 }
8529 
SUMA_WriteNodeDepth(char * prefix,SUMA_PC_XYZ_PROJ * pcp,float * dpth,float mx)8530 SUMA_Boolean SUMA_WriteNodeDepth(char *prefix, SUMA_PC_XYZ_PROJ *pcp,
8531                                  float *dpth, float mx)
8532 {
8533    static char FuncName[]={"SUMA_WriteNodeDepth"};
8534    FILE *fout=NULL;
8535    char *s2=NULL, *s1=NULL;
8536    int ii;
8537 
8538    SUMA_ENTRY;
8539 
8540    if (!pcp || !dpth) {
8541       SUMA_RETURN(NOPE);
8542    }
8543    if (!prefix) {
8544       fout = stdout;
8545    } else {
8546       s2 = SUMA_ModifyName(prefix, "append",".pcdepth", NULL);
8547       s1 = SUMA_Extension(s2,".1D.dset",NOPE); SUMA_ifree(s2);
8548       if (!THD_ok_overwrite() && SUMA_filexists(s1)) {
8549          SUMA_S_Err(
8550            "%s exists already, will not overwrite without -overwrite!",s1);
8551          SUMA_ifree(s1); SUMA_RETURN(NOPE);
8552       }
8553       if (!(fout = fopen(s1, "w"))) {
8554          SUMA_S_Err("No write permissions for %s?", s1);
8555          SUMA_RETURN(NOPE);
8556       }
8557    }
8558 
8559    fprintf(fout,"#Coordinates in original space for:\n"
8560        "#   Center Of Mass               :   %f %f %f\n"
8561        "#   Top node's projection    & ID:   %f %f %f    %d\n"
8562        "#   Bottom node's projection & ID:   %f %f %f    %d\n",
8563                 pcp->avg[0], pcp->avg[1],pcp->avg[2],
8564                 pcp->highest_proj[0], pcp->highest_proj[1],
8565                   pcp->highest_proj[2], pcp->highest_node,
8566                 pcp->lowest_proj[0], pcp->lowest_proj[1],
8567                   pcp->lowest_proj[2], pcp->lowest_node);
8568    fprintf(fout,
8569        "#   Principal Direction          :   %f %f %f\n",
8570                 pcp->target_params[0], pcp->target_params[1],
8571                 pcp->target_params[2]);
8572    fprintf(fout,"#\n#Node depths along 1st principal direction\n");
8573    fprintf(fout,"#   Col. 0 == Node Index\n"
8574                 "#   Col. 1 == Node Depth (from top)\n"
8575                 "#   Col. 2 == Node Height (from bottom)\n");
8576    for (ii=0; ii<pcp->N_xyz; ++ii) {
8577       fprintf(fout,"%d %f %f\n",
8578                ii, dpth[ii], mx-dpth[ii]);
8579    }
8580    if (fout != stdout) fclose(fout); fout = NULL;
8581 
8582    SUMA_ifree(s1);
8583    SUMA_RETURN(YUP);
8584 }
8585 
8586 /*
8587    Compute the depth of each node relative to the topmost node.
8588       xyz: The coords of the nodes,
8589       N_Node: The number of nodes
8590       prjdir (SUMA_PC_PROJ): Depth is computed along one of two
8591                            directions depending on prjdir:
8592         EZ_DIR_PRJ: Depth is computed along principal direction closest to z axis
8593         E1_DIR_PRJ: Depth is computed along 1st principal direction
8594       dpth (float**): If not NULL, will contain allocated vector of
8595                       depths upon return.
8596       thr (float): Depth threshold
8597       cmask (byte **): If not NULL, return a mask of 0 where nodes
8598                        are deeper than threshold.
8599       mxdpth (float *): To contain maximum depth encountered.
8600                         Pass NULL if you do not care for it.
8601       pcpu (SUMA_PC_XYZ_PROJ **): If !NULL, return all of the pcp
8602                         struct in *pcpu
8603    Returns the number of nodes in the mask. -1 on error
8604 */
SUMA_NodeDepth(float * NodeList,int N_Node,SUMA_PC_PROJ prjdir,float ** dpth,float thr,byte ** cmaskp,float * mxdpth,SUMA_PC_XYZ_PROJ ** pcpu)8605 int SUMA_NodeDepth(float *NodeList, int N_Node,
8606                    SUMA_PC_PROJ prjdir, float **dpth,
8607                    float thr, byte **cmaskp, float *mxdpth,
8608                    SUMA_PC_XYZ_PROJ **pcpu)
8609 {
8610    static char FuncName[]={"SUMA_NodeDepth"};
8611    float *xyzp=NULL;
8612    int ii, iimax, iimin;
8613    float flipit=1.0;
8614    byte *cmask=NULL;
8615    int N_inmask = -1;
8616    SUMA_PC_XYZ_PROJ *pcp=NULL;
8617 
8618    SUMA_Boolean LocalHead = NOPE;
8619 
8620    SUMA_ENTRY;
8621 
8622    if (pcpu && *pcpu) {
8623       SUMA_S_Err("Need an empty pointer to *pcpu, or a NULL pcpu");
8624       SUMA_RETURN(-1);
8625    }
8626    if (dpth && *dpth) {
8627       SUMA_S_Err("If passing dpth, *dpth must be NULL");
8628       SUMA_RETURN(-1);
8629    }
8630    if (cmaskp && *cmaskp) {
8631       SUMA_S_Err("If passing cmaskp, *cmaskp must be NULL");
8632       SUMA_RETURN(-1);
8633    }
8634    if (prjdir != EZ_DIR_PRJ &&
8635        prjdir != E1_DIR_PRJ) {
8636       SUMA_S_Err("Not tested for prjdir = %d", prjdir);
8637       SUMA_RETURN(-1);
8638    }
8639    /* PCA of coords, project points along direction closest.
8640       to Z axis then rotate projection to Z axis
8641       Don't mess with parameters of call below without checking
8642       on effect of lowest/highest node locations below        */
8643    pcp = SUMA_Project_Coords_PCA (NodeList, N_Node, -1, NULL,
8644                                   prjdir, ROT_2_Z, 0);
8645 
8646    xyzp = pcp->xyzp;
8647 
8648    /* Find the highest node */
8649    iimax = 0; iimin = 0;
8650    for (ii=1; ii<N_Node; ++ii) {
8651       if (xyzp[3*ii+2] > xyzp[3*iimax+2]) {
8652          iimax = ii;
8653       }
8654       if (xyzp[3*ii+2] < xyzp[3*iimin+2]) {
8655          iimin = ii;
8656       }
8657    }
8658 
8659    /* Insist on minimal original Z for the min?
8660       Note that this is OK for clouds that have
8661       the principal direction friendly with the +ve
8662       Z direction. The proper place for this flipping
8663       should be in SUMA_Project_Coords_PCA using the
8664       closest cardinal direction and before ROT_2_Z
8665       is applied. Flipping in SUMA_Project_Coords_PCA()
8666       is turned off. Needs more debugging. */
8667    flipit = 1.0;
8668    if ( NodeList[3*iimax+2] < NodeList[3*iimin+2]) {
8669       ii = iimax; iimax = iimin; iimin = ii;
8670       flipit = -1.0;
8671    }
8672 
8673    SUMA_LHv("Highest node %d PCR(%f,%f,%f), ORIG(%f,%f,%f)\n"
8674             "Lowest  node %d PCR(%f,%f,%f), ORIG(%f,%f,%f)\n",
8675                 iimax, xyzp[3*iimax], xyzp[3*iimax+1], xyzp[3*iimax+2],
8676                     NodeList[3*iimax], NodeList[3*iimax+1], NodeList[3*iimax+2],
8677                 iimin, xyzp[3*iimin], xyzp[3*iimin+1], xyzp[3*iimin+2],
8678                     NodeList[3*iimin], NodeList[3*iimin+1], NodeList[3*iimin+2]);
8679 
8680    /* store location BEFORE the rotation of the extrema */
8681    pcp->lowest_node = iimin;
8682    pcp->lowest_proj[0]  = xyzp[3*iimin  ];
8683    pcp->lowest_proj[1]  = xyzp[3*iimin+1];
8684    pcp->lowest_proj[2]  = xyzp[3*iimin+2];
8685    pcp->highest_node = iimax;
8686    pcp->highest_proj[0] = xyzp[3*iimax  ];
8687    pcp->highest_proj[1] = xyzp[3*iimax+1];
8688    pcp->highest_proj[2] = xyzp[3*iimax+2];
8689    /* Now undo the rotation, then add the CM back
8690      *** Must not set 'remean' to 1 in call to SUMA_Project_Coords_PCA()
8691          above.                                                          */
8692    SUMA_Apply_Coord_xform(pcp->lowest_proj, 1, 3, pcp->RotMat,
8693                           1, NULL);
8694    SUMA_Apply_Coord_xform(pcp->highest_proj, 1, 3, pcp->RotMat,
8695                           1, NULL);
8696    pcp->lowest_proj[0]  += pcp->avg[0];
8697    pcp->lowest_proj[1]  += pcp->avg[1];
8698    pcp->lowest_proj[2]  += pcp->avg[2];
8699    pcp->highest_proj[0]  += pcp->avg[0];
8700    pcp->highest_proj[1]  += pcp->avg[1];
8701    pcp->highest_proj[2]  += pcp->avg[2];
8702 
8703    if (LocalHead) SUMA_Write_PC_XYZ_Proj(pcp, NULL);
8704 
8705    /* Create a mask of nodes more than XXmm from the highest node */
8706    cmask = (byte *)SUMA_calloc(N_Node, sizeof(byte));
8707    N_inmask = 0;
8708    for (ii=0; ii<N_Node; ++ii) {
8709       if (xyzp[3*iimax+2] - xyzp[3*ii+2] <= thr) {
8710          cmask[ii]=1;
8711          ++N_inmask;
8712       }
8713    }
8714 
8715    if (mxdpth) *mxdpth = flipit * (xyzp[3*iimax+2] - xyzp[3*iimin+2]);
8716 
8717    if (dpth) {
8718       float ref=xyzp[3*iimax+2];
8719       float *ddd = (float*)SUMA_calloc(N_Node, sizeof(float));
8720       for (ii=0; ii<N_Node; ++ii) {
8721          ddd[ii] = flipit * (ref - xyzp[3*ii+2]);
8722       }
8723       *dpth = ddd;
8724    }
8725 
8726    xyzp = NULL;
8727    if (!pcpu) {
8728       pcp = SUMA_Free_PC_XYZ_Proj(pcp);
8729    } else {
8730       *pcpu = pcp;
8731    }
8732 
8733    if (!cmaskp) SUMA_free(cmask);
8734    else *cmaskp = cmask;
8735    cmask = NULL;
8736 
8737    SUMA_RETURN(N_inmask);
8738 }
8739 
8740 
SUMA_is_Constant_Z_Coord(float * NodeList,int N_Node,float tol)8741 int SUMA_is_Constant_Z_Coord(float *NodeList, int N_Node, float tol)
8742 {
8743    double sz=0.0, z=0.0;
8744    int i;
8745    if (tol <= 0.0) tol = 0.01;
8746    for (i=0; i<N_Node; ++i) sz += NodeList[3*i+2];
8747    for (i=0; i<N_Node; ++i) z += SUMA_ABS(NodeList[3*i+2]-sz);
8748    if (z/(double)N_Node < tol) return(1);
8749    return(0);
8750 }
8751 
SUMA_is_Flat_Surf_Coords_PCA(float * xyz,int N_xyz,float tol,float sampfrac)8752 int SUMA_is_Flat_Surf_Coords_PCA (float *xyz, int N_xyz, float tol,
8753                                   float sampfrac)
8754 {
8755    static char FuncName[]={"SUMA_is_Flat_Surf_Coords_PCA"};
8756    int i, i3, *ir=NULL, n_samp = 0;
8757    double trace, pc_vec[9], pc_eig[3], Eq[4], pc0[3], proj[3];
8758    float *xyzp=NULL, fv[3], **fm=NULL, *p1, pcf0[3];
8759    SUMA_Boolean LocalHead = NOPE;
8760 
8761    SUMA_ENTRY;
8762 
8763    SUMA_LH("Xforming");
8764 
8765    if (tol <= 0.0) tol = 0.01;
8766    if (sampfrac <= 0.0) sampfrac = 0.01;
8767 
8768    /* select subset of nodes */
8769    if (!(ir = z_rand_order(0, N_xyz-1, 111111311))) {
8770       SUMA_S_Err("Misere"); SUMA_RETURN(0);
8771    }
8772    n_samp = (int)(sampfrac*N_xyz);
8773    if (n_samp < 1000) n_samp = SUMA_MIN_PAIR(N_xyz, 1000);
8774    if (n_samp > N_xyz) n_samp = N_xyz;
8775 
8776    /*load data */
8777    xyzp = (float *)SUMA_calloc(n_samp*3, sizeof(float));
8778    i=0;
8779    do {
8780       xyzp[i  ] = xyz[3*ir[i]  ];
8781       xyzp[i+n_samp] = xyz[3*ir[i]+1];
8782       xyzp[i+2*n_samp] = xyz[3*ir[i]+2]+SUMA_GRAN(0,1);
8783       ++i;
8784    } while (i<n_samp);
8785    SUMA_free(ir); ir=NULL;
8786 
8787    for (i=0; i<10; ++i) {
8788       fprintf(stderr,"%d: %f %f %f\n",
8789                i, xyzp[3*i], xyzp[3*i+1], xyzp[3*i+2]);
8790    }
8791    if ((trace = pca_fast3(xyzp, n_samp, 0, pc_vec, pc_eig)) < 0){
8792       SUMA_S_Err("Failed calculating PC\n");
8793       SUMA_free(xyzp); xyzp=NULL;
8794       SUMA_RETURN(0);
8795    }
8796    SUMA_free(xyzp);
8797 
8798    SUMA_LHv("PCA results:\n"
8799             "Eig[0]=%f     pc[0]=[%f %f %f]\n"
8800             "Eig[1]=%f     pc[1]=[%f %f %f]\n"
8801             "Eig[2]=%f     pc[2]=[%f %f %f]\n",
8802             pc_eig[0], pc_vec[0], pc_vec[3], pc_vec[6],
8803             pc_eig[1], pc_vec[1], pc_vec[4], pc_vec[7],
8804             pc_eig[2], pc_vec[2], pc_vec[5], pc_vec[8]);
8805 
8806    if (pc_eig[2] / pc_eig[0] < tol) {
8807       SUMA_RETURN(1);
8808    }
8809 
8810    SUMA_RETURN(0);
8811 }
8812 
8813 
8814 static int UseSliceFWHM = 0;
SUMA_Set_UseSliceFWHM(int v)8815 void SUMA_Set_UseSliceFWHM(int v) { UseSliceFWHM = v; }
SUMA_Get_UseSliceFWHM(void)8816 int SUMA_Get_UseSliceFWHM(void) { return(UseSliceFWHM); }
8817 
8818 /*!
8819    A wrapper to make repeated calls to SUMA_estimate_FWHM_1dif for a dataset
8820 */
SUMA_estimate_dset_FWHM_1dif(SUMA_SurfaceObject * SO,SUMA_DSET * dset,int * icols,int N_icols,byte * nmask,int nodup,char * options)8821 float *SUMA_estimate_dset_FWHM_1dif(SUMA_SurfaceObject *SO, SUMA_DSET *dset,
8822                                     int *icols, int N_icols, byte *nmask,
8823                                     int nodup, char *options)
8824 {
8825    static char FuncName[]={"SUMA_estimate_dset_FWHM_1dif"};
8826    int k, jj, N_nmask=0, n= 0;
8827    float *fwhmv=NULL, *fin_orig=NULL;
8828    byte *bfull=NULL;
8829    SUMA_Boolean LocalHead = NOPE;
8830 
8831    SUMA_ENTRY;
8832 
8833 
8834    if (!dset || !SO) {
8835       SUMA_S_Errv("NULL input fim=%p, SO=%p\n", dset, SO);
8836       SUMA_RETURN(NULL);
8837    }
8838    if (!SO->FN || !SO->EL) {
8839       /* do it here */
8840       if (  !SO->EL &&
8841             !(SO->EL = SUMA_Make_Edge_List_eng (
8842                            SO->FaceSetList, SO->N_FaceSet, SO->N_Node,
8843                            SO->NodeList, 0, SO->idcode_str))) {
8844          SUMA_S_Err("Failed to create Edge_List");
8845          SUMA_RETURN(NULL);
8846       }
8847       if (  !SO->FN &&
8848             !(SO->FN = SUMA_Build_FirstNeighb(
8849                         SO->EL, SO->N_Node, SO->idcode_str, 1)) ) {
8850          SUMA_S_Err("Failed to create FirstNeighb");
8851          SUMA_RETURN(NULL);
8852       }
8853    }
8854 
8855 
8856    if (!(fwhmv = (float *)SUMA_calloc(N_icols, sizeof(float)))) {
8857       SUMA_S_Err("Failed to callocate");
8858       SUMA_RETURN(fwhmv);
8859    }
8860 
8861    if (nmask) {
8862       bfull = (byte*)SUMA_malloc(sizeof(byte)*SO->N_Node);
8863       memcpy((void*)bfull, (void*)nmask, sizeof(byte)*SO->N_Node);
8864    }
8865 
8866    /* Begin operation for each column */
8867    for (k=0; k < N_icols; ++k) {
8868       /* get a float copy of the data column */
8869       fin_orig = SUMA_DsetCol2Float (dset, icols[k], 1);
8870       if (!fin_orig) {
8871          SUMA_SL_Crit("Failed to get copy of column. Woe to thee!");
8872          SUMA_free(fwhmv); fwhmv = NULL;
8873          goto CLEANUP;
8874       }
8875       /* make sure column is not sparse, one value per node */
8876       if (k==0) {
8877          SUMA_LH( "Special case k = 0, going to"
8878                   " SUMA_MakeSparseColumnFullSorted");
8879          bfull = NULL;
8880          if (!SUMA_MakeSparseColumnFullSorted(&fin_orig, SDSET_VECFILLED(dset),
8881                                               0.0, &bfull, dset, SO->N_Node)) {
8882             SUMA_S_Err("Failed to get full column vector");
8883             SUMA_free(fwhmv); fwhmv = NULL;
8884             goto CLEANUP;
8885          }
8886          if (bfull) {
8887             SUMA_LH( "Something was filled in "
8888                      "SUMA_MakeSparseColumnFullSorted\n" );
8889             /* something was filled in SUMA_MakeSparseColumnFullSorted */
8890             if (nmask) {   /* combine bfull with nmask */
8891                SUMA_LH( "Merging masks\n" );
8892                for (jj=0; jj < SO->N_Node; ++jj) {
8893                   if (nmask[jj] && !bfull[jj]) nmask[jj] = 0; }
8894             } else { nmask = bfull; }
8895          }
8896          if (nmask) {
8897             N_nmask = 0;
8898             for (n=0; n<SO->N_Node; ++n) { if (nmask[n]) ++ N_nmask; }
8899             SUMA_LHv("Blurring with node mask (%d nodes in mask)\n", N_nmask);
8900             if (!N_nmask) {
8901                SUMA_S_Warn("Empty mask, nothing to do");
8902                SUMA_free(fwhmv); fwhmv = NULL; goto CLEANUP;
8903             }
8904          }
8905       } else {
8906          SUMA_LH( "going to SUMA_MakeSparseColumnFullSorted");
8907          if (!SUMA_MakeSparseColumnFullSorted(&fin_orig, SDSET_VECFILLED(dset),
8908                                               0.0, NULL, dset, SO->N_Node)) {
8909             SUMA_S_Err("Failed to get full column vector");
8910             SUMA_free(fwhmv); fwhmv = NULL;
8911             goto CLEANUP;
8912          }
8913          /* no need for reworking nmask and bfull for each column...*/
8914 
8915       }
8916 
8917       if (SUMA_Get_UseSliceFWHM()) {
8918          fwhmv[k] = SUMA_estimate_slice_FWHM_1dif( SO, fin_orig, nmask,
8919                                                    nodup, NULL, NULL);
8920       } else {
8921          fwhmv[k] = SUMA_estimate_FWHM_1dif( SO, fin_orig, nmask, nodup);
8922       }
8923       if (fin_orig) SUMA_free(fin_orig); fin_orig = NULL;
8924                   /* just in case, this one's still alive from a GOTO */
8925    } /* for k */
8926 
8927    CLEANUP:
8928 
8929    if (fin_orig) SUMA_free(fin_orig); fin_orig = NULL;
8930                      /* just in case, this one's still alive from a GOTO */
8931    if (bfull) SUMA_free(bfull); bfull=NULL;
8932    SUMA_RETURN(fwhmv);
8933 }
8934 
8935 #define OK_FWHM_DBG
8936 static int DbgFWHM_1_dif=0;
SUMA_SetDbgFWHM(int i)8937 void SUMA_SetDbgFWHM(int i) { DbgFWHM_1_dif=i; return; }
SUMA_GetDbgFWHM(void)8938 int SUMA_GetDbgFWHM(void) { return(DbgFWHM_1_dif); }
8939 /*!
8940    Estimate the FWHM on a surface.
8941    FWHM based on implementation in mri_estimate_FWHM_1dif (Forman et. al 1995)
8942    SO (SUMA_SurfaceObject *) La surface
8943    fim (float *) SO->N_Node x 1 vector of data values
8944    mask (byte *) SO->N_Node x 1 mask vector (NULL for no masking)
8945    nodup (int ) 0- allow a segment to be counted twice
8946                (uncool, but slightly faster)
8947                1- Do not allow a segment to be counted twice
8948                (respectful approach)
8949 */
SUMA_estimate_FWHM_1dif(SUMA_SurfaceObject * SO,float * fim,byte * nmask,int nodup)8950 float SUMA_estimate_FWHM_1dif(   SUMA_SurfaceObject *SO, float *fim ,
8951                                  byte *nmask, int nodup )
8952 {
8953    static char FuncName[]={"SUMA_estimate_FWHM_1dif"};
8954 
8955    double ds;                  /* average segment size */
8956    double fsum, fsq, var , arg ;
8957    double dfds, dfdssum, dfdssq, varss;
8958    int count, counts, oke, iseg, k, in, ink;
8959    float ss=-1.0f , par[2], prob=0.0, stat;
8960    byte *visited = NULL;
8961    FILE *fdbg=NULL;
8962    SUMA_Boolean LocalHead=NOPE;
8963 
8964    SUMA_ENTRY;
8965    #ifdef OK_FWHM_DBG
8966       if (SUMA_GetDbgFWHM()) {
8967          SUMA_S_Warn("Function in debug mode. File of same name created!\n");
8968          fdbg = fopen(FuncName,"w");
8969          fprintf(fdbg,"#--------------------\n#n1   n2  SegLen   dfds\n");
8970       }
8971    #endif
8972    if (!fim || !SO) {
8973       SUMA_S_Errv("NULL input fim=%p, SO=%p\n", fim, SO);
8974       SUMA_RETURN(ss);
8975    }
8976 
8977    if (!SO->FN || !SO->EL) {
8978       if (!SUMA_SurfaceMetrics_eng( SO, "EdgeList", NULL,
8979                                     0, SUMAg_CF->DsetList)){
8980          SUMA_SL_Err("Failed to create needed accessories");
8981          SUMA_RETURN(ss);
8982       }
8983    }
8984    if (!SO->MF || !SO->PolyArea) {
8985       if (!SUMA_SurfaceMetrics_eng( SO, "MemberFace|PolyArea",
8986                                     NULL, 0, SUMAg_CF->DsetList)){
8987          SUMA_SL_Err("Failed to create needed accessories");
8988          SUMA_RETURN(ss);
8989       }
8990    }
8991 
8992    if (!SO->FN || !SO->EL || !SO->MF || !SO->PolyArea) {
8993       SUMA_S_Errv("J'ai besoin des voisins(%p) et des cotes (%p), cherie\n"
8994                   "Et en plus, MF(%p) and PolyArea(%p)",
8995                   SO->FN, SO->EL, SO->MF, SO->PolyArea);
8996       SUMA_RETURN(ss);
8997    }
8998    /*----- estimate the variance of the data -----*/
8999 
9000    fsum = 0.0; fsq = 0.0; count = 0;
9001    for (in = 0;  in < SO->N_Node;  in++){
9002       if( !nmask || nmask[in] ) {
9003          count++; arg = fim[in];
9004          fsum += arg; fsq  += arg*arg;
9005       }
9006    }
9007    if( count < 9 || fsq <= 0.0 ){     /* no data? */
9008       SUMA_RETURN(ss) ;
9009    }
9010 
9011    var = (fsq - (fsum * fsum)/count) / (count-1.0);
9012    if( var <= 0.0 ){                  /* crappy data? */
9013       SUMA_RETURN(ss);
9014    }
9015 
9016    if (nodup) {
9017       if (!(visited = (byte *)SUMA_calloc(SO->EL->N_EL, sizeof(byte)))) {
9018          SUMA_S_Err("Failed to bytocate for visited.");
9019          SUMA_RETURN(ss);
9020       }
9021    }
9022 
9023    /*----- estimate the partial derivatives -----*/
9024 
9025    dfdssum = 0.0;
9026    dfdssq  = 0.0;
9027    counts  = 0;
9028    ds = 0.0;
9029    for (in = 0;  in < SO->N_Node;  in++){
9030       if( !nmask || nmask[in] ){
9031          arg = fim[in] ;
9032          for(k=0; k < SO->FN->N_Neighb[in]; ++k) {
9033             ink = SO->FN->FirstNeighb[in][k];
9034             if (!nmask || nmask[ink]) { /* neighbour also in mask */
9035                /* locate the segment, and get distance */
9036                iseg = -1;
9037                if (in < ink) { SUMA_FIND_EDGE(SO->EL, in, ink, iseg); }
9038                else { SUMA_FIND_EDGE(SO->EL, ink, in, iseg); }
9039                if (iseg < 0) {
9040                   SUMA_S_Errv("Could not find segment between "
9041                               "nodes %d and %d\n"
9042                               "This should not happen.\n", in, ink);
9043                   SUMA_RETURN(ss);
9044                }
9045                if (nodup) { /* make sure edge is fresh */
9046                   if (visited[iseg]) oke = 0;
9047                   else { oke = 1; visited[iseg] = 1; }
9048                } else oke = 1;
9049                if (oke) {
9050                   ds += SO->EL->Le[iseg];
9051                   dfds = (fim[ink] - arg) ;
9052                   dfdssum += dfds; dfdssq += dfds*dfds;
9053                   #ifdef OK_FWHM_DBG
9054                      if (SUMA_GetDbgFWHM()) {
9055                         fprintf( fdbg,"%5d %5d   %.3f  %.3f\n",
9056                                  in, ink, SO->EL->Le[iseg], dfds);
9057                      }
9058                   #endif
9059                   counts++;
9060                }
9061             }
9062          }
9063       }
9064    }
9065 
9066 
9067    if (visited) SUMA_free(visited); visited = NULL;
9068 
9069    /*----- estimate the variance of the partial derivatives -----*/
9070 
9071    varss = (counts < 36) ? 0.0
9072                     : (dfdssq - (dfdssum * dfdssum)/counts) / (counts-1.0);
9073    ds /= (double)counts;  /* the average segment length */
9074 
9075    /*----- now estimate the FWHMs -----*/
9076 
9077    /*---- 2.35482 = sqrt(8*log(2)) = sigma-to-FWHM conversion factor ----*/
9078 
9079    /* with random noise, varss should be about 2*var if the ratio of varss/var
9080    is not significantly different from 1 then set FWHM to 0.0 rather than the
9081    error flag */
9082    par[0] = (float)SO->N_Node;
9083    par[1] = (float)SO->N_Node; /* assuming independence of course */
9084    prob = THD_stat_to_pval(   SUMA_MAX_PAIR(varss/(2.0*var),(2.0*var)/varss),
9085                               NI_STAT_FTEST ,
9086                               par ) ;
9087    if (prob > 0.01) {
9088       /* so what would the smallest acceptable FWHM be? */
9089       stat = THD_pval_to_stat (0.01, NI_STAT_FTEST, par);
9090       arg = 1.0 - 0.5*(2.0/stat);
9091       ss = 2.35482*sqrt( -1.0 / (4.0*log(arg)) );
9092       SUMA_S_Notev(  "   Distribution of data is possibly random noise (p=%f)\n"
9093                      "   Expect fwhm to be no different from 0 \n"
9094                      "   FWHM values up to %.2f(segments) or %.2f(mm)\n"
9095                      "   are likely meaningless (at p=0.01) on this mesh.\n\n",
9096                      prob, ss, ss*ds);
9097    }
9098    arg = 1.0 - 0.5*(varss/var);
9099    if (arg <= 0.0 || arg >= 1.0) {
9100       if (arg <=0 && prob > 0.01) ss = 0.0f; else ss = -1.0f;
9101    } else {
9102       ss = 2.35482*sqrt( -1.0 / (4.0*log(arg)) )*ds;
9103    }
9104 
9105    #ifdef OK_FWHM_DBG
9106       if (SUMA_GetDbgFWHM()) {
9107          fprintf(fdbg,  "#counts=%d\n#var=%f\n"
9108                         "#varss=%f\n#ds=%.3f\n"
9109                         "#arg=%.3f\n#ss=%f\n",
9110                   counts, var, varss, ds, arg, ss);
9111          fclose(fdbg);
9112       }
9113    #endif
9114    SUMA_RETURN(ss) ;
9115 }
9116 /*!
9117    Estimate the FWHM on a surface using the slice strips.
9118    FWHM based on implementation in mri_estimate_FWHM_1dif (Forman et. al 1995)
9119    SO (SUMA_SurfaceObject *) La surface
9120    fim (float *) SO->N_Node x 1 vector of data values
9121    mask (byte *) SO->N_Node x 1 mask vector (NULL for no masking)
9122    nodup (int ) 0- allow a segment to be counted twice
9123                   (uncool, but slightly faster)
9124                1- Do not allow a segment to be counted twice
9125                   (respectful approach)
9126 */
SUMA_estimate_slice_FWHM_1dif(SUMA_SurfaceObject * SO,float * fim,byte * nmask,int nodup,float * ssvr,DList ** striplist_vec)9127 float SUMA_estimate_slice_FWHM_1dif(
9128          SUMA_SurfaceObject *SO, float *fim , byte *nmask,
9129          int nodup, float *ssvr, DList **striplist_vec)
9130 {
9131    static char FuncName[]={"SUMA_estimate_slice_FWHM_1dif"};
9132 
9133    double ds;                  /* average segment size */
9134    double fsum, fsq, var , arg , arg_n;
9135    double dfds, dfdssum, dfdssq, varss;
9136    int count, counts, oke, iseg, k, in0, in1, ink, in0_n, in1_n, ipl;
9137    float ssc=-1.0f , ssv[3], par[2], prob=0.0, stat;
9138    DList *striplist=NULL;
9139    float Eq[4], *p4=NULL, *p4_n=NULL, U[3], Un;
9140    void *vp=NULL;
9141    byte *visited = NULL;
9142    FILE *fdbg=NULL;
9143    SUMA_STRIP *strip=NULL;
9144    DListElmt *loope, *loopp, *loope_n, *loopp_n, *listelm;
9145    SUMA_Boolean clsd = NOPE;
9146    SUMA_Boolean LocalHead=NOPE;
9147 
9148    SUMA_ENTRY;
9149    #ifdef OK_FWHM_DBG
9150       if (SUMA_GetDbgFWHM()) {
9151          SUMA_S_Warn("Function in debug mode. File of same name created!\n");
9152          fdbg = fopen(FuncName,"w");
9153          fprintf(fdbg,"#--------------------\n#n1   n2  SegLen   dfds\n");
9154       }
9155    #endif
9156    if (!fim || !SO) {
9157       SUMA_S_Errv("NULL input fim=%p, SO=%p\n", fim, SO);
9158       SUMA_RETURN(ssc);
9159    }
9160 
9161    if (!SO->FN || !SO->EL) {
9162       if (!SUMA_SurfaceMetrics_eng( SO, "EdgeList", NULL,
9163                                     0, SUMAg_CF->DsetList)){
9164          SUMA_SL_Err("Failed to create needed accessories");
9165          SUMA_RETURN(ssc);
9166       }
9167    }
9168    if (!SO->MF || !SO->PolyArea) {
9169       if (!SUMA_SurfaceMetrics_eng( SO, "MemberFace|PolyArea",
9170                                     NULL, 0, SUMAg_CF->DsetList)){
9171          SUMA_SL_Err("Failed to create needed accessories");
9172          SUMA_RETURN(ssc);
9173       }
9174    }
9175 
9176    if (!SO->FN || !SO->EL || !SO->MF || !SO->PolyArea) {
9177       SUMA_S_Errv("J'ai besoin des voisins(%p) et des cotes (%p), cherie\n"
9178                   "Et en plus, MF(%p) and PolyArea(%p)",
9179             SO->FN, SO->EL, SO->MF, SO->PolyArea);
9180       SUMA_RETURN(ssc);
9181    }
9182    /*----- estimate the variance of the data -----*/
9183 
9184    fsum = 0.0; fsq = 0.0; count = 0;
9185    for (in0 = 0;  in0 < SO->N_Node;  in0++){
9186       if( !nmask || nmask[in0] ) { count++; arg = fim[in0]; fsum += arg; fsq  += arg*arg; }
9187    }
9188    if( count < 9 || fsq <= 0.0 ){     /* no data? */
9189       SUMA_RETURN(ssc) ;
9190    }
9191 
9192    var = (fsq - (fsum * fsum)/count) / (count-1.0);
9193    if( var <= 0.0 ){                  /* crappy data? */
9194       SUMA_RETURN(ssc);
9195    }
9196 
9197    ssv[0] = ssv[1] = ssv[2] = -1.0;
9198    for (ipl=0; ipl<3;++ipl) {
9199       if (!striplist_vec) { /* need to create your own */
9200          /* get the intersection strips, start alond the various directions */
9201          Eq[0] = Eq[1]=Eq[2]=Eq[3] = 0.0;
9202          Eq[ipl] = 1.0; Eq[3] = -SO->Center[ipl];
9203                         /* 0==Saggittal, 1==Coronal, 2==Axial */
9204          SUMA_LHv("Kill me!\nEq:[%f %f %f %f], step: %f\n",
9205                   Eq[0], Eq[1], Eq[2], Eq[3], SO->EL->AvgLe);
9206          if (!(striplist = SUMA_SliceAlongPlane(SO, Eq, SO->EL->AvgLe))) {
9207             SUMA_S_Err("Failed to slice along plane");
9208             SUMA_RETURN(ssc);
9209          }
9210          /*SUMA_display_edge_striplist(striplist, &(SUMAg_SVv[0]), SO,
9211                                        "ShowConnectedPoints"); */
9212       } else {
9213          striplist = striplist_vec[ipl];
9214       }
9215 
9216       /*----- estimate the partial derivatives -----*/
9217       SUMA_LHv("Have a striplist of %d elements\n", dlist_size(striplist));
9218       dfdssum = 0.0;
9219       dfdssq  = 0.0;
9220       counts  = 0;
9221       ds = 0.0;
9222       strip = NULL;
9223       listelm = NULL;
9224       clsd = NOPE;
9225       do {
9226          if (!listelm) listelm = dlist_head(striplist);
9227          else listelm = dlist_next(listelm);
9228          strip = (SUMA_STRIP*)listelm->data;
9229          loope = NULL;
9230          loopp = NULL;
9231          SUMA_LHv("Have a strip of %d points/edges\n", dlist_size(strip->Edges));
9232          if ((clsd = SUMA_isEdgeStripClosed(strip->Edges, SO))) {
9233                                                          /* close list */
9234             dlist_ins_next(strip->Edges, dlist_tail(strip->Edges),
9235                            (dlist_head(strip->Edges))->data);
9236             dlist_ins_next(strip->Points, dlist_tail(strip->Points),
9237                            (dlist_head(strip->Points))->data);
9238          }
9239          do {
9240             if (!loope) {
9241                loope = dlist_head(strip->Edges);
9242                loopp = dlist_head(strip->Points);
9243             }
9244             loope_n = dlist_next(loope);  /* the next point */
9245             loopp_n = dlist_next(loopp);
9246             /* which nodes from the edge? */
9247             in0 = SO->EL->EL[(INT_CAST)loope->data][0];
9248             in1 = SO->EL->EL[(INT_CAST)loope->data][1];
9249             in0_n = SO->EL->EL[(INT_CAST)loope_n->data][0];
9250             in1_n = SO->EL->EL[(INT_CAST)loope_n->data][1];
9251             if( !nmask || (nmask[in0]&& nmask[in1] &&
9252                            nmask[in0_n]&& nmask[in1_n])){
9253                p4 = (float*)loopp->data;
9254                arg = p4[3]*fim[in0] + (1.0-p4[3])*fim[in1] ;
9255                      /* interpolated value at intersection point on edge */
9256                p4_n = (float*)loopp_n->data;
9257                arg_n = p4_n[3]*fim[in0_n] + (1.0-p4_n[3])*fim[in1_n] ;
9258                SUMA_UNIT_VEC(p4, p4_n, U, Un);
9259                ds += Un;
9260                dfds = arg_n - arg;
9261                dfdssum += dfds; dfdssq += dfds*dfds;
9262                #ifdef OK_FWHM_DBG
9263                   if (SUMA_GetDbgFWHM()) {
9264                      fprintf(fdbg,"%5d %5d %5d %5d   %.3f  %.3f\n",
9265                               in0, in1, in0_n, in1_n, Un, dfds);
9266                   }
9267                #endif
9268                counts++;
9269 
9270             }
9271             loope = loope_n; loopp = loopp_n;
9272          } while (loope != dlist_tail(strip->Edges));
9273          if (clsd) { /* now remove last addition */
9274             dlist_remove(strip->Edges, dlist_tail(strip->Edges), &vp);
9275             dlist_remove(strip->Points, dlist_tail(strip->Points), &vp);
9276          }
9277       } while (listelm != dlist_tail(striplist));
9278 
9279 
9280       /*----- estimate the variance of the partial derivatives -----*/
9281       SUMA_LH("Mmmm, La Variance");
9282       varss = (counts < 36) ? 0.0
9283                           : (dfdssq - (dfdssum * dfdssum)/counts) / (counts-1.0);
9284       ds /= (double)counts;  /* the average segment length */
9285 
9286       /*----- now estimate the FWHMs -----*/
9287 
9288       /*---- 2.35482 = sqrt(8*log(2)) = sigma-to-FWHM conversion factor ----*/
9289 
9290       /* with random noise, varss should be about 2*var if the ratio of varss/var
9291       is not significantly different from 1 then set FWHM to 0.0 rather than the
9292       error flag */
9293       SUMA_LH("Mmmm, Le Bruit");
9294       par[0] = (float)SO->N_Node;
9295       par[1] = (float)SO->N_Node; /* assuming independence of course */
9296       prob = THD_stat_to_pval( SUMA_MAX_PAIR(varss/(2.0*var), (2.0*var)/varss)  , NI_STAT_FTEST , par ) ;
9297       if (prob > 0.01) {
9298          /* so what would the smallest acceptable FWHM be? */
9299          stat = THD_pval_to_stat (0.01, NI_STAT_FTEST, par);
9300          arg = 1.0 - 0.5*(2.0/stat);
9301          ssv[ipl] = 2.35482*sqrt( -1.0 / (4.0*log(arg)) );
9302          SUMA_S_Notev(  "   Distribution of data is possibly random noise (p=%f)\n"
9303                         "   Expect fwhm to be no different from 0 \n"
9304                         "   FWHM values up to %.2f(segments) or %.2f(mm)\n"
9305                         "   are likely meaningless (at p=0.01) on this mesh.\n\n", prob, ssv[ipl], ssv[ipl]*ds);
9306       }
9307       arg = 1.0 - 0.5*(varss/var);
9308       if (arg <= 0.0 || arg >= 1.0) {
9309          if (arg <=0 && prob > 0.01) ssv[ipl] = 0.0f; else ssv[ipl] = -1.0f;
9310       } else {
9311          ssv[ipl] = 2.35482*sqrt( -1.0 / (4.0*log(arg)) )*ds;
9312       }
9313       SUMA_LHv("The FWHM along plane %d: %f\n", ipl, ssv[ipl]);
9314 
9315       #ifdef OK_FWHM_DBG
9316          if (SUMA_GetDbgFWHM()) {
9317             fprintf(fdbg,"#counts=%d\n#var=%f\n#varss=%f\n#ds=%.3f\n#arg=%.3f\n#ss=%f\n", counts, var, varss, ds, arg, ssv[ipl]);
9318             fclose(fdbg);
9319          }
9320       #endif
9321       if (striplist_vec) {
9322          /* not yours, do not free */
9323          striplist = NULL;
9324       } else {
9325          SUMA_LH("Mmmm, La Liberte");
9326          SUMA_FREE_DLIST(striplist); striplist = NULL;
9327       }
9328    }
9329    /* combine, mean for now */
9330    if (ssv[0] >=0.0 && ssv[1]>=0.0 && ssv[2] >= 0.0) {
9331       ssc = (ssv[0]+ssv[1]+ssv[2])/3.0;
9332       if (ssvr) { ssvr[0] = ssv[0]; ssvr[1] = ssv[1]; ssvr[2] = ssv[2]; }
9333    } else {
9334       ssc = -1.0;
9335    }
9336    SUMA_RETURN(ssc) ;
9337 }
9338 
SUMA_Offset_Smooth_dset(SUMA_SurfaceObject * SO,float FWHM,float OffsetLim,int N_iter,SUMA_DSET * dset,SUMA_COMM_STRUCT * cs,byte * nmask,byte strict_mask)9339 SUMA_Boolean SUMA_Offset_Smooth_dset(
9340    SUMA_SurfaceObject *SO,
9341    float FWHM, float OffsetLim,
9342    int N_iter,
9343    SUMA_DSET *dset,
9344    SUMA_COMM_STRUCT *cs, byte *nmask, byte strict_mask)
9345 {
9346    static char FuncName[]={"SUMA_Offset_Smooth_dset"};
9347    float *fout_final = NULL, *fbuf=NULL, *fin=NULL, *fout=NULL,
9348          *fin_next = NULL, *fin_orig = NULL;
9349    float fp, dfp, fpj, wt, wts, sig, fwhm_orig, fwhm_out;
9350    double dj, ds2, scl;
9351    int n , k, j, niter, vnk, os, jj, nj=-1, *icols=NULL, N_icols, N_nmask;
9352    byte *bfull=NULL;
9353    SUMA_OFFSET_STRUCT *OffS_out = NULL;
9354 
9355    SUMA_Boolean LocalHead = NOPE;
9356 
9357    SUMA_ENTRY;
9358 
9359    SUMA_S_Warn("Niter is not treated properly");
9360    SUMA_S_Warn("No useful weighting in place");
9361 
9362    SUMA_S_Warn("Useless and obsolete. DO NOT USE");
9363    SUMA_RETURN(NOPE);
9364 
9365    if (!SO || !dset) {
9366       SUMA_S_Errv("NULL SO (%p) or dset(%p)\n", SO, dset);
9367       SUMA_RETURN(NOPE);
9368    }
9369 
9370    if (!SO->FN) {
9371       SUMA_SL_Err("NULL SO->FN\n");
9372       SUMA_RETURN(NOPE);
9373    }
9374 
9375    /* what columns can we process ?*/
9376    icols = SUMA_FindNumericDataDsetCols(dset, &N_icols);
9377 
9378    if (N_icols <= 0) {
9379       SUMA_SL_Err("No approriate data columns in dset");
9380       SUMA_RETURN(NOPE);
9381    }
9382 
9383    /* allocate for buffer and output */
9384    fbuf = (float *)SUMA_calloc(SO->N_Node, sizeof(float));
9385    fout_final = (float *)SUMA_calloc(SO->N_Node, sizeof(float));
9386    if (!fbuf || !fout_final) {
9387       SUMA_SL_Crit("Failed to allocate for fbuf and fout_final\n");
9388       SUMA_RETURN(NOPE);
9389    }
9390 
9391    if (cs->Send && N_icols > 1) {
9392       SUMA_S_Warn("Only 1st data column will be sent to SUMA in talk_mode.");
9393    }
9394 
9395    if (N_iter < 1) {
9396       SUMA_S_Errv("Niter = %d!\n", N_iter);
9397    }
9398    /* reduce FWHM by Niter */
9399    FWHM = FWHM/sqrt(N_iter);
9400 
9401    /* calculate sigma */
9402    if (FWHM > 0.0) {
9403       sig = FWHM /   2.354820;
9404       ds2 = 2*sig*sig;
9405       scl = iSQ_2PI/sig;
9406    } else {
9407       SUMA_S_Errv("Bad FWHM %f", FWHM);
9408       SUMA_RETURN(NOPE);
9409    }
9410 
9411    /* calculate the offset limit, if allowed */
9412    if (OffsetLim < 0.0) {
9413       if (sig > 0.0) {
9414          OffsetLim = 3.5*sig;
9415       } else {
9416          SUMA_S_Errv("Have OffsetLim =%f and no FWHM (%f) from which to estimate it.\n", OffsetLim, FWHM);
9417          SUMA_RETURN(NOPE);
9418       }
9419    }
9420 
9421    /* Check for plausible values */
9422    if (OffsetLim < 3*SO->EL->AvgLe) {
9423       int Niter_sug = SUMA_MAX_PAIR(1, (int)((float)N_iter*SUMA_POW2(OffsetLim/3.0/(float)SO->EL->AvgLe)));
9424       fprintf(SUMA_STDERR,"Error %s:%d\n"
9425                   "********************************************\n"
9426                   "Inapropriate values for Niter of %d and/or\n"
9427                   "FWHM of %.1f. Per iteration, fwhm is %.4f\n"
9428                   "and OffsetLim is set to %f.\n"
9429                   "But the internodal distance of %.3f is\n"
9430                   "too large for proper estimation.\n",
9431                   FuncName, __LINE__,
9432                   N_iter, FWHM*sqrt(N_iter), FWHM, OffsetLim, SO->EL->AvgLe);
9433       if (Niter_sug < N_iter) {
9434          fprintf(SUMA_STDERR,"Try replacing Niter by %d. If you still get\n"
9435                   "this warning then your FWHM is too small for your\n"
9436                   "mesh\n"
9437                   "********************************************\n",
9438                    Niter_sug);
9439       } else {
9440          fprintf(SUMA_STDERR,"Your FWHM is too small for this \n"
9441                   "mesh\n"
9442                   "********************************************\n");
9443       }
9444       SUMA_RETURN(NOPE);
9445    }
9446    SUMA_LHv("OffsetLim set to %f\nSigma set to %f per iteration with %d iterations (FWHM per iteration=%f)\n", OffsetLim, sig, N_iter, FWHM);
9447 
9448    /* Begin filtering operation for each column */
9449    for (k=0; k < N_icols; ++k) {
9450       /* get a float copy of the data column */
9451       fin_orig = SUMA_DsetCol2Float (dset, icols[k], 1);
9452       if (!fin_orig) {
9453          SUMA_SL_Crit("Failed to get copy of column. Woe to thee!");
9454          SUMA_RETURN(NOPE);
9455       }
9456       /* make sure column is not sparse, one value per node */
9457       if (k==0) {
9458          SUMA_LH( "Special case k = 0, going to SUMA_MakeSparseColumnFullSorted");
9459          bfull = NULL;
9460          if (!SUMA_MakeSparseColumnFullSorted(&fin_orig, SDSET_VECFILLED(dset), 0.0, &bfull, dset, SO->N_Node)) {
9461             SUMA_S_Err("Failed to get full column vector");
9462             SUMA_RETURN(NOPE);
9463          }
9464          if (bfull) {
9465             SUMA_LH( "Something was filled in SUMA_MakeSparseColumnFullSorted\n" );
9466             /* something was filled in good old SUMA_MakeSparseColumnFullSorted */
9467             if (nmask) {   /* combine bfull with nmask */
9468                SUMA_LH( "Merging masks\n" );
9469                for (jj=0; jj < SO->N_Node; ++jj) { if (nmask[jj] && !bfull[jj]) nmask[jj] = 0; }
9470             } else { nmask = bfull; }
9471          }
9472          if (nmask) {
9473             N_nmask = 0;
9474             for (n=0; n<SO->N_Node; ++n) { if (nmask[n]) ++ N_nmask; }
9475             SUMA_LHv("Blurring with node mask (%d nodes in mask)\n", N_nmask);
9476             if (!N_nmask) {
9477                SUMA_S_Warn("Empty mask, nothing to do");
9478                goto CLEANUP;
9479             }
9480          }
9481          /* now calculate the neighbor offset structure */
9482          SUMA_LHv("Calculating OffS_out FWHM=%f, OffsetLim = %f\n", FWHM, OffsetLim);
9483          OffS_out = SUMA_FormNeighbOffset (SO, OffsetLim, NULL, nmask, FWHM);
9484       } else {
9485          SUMA_LH( "going to SUMA_MakeSparseColumnFullSorted");
9486          if (!SUMA_MakeSparseColumnFullSorted(&fin_orig, SDSET_VECFILLED(dset), 0.0, NULL, dset, SO->N_Node)) {
9487             SUMA_S_Err("Failed to get full column vector");
9488             SUMA_RETURN(NOPE);
9489          }
9490          /* no need for reworking nmask and bfull for each column...*/
9491 
9492       }
9493 
9494       if (cs->Send && k == 0) { /* send the first monster */
9495          if (!SUMA_SendToSuma (SO, cs, (void *)fin_orig, SUMA_NODE_RGBAb, 1)) {
9496             SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCommunication halted.");
9497          }
9498       }
9499 
9500       if (SUMA_Get_UseSliceFWHM()) {
9501          fwhm_orig = SUMA_estimate_slice_FWHM_1dif( SO, fin_orig, nmask, 1, NULL, NULL);
9502       } else {
9503          fwhm_orig = SUMA_estimate_FWHM_1dif(SO, fin_orig, nmask, 1);
9504       }
9505       SUMA_LHv("FWHM_orig for col. %d is : %f\n", k, fwhm_orig);
9506 
9507       /* filter this column for each of the iterations */
9508       fin_next = fin_orig;
9509       for (niter=0; niter < N_iter; ++niter) {
9510          SUMA_LHv("niter %d\n", niter);
9511          if ( niter % 2 ) { /* odd */
9512             fin = fin_next; /* input from previous output buffer */
9513             fout = fout_final; /* results go into final vector */
9514             fin_next = fout_final; /* in the next iteration, the input is from fout_final */
9515          } else { /* even */
9516             /* input data is in fin_new */
9517             fin = fin_next;
9518             fout = fbuf; /* results go into buffer */
9519             fin_next = fbuf; /* in the next iteration, the input is from the buffer */
9520          }
9521 
9522          if (!nmask) {
9523             for (n=0; n < SO->N_Node; ++n) {
9524                fp = fin[n]; /* kth value at node n */
9525                wt = iSQ_2PI/sig;
9526                dfp = wt*fp; wts = wt;
9527                /* if (n == 1358) { fprintf(SUMA_STDOUT,"fin[%d]=%f; Have %d neighbs, wt = %f\n", n, fin[n], OffS_out[n].N_Neighb, wt); } */
9528                for (j=0; j<OffS_out[n].N_Neighb; ++j) {
9529                   nj = OffS_out[n].Neighb_ind[j];
9530                   fpj = fin[nj]; /* value at jth neighbor of n */
9531                   wt = OffS_out[n].Neighb_dist[j];   /*  */
9532                   dfp += wt * (fpj);  wts += wt;
9533                   /* if (n == 1358) { fprintf(SUMA_STDOUT,"   fin[%d]=%f; wt=%f, dfp=%f; wts=%f\n", nj, fin[nj],  wt, dfp, wts); }  */
9534                }/* for j*/
9535                fout[n] = dfp/(wts);
9536             }/* for n */
9537          }else{/* masking potential */
9538             for (n=0; n < SO->N_Node; ++n) {
9539                fp = fin[n]; /* kth value at node n */
9540                wt = iSQ_2PI/sig;
9541                dfp = wt*fp; wts = wt;
9542                if (nmask[n]) {
9543                   for (j=0; j<OffS_out[n].N_Neighb; ++j) {
9544                      nj = OffS_out[n].Neighb_ind[j];
9545                      if (nmask[nj] || !strict_mask) { /* consider only neighbors that are in mask if strict_mask is 1*/
9546                         fpj = fin[nj]; /* value at jth neighbor of n */
9547                         wt = OffS_out[n].Neighb_dist[j];
9548                         dfp += wt * (fpj);  wts += wt;
9549                      }
9550                   }/* for j*/
9551                }
9552                fout[n] = dfp/(wts);
9553             }/* for n */
9554          }/* masking potential */
9555 
9556         if (cs->Send && k == 0) {
9557             if (!SUMA_SendToSuma (SO, cs, (void *)fout, SUMA_NODE_RGBAb, 1)) {
9558                SUMA_SL_Warn("Failed in SUMA_SendToSuma\nCommunication halted.");
9559             }
9560          }
9561 
9562       } /* for niter */
9563 
9564       if (fin_orig) SUMA_free(fin_orig); fin_orig = NULL;
9565 
9566       /* Now we need to shove the filtered data back into the dset */
9567       if (N_iter % 2) { /* odd */
9568          fout = fbuf;
9569       } else fout = fout_final;
9570 
9571       if (!SUMA_Float2DsetCol (dset, icols[k], fout, 1, nmask)) {
9572          SUMA_S_Err("Failed to update dset's values");
9573          SUMA_RETURN(NOPE);
9574       }
9575 
9576       if (SUMA_Get_UseSliceFWHM()) {
9577          fwhm_out = SUMA_estimate_slice_FWHM_1dif( SO, fout, nmask, 1, NULL, NULL);
9578       } else {
9579          fwhm_out = SUMA_estimate_FWHM_1dif(SO, fout, nmask, 1);
9580       }
9581       SUMA_LHv("FWHM_out for col. %d is : %f\n", k, fwhm_out);
9582 
9583 
9584    } /* for each col */
9585 
9586    CLEANUP:
9587    OffS_out = SUMA_free_NeighbOffset (SO, OffS_out);
9588    if (fin_orig) SUMA_free(fin_orig); fin_orig = NULL;
9589       /* just in case, this one's still alive from a GOTO */
9590    if (bfull) SUMA_free(bfull); bfull = NULL;
9591    if (fbuf) SUMA_free(fbuf); fbuf = NULL;
9592    if (fout_final) SUMA_free(fout_final); fout_final = NULL;
9593 
9594    SUMA_RETURN(YUP);
9595 }
9596 
9597 
9598 /*!
9599    \brief determine overall orientation of triangle normals
9600           and change triangle orientation if required
9601    \param NodeList (float *) xyz vector of node coords
9602    \param N_Node (int) number of nodes
9603    \param FaceSetList (int *) [n1 n2 n3] vector of triangles
9604    \param N_FaceSet (int) number of triangles
9605    \param orient (int) 0: Do not change orientation
9606                        1: make sure most normals point outwards from center.
9607                           Flip all triangles if necessary (unless Force is used)
9608                       -1: make sure most normals point towards center.
9609                           Flip all triangles if necessary (unless Force is used)
9610    \param Force (int) 1: Force the flipping of only those triangles whose normals
9611                          point in the wrong direction (opposite to orient).
9612                       2: like 1, but don't warn about consistency
9613           ** With this option, you might very well destroy the winding
9614              consistency of a surface! **
9615    \param cu (float *): User provided coordinate center. If NULL,
9616                         center is computed from NodeList
9617    \param   fliphappened (byte *) If not null, this flag gets set to 1 if any
9618                                   flipping occurred.
9619    \return ans (int):   0: error
9620                         1: most normals were pointing outwards before any fixes
9621                        -1: most normals were pointing inwards before any fixes
9622 */
SUMA_OrientTriangles(float * NodeList,int N_Node,int * FaceSetList,int N_FaceSet,int orient,int Force,float * cu,byte * fliphappened)9623 int SUMA_OrientTriangles (float *NodeList, int N_Node, int *FaceSetList,
9624                           int N_FaceSet, int orient, int Force,
9625                           float *cu, byte *fliphappened)
9626 {
9627    static char FuncName[]={"SUMA_OrientTriangles"};
9628    int i, j, ip, negdot, posdot, sgn, NP, ND, n1, n2, n3, flip, FlipHappened;
9629    float d1[3], d2[3], c[3], tc[3], U[3], dot, *norm, mag;
9630    FILE *fout = NULL;
9631    SUMA_Boolean LocalHead = NOPE;
9632 
9633    SUMA_ENTRY;
9634 
9635    FlipHappened = 0;
9636 
9637    if (!NodeList || !FaceSetList || !N_Node || !N_FaceSet) {
9638       SUMA_SL_Err("Null or no input");
9639       SUMA_RETURN(0);
9640    }
9641    norm = (float *)SUMA_calloc(3*N_FaceSet, sizeof(float));
9642    if (!norm) {
9643       SUMA_SL_Crit("Failed to allocate for norm"); SUMA_RETURN(0);
9644    }
9645    if (Force == 1) {
9646       SUMA_SL_Warn("Using Force option! "
9647                    "You might destroy triangulation consistency of surface!");
9648    }
9649    NP = ND = 3;
9650    /* calculate the center coordinate */
9651    if (cu) {
9652       c[0] = cu[0];
9653       c[1] = cu[1];
9654       c[2] = cu[2];
9655    } else {
9656       c[0] = c[1] = c[2] = 0.0;
9657       for (i=0; i < N_Node; ++i) {
9658          ip = ND * i;
9659          c[0] += NodeList[ip]; c[1] += NodeList[ip+1]; c[2] += NodeList[ip+2];
9660       }
9661       c[0] /= N_Node; c[1] /= N_Node; c[2] /= N_Node;
9662    }
9663    /* calculate normals for each triangle, taken from SUMA_SurfNorm*/
9664    if (0 && LocalHead) {
9665       SUMA_SL_Note("Writing SUMA_OrientTriangles.1D");
9666       fout = fopen("SUMA_OrientTriangles.1D", "w");
9667    }
9668    negdot = 0; posdot = 0;
9669    for (i=0; i < N_FaceSet; i++) {
9670       ip = NP * i;
9671          /* node indices making up triangle */
9672       n1 = FaceSetList[ip]; n2 = FaceSetList[ip+1]; n3 = FaceSetList[ip+2];
9673          /* centroid of triangle */
9674       tc[0] = (NodeList[3*n1]   + NodeList[3*n2]   + NodeList[3*n3]  )/3;
9675       tc[1] = (NodeList[3*n1+1] + NodeList[3*n2+1] + NodeList[3*n3+1])/3;
9676       tc[2] = (NodeList[3*n1+2] + NodeList[3*n2+2] + NodeList[3*n3+2])/3;
9677          /* calc normal */
9678       for (j=0; j < 3; j++) {
9679          d1[j] = NodeList[(ND*n1)+j] - NodeList[(ND*n2)+j];
9680          d2[j] = NodeList[(ND*n2)+j] - NodeList[(ND*n3)+j];
9681       }
9682       norm[ip] = d1[1]*d2[2] - d1[2]*d2[1];
9683       norm[ip+1] = d1[2]*d2[0] - d1[0]*d2[2];
9684       norm[ip+2] = d1[0]*d2[1] - d1[1]*d2[0];
9685 
9686       /* dot the normal with vector from center to node */
9687       U[0] = tc[0] - c[0]; U[1] = tc[1] - c[1]; U[2] = tc[2] - c[2];
9688       SUMA_DOTP_VEC(U, &(norm[ip]), dot, 3, float, float);
9689       if (dot < 0) {
9690          ++negdot;
9691          if (0 && LocalHead) {
9692             fprintf (SUMA_STDERR,
9693                      "%s: Triangle %d has a negative dot product %f\n"
9694                      "c  =[%.3f %.3f %.3f]\ntc =[%.3f %.3f %.3f]\n"
9695                      "norm=[%.3f %.3f %.3f]\n",
9696                 FuncName, i, dot, c[0], c[1], c[2], tc[0], tc[1], tc[2],
9697                 norm[ip+0], norm[ip+1], norm[ip+2]); }
9698 
9699       } else {
9700          if (fout) {
9701                SUMA_NORM_VEC(norm,3,mag); if (!mag) mag = 1; mag /= 5;
9702                if (fout) fprintf (fout,"%.3f %.3f %.3f %.3f %.3f %.3f\n",
9703                                   tc[0], tc[1], tc[2],
9704                                   tc[0]+norm[ip+0]/mag,
9705                                   tc[1]+norm[ip+1]/mag,
9706                                   tc[2]+norm[ip+2]/mag);
9707          }
9708          ++posdot;
9709       }
9710 
9711       if (Force) {
9712          if ( (dot < 0 && orient > 0) || (dot > 0 && orient < 0)) {
9713             n1 = FaceSetList[ip];
9714             FaceSetList[ip] = FaceSetList[ip+2]; FaceSetList[ip+2] = n1;
9715             FlipHappened = 1;
9716          }
9717       }
9718    }
9719    if (fout) fclose(fout); fout = NULL;
9720    flip = 0; sgn = 0;
9721    if (posdot >= negdot) {
9722       SUMA_LH("Normals appear to point away from center");
9723       sgn = 1;
9724       if (orient < 0) flip = 1;
9725    } else {
9726       SUMA_LH("Normals appear to point towards center");
9727       sgn = -1;
9728       if (orient > 0) flip = 1;
9729    }
9730 
9731    SUMA_LHv("Found %d positive dot products and %d negative ones.\n",
9732             posdot, negdot);
9733 
9734 
9735    if (flip && !Force) {
9736       SUMA_LH("Flipping all");
9737       FlipHappened = 1;
9738       for (i=0; i < N_FaceSet; i++) {
9739          ip = NP * i;
9740          n1 = FaceSetList[ip];
9741          FaceSetList[ip] = FaceSetList[ip+2]; FaceSetList[ip+2] = n1;
9742       }
9743    }
9744 
9745    if (norm) SUMA_free(norm); norm = NULL;
9746 
9747    if (fliphappened) *fliphappened = FlipHappened;
9748 
9749    SUMA_RETURN(sgn);
9750 }
9751 
9752 /*!
9753    Compute the dot product of the normal direction with the
9754    direction stored in dir
9755    SO Surface with normals and Center already computed
9756    dir 1 direction vector. If NULL then each node
9757        will get a different direction vector formed by the
9758        node and the surface's center.
9759    dots Will contain the dot products. If *dots == NULL,
9760         the function will alocate space, else it will
9761         use the pointer in *dots
9762 */
SUMA_DotNormals(SUMA_SurfaceObject * SO,float * dir,float ** dots)9763 SUMA_Boolean SUMA_DotNormals(SUMA_SurfaceObject *SO, float *dir, float **dots)
9764 {
9765    static char FuncName[]={"SUMA_DotNormals"};
9766    double Un, U[3]={0.0, 0.0, 0.0};
9767    float *dtp=NULL, *X=NULL, *N=NULL;
9768    int ii=0;
9769    SUMA_Boolean LocalHead = NOPE;
9770 
9771    SUMA_ENTRY;
9772 
9773    /* could do test SO->Center -> NO->NodeNormList, but not positive */
9774    /* just omit Center for now                   [22 Jun 2021 rickr] */
9775    if (!SO || !SO->NodeNormList || !dots) {
9776       SUMA_SL_Err("Null or no input");
9777       SUMA_RETURN(NOPE);
9778    }
9779 
9780    if (*dots == NULL) {
9781       *dots = (float *)SUMA_calloc(3*SO->N_Node, sizeof(float));
9782    }
9783    dtp = *dots;
9784 
9785    if (dir) {
9786       U[0] = dir[0]; U[1] = dir[1]; U[2] = dir[2];
9787       SUMA_UNITIZE_VEC(U,3);
9788    }
9789    for (ii=0; ii<SO->N_Node; ++ii) {
9790       N = SO->NodeNormList+3*ii;
9791       if (!dir) { /* direction X-->Center */
9792          X = SO->NodeList+3*ii;
9793          SUMA_UNIT_VEC(X, SO->Center, U, Un);
9794       }
9795       dtp[ii] = SUMA_MT_DOT(N, U);
9796    }
9797 
9798    SUMA_RETURN(YUP);
9799 }
9800 /*
9801    A convenience function for SUMA_OrientTriangles
9802    It takes care of recreating relevant SO fields
9803    if flipping occurred */
SUMA_OrientSOTriangles(SUMA_SurfaceObject * SO,int orient,int Force,float * cu)9804 int SUMA_OrientSOTriangles(SUMA_SurfaceObject *SO,
9805                            int orient, int Force,
9806                            float *cu)
9807 {
9808    static char FuncName[]={"SUMA_OrientSOTriangles"};
9809    int oorient=0;
9810    byte FlipHappened=0;
9811    SUMA_Boolean LocalHead = NOPE;
9812 
9813    SUMA_ENTRY;
9814 
9815    if (!SO) {
9816       SUMA_S_Err("NULL input");
9817       SUMA_RETURN(NOPE);
9818    }
9819    if (!(oorient=SUMA_OrientTriangles (SO->NodeList, SO->N_Node,
9820                               SO->FaceSetList, SO->N_FaceSet,
9821                               orient, Force, cu, &FlipHappened))) {
9822       SUMA_S_Err("Failed to set orientation");
9823       SUMA_RETURN(oorient);
9824    }
9825 
9826    if (FlipHappened) {
9827       SUMA_LH("Recoputing normals and edgelist");
9828       if (SO->EL) SUMA_free_Edge_List(SO->EL); SO->EL = NULL;
9829       if (!SUMA_SurfaceMetrics_eng(SO, "EdgeList",
9830                                    NULL, 0,SUMAg_CF->DsetList)) {
9831          SUMA_SL_Err("Failed to create edge list for SO");
9832          SUMA_RETURN(0);
9833       }
9834       SUMA_RECOMPUTE_NORMALS(SO);
9835    }
9836 
9837    SUMA_RETURN(oorient);
9838 }
9839 
SUMA_FlipSOTriangles(SUMA_SurfaceObject * SO)9840 SUMA_Boolean SUMA_FlipSOTriangles(SUMA_SurfaceObject *SO) {
9841    static char FuncName[]={"SUMA_FlipSOTriangles"};
9842    SUMA_ENTRY;
9843    if (!SO || !SO->FaceSetList) SUMA_RETURN(NOPE);
9844    SUMA_FlipTriangles(SO->FaceSetList, SO->N_FaceSet);
9845    SUMA_RECOMPUTE_NORMALS(SO);
9846    SUMA_RETURN(YUP);
9847 }
9848 
SUMA_FlipTriangles(int * FaceSetList,int N_FaceSet)9849 SUMA_Boolean SUMA_FlipTriangles (int *FaceSetList,int N_FaceSet)
9850 {
9851    static char FuncName[]={"SUMA_FlipTriangles"};
9852    int i, ip, NP, n1;
9853    SUMA_Boolean LocalHead = NOPE;
9854 
9855    SUMA_ENTRY;
9856 
9857 
9858    if (!FaceSetList || !N_FaceSet) {
9859       SUMA_SL_Err("Null or no input");
9860       SUMA_RETURN(NOPE);
9861    }
9862 
9863    NP = 3;
9864    for (i=0; i < N_FaceSet; i++) {
9865       ip = NP * i;
9866       n1 = FaceSetList[ip];
9867       FaceSetList[ip] = FaceSetList[ip+2];
9868       FaceSetList[ip+2] = n1;
9869    }
9870 
9871    SUMA_RETURN(YUP);
9872 }
9873 
9874 /*
9875    \brief A surface to merge a bunch of surfaces into one big mamma
9876 */
SUMA_MergeSurfs(SUMA_SurfaceObject ** SOv,int N_SOv)9877 SUMA_SurfaceObject *SUMA_MergeSurfs(SUMA_SurfaceObject **SOv, int N_SOv)
9878 {
9879    static char FuncName[]={"SUMA_MergeSurfs"};
9880    SUMA_SurfaceObject *SO=NULL, *iso=NULL;
9881    int i = 0, cnt = 0, n3=0;
9882    int N_Node = 0, NodeOffset=0, *meshp=NULL;
9883    SUMA_Boolean LocalHead = NOPE;
9884 
9885    SUMA_ENTRY;
9886 
9887    if (!SOv || N_SOv < 1) {
9888       SUMA_SL_Err("Null input");
9889       SUMA_RETURN(SO);
9890    }
9891 
9892    /* count the number of nodes and initialize imask*/
9893    SO = SUMA_Alloc_SurfObject_Struct(1);
9894    if (!SO) {
9895       SUMA_SL_Err("Failed to allocate");
9896       SUMA_RETURN(SO);
9897    }
9898 
9899    for (i=0; i<N_SOv; ++i) {
9900       if ((iso = SOv[i])) {
9901          if (i==0 || SO->N_Node == 0) {
9902             SO->NodeDim = iso->NodeDim;
9903             SO->FaceSetDim = iso->FaceSetDim;
9904          } else if (iso->NodeDim != SO->NodeDim) {
9905             SUMA_S_Errv("Bad dimensions for %s, skipping it\n",
9906                         iso->Label);
9907             SOv[i] = NULL;
9908             continue;
9909          }
9910          SO->N_Node += iso->N_Node;
9911          SO->N_FaceSet += iso->N_FaceSet;
9912       }
9913    }
9914 
9915    if (!(SO->NodeList = (float *)SUMA_calloc(SO->N_Node*SO->NodeDim,
9916                                        sizeof(float))) ||
9917        !(SO->FaceSetList = (int *)SUMA_calloc(SO->N_FaceSet*SO->FaceSetDim,
9918                                          sizeof(int)))) {
9919       SUMA_S_Errv("Could not allocate for %d nodes, %d triangles\n",
9920                   SO->N_Node, SO->N_FaceSet);
9921       SUMA_ifree(SO->NodeList);
9922       SUMA_ifree(SO->FaceSetList);
9923       SUMA_Free_Surface_Object(SO);
9924       SUMA_RETURN(NULL);
9925    }
9926    cnt = 0;
9927    for (i=0; i<N_SOv; ++i) {
9928       if ((iso = SOv[i])) {
9929          memcpy(SO->NodeList+(cnt*iso->NodeDim),iso->NodeList,
9930                   sizeof(float)*iso->N_Node*iso->NodeDim);
9931          cnt += iso->N_Node;
9932       }
9933    }
9934    cnt = 0;
9935    for (i=0; i<N_SOv; ++i) {
9936       if ((iso = SOv[i])) {
9937          if (cnt == 0) {
9938             memcpy(SO->FaceSetList+(cnt*iso->FaceSetDim),iso->FaceSetList,
9939                      sizeof(int)*iso->N_FaceSet*iso->FaceSetDim);
9940             NodeOffset = iso->N_Node;
9941          } else {
9942             meshp = SO->FaceSetList+(cnt*iso->FaceSetDim);
9943             for (n3=0; n3<iso->N_FaceSet*iso->FaceSetDim; ++n3) {
9944                *meshp = iso->FaceSetList[n3]+NodeOffset; ++meshp;
9945             }
9946             NodeOffset += iso->N_Node;
9947          }
9948          cnt += iso->N_FaceSet;
9949       }
9950    }
9951 
9952    SUMA_RETURN(SO);
9953 }
9954 
9955 
9956 /*
9957    \brief a function to turn a surface patch (not all vertices are in use) into a surface where all nodes are used.
9958    \param NodeList (float *) N_Nodelist * 3 vector containing xyz triplets for vertex coordinates
9959    \param N_NodeList (int) you know what
9960    \param PatchFaces (int *) N_PatchFaces * PatchDim vector containing node indices forming triangulation
9961    \param N_PatchFaces (int) obvious
9962    \param PatchDim (int) 3 for triangular, 4 for rectangular patches etc. ..
9963    \return SO (SUMA_SurfaceObject *) surface object structure with NodeList, N_NodeList, FaceSetList and N_FaceSetList
9964                                      filled. Note node indexing in SO is not related to the indexing in patch.
9965    - Nothing but NodeList and FaceSetList is created here. KEEP IT THAT WAY
9966 */
9967 
SUMA_Patch2Surf(float * NodeList,int N_NodeList,int * PatchFaces,int N_PatchFaces,int PatchDim)9968 SUMA_SurfaceObject *SUMA_Patch2Surf(float *NodeList, int N_NodeList, int *PatchFaces, int N_PatchFaces, int PatchDim)
9969 {
9970    static char FuncName[]={"SUMA_Patch2Surf"};
9971    SUMA_SurfaceObject *SO=NULL;
9972    int i = 0, cnt = 0;
9973    int *imask = NULL;
9974    int N_Node = 0;
9975    SUMA_Boolean LocalHead = NOPE;
9976 
9977    SUMA_ENTRY;
9978 
9979    if (!NodeList || !PatchFaces) {
9980       SUMA_SL_Err("Null input");
9981       SUMA_RETURN(SO);
9982    }
9983 
9984    imask = (int*)SUMA_calloc(N_NodeList , sizeof(int));
9985    if (!imask) {
9986       SUMA_SL_Err("Failed to allocate");
9987       SUMA_RETURN(SO);
9988    }
9989    /* count the number of nodes and initialize imask*/
9990    SO = SUMA_Alloc_SurfObject_Struct(1);
9991    if (!SO) {
9992       SUMA_SL_Err("Failed to allocate");
9993       SUMA_RETURN(SO);
9994    }
9995    SO->N_FaceSet = N_PatchFaces;
9996    SO->N_Node = 0;
9997    for (i=0; i<3*N_PatchFaces; ++i) {
9998       if (!imask[PatchFaces[i]]) {
9999          imask[PatchFaces[i]] = -1;
10000          ++SO->N_Node;
10001       }
10002    }
10003    if (LocalHead) {
10004       fprintf (SUMA_STDERR,"%s: %d nodes in patch\n", FuncName, SO->N_Node);
10005    }
10006    SO->NodeList = (float *)SUMA_malloc(sizeof(float)*3*SO->N_Node);
10007    SO->FaceSetList = (int *)SUMA_malloc(sizeof(int)*PatchDim*N_PatchFaces);
10008    if (!SO->NodeList || !SO->FaceSetList) {
10009       SUMA_SL_Err("Failed to allocate");
10010       SUMA_RETURN(SO);
10011    }
10012    SO->NodeDim = 3;
10013    SO->FaceSetDim = PatchDim;
10014 
10015    cnt = 0;
10016    for (i=0; i<3*N_PatchFaces; ++i) {
10017       if (imask[PatchFaces[i]] < 0) {
10018          imask[PatchFaces[i]] = cnt;
10019          SO->NodeList[3*cnt  ] = NodeList[3*PatchFaces[i]  ];
10020          SO->NodeList[3*cnt+1] = NodeList[3*PatchFaces[i]+1];
10021          SO->NodeList[3*cnt+2] = NodeList[3*PatchFaces[i]+2];
10022          ++cnt;
10023       }
10024       SO->FaceSetList[i] = imask[PatchFaces[i]];
10025    }
10026 
10027    SUMA_RETURN(SO);
10028 }
10029 /*!
10030    \brief a function to return a mask indicating if a node is
10031    part of a patch or not
10032    isNodeInNodes = SUMA_MaskOfNodesInPatch( SUMA_SurfaceObject *SO, int * N_NodesUsedInPatch);
10033 
10034    \param SO (SUMA_SurfaceObject *) the surface object
10035    \param N_NodesUsedInPatch (int *) will contain the number of nodes used in the mesh of the patch (that is SO->FaceSetList)
10036                                      if *N_NodesUsedInPatch == SO->N_Node then all nodes in the nodelist are used
10037                                      in the mesh
10038    \return isNodeInNodes (SUMA_Boolean *) a vector SO->N_Node long such that if isNodeInNodes[n] = YUP then node n is used
10039                                     in the mesh
10040 */
SUMA_MaskOfNodesInPatch(SUMA_SurfaceObject * SO,int * N_NodesUsedInPatch)10041 byte *SUMA_MaskOfNodesInPatch(
10042                   SUMA_SurfaceObject *SO, int *N_NodesUsedInPatch)
10043 {
10044    static char FuncName[]={"SUMA_MaskOfNodesInPatch"};
10045    int k;
10046    byte *NodesInPatchMesh = NULL;
10047 
10048    SUMA_ENTRY;
10049 
10050    *N_NodesUsedInPatch = 0;
10051 
10052    if (!SO) {
10053       SUMA_SL_Err("NULL SO");
10054       SUMA_RETURN(NULL);
10055    }
10056 
10057    if (!SO->FaceSetList || !SO->N_FaceSet) {
10058       SUMA_SL_Err("NULL or empty SO->FaceSetList");
10059       SUMA_RETURN(NULL);
10060    }
10061 
10062    NodesInPatchMesh = (byte *)
10063                         SUMA_calloc(SO->N_Node, sizeof(byte));
10064    if (!NodesInPatchMesh) {
10065       SUMA_SL_Crit("Failed to allocate for NodesInPatchMesh");
10066       SUMA_RETURN(NULL);
10067    }
10068    for (k=0; k<SO->FaceSetDim*SO->N_FaceSet; ++k) {
10069       if (!NodesInPatchMesh[SO->FaceSetList[k]]) {
10070          ++*N_NodesUsedInPatch;
10071          NodesInPatchMesh[SO->FaceSetList[k]] = 1;
10072       }
10073    }
10074 
10075    SUMA_RETURN(NodesInPatchMesh);
10076 }
10077 
10078 /*!
10079    Given a set of node indices, return a patch of the original surface
10080    that contains them.
10081 
10082    Patch = SUMA_getPatch (NodesSelected, N_Nodes, MaxNodeIndex,
10083                           Full_FaceSetList, N_Full_FaceSetList, Memb, MinHits)
10084 
10085    \param NodesSelected (int *) N_Nodes x 1 Vector
10086             containing indices of selected nodes.
10087             These are indices into NodeList making up the surface formed by
10088             Full_FaceSetList.
10089    \param N_Nodes (int) number of elements in NodesSelected
10090    \param Full_FaceSetList (int *) N_Full_FaceSetList  x 3 vector
10091             containing the triangles forming the surface
10092    \param N_Full_FaceSetList (int) number of triangular facesets forming
10093             the surface
10094    \param Memb (SUMA_MEMBER_FACE_SETS *) structure containing the node
10095             membership information (result of SUMA_MemberFaceSets function)
10096    \param MinHits (int) minimum number of selected nodes in a triangle to let
10097             that triangle into the patch.
10098          Minimum is 1, Maximum logical is 3, assuming you do not have
10099          repeated node indices in NodesSelected.
10100    \param FixBowTie if  0: do nothing
10101                         1: check for potential bowtie cases
10102                         2: Fix bowtie cases
10103    \ret Patch (SUMA_PATCH *) Structure containing the patch's FaceSetList,
10104          FaceSetIndex (into original surface) and number of elements.
10105          returns NULL in case of trouble.
10106          Free Patch with SUMA_freePatch(Patch);
10107 
10108    \sa SUMA_MemberFaceSets, SUMA_isinbox, SUMA_PATCH
10109 */
10110 
SUMA_getPatch(int * NodesSelected,int N_Nodes,int MaxNodeIndex,int * Full_FaceSetList,int N_Full_FaceSetList,SUMA_MEMBER_FACE_SETS * Memb,int MinHits,int FixBowTie,int verb)10111 SUMA_PATCH * SUMA_getPatch (  int *NodesSelected, int N_Nodes, int MaxNodeIndex,
10112                               int *Full_FaceSetList, int N_Full_FaceSetList,
10113                               SUMA_MEMBER_FACE_SETS *Memb, int MinHits,
10114                               int FixBowTie, int verb)
10115 {
10116    static char FuncName[]={"SUMA_getPatch"};
10117    int * BeenSelected, *MakeGolden=NULL;
10118    int i, j, node, ip, ip2, NP, BadBones=0, igolden = 0;
10119    SUMA_PATCH *Patch;
10120    SUMA_Boolean LocalHead = NOPE;
10121 
10122    SUMA_ENTRY;
10123 
10124    if (verb > 1) LocalHead = YUP;
10125    if (!NodesSelected || !Full_FaceSetList || !Memb) {
10126       SUMA_S_Errv("NULL input %p, %p, %p\n",
10127                   NodesSelected, Full_FaceSetList, Memb);
10128       SUMA_RETURN(NULL);
10129    }
10130 
10131    /* SUMA_LH("Allocating"); */
10132    NP = 3;
10133    BeenSelected = (int *)SUMA_calloc (N_Full_FaceSetList, sizeof(int));
10134    Patch = (SUMA_PATCH *)SUMA_calloc(1, sizeof(SUMA_PATCH));
10135    if (FixBowTie) {
10136       MakeGolden = (int *)SUMA_calloc (N_Full_FaceSetList, sizeof(int));
10137    }
10138    if (!BeenSelected || !Patch) {
10139       fprintf (SUMA_STDERR,
10140                "Error %s: Could not allocate for BeenSelected or patch.\n",
10141                FuncName);
10142       SUMA_RETURN(NULL);
10143    }
10144 
10145    /* SUMA_LH("BeenSelected"); */
10146    /* find out the total number of facesets these nodes are members of */
10147    Patch->N_FaceSet = 0; /* total number of facesets containing these nodes */
10148    for (i=0; i < N_Nodes; ++i) {
10149       node = NodesSelected[i];
10150       if (node > MaxNodeIndex) {
10151          SUMA_S_Errv("Have a node (%d) in the patch that does "
10152                     "not belong on this surface of %d nodes.\n"
10153                     "Make sure ROI is being loaded onto correct surface.\n",
10154                     node, MaxNodeIndex+1);
10155          if (BeenSelected) SUMA_free(BeenSelected); BeenSelected = NULL;
10156          if (MakeGolden) SUMA_free(MakeGolden); MakeGolden = NULL;
10157          if (Patch) SUMA_freePatch(Patch); Patch=NULL;
10158          SUMA_RETURN(NULL);
10159       } else {
10160          for (j=0; j < Memb->N_Memb[node]; ++j) {
10161             if (Memb->NodeMemberOfFaceSet[node][j] < N_Full_FaceSetList) {
10162                if (!BeenSelected[Memb->NodeMemberOfFaceSet[node][j]]) {
10163                   /* this faceset has not been selected, select it */
10164                   ++ Patch->N_FaceSet;
10165                }
10166                ++ BeenSelected[Memb->NodeMemberOfFaceSet[node][j]];
10167             } else {
10168                SUMA_S_Warnv("Node %d >= %d\n",
10169                            Memb->NodeMemberOfFaceSet[node][j],
10170                             N_Full_FaceSetList);
10171             }
10172          }
10173       }
10174    }
10175 
10176 
10177    /* SUMA_LH("Loading"); */
10178    /* now load these facesets into a new matrix */
10179    Patch->FaceSetList = (int *) SUMA_calloc (Patch->N_FaceSet * 3, sizeof(int));
10180    Patch->FaceSetIndex = (int *) SUMA_calloc (Patch->N_FaceSet, sizeof(int));
10181    Patch->nHits = (int *) SUMA_calloc (Patch->N_FaceSet, sizeof(int));
10182 
10183    if (!Patch->FaceSetList || !Patch->FaceSetIndex || !Patch->nHits) {
10184       fprintf (SUMA_STDERR,
10185                "Error %s: Could not allocate for Patch->FaceSetList ||"
10186                " Patch_FaceSetIndex.\n", FuncName);
10187       SUMA_RETURN(NULL);
10188    }
10189 
10190    do {
10191       igolden = 0;
10192       j=0;
10193       for (i=0; i < N_Full_FaceSetList; ++i) {
10194          if (BeenSelected[i] >= MinHits) {
10195             /* SUMA_LHv("Triangle %d has been selected (%d)\n",
10196                         i, BeenSelected[i]);    */
10197             Patch->nHits[j] = BeenSelected[i];
10198             Patch->FaceSetIndex[j] = i;
10199             ip = NP * j;
10200             ip2 = NP * i;
10201             Patch->FaceSetList[ip] = Full_FaceSetList[ip2];
10202             Patch->FaceSetList[ip+1] = Full_FaceSetList[ip2+1];
10203             Patch->FaceSetList[ip+2] = Full_FaceSetList[ip2+2];
10204             ++j;
10205          } else {
10206             if (BeenSelected[i] > 0)
10207                BeenSelected[i]= -BeenSelected[i]; /* mark as rejected,
10208                      zero means never selected */
10209          }
10210       }
10211 
10212       /* reset the numer of facesets because it might have changed given the
10213       MinHits condition, It won't change if MinHits = 1.
10214       It's OK not to change the allocated space as long as you are
10215       using 1D arrays*/
10216       Patch->N_FaceSet = j;
10217       /* SUMA_LHv("The patch has %d triangles\n", Patch->N_FaceSet); */
10218       if (FixBowTie) { /* You should not need to check, if MinHits == 1
10219                   BowTies should not happen in that case...
10220                   Perhaps add && MinHits > 1 at some point */
10221          SUMA_SurfaceObject *SOp=NULL;
10222          int n2[2], in2=0, iii=0, t=0, tp=0,
10223                Incident[200], N_Incident=0, N_ExposedEdges=0;
10224          SOp = SUMA_Alloc_SurfObject_Struct(1);
10225          SOp->N_Node = N_Nodes;
10226          SOp->NodeList = NULL;
10227          SOp->NodeDim = 3;
10228          SOp->N_FaceSet = Patch->N_FaceSet;
10229          SOp->FaceSetList = Patch->FaceSetList;
10230          SOp->FaceSetDim = 3;
10231          if (!SUMA_SurfaceMetrics_eng(SOp, "EdgeList",
10232                                  NULL, 0, SUMAg_CF->DsetList)) {
10233             SUMA_S_Err("Failed to create EdgeList");
10234             SUMA_RETURN(NULL);
10235          }
10236          BadBones = 0;
10237          for (i=0; i<N_Nodes; ++i) {
10238             node = NodesSelected[i];
10239             N_ExposedEdges = 0;
10240             /* SUMA_LHv("Node %d, member of %d triangles\n",
10241                      node, Memb->N_Memb[node]); */
10242             for (j=0; j<Memb->N_Memb[node]; ++j) {
10243                /* is triangle selected? */
10244                t= Memb->NodeMemberOfFaceSet[node][j];
10245                /* SUMA_LHv("Triangle %d [%d %d %d]...\n",
10246                         t, Full_FaceSetList[3*t],
10247                         Full_FaceSetList[3*t+1],
10248                         Full_FaceSetList[3*t+2]);*/
10249                if (BeenSelected[t] > 0) { /* triangle is part of patch */
10250                   tp = SUMA_whichTri(SOp->EL,
10251                                      Full_FaceSetList[3*t],
10252                                      Full_FaceSetList[3*t+1],
10253                                      Full_FaceSetList[3*t+2],1, !verb);
10254                   /* SUMA_LHv("Triangle %d selected (%d)\n"
10255                            "Patch equivalent %d\n",
10256                            t, BeenSelected[t], tp); */
10257                   /* count exposed edges that contain node */
10258                   in2 = 0;
10259                   for (iii=0; iii<3;++iii) {
10260                      if (SOp->FaceSetList[3*tp+iii] != node) {
10261                         n2[in2] = SOp->FaceSetList[3*tp+iii]; ++in2;
10262                      }
10263                   }
10264                   for (in2=0; in2<2; ++in2) {
10265                      if (!SUMA_Get_Incident(node, n2[in2], SOp->EL,
10266                                            Incident, &N_Incident, 1, 0)) {
10267                         SUMA_S_Err("Failed in Get_Incident");
10268                         SUMA_RETURN(NULL);
10269                      }
10270                      /* SUMA_LHv("Node %d, Tri %d (%d in patch), "
10271                                  "N_Incident = %d\n",
10272                                  node, t, tp, N_Incident);  */
10273                      if (N_Incident == 1) { ++N_ExposedEdges; }
10274                   }
10275                } else {
10276                   /* SUMA_LHv("Triangle %d NOT selected (%d)\n",
10277                            t, BeenSelected[t]); */
10278                }
10279             }
10280             /* SUMA_LHv("Node %d, N_ExposedEdges = %d\n",
10281                         node, N_ExposedEdges); */
10282             if (N_ExposedEdges > 2) {
10283                if (verb) {
10284                   SUMA_S_Notev(
10285                      "Bowtie at node %d in stiched surface. %d exposed edges.\n"
10286                      "Exposed edges > 2 are trouble for volume computations.\n"
10287                      "%s\n",
10288                      node, N_ExposedEdges,
10289                      FixBowTie > 1 ?
10290                         "Patch will be modified" :
10291                         "Patch will not be modified");
10292                }
10293                if (MinHits == 1) {
10294                   SUMA_S_Warn("Bowtie artifact was not expected\n"
10295                                "with MinHits == 1. No repair can be made.\n"
10296                                "If initial surface is a 2-manifold, \n"
10297                                "send us the data and command you used.\n" );
10298 
10299                } else if (FixBowTie > 1) {
10300                   /* SUMA_LH("Triggering repair job"); */
10301                   /* trigger repair job */
10302                   BadBones = 1;
10303                   /* increment BeenSelected at those triangles containing node
10304                      but not enough MinHits */
10305                   for (j=0; j<Memb->N_Memb[node]; ++j) {
10306                      /* is triangle selected? */
10307                      t= Memb->NodeMemberOfFaceSet[node][j];
10308                      if (BeenSelected[t] < 0) { /* it was selected but did not
10309                                                    meet min_hits, criterion */
10310                         /* plan to make it golden, once
10311                                                 first pass is over */
10312                         MakeGolden[igolden] = t;
10313                         ++igolden;
10314                         /* SUMA_LHv("Made triangle %d golden for node %d\n",
10315                                  t, node); */
10316                      }
10317                   }
10318                }
10319             }
10320          }
10321          /* cleanup */
10322          SOp->FaceSetList = NULL; /* pointer copy from Patch->FaceSetList */
10323          SUMA_Free_Surface_Object(SOp); SOp=NULL;
10324       }
10325       /* Now goldenate */
10326       if (igolden) {
10327          for (i=0; i<igolden; ++i) BeenSelected[MakeGolden[i]] = 3;
10328       }
10329       igolden = 0;
10330    } while (FixBowTie > 1 && BadBones);
10331 
10332    if (BeenSelected) SUMA_free(BeenSelected); BeenSelected = NULL;
10333    if (MakeGolden) SUMA_free(MakeGolden); MakeGolden = NULL;
10334    SUMA_RETURN(Patch);
10335 }
10336 
10337 /*!
10338    ans = SUMA_freePatch (SUMA_PATCH *Patch) ;
10339    frees Patch pointer
10340    \param Patch (SUMA_PATCH *) Surface patch pointer
10341    \ret ans (SUMA_Boolean)
10342    \sa SUMA_getPatch
10343 */
10344 
SUMA_freePatch(SUMA_PATCH * Patch)10345 SUMA_Boolean SUMA_freePatch (SUMA_PATCH *Patch)
10346 {
10347    static char FuncName[]={"SUMA_freePatch"};
10348 
10349    SUMA_ENTRY;
10350 
10351 
10352    if (Patch->FaceSetIndex) SUMA_free(Patch->FaceSetIndex);
10353    if (Patch->FaceSetList) SUMA_free(Patch->FaceSetList);
10354    if (Patch->nHits) SUMA_free(Patch->nHits);
10355    if (Patch) SUMA_free(Patch);
10356    SUMA_RETURN(YUP);
10357 
10358 }
10359 
SUMA_ShowPatch(SUMA_PATCH * Patch,FILE * Out)10360 SUMA_Boolean SUMA_ShowPatch (SUMA_PATCH *Patch, FILE *Out)
10361 {
10362    static char FuncName[]={"SUMA_ShowPatch"};
10363    int ip, i;
10364 
10365    SUMA_ENTRY;
10366 
10367    if (!Out) Out = stderr;
10368 
10369    fprintf (Out, "Patch Contains %d triangles:\n", Patch->N_FaceSet);
10370    fprintf (Out, "FaceIndex (nHits): FaceSetList[0..2]\n");
10371    for (i=0; i < Patch->N_FaceSet; ++i) {
10372       ip = 3 * i;
10373       fprintf (Out, "%d(%d):   %d %d %d\n",
10374             Patch->FaceSetIndex[i], Patch->nHits[i], Patch->FaceSetList[ip],
10375             Patch->FaceSetList[ip+1], Patch->FaceSetList[ip+2]);
10376    }
10377 
10378    SUMA_RETURN(YUP);
10379 }
10380 
10381 /*!
10382    \brief Returns the contour of a patch
10383 
10384    if you have the patch already created, then pass it in the last argument
10385    mode (int) 0: nice contour, not necessarily outermost boundary
10386               1: outermost edge, might look a tad jagged
10387    \param isNodeInNodes (byte **) NULL: Do not use this
10388                                  if *isNodeInNodes = NULL then on output,
10389                                  the function assigns its internally
10390                                  created version
10391                                  if *isNodeInNodes != NULL then the
10392                                  function uses *isNodeInNodes as
10393                                  the PROPERLY INITIALIZED VERSION of
10394                                  Nodes. In other terms, *isNodeInNodes[k] = 1
10395                                  ONLY if node k is one of the nodes in
10396                                  Nodes vector.
10397 */
SUMA_GetContour(SUMA_SurfaceObject * SO,int * Nodes,int N_Node,int * N_ContEdges,int ContourMode,SUMA_PATCH * UseThisPatch,byte ** isNodeInNodes,int verb)10398 SUMA_CONTOUR_EDGES * SUMA_GetContour (
10399          SUMA_SurfaceObject *SO, int *Nodes, int N_Node,
10400          int *N_ContEdges, int ContourMode,
10401          SUMA_PATCH *UseThisPatch, byte **isNodeInNodes, int verb)
10402 {
10403    static char FuncName[]={"SUMA_GetContour"};
10404    SUMA_EDGE_LIST * SEL=NULL;
10405    SUMA_PATCH *Patch = NULL;
10406    int i, Tri, Tri1, Tri2, sHits;
10407    SUMA_CONTOUR_EDGES *CE = NULL;
10408    byte *isNode=NULL;
10409    SUMA_Boolean LocalHead = NOPE;
10410 
10411    SUMA_ENTRY;
10412 
10413    if (verb > 1) LocalHead = YUP;
10414 
10415    *N_ContEdges = -1;
10416 
10417    /* get the Node member structure if needed*/
10418    if (!SO->MF) {
10419       SUMA_SLP_Err("Member FaceSet not created.\n");
10420       SUMA_RETURN(CE);
10421    }
10422 
10423    /* create a flag vector of which node are in Nodes */
10424    if (!isNodeInNodes || !*isNodeInNodes) {
10425       isNode = (byte *) SUMA_calloc(SO->N_Node, sizeof(byte));
10426       if (!isNode) {
10427          SUMA_SLP_Crit("Failed to allocate for isNode");
10428          SUMA_RETURN(CE);
10429       }
10430 
10431       if (LocalHead) {  /* only check in debugging mode. for efficiency */
10432          for (i=0; i < N_Node; ++i)
10433             if (Nodes[i] >= SO->N_Node) {
10434                SUMA_S_Errv("Nodes[%d]=%d >= SO->N_Node %d\n",
10435                            i, Nodes[i], SO->N_Node);
10436                SUMA_RETURN(CE);
10437             }
10438       }
10439       for (i=0; i < N_Node; ++i) isNode[Nodes[i]] = YUP;
10440    } else {
10441       isNode = *isNodeInNodes;
10442    }
10443 
10444    if (UseThisPatch) {
10445       SUMA_LH("Using passed patch");
10446       Patch = UseThisPatch;
10447    } else {
10448       SUMA_LH("Creating patch");
10449       switch (ContourMode) {
10450          case 0:
10451             Patch = SUMA_getPatch (Nodes, N_Node, SO->N_Node, SO->FaceSetList,
10452                                     SO->N_FaceSet, SO->MF, 2, 0, verb);
10453             break;
10454          case 1:
10455             Patch = SUMA_getPatch (Nodes, N_Node, SO->N_Node,
10456                                     SO->FaceSetList, SO->N_FaceSet,
10457                                     SO->MF, 1, 0, verb);
10458             break;
10459          default:
10460             SUMA_SL_Err("Bad contour mode"); SUMA_RETURN(NULL);
10461             break;
10462       }
10463       if (!Patch) {
10464          SUMA_S_Err("Failed to form patch");
10465          SUMA_RETURN(NULL);
10466       }
10467   }
10468   if (LocalHead) SUMA_ShowPatch (Patch,NULL);
10469 
10470    if (Patch->N_FaceSet) {
10471       SEL = SUMA_Make_Edge_List_eng (  Patch->FaceSetList,
10472                                        Patch->N_FaceSet, SO->N_Node,
10473                                        SO->NodeList, 0, NULL);
10474 
10475       if (0 && LocalHead) SUMA_Show_Edge_List (SEL, NULL);
10476       /* allocate for maximum */
10477       CE = (SUMA_CONTOUR_EDGES *)
10478             SUMA_calloc(SEL->N_EL, sizeof(SUMA_CONTOUR_EDGES));
10479       if (!CE) {
10480          SUMA_SLP_Crit("Failed to allocate for CE");
10481          SUMA_RETURN(CE);
10482       }
10483 
10484       switch (ContourMode) {
10485          case 0: /* a pretty contour, edges used here may
10486                      not be the outermost of the patch */
10487             /* edges that are part of unfilled triangles are good */
10488             i = 0;
10489             *N_ContEdges = 0;
10490             while (i < SEL->N_EL) {
10491                if (SEL->ELps[i][2] == 2) {
10492                   Tri1 = SEL->ELps[i][1];
10493                   Tri2 = SEL->ELps[i+1][1];
10494                   sHits = Patch->nHits[Tri1] + Patch->nHits[Tri2];
10495                   if (sHits == 5 || sHits == 4) {
10496                      /* one tri with 3 hits and one with 2 hits
10497                         or 2 Tris with 2 hits each */
10498                      /* Pick edges that are part of only one
10499                         triangle with three hits */
10500                      /* or two triangles with two hits */
10501                      /* There's one more condition, both nodes
10502                         have to be a part of the original list */
10503                      if (isNode[SEL->EL[i][0]] && isNode[SEL->EL[i][1]]) {
10504                         CE[*N_ContEdges].n1 = SEL->EL[i][0];
10505                         CE[*N_ContEdges].n2 = SEL->EL[i][1];
10506                         ++ *N_ContEdges;
10507 
10508                         if (LocalHead) {
10509                            fprintf (SUMA_STDERR,
10510                                     "%s: Found edge made up of nodes [%d %d]\n",
10511                               FuncName, SEL->EL[i][0], SEL->EL[i][1]);
10512                         }
10513                      }
10514                   }
10515                }
10516 
10517                if (SEL->ELps[i][2] > 0) {
10518                   i += SEL->ELps[i][2];
10519                } else {
10520                   i ++;
10521                }
10522             }
10523             break;
10524          case 1: /* outermost contour, not pretty,
10525                    but good for getting the outermost edge */
10526             i = 0;
10527             *N_ContEdges = 0;
10528             while (i < SEL->N_EL) {
10529                if (SEL->ELps[i][2] == 1) {
10530                   CE[*N_ContEdges].n1 = SEL->EL[i][0];
10531                   CE[*N_ContEdges].n2 = SEL->EL[i][1];
10532                   ++ *N_ContEdges;
10533                   if (LocalHead) {
10534                            fprintf (SUMA_STDERR,
10535                                     "%s: Found edge made up of nodes [%d %d]\n",
10536                                     FuncName, SEL->EL[i][0], SEL->EL[i][1]);
10537                   }
10538                }
10539                if (SEL->ELps[i][2] > 0) {
10540                   i += SEL->ELps[i][2];
10541                } else {
10542                   i ++;
10543                }
10544             }
10545             break;
10546          default:
10547             SUMA_SL_Err("Bad ContourMode");
10548             SUMA_RETURN(NULL);
10549             break;
10550       }
10551 
10552       /* Now reallocate */
10553       if (! *N_ContEdges) {
10554          SUMA_free(CE); CE = NULL;
10555          SUMA_RETURN(CE);
10556       }else {
10557          CE = (SUMA_CONTOUR_EDGES *)
10558                   SUMA_realloc (CE, *N_ContEdges * sizeof(SUMA_CONTOUR_EDGES));
10559          if (!CE) {
10560             SUMA_SLP_Crit("Failed to reallocate for CE");
10561             SUMA_RETURN(CE);
10562          }
10563       }
10564 
10565       SUMA_free_Edge_List (SEL); SEL = NULL;
10566    }
10567 
10568    if (!UseThisPatch) {
10569       SUMA_freePatch (Patch);
10570    }
10571    Patch = NULL;
10572 
10573    if (!isNodeInNodes) {
10574       SUMA_free(isNode); isNode = NULL;
10575    } else {
10576       if (!*isNodeInNodes) {
10577          *isNodeInNodes = isNode; isNode = NULL;
10578       }
10579    }
10580 
10581    SUMA_RETURN(CE);
10582 }
10583 
10584 /*!
10585    \brief Stitch together two isotopic patches to calculate the
10586             volume between them
10587 
10588    \param SO1 : The first surface of the pattie
10589    \param SO2 : The second surface of the pattie
10590    \param Nodes (int *): N_Node x 1 vector of indices containing nodes
10591                         that form the patch
10592    \param N_Node (int): Number of nodes in SO.
10593    \param UseThisSo :   If you send a pointer to an empty (but allocated)
10594                         surface structure,
10595                         The pattie's surface is returned in UseThisSo. Otherwise,                         the temporary surface is tossed in the trash can.
10596    \pram minPatchHits (int): Since you're forming a patch from nodes, you'll need                              to select the minimum number of nodes to be in a
10597                              patch (triangle) before the patch
10598                              is selected. Minimum is 1, Maximum logical is 3.
10599                              If you choose 1, you will have nodes in the patch
10600                              that are not included in Nodes vector. But that is
10601                              the only way to get something back for just
10602                              one node.
10603                              For best volume estimates, set minPatchHits = 1,
10604                              and adjust_contour to 1 (see below).
10605                              If you choose 3, you will have the same number of
10606                              nodes in the patch as you do in the vector Nodes.
10607                              However, you'll get no patches formed if your Nodes
10608                              vector contains, one or two nodes for example ...
10609    \param FixBowTie (int)  0: No fix, No check
10610                            1: Check for patches that have a bow tie node. This
10611                               means two chunks of the patch connect at one node
10612                               only. This make the stiching result in a
10613                               non-2 manifold surface.
10614                            2: Check for and modify patch at bow tie nodes. This
10615                               is done by locally relaxing the minPatchHits
10616                               criterion
10617    \param adjust_countour (int)  0: Do nothing
10618                                  1: For nodes in the patch, that are not in the
10619                                     original Nodes vector. Move their coordinates
10620                                     midway along the segment they form with a
10621                                     node that is in Nodes vector. Essentially,
10622                                     this shrink the contour at nodes in the patch
10623                                     but not in the original selection, before the
10624                                     volume is computed. This results in a better
10625                                     and consistent estimate of volume.
10626                                     This option should be used with
10627                                        minPatchHits=1
10628    \param adjustment_neighbors (byte *) : SO1->N_Node x 1 vector
10629                                  If not NULL, adjustment_neighbors[n] will
10630                                  contain the number of neighbors of n that
10631                                  were used to adjust its contour location.
10632                                  Most of this vector will be 0s. Non-zero
10633                                  values would only occur when adjust_contour is 1
10634    \param verb (int)
10635    Testing so far in /home/ziad/SUMA_test/afni:
10636    for a bunch of nodes:
10637       SurfMeasures   -func node_vol \
10638                      -spec ../SurfData/SUMA/DemoSubj_lh.spec \
10639                      -surf_A lh.smoothwm.asc \
10640                      -surf_B lh.pial.asc \
10641                      -nodes_1D lhpatch.1D.roi'[0]' -out_1D SM_out.1D
10642       SurfPatch      -spec ../SurfData/SUMA/DemoSubj_lh.spec   \
10643                      -surf_A lh.smoothwm -surf_B lh.pial.asc -hits 3 \
10644                      -input lhpatch.1D.roi 0 1
10645       answer is 326, 13% different from sum in SM_out.1D's second column (373)...
10646 
10647    for a single node (remember change -hits option to 1):
10648       SurfMeasures   -func node_vol \
10649                      -spec ../SurfData/SUMA/DemoSubj_lh.spec \
10650                      -surf_A lh.smoothwm.asc -surf_B lh.pial.asc \
10651                      -nodes_1D lhpatch_1node.1D'[0]' -out_1D SM_out_1node.1D
10652       SurfPatch      -spec ../SurfData/SUMA/DemoSubj_lh.spec \
10653                      -surf_A lh.smoothwm -surf_B lh.pial.asc \
10654                      -hits 1 \
10655                      -input lhpatch_1node.1D 0 1
10656       (  divide answer of 2.219910 by 3 (0.73997),
10657          difference at 3rd signifcant digit from SM_out_1node.1D of 0.731866)
10658 
10659    for a box:
10660       SurfMeasures   -func node_vol -spec RectPly.spec \
10661                      -surf_A RectSurf.ply -surf_B RectSurf2.ply \
10662                      -nodes_1D RectAllPatch.1D'[1]' -out_1D SM_out_Rect.1D
10663       SurfPatch      -spec RectPly.spec \
10664                      -surf_A RectSurf.ply -surf_B RectSurf2.ply \
10665                      -hits 3 -input RectAllPatch.1D 0 1 -vol_only
10666 
10667 */
SUMA_Pattie_Volume(SUMA_SurfaceObject * SO1,SUMA_SurfaceObject * SO2,int * Nodes,int N_Node,SUMA_SurfaceObject * UseThisSO,int minPatchHits,int FixBowTie,int adjust_contour,byte * adjustment_neighbors,int verb)10668 double SUMA_Pattie_Volume (SUMA_SurfaceObject *SO1, SUMA_SurfaceObject *SO2,
10669                      int *Nodes, int N_Node, SUMA_SurfaceObject *UseThisSO,
10670                      int minPatchHits, int FixBowTie, int adjust_contour,
10671                      byte *adjustment_neighbors, int verb)
10672 {
10673    static char FuncName[]={"SUMA_Pattie_Volume"};
10674    double Vol = 0.0;
10675    int N_ContEdges=0, i, j, ij, ij3, i3, n, NodesPerPatch,
10676        *NewIndex = NULL, inew3, cnt, n1, n2, trouble, N_nc, prob;
10677    SUMA_PATCH *P1 = NULL;
10678    FILE *fid=NULL;
10679    SUMA_CONTOUR_EDGES *CE = NULL;
10680    SUMA_SurfaceObject *SOc = NULL;
10681    SUMA_SURF_NORM SN;
10682    static byte *isNodeInNodes=NULL;
10683    static int N_isNodeInNodes=0, inotice=0;
10684    double nc1[3], nc2[3];
10685    SUMA_Boolean LocalHead = NOPE;
10686 
10687    SUMA_ENTRY;
10688 
10689    if (verb > 2) LocalHead = YUP;
10690 
10691    if (!SO1 || !SO2 || !Nodes || !N_Node) {
10692       SUMA_SL_Err("Bad input.");
10693       SUMA_RETURN(Vol);
10694    }
10695    if (SO1->N_Node != SO2->N_Node || SO1->N_FaceSet != SO2->N_FaceSet) {
10696       SUMA_SL_Err("Surfaces Not Isotopic");
10697       SUMA_RETURN(Vol);
10698    }
10699 
10700    /* form the patch */
10701    SUMA_LH("Forming patch...");
10702    P1 = SUMA_getPatch (Nodes, N_Node, SO1->N_Node,
10703                        SO1->FaceSetList, SO1->N_FaceSet,
10704                        SO1->MF, minPatchHits, FixBowTie, verb);
10705    if (!P1) {
10706       SUMA_SL_Err("Failed to create patches.\n");
10707       SUMA_RETURN(Vol);
10708    }
10709    if (!P1->N_FaceSet) {
10710       SUMA_SL_Err("No patch could be formed");
10711       SUMA_RETURN(Vol);
10712    }
10713    /* prepare flag vector */
10714    if (isNodeInNodes && N_isNodeInNodes < SO1->N_Node) {/* need a new array */
10715       SUMA_free(isNodeInNodes); isNodeInNodes = NULL;
10716    }
10717    if (!isNodeInNodes) {
10718       isNodeInNodes = (byte *)SUMA_calloc(SO1->N_Node, sizeof(byte));
10719       N_isNodeInNodes = SO1->N_Node;
10720    }
10721 
10722    for (i=0; i<N_Node; ++i) isNodeInNodes[Nodes[i]]=1;/* init flag vector */
10723 
10724 
10725    /* form the contour */
10726    SUMA_LH("Forming contour...");
10727    CE = SUMA_GetContour (SO1, Nodes, N_Node, &N_ContEdges, 1, P1,
10728                          &isNodeInNodes, verb);
10729    if (!N_ContEdges) {
10730       SUMA_SL_Err("No contour edges found.\n"
10731                   "It looks like patches form\n"
10732                   "closed surfaces.\n");
10733       SUMA_RETURN(Vol);
10734    }
10735    if (LocalHead) {
10736       FILE *contout=NULL;
10737       fprintf( SUMA_STDERR,
10738                "%s:\n Found %d contour segments.\n"
10739                "Edges written to ./contout.1D.DO\n",
10740                FuncName, N_ContEdges);
10741 
10742       contout = fopen("contout.1D.DO", "w");
10743       if (contout) {
10744          fprintf(contout, "#node-based_segments\n");
10745          for (i=0; i<N_ContEdges; ++i) {
10746             fprintf(contout, "%d %d\n", CE[i].n1, CE[i].n2);
10747          }
10748          fclose(contout); contout=NULL;
10749       }
10750    }
10751 
10752    /* create a mapping from old numbering scheme to new one */
10753    SUMA_LH("Creating Mapping Index...");
10754    NewIndex = (int *)SUMA_malloc(SO1->N_Node * sizeof(int));
10755    if (!NewIndex) {
10756       SUMA_SL_Crit("Failed to allocate for NewIndex");
10757       SUMA_RETURN(Vol);
10758    }
10759    SUMA_INIT_VEC(NewIndex, SO1->N_Node, -1, int);
10760    NodesPerPatch = 0;
10761    for (i=0; i < P1->N_FaceSet; ++i) {
10762       i3 = 3*i;
10763       n = P1->FaceSetList[i3];
10764          if (NewIndex[n] < 0) { NewIndex[n] = NodesPerPatch; ++NodesPerPatch; }
10765       n = P1->FaceSetList[i3+1];
10766          if (NewIndex[n] < 0) { NewIndex[n] = NodesPerPatch; ++NodesPerPatch; }
10767       n = P1->FaceSetList[i3+2];
10768          if (NewIndex[n] < 0) { NewIndex[n] = NodesPerPatch; ++NodesPerPatch; }
10769    }
10770    if (LocalHead) {
10771       fprintf(SUMA_STDERR,"%s:\n"
10772                   "Number of nodes in patch (%d), in N_Node (%d)\n"
10773                   , FuncName, NodesPerPatch, N_Node);
10774    }
10775 
10776    /* Building composite surface */
10777    SUMA_LH("Building composite surface...");
10778    if (UseThisSO) {
10779       SOc = UseThisSO;
10780       if (SOc->NodeList || SOc->FaceSetList) {
10781          SUMA_SL_Err("You want me to use a filled SurfaceObject structure!\n"
10782                      "How rude!");
10783          SUMA_RETURN(Vol);
10784       }
10785    } else {
10786       SOc = SUMA_Alloc_SurfObject_Struct(1);
10787    }
10788    SOc->N_Node = NodesPerPatch*2;
10789    SOc->N_FaceSet = P1->N_FaceSet*2+2*N_ContEdges;
10790    SOc->NodeDim = 3;
10791    SOc->FaceSetDim = 3;
10792    SOc->NodeList = (float *)SUMA_calloc(SOc->NodeDim*SOc->N_Node, sizeof(float));
10793    SOc->FaceSetList = (int *)
10794                         SUMA_calloc(SOc->FaceSetDim*SOc->N_FaceSet, sizeof(int));
10795    SUMA_LHv("Stitched surface is set to have %d nodes, %d facets\n",
10796             SOc->N_Node, SOc->N_FaceSet);
10797    /* first create the NodeList from S01 && SO2*/
10798    if (!adjust_contour) {
10799       for (i=0; i<SO1->N_Node; ++i) {
10800          if (adjustment_neighbors) adjustment_neighbors[i] = 0; /* initialize */
10801          if (NewIndex[i] >=0) { /* this node is used */
10802             i3 = 3*i;
10803             inew3 = 3 * NewIndex[i];
10804             SOc->NodeList[inew3  ] = SO1->NodeList[i3  ];
10805             SOc->NodeList[inew3+1] = SO1->NodeList[i3+1];
10806             SOc->NodeList[inew3+2] = SO1->NodeList[i3+2];
10807             inew3 = 3 * (NewIndex[i]+NodesPerPatch);
10808             SOc->NodeList[inew3  ] = SO2->NodeList[i3  ];
10809             SOc->NodeList[inew3+1] = SO2->NodeList[i3+1];
10810             SOc->NodeList[inew3+2] = SO2->NodeList[i3+2];
10811          }
10812       }
10813    } else {
10814       for (i=0; i<SO1->N_Node; ++i) {
10815          if (adjustment_neighbors) adjustment_neighbors[i] = 0; /* initialize */
10816          if (NewIndex[i] >=0) { /* this node is used in the patch*/
10817             if (isNodeInNodes[i]) { /* this node is a part of the original set */
10818                i3 = 3*i;
10819                inew3 = 3 * NewIndex[i];
10820                SOc->NodeList[inew3  ] = SO1->NodeList[i3  ];
10821                SOc->NodeList[inew3+1] = SO1->NodeList[i3+1];
10822                SOc->NodeList[inew3+2] = SO1->NodeList[i3+2];
10823                inew3 = 3 * (NewIndex[i]+NodesPerPatch);
10824                SOc->NodeList[inew3  ] = SO2->NodeList[i3  ];
10825                SOc->NodeList[inew3+1] = SO2->NodeList[i3+1];
10826                SOc->NodeList[inew3+2] = SO2->NodeList[i3+2];
10827             } else { /* this a contour node that needs shrinking
10828                      to the center of mass of the contour node
10829                      and its selected neighbors*/
10830                i3 = 3*i;
10831                nc1[0] = SO1->NodeList[i3  ];
10832                nc1[1] = SO1->NodeList[i3+1];
10833                nc1[2] = SO1->NodeList[i3+2];
10834                nc2[0] = SO2->NodeList[i3  ];
10835                nc2[1] = SO2->NodeList[i3+1];
10836                nc2[2] = SO2->NodeList[i3+2];
10837                N_nc = 1;
10838                /* add coords of nodes neighboring i, that are in
10839                Nodes vector */
10840                for (j=0; j<SO1->FN->N_Neighb[i]; ++j) {
10841                   ij = SO1->FN->FirstNeighb[i][j]; ij3 = 3*ij;
10842                   if (isNodeInNodes[ij]) {
10843                      nc1[0] += SO1->NodeList[ij3  ];
10844                      nc1[1] += SO1->NodeList[ij3+1];
10845                      nc1[2] += SO1->NodeList[ij3+2];
10846                      nc2[0] += SO2->NodeList[ij3  ];
10847                      nc2[1] += SO2->NodeList[ij3+1];
10848                      nc2[2] += SO2->NodeList[ij3+2];
10849                      ++N_nc;
10850                   }
10851                }
10852                if (N_nc > 3) {
10853                   /* at N_nc = 2, non-selected node moves along edge
10854                         N_nc = 3, non-selected node moves along triangle
10855                         N_nc >3 , non-selected node will end up off of
10856                                   orginal surface, shrinkage */
10857                   if (verb > 1 || (verb && !inotice)) {
10858                      SUMA_S_Notev("Contour correction will cause some"
10859                                   "shrinkage at node %d (becomes %d, and %d) \n"
10860                                   "with %d selected neighbors\n"
10861                                   "%s",
10862                                   i, NewIndex[i], (NewIndex[i]+NodesPerPatch),
10863                                   N_nc-1,
10864                         (verb < 2) ?
10865                            "Use -verb 2 to see all similar notices.\n":"");
10866                      ++inotice;
10867                   }
10868                }
10869                if (adjustment_neighbors)
10870                   adjustment_neighbors[i] = (byte)(N_nc-1);
10871                nc1[0]/=(double)N_nc; nc1[1]/=(double)N_nc; nc1[2]/=(double)N_nc;
10872                nc2[0]/=(double)N_nc; nc2[1]/=(double)N_nc; nc2[2]/=(double)N_nc;
10873                SUMA_LHv("node %d moved towards %d neighbors from \n"
10874                             "%f %f %f to %f %f %f, and \n"
10875                             "%f %f %f to %f %f %f\n",
10876                    i, N_nc-1,
10877                    SO1->NodeList[i3  ], SO1->NodeList[i3+1], SO1->NodeList[i3+2],
10878                    nc1[0], nc1[1], nc1[2],
10879                    SO2->NodeList[i3  ], SO2->NodeList[i3+1], SO2->NodeList[i3+2],
10880                    nc2[0], nc2[1], nc2[2]);
10881 
10882                inew3 = 3 * NewIndex[i];
10883                SOc->NodeList[inew3  ] = (float)(nc1[0]);
10884                SOc->NodeList[inew3+1] = (float)(nc1[1]);
10885                SOc->NodeList[inew3+2] = (float)(nc1[2]);
10886                inew3 = 3 * (NewIndex[i]+NodesPerPatch);
10887                SOc->NodeList[inew3  ] = (float)(nc2[0]);
10888                SOc->NodeList[inew3+1] = (float)(nc2[1]);
10889                SOc->NodeList[inew3+2] = (float)(nc2[2]);
10890             }
10891          }
10892       }
10893    }
10894    /* recycle isNodeInNodes for efficiency */
10895    for (i=0; i<N_Node; ++i)
10896       isNodeInNodes[Nodes[i]]=0;
10897 
10898    /* Now add the pre-existing patches */
10899    cnt = 0;
10900    for (i=0; i<P1->N_FaceSet; ++i) {
10901       i3 = 3*i;
10902       n = P1->FaceSetList[i3  ]; SOc->FaceSetList[cnt] = NewIndex[n]; ++cnt;
10903       n = P1->FaceSetList[i3+1]; SOc->FaceSetList[cnt] = NewIndex[n]; ++cnt;
10904       n = P1->FaceSetList[i3+2]; SOc->FaceSetList[cnt] = NewIndex[n]; ++cnt;
10905    }
10906    for (i=0; i<P1->N_FaceSet; ++i) { /* Now for SO2's */
10907       i3 = 3*i;
10908       n = P1->FaceSetList[i3  ];
10909          SOc->FaceSetList[cnt] = NewIndex[n]+NodesPerPatch; ++cnt;
10910       n = P1->FaceSetList[i3+1];
10911          SOc->FaceSetList[cnt] = NewIndex[n]+NodesPerPatch; ++cnt;
10912       n = P1->FaceSetList[i3+2];
10913          SOc->FaceSetList[cnt] = NewIndex[n]+NodesPerPatch; ++cnt;
10914    }
10915 
10916    /* Now you need to add the stitches,
10917       for each segment you'll need 2 triangles*/
10918    for (i=0; i<N_ContEdges; ++i) {
10919       n1 = NewIndex[CE[i].n1]; n2 = NewIndex[CE[i].n2];
10920       SOc->FaceSetList[cnt] = n1; ++cnt;
10921       SOc->FaceSetList[cnt] = n2; ++cnt;
10922       SOc->FaceSetList[cnt] = n2+NodesPerPatch; ++cnt;
10923       SOc->FaceSetList[cnt] = n1; ++cnt;
10924       SOc->FaceSetList[cnt] = n2+NodesPerPatch; ++cnt;
10925       SOc->FaceSetList[cnt] = n1+NodesPerPatch; ++cnt;
10926    }
10927 
10928    /* calculate EdgeList */
10929    if (!SUMA_SurfaceMetrics_eng(SOc, "EdgeList", NULL, 0, SUMAg_CF->DsetList)){
10930       SUMA_SL_Err("Failed to create EdgeList");
10931       SUMA_RETURN(Vol);
10932    }
10933 
10934    /* make sure that's a closed surface */
10935    if (SOc->EL->max_N_Hosts != 2 || SOc->EL->min_N_Hosts != 2) {
10936       SUMA_SL_Err("Created surface is not a closed one.\n"
10937                   "Or patches have tessellation problems.\n"
10938                   "Small holes in the ROI could cause such a problem.");
10939       if (LocalHead) {
10940          int Inci[200], N_Inci=-1;
10941          if (!(SUMA_Save_Surface_Object_Wrap ( "BadPatch.ply", NULL,
10942                                                 SOc, SUMA_PLY, SUMA_ASCII,
10943                                                 NULL))) {
10944             fprintf (SUMA_STDERR,
10945                      "Error %s: Failed to write surface object.\n", FuncName);
10946          }
10947          for (i=0; i<SOc->EL->N_EL; ++i) {
10948             if (SOc->EL->ELps[i][2] > 2) {
10949                fprintf(SUMA_STDERR,"Trouble triangles on edge [%d %d]\n",
10950                                     SOc->EL->EL[i][0], SOc->EL->EL[i][1]);
10951                if (!SUMA_Get_Incident( SOc->EL->EL[i][0],
10952                                        SOc->EL->EL[i][1], SOc->EL,
10953                                        Inci, &N_Inci, 1, 0)) {
10954                   SUMA_S_Err("Failed to get incident triangle!");
10955                   SUMA_RETURN(Vol);
10956                }
10957                for (n=0; n<N_Inci; ++n) {
10958                   fprintf(SUMA_STDERR,"   %d", Inci[n]);
10959                }
10960                fprintf(SUMA_STDERR,"\n");
10961             }
10962          }
10963       }
10964       SUMA_RETURN(Vol);
10965    }
10966 
10967    /* fix the winding */
10968    if (!SUMA_MakeConsistent(SOc->FaceSetList, SOc->N_FaceSet,
10969                             SOc->EL, 0, &trouble)) {
10970       SUMA_SL_Err("Failed to make surface consistent");
10971       SUMA_RETURN(Vol);
10972    }
10973 
10974    /* Now calculate FaceSetNormals and triangle areas*/
10975    SN = SUMA_SurfNorm(SOc->NodeList,  SOc->N_Node,
10976                       SOc->FaceSetList, SOc->N_FaceSet );
10977    SOc->NodeNormList = SN.NodeNormList;
10978    SOc->FaceNormList = SN.FaceNormList;
10979 
10980    if (!SUMA_SurfaceMetrics_eng(SOc, "PolyArea", NULL, 0, SUMAg_CF->DsetList)){
10981       SUMA_SL_Err("Failed to create EdgeList");
10982       SUMA_RETURN(Vol);
10983    }
10984 
10985    /* debug */
10986    if (LocalHead) {
10987       fid = fopen(adjust_contour ?
10988                      "adjusted_NodeList.1D":"not_adjusted_NodeList.1D", "w");
10989       if (fid) {
10990          SUMA_disp_vecmat (SOc->NodeList, SOc->N_Node,
10991                            SOc->NodeDim, 1, SUMA_ROW_MAJOR, fid, NOPE);
10992          fclose(fid);
10993       }
10994 
10995       fid = fopen(adjust_contour ?
10996                      "adjusted_FaceSetList.1D":"not_adjusted_FaceSetList.1D",
10997                      "w");
10998       if (fid) {
10999          SUMA_disp_vecdmat(SOc->FaceSetList, SOc->N_FaceSet,
11000                            SOc->FaceSetDim, 1, SUMA_ROW_MAJOR, fid , NOPE);
11001          fclose(fid);
11002       }
11003    }
11004 
11005    /* calculate the volume */
11006    SUMA_LH("Calculating volume");
11007    Vol = SUMA_Mesh_Volume(SOc, NULL, -1, 1, &prob);
11008    if (prob) {
11009       SUMA_S_Warn("Precision problem in Mesh_Volume.\n"
11010                   "Try recalculating volume with a rotated\n"
11011                   "version of this surface.\n");
11012    }
11013    if (LocalHead) {
11014       fprintf (SUMA_STDERR,"%s:\n"
11015                            "Volume = %f\n", FuncName, Vol);
11016    }
11017 
11018    /* cleanup */
11019    SUMA_LH("Cleanup");
11020    if (P1) SUMA_freePatch(P1); P1 = NULL;
11021    if (NewIndex) SUMA_free(NewIndex); NewIndex = NULL;
11022    if (SOc != UseThisSO) SUMA_Free_Surface_Object(SOc); SOc = NULL;
11023    if (CE) SUMA_free(CE); CE=NULL;
11024 
11025    SUMA_RETURN(Vol);
11026 }
11027 
SUMA_FillScaleXform(double xform[][4],double sc[3])11028 SUMA_Boolean SUMA_FillScaleXform(double xform[][4], double sc[3])
11029 {
11030    static char FuncName[]={"SUMA_FillScaleXform"};
11031    int nrow, ncol, i;
11032    SUMA_Boolean LocalHead = NOPE;
11033 
11034    SUMA_ENTRY;
11035    for(nrow=0;nrow<4;++nrow)
11036       for (ncol=0; ncol<4;++ncol)
11037          xform[nrow][ncol] = 0.0;
11038    xform[0][0] = sc[0];
11039    xform[1][1] = sc[1];
11040    xform[2][2] = sc[2];
11041    xform[3][3] = 1.0;
11042 
11043    SUMA_RETURN(YUP);
11044 }
11045 
SUMA_FillXYnegXform(double xform[][4])11046 SUMA_Boolean SUMA_FillXYnegXform(double xform[][4])
11047 {
11048    static char FuncName[]={"SUMA_FillXYnegXform"};
11049    int nrow, ncol, i;
11050    SUMA_Boolean LocalHead = NOPE;
11051 
11052    SUMA_ENTRY;
11053    for(nrow=0;nrow<4;++nrow)
11054       for (ncol=0; ncol<4;++ncol)
11055          xform[nrow][ncol] = 0.0;
11056    xform[0][0] = -1.0;
11057    xform[1][1] = -1.0;
11058    xform[2][2] =  1.0;
11059    xform[3][3] =  1.0;
11060 
11061    SUMA_RETURN(YUP);
11062 }
11063 
11064 
SUMA_FillRandXform(double xform[][4],int seed,int type)11065 SUMA_Boolean SUMA_FillRandXform(double xform[][4], int seed, int type)
11066 {
11067    static char FuncName[]={"SUMA_FillRandXform"};
11068    float a[3], phi, q[4];
11069    GLfloat m[4][4];
11070    int nrow, ncol, i;
11071    SUMA_Boolean LocalHead = NOPE;
11072 
11073    SUMA_ENTRY;
11074 
11075    srand(seed);
11076    switch(type) {
11077       case 1: /* shift */
11078          for(nrow=0;nrow<4;++nrow)
11079             for (ncol=0; ncol<4;++ncol)
11080                xform[nrow][ncol] = 0.0;
11081 
11082          xform[0][0] = xform[1][1] = xform[2][2] = xform[3][3] = 1.0;
11083 
11084          xform[0][3] = (double)rand()/(double)RAND_MAX;
11085          xform[1][3] = (double)rand()/(double)RAND_MAX;
11086          xform[2][3] = (double)rand()/(double)RAND_MAX;
11087          break;
11088       case 2: /* Rigid */
11089          a[0] = (float)((double)rand()/(double)RAND_MAX);
11090          a[1] = (float)((double)rand()/(double)RAND_MAX);
11091          a[2] = (float)((double)rand()/(double)RAND_MAX);
11092          phi = (float)((double)rand()/(double)RAND_MAX)*3.1415;
11093          axis_to_quat(a,  phi, q);
11094          normalize_quat( q );
11095          SUMA_build_rotmatrix(m, q);
11096          for(nrow=0;nrow<4;++nrow)
11097             for (ncol=0; ncol<4;++ncol)
11098                xform[nrow][ncol] = m[nrow][ncol];
11099          /* put in a shift, tiz zero still */
11100          xform[0][3] = (double)rand()/(double)RAND_MAX;
11101          xform[1][3] = (double)rand()/(double)RAND_MAX;
11102          xform[2][3] = (double)rand()/(double)RAND_MAX;
11103          break;
11104       case 3: /* affine */
11105          i = 0;
11106          while (i < 12) {
11107             xform[i/4][0] = (double)rand()/(double)RAND_MAX; ++i;
11108             xform[i/4][1] = (double)rand()/(double)RAND_MAX; ++i;
11109             xform[i/4][2] = (double)rand()/(double)RAND_MAX; ++i;
11110             xform[i/4][3] = (double)rand()/(double)RAND_MAX; ++i;
11111          }
11112          xform[3][0] = 0.0;
11113          xform[3][1] = 0.0;
11114          xform[3][2] = 0.0;
11115          break;
11116       default:
11117          SUMA_S_Errv("Bad random matrix type %d\n", type);
11118          for(nrow=0;nrow<4;++nrow)
11119             for (ncol=0; ncol<4;++ncol)
11120                xform[nrow][ncol] = 0.0;
11121          SUMA_RETURN(NOPE);
11122          break;
11123 
11124    }
11125 
11126    xform[3][3] = 1.0;
11127 
11128 
11129    SUMA_LHv("Random xform type %d, seed %d:\n"
11130                "%.6f\t%.6f\t%.6f\t%.6f\n"
11131                "%.6f\t%.6f\t%.6f\t%.6f\n"
11132                "%.6f\t%.6f\t%.6f\t%.6f\n"
11133                "%.6f\t%.6f\t%.6f\t%.6f\n",
11134                type, seed,
11135                xform[0][0], xform[0][1], xform[0][2], xform[0][3],
11136                xform[1][0], xform[1][1], xform[1][2], xform[1][3],
11137                xform[2][0], xform[2][1], xform[2][2], xform[2][3],
11138                xform[3][0], xform[3][1], xform[3][2], xform[3][3]);
11139 
11140    SUMA_RETURN(YUP);
11141 }
11142 
11143 /*!
11144    \brief Calculate the volume of a mesh per the
11145    method in Hughes, S.W. et al. Phys. Med. Biol. 1996
11146 
11147    Tested with Icosahedron surface (see direct vol computation in CreateIcosahedron)
11148 
11149    Tested with tetrahedron below, which has a volume of 0.166667
11150    Also, the same volume was obtained with a rotated version of the same tetrahedron
11151 TetraFaceSetList.1D
11152 0 1 2
11153 0 3 1
11154 0 2 3
11155 2 1 3
11156 
11157 TetraNodeList.1D
11158 0 0 0
11159 1 0 0
11160 0 1 0
11161 1 1 1
11162 which should have a volume of (1/3)A*h of 1/3 * 0.5 * 1
11163 
11164    Volume of smoothwm surface did not change if surface was rotated and shifted,
11165    a very good thing.
11166 
11167 If you try to calculate the volume of a boxe's surface with the box's axes
11168 in alignment with the X, Y and Z directions, you will get nan for an answer
11169 because the sum of the weights is ~= 0 . If you rotate the surface, the problem
11170 will be solved.
11171 
11172    - It is extremely important that the surface mesh be consistently defined.
11173    - Detecting consistency must be done ahead of time (need program to do that)
11174    because doing it here will slow the computations a lot and consistency fix
11175    requires recalculating the normals and all that depends on them (quite a bit).
11176 
11177 */
11178 
SUMA_Mesh_Volume(SUMA_SurfaceObject * SO,int * FSI,int N_FaceSet,int verb,int * prec_warn)11179 double SUMA_Mesh_Volume(SUMA_SurfaceObject *SO,
11180                         int *FSI, int N_FaceSet,
11181                         int verb, int *prec_warn)
11182 {
11183    static char FuncName[]={"SUMA_Mesh_Volume"};
11184    double Vol = 0.0, c[3], anx, any, anz, sx, sy, sz, kx, ky, kz, kt;
11185    float *pa = NULL;
11186    int i, fc;
11187    SUMA_Boolean LocalHead = NOPE;
11188 
11189    SUMA_ENTRY;
11190 
11191    if (!SO) { SUMA_SL_Err("NULL SO"); SUMA_RETURN(Vol);  }
11192    if (!SO->FaceNormList) { SUMA_RECOMPUTE_NORMALS(SO); }
11193    if (!SO->PolyArea) {
11194       if (!SUMA_SurfaceMetrics_eng (SO, "PolyArea",
11195                                     NULL, 0, SUMAg_CF->DsetList)) {
11196          SUMA_SL_Err("Failed to compute SO->PolyArea"); SUMA_RETURN(Vol);
11197       }
11198    }
11199    pa = SO->PolyArea;
11200 
11201    if (FSI || N_FaceSet != -1) {
11202       SUMA_SL_Err("FSI and N_FaceSet are two stupid options never to be used.\n"
11203                   "Use NULL and -1, respectively.");
11204       SUMA_RETURN(Vol);
11205    }
11206 
11207    if (!FSI) {
11208       N_FaceSet = SO->N_FaceSet;
11209    }
11210 
11211 
11212    /* calculate vector of areas * normals */
11213    kx = ky = kz = sx = sy = sz = 0.0;
11214    for (i=0; i<N_FaceSet; ++i) {
11215       if (FSI) fc = FSI[i];
11216       else fc = i;
11217       SUMA_FACE_CENTROID(SO, fc, c);
11218       #if 0
11219          if (LocalHead)
11220             fprintf(SUMA_STDERR,"Area: %f , normal (%f, %f, %f)\n",
11221                pa[fc],
11222                SO->FaceNormList[3*fc],
11223                SO->FaceNormList[3*fc+1],
11224                SO->FaceNormList[3*fc+2]);
11225       #endif
11226       anx = pa[fc] * SO->FaceNormList[3*fc];   kx += anx;  sx += c[0] * anx;
11227       any = pa[fc] * SO->FaceNormList[3*fc+1]; ky += any;  sy += c[1] * any;
11228       anz = pa[fc] * SO->FaceNormList[3*fc+2]; kz += anz;  sz += c[2] * anz;
11229    }
11230    kt = (kx+ky+kz); /* need to normalize k so that sum is 1, kx, ky and kz
11231       are supposed to "weight the volume according to its orientation
11232       relative to the axes." For a sphere, you can use 1/3 for kx, ky and kz. */
11233    if (fabs(kt) < 1e-8) {
11234       if (prec_warn) *prec_warn = 1;
11235       if (verb) {
11236          SUMA_SL_Warn("Weight constants sum to ~= 0.\n"
11237                       "Volume measurements may be off.\n"
11238                       "If your surface's axes are along\n"
11239                       "the X, Y and Z directions, as you \n"
11240                       "could have with a box's surface, rotating\n"
11241                       "the surface will solve the problem.");
11242       }
11243       if (LocalHead)
11244          fprintf(SUMA_STDERR, "%s:\n"
11245                            "kx + ky + kz = kt\n"
11246                            "%f + %f + %f = %f\n"
11247                            "sx, sy, sz = %f, %f, %f\n",
11248                            FuncName, kx, ky, kz, kx+ky+kz, sx, sy, sz);
11249    } else {
11250       if (prec_warn) *prec_warn = 0;
11251    }
11252    if (kt == 0.0f) { kx = ky = kz = 0.0; }
11253    else {
11254       kx /= kt;
11255       ky /= kt;
11256       kz /= kt;
11257    }
11258    if (LocalHead) {
11259       fprintf(SUMA_STDERR, "%s:\n"
11260                            "%f + %f + %f = %f\n"
11261                            "sx, sy, sz = %f, %f, %f\n",
11262                            FuncName, kx, ky, kz, kx+ky+kz, sx, sy, sz);
11263    }
11264    Vol = kx * sx + ky *sy + kz * sz;
11265 
11266    SUMA_RETURN(Vol);
11267 }
11268 
11269 /*!
11270    \brief computes the total area of a surface.
11271    NOTE: This function will replace whatever values you have in SO->PolyArea.
11272    If SO->PolyArea is NULL, it will remain that way.
11273 */
SUMA_Mesh_Area(SUMA_SurfaceObject * SO,int * FaceSets,int N_FaceSet)11274 double SUMA_Mesh_Area(SUMA_SurfaceObject *SO, int *FaceSets, int N_FaceSet)
11275 {
11276    static char FuncName[]={"SUMA_Mesh_Area"};
11277    double A = 0.0, a = 0.0;
11278    int i, i3;
11279    float *n0, *n1, *n2;
11280    SUMA_Boolean LocalHead = NOPE;
11281 
11282    SUMA_ENTRY;
11283 
11284    if (!SO) { SUMA_SL_Err("NULL SO"); SUMA_RETURN(A);  }
11285    if (!SO->FaceSetList) { SUMA_SL_Err("NULL SO->FaceSetList"); SUMA_RETURN(A);  }
11286 
11287    if (!FaceSets ) {
11288       if (N_FaceSet != -1) {
11289          SUMA_SL_Err("With NULL FaceSets, use -1 for N_FaceSet");
11290          SUMA_RETURN(A);
11291       }
11292       N_FaceSet = SO->N_FaceSet;
11293       FaceSets = SO->FaceSetList;
11294    }else {
11295       if (N_FaceSet < 0) {
11296          SUMA_SL_Err("N_FaceSet < 0");
11297          SUMA_RETURN(A);
11298       }
11299    }
11300 
11301    A = 0.0;
11302    if (SO->PolyArea) {
11303       for (i=0;  i<N_FaceSet; ++i) {
11304          i3 = 3*i;
11305          n0 = &(SO->NodeList[3*FaceSets[i3]]);
11306          n1 = &(SO->NodeList[3*FaceSets[i3+1]]);
11307          n2 = &(SO->NodeList[3*FaceSets[i3+2]]);
11308          SUMA_TRI_AREA( n0, n1, n2, a);
11309          SO->PolyArea[i] = (float)a;
11310          A += a;
11311       }
11312    } else {
11313       for (i=0;  i<N_FaceSet; ++i) {
11314          i3 = 3*i;
11315          n0 = &(SO->NodeList[3*FaceSets[i3]]);
11316          n1 = &(SO->NodeList[3*FaceSets[i3+1]]);
11317          n2 = &(SO->NodeList[3*FaceSets[i3+2]]);
11318          SUMA_TRI_AREA( n0, n1, n2, a);
11319          A += a;
11320       }
11321    }
11322    if (LocalHead) {
11323       fprintf(SUMA_STDERR,"%s:\n   A = %f\n", FuncName, A);
11324    }
11325    SUMA_RETURN(A);
11326 }
11327 /*!
11328 
11329 From File : Plane_Equation.c
11330 Author : Ziad Saad
11331 Date : Thu Nov 19 14:55:54 CST 1998
11332 
11333 \brief   finds the plane passing through 3 points
11334 
11335 
11336 Usage :
11337       Eq = SUMA_Plane_Equation (P1, P2, P3, thisEq)
11338 
11339 
11340    \param P1 (float *) 1x3 vector Coordinates of Point1
11341    \param P2 (float *) 1x3 vector Coordinates of Point2
11342    \param P3 (float *) 1x3 vector Coordinates of Point3
11343    \param thisEq (float *) use this pointer to store Eq rather than create a new one (set to NULL if you want a new one)
11344    \return Eq (float *) 1x4 vector containing the equation of the plane containing the three points.
11345          The equation of the plane is :
11346          Eq[0] X + Eq[1] Y + Eq[2] Z + Eq[3] = 0
11347 
11348          If the three points are colinear, Eq = [0 0 0 0]
11349 
11350    \sa SUMA_PLANE_NORMAL_POINT
11351 */
SUMA_Plane_Equation(float * P1,float * P2,float * P3,float * usethisEq)11352 float * SUMA_Plane_Equation (float * P1, float *P2, float *P3, float *usethisEq)
11353 {/*SUMA_Plane_Equation*/
11354    float *Eq;
11355    static char FuncName[] = {"SUMA_Plane_Equation"};
11356 
11357    SUMA_ENTRY;
11358    if (usethisEq) Eq = usethisEq;
11359    else Eq = (float *) SUMA_calloc(4,sizeof(float));
11360    if (!Eq)
11361       {
11362          fprintf (SUMA_STDERR, "Error %s: Failed to allocate.\n", FuncName);
11363          SUMA_RETURN (NULL);
11364       }
11365 
11366    Eq[0] = P1[1] * (P2[2]-P3[2])
11367          + P2[1] * (P3[2]-P1[2])
11368          + P3[1] * (P1[2]-P2[2]);
11369 
11370    Eq[1] = P1[2] * (P2[0]-P3[0])
11371          + P2[2] * (P3[0]-P1[0])
11372          + P3[2] * (P1[0]-P2[0]);
11373 
11374    Eq[2] = P1[0] * (P2[1]-P3[1])
11375          + P2[0] * (P3[1]-P1[1])
11376          + P3[0] * (P1[1]-P2[1]);
11377 
11378    Eq[3] =  - P1[0] * (P2[1] * P3[2] - P3[1] * P2[2])
11379             - P2[0] * (P3[1] * P1[2] - P1[1] * P3[2])
11380             - P3[0] * (P1[1] * P2[2] - P2[1] * P1[2]);
11381 
11382    SUMA_RETURN (Eq);
11383 }/*SUMA_Plane_Equation*/
11384 
11385 
11386 /*!
11387 
11388 \brief Determines the intersection of a plane and a surface
11389 
11390 
11391 
11392    SPI = SUMA_Surf_Plane_Intersect (SO, PlaneEq)
11393 
11394 \param SO (SUMA_SurfaceObject *) Pointer to surface object structure.
11395 \param  PlaneEq (float *) : 4x1 vector containing the 4 coefficients of the equation
11396                      of the plane to intersect the surface
11397                      PlaneEq[0] X + PlaneEq[1] Y + PlaneEq[2] Z + PlaneEq[3] = 0
11398 \return SPI (SUMA_SURF_PLANE_INTERSECT *) Pointer to intersection structure. See help on fields in SUMA_define.h
11399                                              NULL in case of error.
11400 
11401 
11402 
11403 \sa SUMA_SEGMENT_PLANE_INTERSECT
11404 \sa an older matlab version in Surf_Plane_Intersect_2.m
11405 \sa SUMA_PlaneEq
11406 \sa SUMA_Allocate_SPI
11407 \sa SUMA_free_SPI
11408 
11409 
11410 */
11411 
SUMA_Surf_Plane_Intersect(SUMA_SurfaceObject * SO,float * PlaneEq)11412 SUMA_SURF_PLANE_INTERSECT *SUMA_Surf_Plane_Intersect (SUMA_SurfaceObject *SO, float *PlaneEq)
11413 {/*SUMA_Surf_Plane_Intersect*/
11414    static char FuncName[]={"SUMA_Surf_Plane_Intersect"};
11415    int  i, k , k3, i3, n1, n2;
11416    float DT_ABVBEL, DT_POSNEG, u;
11417    float *NodePos;
11418    SUMA_SURF_PLANE_INTERSECT *SPI;
11419    struct  timeval start_time, start_time2;
11420    SUMA_Boolean LocalHead = NOPE;
11421    SUMA_Boolean  Hit;
11422 
11423    SUMA_ENTRY;
11424 
11425    /* Start timer for next function */
11426    SUMA_etime(&start_time2,0);
11427 
11428    if (LocalHead)   fprintf(SUMA_STDERR, "%s : Determining intersecting segments ...\n", FuncName);
11429 
11430    /* allocate for the return structure.
11431    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
11432    elements of the following vector fields:
11433    IsTriHit[] = NOPE;
11434    TriBranch[] = 0
11435    */
11436    SPI = SUMA_Allocate_SPI (SO);
11437    if (!SPI) {
11438       fprintf (SUMA_STDERR, "Error %s: Failed in SUMA_Allocate_SPI\n", FuncName);
11439       SUMA_RETURN (SPI);
11440    }
11441 
11442    /* allocate for temporary stuff */
11443    NodePos = (float *) SUMA_calloc (SO->N_Node , sizeof(float));
11444 
11445    if (!NodePos )
11446       {
11447          fprintf (SUMA_STDERR, "Error %s: Could not allocate in SUMA_Surf_Plane_Intersect\n", FuncName);
11448          SUMA_free_SPI (SPI); SPI = NULL;
11449          SUMA_RETURN (SPI);
11450       }
11451 
11452 
11453    /* Start timer for next function */
11454    SUMA_etime(&start_time,0);
11455 
11456    /* Find out which nodes are above and which are below the plane */
11457    for (k=0;k<SO->N_Node; ++k)
11458       {
11459          k3 = 3*k;
11460          NodePos[k] = PlaneEq[0] * SO->NodeList[k3] + PlaneEq[1] * SO->NodeList[k3+1]
11461                      +PlaneEq[2] * SO->NodeList[k3+2] + PlaneEq[3] ;
11462       }
11463 
11464    /* stop timer */
11465    DT_ABVBEL = SUMA_etime(&start_time,1);
11466 
11467 
11468    /*
11469       NodePos is < 0 for nodes below the plane and > 0 for points above the plane
11470       Go through each connection and determine if it intersects the plane
11471       If a segment intersects the surface, it means that the sign
11472       of p  would be <= 0 (each point is on a different side of the plane)
11473    */
11474 
11475    /* Start timer for next function */
11476    SUMA_etime(&start_time,0);
11477 
11478    /*
11479       Determine the segments intersecting the surface,
11480       The triangles that contain these segments.
11481       The nodes that form the intersected segments
11482    */
11483    SPI->N_IntersEdges = 0;
11484    SPI->N_IntersTri = 0;
11485    SPI->N_NodesInMesh = 0;
11486    k=0;
11487    Hit = NOPE;
11488    while (k < SO->EL->N_EL)
11489       {
11490          /* find out if segment intersects */
11491          /* 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:
11492                                                                                     if (SUMA_SIGN(a) != SUMA_SIGN(b)) ... */
11493          if (SUMA_SIGN(NodePos[SO->EL->EL[k][0]]) != SUMA_SIGN(NodePos[SO->EL->EL[k][1]]) ) {
11494             Hit = YUP;
11495             /* find the intersection point in that segment */
11496             u = -NodePos[SO->EL->EL[k][0]] / (NodePos[SO->EL->EL[k][1]] - NodePos[SO->EL->EL[k][0]]);
11497             i3 = 3 * k;
11498             n1 = 3 * SO->EL->EL[k][0];
11499             n2 = 3 * SO->EL->EL[k][1];
11500 
11501             SPI->IntersNodes[i3] = SO->NodeList[n1] + u * ( SO->NodeList[n2] - SO->NodeList[n1] ); ++i3; ++n2; ++n1;
11502             SPI->IntersNodes[i3] = SO->NodeList[n1] + u * ( SO->NodeList[n2] - SO->NodeList[n1] ); ++i3; ++n2; ++n1;
11503             SPI->IntersNodes[i3] = SO->NodeList[n1] + u * ( SO->NodeList[n2] - SO->NodeList[n1] ); ++i3; ++n2; ++n1;
11504 
11505 
11506             /* fprintf (SUMA_STDERR,"%s: Edge %d (nodes %d %d), IntersNodes[%d]= [%f, %f, %f]\n",
11507                FuncName, k, SO->EL->EL[k][0], SO->EL->EL[k][1], 3*k, SPI->IntersNodes[3*k],
11508                SPI->IntersNodes[3*k+1], SPI->IntersNodes[3*k+2]);
11509             */
11510 
11511             /* Store the intersected segment */
11512             SPI->IntersEdges[SPI->N_IntersEdges] = k;
11513             ++SPI->N_IntersEdges;
11514 
11515             /* mark this segment in the boolean vector to speed up some other functions */
11516             SPI->isEdgeInters[k] = YUP;
11517 
11518             /* Store the index of the triangle hosting this edge*/
11519             if (!SPI->isTriHit[SO->EL->ELps[k][1]]) {
11520                SPI->IntersTri[SPI->N_IntersTri] = SO->EL->ELps[k][1];
11521                ++(SPI->N_IntersTri);
11522                SPI->isTriHit[SO->EL->ELps[k][1]] = YUP;
11523             }
11524 
11525             /* mark the nodes forming the intersection edges */
11526             if (!SPI->isNodeInMesh[SO->EL->EL[k][0]]) {
11527                SPI->isNodeInMesh[SO->EL->EL[k][0]] = YUP;
11528                ++(SPI->N_NodesInMesh);
11529             }
11530             if (!SPI->isNodeInMesh[SO->EL->EL[k][1]]) {
11531                SPI->isNodeInMesh[SO->EL->EL[k][1]] = YUP;
11532                ++(SPI->N_NodesInMesh);
11533             }
11534          } else {
11535             Hit = NOPE;
11536          }
11537 
11538             /*  skip ahead of duplicate edge listings */
11539             if (SO->EL->ELps[k][2] > 0) {
11540                if (Hit) { /* you must mark these triangles */
11541                   i3 = 3 * k;
11542                   for (i=1; i < SO->EL->ELps[k][2]; ++i) {
11543                      SPI->isEdgeInters[k+i] = YUP;
11544                      n1 = 3 * (k+i);
11545                      SPI->IntersNodes[n1] = SPI->IntersNodes[i3]; ++i3; ++n1;
11546                      SPI->IntersNodes[n1] = SPI->IntersNodes[i3]; ++i3; ++n1;
11547                      SPI->IntersNodes[n1] = SPI->IntersNodes[i3]; ++i3; ++n1;
11548                      /*
11549                      fprintf (SUMA_STDERR,"%s: Edge %d, IntersNodes[%d]= [%f, %f, %f]\n",
11550                         FuncName, k+i, n1, SPI->IntersNodes[3*(k+i)], SPI->IntersNodes[3*(k+i)+1], SPI->IntersNodes[3*(k+i)+2]);
11551                      */
11552                      if (!SPI->isTriHit[SO->EL->ELps[k+i][1]]) {
11553                         SPI->IntersTri[SPI->N_IntersTri] = SO->EL->ELps[k+i][1];
11554                         ++(SPI->N_IntersTri);
11555                         SPI->isTriHit[SO->EL->ELps[k+i][1]] = YUP;
11556                      }
11557                   }
11558                }
11559                k += SO->EL->ELps[k][2];
11560             } else ++k;
11561       }
11562 
11563 
11564    /* stop timer */
11565    DT_POSNEG = SUMA_etime(&start_time,1);
11566 
11567    if (LocalHead) fprintf (SUMA_STDERR, "%s: Found %d intersect segments, %d intersected triangles, %d nodes in mesh (exec time %f + %f = %f secs).\n",
11568       FuncName, SPI->N_IntersEdges, SPI->N_IntersTri, SPI->N_NodesInMesh, DT_ABVBEL, DT_POSNEG, DT_ABVBEL + DT_POSNEG);
11569 
11570 /* free locally allocated memory */
11571 if (NodePos) SUMA_free (NodePos);
11572 
11573 
11574 SUMA_RETURN (SPI);
11575 }/*SUMA_Surf_Plane_Intersect*/
11576 
11577 /*!
11578    A function to intersect a surface along a set of parallel planes.
11579    The set of planes is defined by the equation of one of them and by
11580    step, the spacing between them.
11581    If step is < 0, step is set to be the average segment length
11582 */
SUMA_SliceAlongPlane(SUMA_SurfaceObject * SO,float * Eq,float step)11583 DList *SUMA_SliceAlongPlane(SUMA_SurfaceObject *SO, float *Eq, float step)
11584 {
11585    static char FuncName[]={"SUMA_SliceAlongPlane"};
11586    DList *striplist=NULL, *stripnext=NULL;
11587    int i=-1, imin, imax;
11588    float d, dmin, dmax, nrm;
11589    SUMA_SURF_PLANE_INTERSECT *SPIl = NULL;
11590    SUMA_Boolean LocalHead = NOPE;
11591 
11592    SUMA_ENTRY;
11593 
11594    if (!SO || !SO->EL || !Eq) {
11595       SUMA_S_Err("NULL input");
11596       SUMA_RETURN(striplist);
11597    }
11598    if (step < 0) step = SO->EL->AvgLe;
11599 
11600    /* calculate elevations from plane */
11601    dmin = 1000000.0; dmax = -100000000.0; imin = imax = -1;
11602    for (i=0; i<SO->N_Node; ++i) {
11603       d = Eq[0]*SO->NodeList[3*i] + Eq[1]*SO->NodeList[3*i+1] + Eq[2]*SO->NodeList[3*i+2] + Eq[3];
11604       if (d > dmax) { dmax = d; imax = i; }
11605       else if (d < dmin) { dmin = d; imin = i; }
11606    }
11607    nrm = sqrt(Eq[0]*Eq[0]+Eq[1]*Eq[1]+Eq[2]*Eq[2]);
11608    SUMA_LHv("Farthest nodes are %d at %f and %d at %f\n",imax,  dmax/nrm, imin, dmin/nrm);
11609    d = Eq[3];
11610    Eq[3] = Eq[3]-dmax;
11611    do {
11612       SUMA_LHv("Eq now [%f %f %f %f]\n", Eq[0], Eq[1], Eq[2], Eq[3]);
11613       SPIl = SUMA_Surf_Plane_Intersect (SO, Eq);
11614       if (!striplist) {
11615          striplist = SUMA_SPI_to_EdgeStrips(SO, SPIl);
11616       } else {
11617          stripnext = SUMA_SPI_to_EdgeStrips(SO, SPIl);
11618          if (stripnext) {
11619             SUMA_MergeLists_Beg2_End1(stripnext,striplist);
11620             SUMA_FREE_DLIST(stripnext);/* get rid of stripnext */
11621          }
11622       }
11623       if (SPIl) SUMA_free_SPI (SPIl);
11624       Eq[3] = Eq[3]+step*nrm;
11625    } while (Eq[3]<= d - dmin);
11626 
11627    SUMA_RETURN(striplist);
11628 }
11629 
11630 /*!
11631    \brief a wrapper function for SUMA_Surf_Plane_Intersect that returns the intersection
11632    in the form of an ROI datum
11633 
11634    \param SO (SUMA_SurfaceObject *)
11635    \param Nfrom (int) index of node index on SO from which the path should begin
11636    \param Nto (int) index of node index on SO where the path will end
11637    \param P (float *) XYZ of third point that will form the cutting plane with Nfrom and Nto's coordinates
11638             This point is usually, the Near Plane clipping point of Nto's picking line.
11639    \return ROId (SUMA_ROI_DATUM *) pointer to ROI datum structure which contains the NodePath from Nfrom to Nto
11640    along with other goodies.
11641 */
SUMA_Surf_Plane_Intersect_ROI(SUMA_SurfaceObject * SO,int Nfrom,int Nto,float * P)11642 SUMA_ROI_DATUM *SUMA_Surf_Plane_Intersect_ROI (SUMA_SurfaceObject *SO, int Nfrom, int Nto, float *P)
11643 {
11644    static char FuncName[]={"SUMA_Surf_Plane_Intersect_ROI"};
11645    SUMA_ROI_DATUM *ROId=NULL;
11646    SUMA_Boolean LocalHead = NOPE;
11647    int N_left;
11648    SUMA_SURF_PLANE_INTERSECT *SPI = NULL;
11649    SUMA_ROI *ROIe = NULL, *ROIt = NULL, *ROIn = NULL, *ROIts = NULL;
11650    float *Eq = NULL;
11651    /* The 3 flags below are for debugging. */
11652    SUMA_Boolean DrawIntersEdges=NOPE; /* Draw edges intersected by plane   */
11653    SUMA_Boolean DrawIntersTri = NOPE; /* Draw triangles intersected by plane */
11654    SUMA_Boolean DrawIntersNodeStrip = NOPE; /* Draw intersection node strip which is the shortest path between beginning and ending nodes */
11655    SUMA_Boolean DrawIntersTriStrip=NOPE; /* Draw intersection triangle strip which is the shortest path between beginning and ending nodes */
11656 
11657    SUMA_ENTRY;
11658 
11659    /* computing plane's equation */
11660    Eq = SUMA_Plane_Equation ( &(SO->NodeList[3*Nfrom]),
11661                               P,
11662                               &(SO->NodeList[3*Nto]) , NULL);
11663 
11664    if (!Eq) {
11665       fprintf(SUMA_STDOUT,"Error %s: Failed in SUMA_Plane_Equation.\n", FuncName);
11666       SUMA_RETURN(ROId);
11667    }
11668 
11669    if (LocalHead) fprintf (SUMA_STDERR, "%s: Computing Intersection with Surface.\n", FuncName);
11670    SPI = SUMA_Surf_Plane_Intersect (SO, Eq);
11671    if (!SPI) {
11672       fprintf(SUMA_STDOUT,"Error %s: Failed in SUMA_Surf_Plane_Intersect.\n", FuncName);
11673       SUMA_RETURN(ROId);
11674    }
11675 
11676    #if 0 /* some sample code to play with surface slicing interactively.
11677             This breaks the ROI drawing cycle. Use only for debugging. */
11678    {
11679       DList *striplist=NULL;
11680       SUMA_S_Warn("Kill me!");
11681       striplist = SUMA_SliceAlongPlane(SO, Eq, SO->EL->AvgLe);
11682       SUMA_display_edge_striplist(striplist, &(SUMAg_SVv[0]), SO, "ShowEdges, ShowConnectedPoints, ShowPoints");
11683       SUMA_FREE_DLIST(striplist);
11684       SUMA_RETURN(ROId);
11685    }
11686    #endif
11687 
11688    if (Eq) SUMA_free(Eq); Eq = NULL; /* not needed further */
11689 
11690    if (DrawIntersEdges) {
11691       /* Show all intersected edges */
11692       ROIe =  SUMA_AllocateROI ( SO->idcode_str,      SUMA_ROI_EdgeGroup,
11693                                  "SurfPlane Intersection - Edges",
11694                                  SPI->N_IntersEdges,  SPI->IntersEdges);
11695       if (!ROIe) {
11696          fprintf(SUMA_STDERR,
11697                  "Error %s: Failed in SUMA_AllocateROI.\n", FuncName);
11698       } else {
11699          if (!SUMA_AddDO (SUMAg_DOv, &SUMAg_N_DOv, (void *)ROIe, ROIO_type, SUMA_WORLD)) {
11700             fprintf(SUMA_STDERR,"Error %s: Failed in SUMA_AddDO.\n", FuncName);
11701          }
11702       }
11703    }
11704 
11705    if (DrawIntersTri) {
11706       /* Show all intersected triangles */
11707       ROIt =  SUMA_AllocateROI (SO->idcode_str, SUMA_ROI_FaceGroup, "SurfPlane Intersection - Triangles", SPI->N_IntersTri, SPI->IntersTri);
11708       if (!ROIt) {
11709          fprintf(SUMA_STDERR,"Error %s: Failed in SUMA_AllocateROI.\n", FuncName);
11710       } else {
11711          if (!SUMA_AddDO (SUMAg_DOv, &SUMAg_N_DOv, (void *)ROIt, ROIO_type, SUMA_WORLD)) {
11712             fprintf(SUMA_STDERR,"Error %s: Failed in SUMA_AddDO.\n", FuncName);
11713          }
11714       }
11715    }
11716 
11717    /* create ROId */
11718    ROId = SUMA_AllocROIDatum ();
11719    ROId->Type = SUMA_ROI_NodeSegment;
11720 
11721    /* calculate shortest path */
11722    N_left = SPI->N_NodesInMesh;
11723    ROId->nPath = SUMA_Dijkstra (SO, Nfrom, Nto, SPI->isNodeInMesh, &N_left, 1, &(ROId->nDistance), &(ROId->N_n));
11724    if (ROId->nDistance < 0 || !ROId->nPath) {
11725       fprintf(SUMA_STDERR,"\aError %s: Failed in fast SUMA_Dijkstra.\n*** Two points are not connected by intersection. Repeat last selection.\n", FuncName);
11726 
11727       /* clean up */
11728       if (SPI) SUMA_free_SPI (SPI);
11729       SPI = NULL;
11730       if (ROId) SUMA_FreeROIDatum (ROId);
11731       SUMA_RETURN(NULL);
11732    }
11733 
11734    if (LocalHead) fprintf (SUMA_STDERR, "%s: Shortest inter nodal distance along edges between nodes %d <--> %d (%d nodes) is %f.\n",
11735       FuncName, Nfrom, Nto, ROId->N_n, ROId->nDistance);
11736 
11737 
11738    #if 0
11739 
11740       /* FOR FUTURE USAGE:
11741          When drawing on the surface, it is possible to end up with node paths for which the
11742          triangle strip tracing routines fail. That's paretly because it is possible for these
11743          node paths to visit one node more than once, eg: ... 34 23 34 ....
11744          That is OK for drawing purposes but not for say, making measurements on the surface.
11745       */
11746 
11747       /* calculate shortest path along the intersection of the plane with the surface */
11748       /* get the triangle path corresponding to shortest distance between Nx and Ny */
11749 
11750       /* Old Method: Does not result is a strip of triangle that is continuous or connected
11751       by an intersected edge. Function is left here for historical reasons.
11752          tPath = SUMA_NodePath_to_TriPath_Inters (SO, SPI, Path, N_Path, &N_Tri); */
11753 
11754       /* you should not need to go much larger than NodeDist except when you are going for
11755       1 or 2 triangles away where discrete jumps in d might exceed the limit.
11756       Ideally, you want this measure to be 1.5 NodeDist or say, 15 mm, whichever is less.... */
11757 
11758       /* THIS SHOULD BE OPTIONAL */
11759       ROId->tPath = SUMA_IntersectionStrip (SO, SPI, ROId->nPath, ROId->N_n, &(ROId->tDistance), 2.5 *ROId->nDistance, &(ROId->N_t));
11760       if (!ROId->tPath) {
11761          fprintf(SUMA_STDERR,"Error %s: Failed in SUMA_IntersectionStrip. Proceeding\n", FuncName);
11762          /* do not stop here, it is OK if you can't find the triangle strip */
11763          /* if (ROId) SUMA_FreeROIDatum (ROId);
11764          SUMA_RETURN(NULL);   */
11765       } else {
11766          /* ROId->tPath has a potentially enourmous chunk of memory allocated for it. Trim the fat. */
11767          {
11768             int *tPath_tmp=NULL, i_tmp=0;
11769             tPath_tmp = (int *)SUMA_calloc (ROId->N_t, sizeof(int));
11770             if (!tPath_tmp) {
11771                SUMA_RegisterMessage (SUMAg_CF->MessageList, "Failed to allocate for tpath_tmp", FuncName, SMT_Critical, SMA_LogAndPopup);
11772                SUMA_RETURN(NULL);
11773             }
11774             for (i_tmp=0; i_tmp<ROId->N_t; ++i_tmp) tPath_tmp[i_tmp] = ROId->tPath[i_tmp];
11775             SUMA_free(ROId->tPath);
11776             ROId->tPath = tPath_tmp;
11777          }
11778 
11779          fprintf (SUMA_STDERR, "%s: Shortest inter nodal distance along surface between nodes %d <--> %d is %f.\nTiangle 1 is %d\n",
11780             FuncName, Nfrom, Nto, ROId->tDistance, ROId->tPath[0]);
11781 
11782          if (DrawIntersTriStrip) {
11783             /* Show intersected triangles, along shortest path */
11784             ROIts =  SUMA_AllocateROI (SO->idcode_str, SUMA_ROI_FaceGroup, "SurfPlane Intersection - Triangles- Shortest", ROId->N_t, ROId->tPath);
11785             if (!ROIts) {
11786                fprintf(SUMA_STDERR,"Error %s: Failed in SUMA_AllocateROI.\n", FuncName);
11787                if (ROIn) SUMA_freeROI(ROIn);
11788                if (ROIts) SUMA_freeROI(ROIts);
11789                if (ROId) SUMA_FreeROIDatum (ROId);
11790                SUMA_RETURN(NULL);
11791             }
11792             if (!SUMA_AddDO (SUMAg_DOv, &SUMAg_N_DOv, (void *)ROIts, ROIO_type, SUMA_WORLD)) {
11793                fprintf(SUMA_STDERR,"Error %s: Failed in SUMA_AddDO.\n", FuncName);
11794                if (ROIn) SUMA_freeROI(ROIn);
11795                if (ROIts) SUMA_freeROI(ROIts);
11796                if (ROId) SUMA_FreeROIDatum (ROId);
11797                SUMA_RETURN(NULL);
11798             }
11799          }
11800 
11801          if (ROId->nPath && DrawIntersNodeStrip) {
11802             #if 0
11803                /* Show me the Path */
11804                for (ii=0; ii < ROId->N_n; ++ii) fprintf(SUMA_STDERR," %d\t", ROId->nPath[ii]);
11805             #endif
11806 
11807             /* Show Path */
11808             ROIn =  SUMA_AllocateROI (SO->idcode_str, SUMA_ROI_NodeGroup, "SurfPlane Intersection - Nodes", ROId->N_n, ROId->nPath);
11809             if (!ROIn) {
11810                fprintf(SUMA_STDERR,"Error %s: Failed in SUMA_AllocateROI.\n", FuncName);
11811                if (ROIn) SUMA_freeROI(ROIn);
11812                if (ROIts) SUMA_freeROI(ROIts);
11813                if (ROId) SUMA_FreeROIDatum (ROId);
11814                SUMA_RETURN(NULL);
11815             }
11816             if (!SUMA_AddDO (SUMAg_DOv, &SUMAg_N_DOv, (void *)ROIn, ROIO_type, SUMA_WORLD)) {
11817                fprintf(SUMA_STDERR,"Error %s: Failed in SUMA_AddDO.\n", FuncName);
11818                if (ROIn) SUMA_freeROI(ROIn);
11819                if (ROIts) SUMA_freeROI(ROIts);
11820                if (ROId) SUMA_FreeROIDatum (ROId);
11821                SUMA_RETURN(NULL);
11822             }
11823 
11824          }
11825       }
11826    #endif
11827 
11828    if (LocalHead) fprintf(SUMA_STDERR,"%s: Freeing Eq...\n", FuncName);
11829    if (Eq) SUMA_free(Eq);
11830 
11831    if (LocalHead) fprintf(SUMA_STDERR,"%s: Freeing SPI...\n", FuncName);
11832    if (SPI) SUMA_free_SPI (SPI);
11833 
11834    if (LocalHead) fprintf(SUMA_STDERR,"%s:Done Freeing...\n", FuncName);
11835 
11836    SUMA_RETURN(ROId);
11837 }
11838 
11839 /*!
11840 
11841 \brief
11842 SBv =  SUMA_AssignTriBranch (SO, SPI, Nx, BranchCount, DoCopy)
11843 \param SO (SUMA_SurfaceObject *) Pointer to Surface Object structure
11844 \param SPI (SUMA_SURF_PLANE_INTERSECT *) Pointer to Surface Plane Intersection structure
11845 \param Nx (int) Node index to start the first branch at. This parameter is optional. pass -1 if you do not want it set.
11846 \param BranchCount (int *) Pointer to the total number of branches found.
11847 \param DoCopy (SUMA_Boolean) flag indicating whether to preserve (YUP) the values in SPI->IntersEdges and SPI->N_IntersEdges or not (NOPE).
11848                              If you choose YUP, a copy of SPI->IntersEdges is made and manipulated.
11849 \return Bv (SUMA_TRI_BRANCH*) Pointer to a vector of *BranchCount branches formed by the intersections.
11850       A branch is formed by a series of connected triangles. A Branch can be a loop but that is not determined in this function.
11851       NULL if trouble is encountered.
11852       On some surfaces with cuts you may have a branch split in two which requires welding.
11853       No such thing is done here but a warning is printed out.
11854 
11855 NOTE: The vector SPI->IntersEdges is modified by this function.
11856 \sa SUMA_free_STB for freeing Bv
11857 */
11858 
11859 #define SUMA_MAX_BRANCHES 300
11860 
11861 /* assign a branch to each triangle intersected */
SUMA_AssignTriBranch(SUMA_SurfaceObject * SO,SUMA_SURF_PLANE_INTERSECT * SPI,int Nx,int * BranchCount,SUMA_Boolean DoCopy)11862 SUMA_TRI_BRANCH* SUMA_AssignTriBranch (SUMA_SurfaceObject *SO, SUMA_SURF_PLANE_INTERSECT *SPI,
11863                                        int Nx, int *BranchCount, SUMA_Boolean DoCopy)
11864 {
11865    static char FuncName[]={"SUMA_AssignTriBranch"};
11866    int *IntersEdgesCopy = NULL, N_IntersEdgesCopy, i_Branch, E1, kedge, i,
11867          N_iBranch[SUMA_MAX_BRANCHES], NBlist[SUMA_MAX_BRANCHES], iBranch = 0,
11868          N_Branch, Bcnt, ilist, j, ivisit, *VisitationOrder, TriCheck;
11869    SUMA_Boolean Local_IntersEdgesCopy = NOPE;
11870    int *TriBranch = NULL; /*!< Vector of SO->EL->N_EL / 3 elements but with only N_IntersTri meaningful values. If TriBranch[j] = b
11871                      then triangle j in SO->FaceSet is a member of Branch b. Branch numbering starts at 1 and may not be consecutive.
11872                      A branch is a collection of connected triangles and may form a closed loop */
11873    SUMA_TRI_BRANCH *Bv = NULL;
11874    SUMA_Boolean LocalHead = NOPE;
11875 
11876    SUMA_ENTRY;
11877 
11878 
11879    i_Branch = 0;
11880 
11881    /* make a copy of SPI->N_IntersEdges since this vector will be modified and might be needed later,
11882       might make that optional later and just copy pointers if IntersEdgesCopy is not needed elsewhere.
11883       IntersEdgesCopy flag is used to decide on freeing IntersEdgesCopy or not.*/
11884 
11885    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 */
11886    TriBranch = (int *)SUMA_calloc (SO->EL->N_EL / 3,  sizeof(int));
11887 
11888    if (!VisitationOrder || !TriBranch) {
11889       fprintf (SUMA_STDERR, "Error %s: Failed to allocate.\n", FuncName);
11890       if (TriBranch) SUMA_free(TriBranch);
11891       if (VisitationOrder) SUMA_free(VisitationOrder);
11892       SUMA_RETURN (NULL);
11893    }
11894 
11895    N_IntersEdgesCopy = SPI->N_IntersEdges;
11896    if (DoCopy) {
11897       IntersEdgesCopy = (int *) SUMA_calloc (N_IntersEdgesCopy, sizeof (int));
11898       Local_IntersEdgesCopy = YUP;
11899       for (i=0; i < N_IntersEdgesCopy; ++i) {
11900          IntersEdgesCopy[i] = SPI->IntersEdges[i];
11901       }
11902    }else {
11903       Local_IntersEdgesCopy = NOPE;
11904       IntersEdgesCopy = SPI->IntersEdges;
11905    }
11906 
11907    if (!IntersEdgesCopy) {
11908      fprintf (SUMA_STDERR, "Error %s: Failed to allocate for or receive IntersEdgesCopy.\n", FuncName);
11909      if (TriBranch) SUMA_free(TriBranch);
11910      if (VisitationOrder) SUMA_free(VisitationOrder);
11911      SUMA_RETURN (NULL);
11912    }
11913 
11914    ivisit = 0;
11915    while (N_IntersEdgesCopy) {
11916 
11917       if (!i_Branch && Nx >= 0) {
11918          /* start from edge containing Nx, this is only done at the starting point (i_Branch = 0) */
11919          E1 = -1;
11920          i=0;
11921          while (i < N_IntersEdgesCopy && E1 < 0) {
11922             if ( (SO->EL->EL[IntersEdgesCopy[i]][0] == Nx) || (SO->EL->EL[IntersEdgesCopy[i]][1] == Nx) ) {
11923                E1 = IntersEdgesCopy[i];
11924                kedge = i;
11925             }
11926             ++i;
11927          }
11928       }else {
11929          /* no starting orders, start from any decent edge */
11930          /* find an edge with one hosting triangle */
11931          E1 = SUMA_Find_Edge_Nhost (SO->EL, IntersEdgesCopy, N_IntersEdgesCopy, &kedge, 1);
11932       }
11933 
11934       if (E1 < 0) { /* no such edge found, take first edge in InInter */
11935             kedge = 0;
11936             E1 = IntersEdgesCopy[kedge];
11937             if (LocalHead) fprintf (SUMA_STDERR, "%s: No 1 host edge edge found.\n", FuncName);
11938       }else {
11939             if (LocalHead) fprintf (SUMA_STDERR, "%s: Found edge.\n", FuncName);
11940       }
11941 
11942       /* remove this edge from the list */
11943       --(N_IntersEdgesCopy);
11944       if (LocalHead) fprintf (SUMA_STDERR, "%s: kedge = %d, N_IntersEdgesCopy = %d.\n", FuncName, kedge, N_IntersEdgesCopy);
11945       IntersEdgesCopy[kedge] = IntersEdgesCopy[N_IntersEdgesCopy];
11946 
11947       /* start a new i_Branch - All i_Branch indices must be > 0*/
11948       ++i_Branch;
11949       if (i_Branch > SUMA_MAX_BRANCHES-1) {
11950          fprintf (SUMA_STDERR, "Error %s: No more than %d branches allowed.\n", FuncName, SUMA_MAX_BRANCHES);
11951          SUMA_RETURN (NULL);
11952       }
11953 
11954       /* mark the triangle containing E1 */
11955       if (LocalHead) fprintf (SUMA_STDERR, "%s: Marking triangle %d with branch %d.\n", FuncName, SO->EL->ELps[E1][1], i_Branch);
11956       TriBranch[SO->EL->ELps[E1][1]] = i_Branch;
11957       VisitationOrder[ivisit] = SO->EL->ELps[E1][1]; ++ivisit;
11958 
11959       if (LocalHead) fprintf (SUMA_STDERR, "%s: Called recursive SUMA_Mark_Tri.\n", FuncName);
11960       if (!SUMA_Mark_Tri (SO->EL, E1, i_Branch, TriBranch, IntersEdgesCopy, &(N_IntersEdgesCopy), VisitationOrder, &ivisit)) {
11961          fprintf (SUMA_STDERR, "Error %s: Failed in SUMA_Mark_Tri.\n", FuncName);
11962       }
11963       if (LocalHead) fprintf (SUMA_STDERR, "%s: Returned from recursive SUMA_Mark_Tri.\n", FuncName);
11964 
11965       /* repeat till all edges are used up */
11966    }
11967 
11968    if (Local_IntersEdgesCopy) {
11969       if (LocalHead) fprintf (SUMA_STDERR, "%s: freeing IntersEdgesCopy.\n", FuncName);
11970       SUMA_free(IntersEdgesCopy);
11971    }else {
11972       /* also change N_IntersEdges */
11973       SPI->N_IntersEdges = N_IntersEdgesCopy;
11974    }
11975 
11976    /* SUMA_disp_dvect (TriBranch, SO->N_FaceSet);  */
11977 
11978    N_Branch = i_Branch;
11979 
11980    /* determine the number of branch elements to allocate for - IDIOT PROOF, doing it in the recursive function SUMA_Mark_Tri was annoying*/
11981    for (i=0; i <= N_Branch; ++i) N_iBranch[i] = 0; /* remember, Branch numbering starts at 1 */
11982    if (LocalHead) fprintf (SUMA_STDERR, "%s: Searching all %d intersected triangles.\n", FuncName, SPI->N_IntersTri);
11983    Bcnt = 0;
11984    for (i=0; i < SO->N_FaceSet; ++i) {
11985       if (TriBranch[i]) {
11986          /* fprintf (SUMA_STDERR, "%d:%d\t", TriBranch[i], N_iBranch[TriBranch[i]]); */
11987          ++Bcnt;
11988          N_iBranch[TriBranch[i]] = N_iBranch[TriBranch[i]] + 1;
11989       }
11990    }
11991 
11992    #if 0
11993       fprintf (SUMA_STDERR, "Values in N_iBranch, idiot proof:\n");
11994       SUMA_disp_dvect (N_iBranch, N_Branch+1);
11995       fprintf (SUMA_STDERR, "\n");
11996    #endif
11997 
11998    if (LocalHead) fprintf (SUMA_STDERR, "%s: Found %d triangles belonging to a branch out of %d intersected triangles.\n", FuncName, Bcnt, SPI->N_IntersTri);
11999 
12000    /* Now you want to create a vector of N_Branches to represent the intersection */
12001    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 ...*/
12002    if (!Bv) {
12003       fprintf (SUMA_STDERR, "Error %s: Could not allocate for Bv.\n", FuncName);
12004       SUMA_RETURN (NULL);
12005    }
12006 
12007    /* initialize allocated Bv elements */
12008    for (i=0; i<= N_Branch; ++i) { /* You have allocated for N_Branch+1*/
12009       Bv[i].list = NULL;
12010       Bv[i].N_list = 0;
12011    }
12012 
12013    Bcnt = 0;
12014    for (i=0; i<= N_Branch; ++i) { /* Branch numbering starts at 1 */
12015       if (N_iBranch[i]) {
12016          /* something in that branch, allocate and initialize*/
12017          if (LocalHead) fprintf (SUMA_STDERR, "%s: Allocating for %d elements, Old Branch %d, New Branch %d.\n", FuncName, N_iBranch[i], i, Bcnt);
12018          Bv[Bcnt].list = (int *) SUMA_calloc (N_iBranch[i]+1, sizeof(int));
12019          Bv[Bcnt].N_list = N_iBranch[i];
12020          Bv[Bcnt].iBranch = Bcnt;
12021          NBlist[i] = Bcnt; /* store new indexing for Branches */
12022          ++Bcnt;
12023       }
12024 
12025    }
12026 
12027    /* store the total number of used branches */
12028    *BranchCount = Bcnt;
12029 
12030    /* now fill up the branches*/
12031    if (LocalHead) fprintf (SUMA_STDERR, "%s: Filling up branches...\n", FuncName);
12032    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 */
12033    for (i=0; i < SPI->N_IntersTri; ++i) { /* only go over visited triangles */
12034       TriCheck = TriBranch[VisitationOrder[i]]; /* returns the branch number of triangle VisitationOrder[i] */
12035       if (TriCheck) {
12036          Bcnt = NBlist[TriCheck]; /* get the new branch number from the original (old) branch number */
12037          #if 0
12038          fprintf (SUMA_STDERR,"%s: Tricheck = %d\n", FuncName, TriCheck); */
12039          if (Bcnt >= *BranchCount) {
12040             fprintf (SUMA_STDERR, "\aError %s: BranchCount = %d <= Bcnt = %d.\n", FuncName, *BranchCount, Bcnt);
12041          }
12042          if (N_iBranch[Bcnt] >= Bv[Bcnt].N_list) {
12043             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);
12044          }
12045          #endif
12046          Bv[Bcnt].list[N_iBranch[Bcnt]] = VisitationOrder[i]; /* store the index of the visited triangle in that branch */
12047          N_iBranch[Bcnt] += 1; /* store the number of elements in that branch */
12048       }
12049    }
12050 
12051    if (LocalHead) fprintf (SUMA_STDERR, "%s: freeing ...\n", FuncName);
12052    if (VisitationOrder) SUMA_free(VisitationOrder);
12053    if (TriBranch) SUMA_free(TriBranch);
12054    SUMA_RETURN (Bv);
12055 }
12056 
12057 /*!
12058    Function to show the contents of a SUMA_TRI_BRANCH structure
12059 */
SUMA_show_STB(SUMA_TRI_BRANCH * B,FILE * Out)12060 SUMA_Boolean SUMA_show_STB (SUMA_TRI_BRANCH *B, FILE *Out)
12061 {
12062    static char FuncName[]={"SUMA_show_STB"};
12063    int i;
12064 
12065    SUMA_ENTRY;
12066 
12067    if (!Out) Out = SUMA_STDERR;
12068 
12069    if (!B) {
12070       fprintf (Out, "%s: Empy structure.\n", FuncName);
12071    }
12072 
12073    fprintf (Out, "%s:\tBranch #%d. %d elements in list\nlist:\t", FuncName, B->iBranch, B->N_list);
12074    for (i=0; i < B->N_list; ++i) {
12075       fprintf (Out, "%d\t", B->list[i]);
12076    }
12077    fprintf (Out, "\n");
12078 
12079    SUMA_RETURN (YUP);
12080 }
12081 
12082 /*!
12083    Function to free a vector of SUMA_TRI_BRANCH structures.
12084 */
12085 
SUMA_free_STB(SUMA_TRI_BRANCH * Bv,int N_Bv)12086 void SUMA_free_STB (SUMA_TRI_BRANCH *Bv, int N_Bv)
12087 {
12088 
12089    static char FuncName[]={"SUMA_free_STB"};
12090    int i;
12091 
12092    SUMA_ENTRY;
12093 
12094    for (i=0; i < N_Bv; ++i) {
12095       if (Bv[i].list) SUMA_free(Bv[i].list);
12096    }
12097    if (Bv) SUMA_free(Bv);
12098 
12099    SUMA_RETURNe;
12100 
12101 }
12102 
12103 /*!
12104    \brief Allocates a structure for computing the intersection of a surface with a plane
12105    The allocation is done conservatively, expecting the worse case scenario.
12106 
12107    \param SO (SUMA_SurfaceObject *) Surface Object structure used to get number of nodes, edges, etc ...
12108    \return SPI (SUMA_SURF_PLANE_INTERSECT *) Pointer to surface plane intersection structure.
12109          see structure definition for more info.
12110 
12111 */
SUMA_Allocate_SPI(SUMA_SurfaceObject * SO)12112 SUMA_SURF_PLANE_INTERSECT * SUMA_Allocate_SPI (SUMA_SurfaceObject *SO)
12113 {
12114    static char FuncName[]={"SUMA_Allocate_SPI"};
12115    int i;
12116    SUMA_SURF_PLANE_INTERSECT *SPI = NULL;
12117 
12118    SUMA_ENTRY;
12119 
12120    SPI = (SUMA_SURF_PLANE_INTERSECT *) SUMA_malloc(sizeof(SUMA_SURF_PLANE_INTERSECT));
12121    if (!SPI) {
12122       fprintf (SUMA_STDERR, "Error %s: Could not allocate for SPI\n", FuncName);
12123       SUMA_RETURN (SPI);
12124    }
12125 
12126    SPI->IntersEdges = (int *) SUMA_calloc (SO->EL->N_EL, sizeof(int)); /* allocate for the max imaginable*/
12127    SPI->IntersNodes = (float *) SUMA_calloc (3 * SO->EL->N_EL, sizeof(float));
12128    SPI->isEdgeInters = (SUMA_Boolean *) SUMA_calloc (SO->EL->N_EL, sizeof(SUMA_Boolean));
12129    SPI->IntersTri = (int *) SUMA_calloc (SO->N_FaceSet, sizeof(int));
12130    SPI->isNodeInMesh = (SUMA_Boolean *) SUMA_calloc (SO->N_Node, sizeof(SUMA_Boolean));
12131    SPI->isTriHit = (SUMA_Boolean *) SUMA_calloc (SO->N_FaceSet, sizeof(SUMA_Boolean));
12132 
12133    if (!SPI->IntersEdges || !SPI->IntersTri || !SPI->IntersNodes || !SPI->isTriHit || !SPI->isEdgeInters)
12134       {
12135          fprintf (SUMA_STDERR, "Error %s: Could not allocate \n", FuncName);
12136          SUMA_RETURN (SPI);
12137       }
12138 
12139    SPI->N_IntersEdges = 0;
12140    SPI->N_IntersTri = 0;
12141    SPI->N_NodesInMesh = 0;
12142    SUMA_RETURN (SPI);
12143 }
12144 
12145 /*!
12146 free the SPI structure
12147 */
SUMA_free_SPI(SUMA_SURF_PLANE_INTERSECT * SPI)12148 void SUMA_free_SPI (SUMA_SURF_PLANE_INTERSECT *SPI)
12149 {
12150    static char FuncName[]={"SUMA_free_SPI"};
12151 
12152    SUMA_ENTRY;
12153 
12154    if (!SPI) SUMA_RETURNe;
12155    if (SPI->IntersTri) SUMA_free(SPI->IntersTri);
12156    if (SPI->IntersNodes) SUMA_free(SPI->IntersNodes);
12157    if (SPI->IntersEdges) SUMA_free(SPI->IntersEdges);
12158    if (SPI->isNodeInMesh) SUMA_free(SPI->isNodeInMesh);
12159    if (SPI->isTriHit) SUMA_free (SPI->isTriHit);
12160    if (SPI->isEdgeInters) SUMA_free (SPI->isEdgeInters);
12161 
12162    if (SPI) SUMA_free(SPI);
12163 
12164    SUMA_RETURNe;
12165 }
12166 
12167 /*!
12168 Show the SPI structure
12169 */
SUMA_Show_SPI(SUMA_SURF_PLANE_INTERSECT * SPI,FILE * Out,SUMA_SurfaceObject * SO,char * opref,SUMA_SurfaceViewer * sv)12170 SUMA_Boolean SUMA_Show_SPI (SUMA_SURF_PLANE_INTERSECT *SPI, FILE * Out,
12171                            SUMA_SurfaceObject *SO, char *opref,
12172                            SUMA_SurfaceViewer *sv)
12173 {
12174    static char FuncName[]={"SUMA_Show_SPI"};
12175    int i, j;
12176    char *fname=NULL;
12177    FILE *fout=NULL;
12178    SUMA_Boolean LocalHead = NOPE;
12179    SUMA_ENTRY;
12180 
12181    if (!Out) Out = SUMA_STDERR;
12182 
12183    if (!SPI) {
12184       fprintf (Out,"Error %s: NULL POINTER.\n", FuncName);
12185    }
12186 
12187    fprintf (Out,"Intersection Edges: %d\n[", SPI->N_IntersEdges);
12188    for (i=0; i < SPI->N_IntersEdges; ++i) {
12189       fprintf (Out, "%d, %d\n",
12190                SO->EL->EL[SPI->IntersEdges[i]][0],
12191                SO->EL->EL[SPI->IntersEdges[i]][1]);
12192    }
12193    fprintf (Out," ]\n");
12194    if (opref) {
12195       fname = SUMA_append_string(opref, "_edges.1D");
12196       fout = fopen(fname,"w");
12197       fprintf (fout, "#segments\n#segments intersected by plane");
12198       for (i=0; i < SPI->N_IntersEdges; ++i) {
12199          fprintf (fout, "%f %f %f    %f %f %f\n",
12200             SO->NodeList[3*SO->EL->EL[SPI->IntersEdges[i]][0]],
12201             SO->NodeList[3*SO->EL->EL[SPI->IntersEdges[i]][0]+1],
12202             SO->NodeList[3*SO->EL->EL[SPI->IntersEdges[i]][0]+2],
12203             SO->NodeList[3*SO->EL->EL[SPI->IntersEdges[i]][1]],
12204             SO->NodeList[3*SO->EL->EL[SPI->IntersEdges[i]][1]+1],
12205             SO->NodeList[3*SO->EL->EL[SPI->IntersEdges[i]][1]+2]);
12206       }
12207       fclose(fout); fout = NULL; SUMA_free(fname); fname=NULL;
12208    }
12209    if (sv) {
12210       SUMA_SegmentDO *SDO = NULL;
12211       if ((SDO = SUMA_Alloc_SegmentDO (SPI->N_IntersEdges,
12212                                  "Show_SPI_segs", 0, SO->idcode_str,
12213                                  0, LS_type, SO_type, NULL))) {
12214          SDO->do_type = LS_type;
12215          for (i=0; i < SPI->N_IntersEdges; ++i) {
12216             for (j=0; j<3;++j) {
12217                SDO->n0[3*i+j] =
12218                   SO->NodeList[3*SO->EL->EL[SPI->IntersEdges[i]][0]+j];
12219                SDO->n1[3*i+j] =
12220                   SO->NodeList[3*SO->EL->EL[SPI->IntersEdges[i]][1]+j];
12221             }
12222          }
12223          /* addDO */
12224          if (!SUMA_AddDO(  SUMAg_DOv, &SUMAg_N_DOv,
12225                            (void *)SDO, LS_type, SUMA_WORLD)) {
12226             fprintf(SUMA_STDERR,"Error %s: Failed in SUMA_AddDO.\n", FuncName);
12227             SUMA_RETURN(NOPE);
12228          }
12229 
12230          /* register DO with viewer */
12231          if (!SUMA_RegisterDO(SUMAg_N_DOv-1, sv)) {
12232             fprintf(SUMA_STDERR,
12233                      "Error %s: Failed in SUMA_RegisterDO.\n", FuncName);
12234             SUMA_RETURN(NOPE);
12235          }
12236 
12237          /* redisplay curent only*/
12238          sv->ResetGLStateVariables = YUP;
12239          SUMA_handleRedisplay((XtPointer)sv->X->GLXAREA);
12240       }
12241    }
12242 
12243    fprintf (Out,"Intersection Nodes: %d\n[", SPI->N_IntersEdges);
12244    for (i=0; i < SO->EL->N_EL; ++i) {
12245       if (SPI->isEdgeInters[i])
12246          fprintf (Out, "%f, %f, %f, ",
12247                   SPI->IntersNodes[3*i],
12248                   SPI->IntersNodes[3*i+1],
12249                   SPI->IntersNodes[3*i+2]);
12250    }
12251    fprintf (Out," ]\n");
12252    if (opref) {
12253       fname = SUMA_append_string(opref, "_interspoints.1D");
12254       fout = fopen(fname,"w");
12255       fprintf (fout, "#spheres\n#locations of intersections\n");
12256       for (i=0; i < SO->EL->N_EL; ++i) {
12257          if (SPI->isEdgeInters[i])
12258             fprintf (fout, "%f %f %f\n ",
12259                      SPI->IntersNodes[3*i],
12260                      SPI->IntersNodes[3*i+1],
12261                      SPI->IntersNodes[3*i+2]);
12262       }
12263       fclose(fout); fout = NULL; SUMA_free(fname); fname=NULL;
12264    }
12265    if (sv) {
12266       SUMA_SphereDO *SDO = NULL;
12267       if ((SDO = SUMA_Alloc_SphereDO (SPI->N_IntersEdges,
12268                               "Show_SPI_interspoints", NULL, SP_type))) {
12269          SDO->do_type = SP_type;
12270          SDO->CommonRad = SO->EL->AvgLe/6.0;
12271          for (i=0; i < SPI->N_IntersEdges; ++i) {
12272             {
12273                SDO->cxyz[3*i  ] = SPI->IntersNodes[3*SPI->IntersEdges[i]  ];
12274                SDO->cxyz[3*i+1] = SPI->IntersNodes[3*SPI->IntersEdges[i]+1];
12275                SDO->cxyz[3*i+2] = SPI->IntersNodes[3*SPI->IntersEdges[i]+2];
12276             }
12277          }
12278          /* addDO */
12279          if (!SUMA_AddDO(  SUMAg_DOv, &SUMAg_N_DOv,
12280                            (void *)SDO, SP_type, SUMA_WORLD)) {
12281             fprintf(SUMA_STDERR,"Error %s: Failed in SUMA_AddDO.\n", FuncName);
12282             SUMA_RETURN(NOPE);
12283          }
12284 
12285          /* register DO with viewer */
12286          if (!SUMA_RegisterDO(SUMAg_N_DOv-1, sv)) {
12287             fprintf(SUMA_STDERR,
12288                      "Error %s: Failed in SUMA_RegisterDO.\n", FuncName);
12289             SUMA_RETURN(NOPE);
12290          }
12291 
12292          /* redisplay curent only*/
12293          sv->ResetGLStateVariables = YUP;
12294          SUMA_handleRedisplay((XtPointer)sv->X->GLXAREA);
12295       }
12296 
12297    }
12298    fprintf (Out,"Intersected Triangles: %d\n[", SPI->N_IntersTri);
12299    for (i=0; i < SPI->N_IntersTri; ++i) {
12300       fprintf (Out, "t%d\t", SPI->IntersTri[i]);
12301    }
12302    fprintf (Out," ]\n");
12303    SUMA_RETURN(YUP);
12304 }
12305 
12306 #define NO_LOG
SUMA_Mark_Tri(SUMA_EDGE_LIST * EL,int E1,int iBranch,int * TriBranch,int * IsInter,int * N_IsInter,int * VisitationOrder,int * ivisit)12307 SUMA_Boolean SUMA_Mark_Tri (SUMA_EDGE_LIST  *EL, int E1, int iBranch, int *TriBranch, int *IsInter, int *N_IsInter, int *VisitationOrder, int *ivisit)
12308 {
12309    static char FuncName[]={"SUMA_Mark_Tri"};
12310    int Tri = -1, Found, k, kedge = 0, E2, Ntri = 0;
12311    static int In = 0;
12312    SUMA_Boolean LocalHead = NOPE;
12313 
12314    /* this is a recursive function, you don't want to log every time it is called */
12315    /* SUMA_EN TRY;  *//* syntax (space) error on purpose, to avoid upsetting AnalyzeTrace on this file  */
12316 
12317    ++In;
12318    if (LocalHead) fprintf (SUMA_STDERR, "%s: Entered #%d.\n", FuncName, In);
12319 
12320    /* find the next triangle hosting E1, if possible, otherwise it is the end of the branch. */
12321    if (EL->ELps[E1][2] != 2) { /* reached a dead end , end of branch */
12322       /* mark triangle, remove E1 from list and return */
12323       if (LocalHead) fprintf (SUMA_STDERR, "%s: reached end of branch.\n", FuncName);
12324       kedge = 0;
12325       Found = NOPE;
12326       while (!Found && kedge < *N_IsInter) {
12327          if (IsInter[kedge] == E1) {
12328             Found = YUP;
12329             *N_IsInter = *N_IsInter - 1;
12330             IsInter[kedge] = IsInter[*N_IsInter];
12331          } else ++kedge;
12332       }
12333       return (YUP);
12334    }else {
12335       Tri = EL->ELps[E1][1];
12336       if (TriBranch[Tri]) { /* try second triangle */
12337          Tri = EL->ELps[E1+1][1];
12338       }
12339       if (LocalHead) fprintf (SUMA_STDERR, "%s: moving on to triangle %d.\n", FuncName, Tri);
12340    }
12341 
12342    if (!TriBranch[Tri]) {
12343       /* unvisited, mark with iBranch */
12344       TriBranch[Tri] = iBranch;
12345       VisitationOrder[*ivisit] = Tri;
12346       ++(*ivisit);
12347       /* find other edges in this triangle that have been intersected */
12348       Found = NOPE;
12349       k = 0;
12350       while (!Found && k < 3) {
12351          E2 = EL->Tri_limb[Tri][k]; /* this may not be the first occurence of this edge since the list contains duplicates */
12352          if (LocalHead) {
12353             fprintf (SUMA_STDERR, "%s: Trying edge E2 %d (%d %d), tiangle %d, edge %d.\n",
12354                      FuncName, E2, EL->EL[E2][0], EL->EL[E2][1], Tri, k);
12355          }
12356          while (EL->ELps[E2][2] < 0) { /* find the first occurence of this edge in the list */
12357             E2--;
12358          }
12359          if (LocalHead) fprintf (SUMA_STDERR, "%s: E2 changed to %d. E1 is %d\n", FuncName, E2, E1);
12360          if (E2 != E1) {
12361             /* was E2 intersected ? */
12362             kedge = 0;
12363             while (!Found && kedge < *N_IsInter) {
12364                if (IsInter[kedge] == E2) {
12365                   Found = YUP;
12366                   if (LocalHead) fprintf (SUMA_STDERR, "%s: E2 is intersected.\n", FuncName);
12367                }
12368                else ++kedge;
12369             }
12370          }
12371          ++k;
12372       }
12373 
12374       if (!Found) {
12375          fprintf (SUMA_STDERR, "Error %s: No second edge found.\n", FuncName);
12376          return (NOPE);
12377       } else {
12378          if (LocalHead) fprintf (SUMA_STDERR, "%s: Removing E2 from List and calling SUMA_Mark_Tri.\n", FuncName);
12379          /* remove this new edge from the list */
12380          *N_IsInter = *N_IsInter - 1;
12381          IsInter[kedge] = IsInter[*N_IsInter];
12382 
12383          /* continue visitation */
12384          if (!SUMA_Mark_Tri (EL, E2, iBranch, TriBranch, IsInter, N_IsInter, VisitationOrder, ivisit)) {
12385             fprintf (SUMA_STDERR, "Error %s: Failed in SUMA_Mark_Tri.\n", FuncName);
12386             return (NOPE);
12387          }
12388          return (YUP);
12389       }
12390    } else {
12391       if (TriBranch[Tri] != iBranch) {
12392          fprintf (SUMA_STDERR, "\a%s: Branches colliding, Must weld %d to %d.\n", FuncName, iBranch, TriBranch[Tri]);
12393 
12394          /* DO NOT MODIFY THE VALUE OF BRANCH or you will mistakingly link future branches*/
12395       }
12396       /* visited, end of branch return */
12397       if (LocalHead) fprintf (SUMA_STDERR, "%s: End of branch. Returning.\n", FuncName);
12398       return (YUP);
12399    }
12400 
12401    fprintf (SUMA_STDERR, "Error %s: Should not be here.\n", FuncName);
12402    return (NOPE);
12403 }
12404 
12405 /*!
12406    E = SUMA_Find_Edge_N_Host (EL, IsInter, N_IsInter, Nhost);
12407    \brief Finds an edge that has Nhost hosting triangles. Only the edges indexed in IsInter are examined
12408    \param EL (SUMA_EDGE_LIST *) Complete Edge list structure for surface
12409    \param IsInter (int *) vector containing indices into EL->EL matrix which contains the EdgeList
12410    \param N_IsInter (int) number of elements in IsInter
12411    \param kedge (int *) pointer to index into IsInter where E was found
12412    \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
12413    \return E (int) index into EL->EL of the first edge (of those listed in IsInter) encountered that has N hosting triangles.
12414       -1 is returned if no edges are found
12415 
12416    This function is meant to be used with SUMA_Surf_Plane_Intersect
12417 */
SUMA_Find_Edge_Nhost(SUMA_EDGE_LIST * EL,int * IsInter,int N_IsInter,int * i,int Nhost)12418 int SUMA_Find_Edge_Nhost (SUMA_EDGE_LIST  *EL, int *IsInter, int N_IsInter, int *i, int Nhost)
12419 {
12420    static char FuncName[]={"SUMA_Find_Edge_Nhost"};
12421 
12422    SUMA_ENTRY;
12423 
12424    for (*i=0; *i < N_IsInter; ++(*i)) {
12425       if (EL->ELps[IsInter[*i]][2] == Nhost) SUMA_RETURN (IsInter[*i]);
12426    }
12427 
12428    SUMA_RETURN (-1);
12429 
12430 }
12431 
12432 /*!
12433 \brief Path = SUMA_Dijkstra (SO, Nx, Ny, isNodeInMesh, N_isNodeInMesh, Method_Number, Path_length, N_Path);
12434       Finds the shortest distance between nodes Nx and Ny on SO with a restriction on the number of nodes
12435       available for travel. In other terms, the search space is limited to a subset of the nodes forming SO.
12436       The subset of nodes is stored in isNodeInMesh. This subset is typically specified in SUMA_Surf_Plane_Intersect.
12437 
12438 
12439  Path = SUMA_Dijkstra ( SO, Nx,  Ny,
12440                         isNodeInMesh, N_isNodeInMesh, Method_Number,
12441                         Path_length, N_Path)
12442 \param SO (SUMA_SurfaceObject *) The surface Object structure.
12443             NodeList, EL and FN are needed.
12444 \param Nx (int) The node index (referring to SO's nodes) where the search begins.
12445 \param Ny (int) The node index (referring to SO's nodes) where the search ends.
12446 \param isNodeInMesh (SUMA_Boolean *)
12447          Pointer to SO->N_Node long vector such that
12448          if (isNodeInMesh[i]) then node i is part of the
12449          mesh that is used in the search path. This mesh is a subset
12450          of SO->FaceSetList and is typically obtained when one
12451          runs SUMA_Surf_Plane_Intersect. Running SUMA_Dijkstra on
12452          a complete surface is only for very patient people.
12453    NOTE:  This vector is modified as a node is visited. Make sure you
12454          do not use it after this function has been called.
12455          Set to NULL if you want all nodes used.
12456 \param N_isNodeInMesh (int *) Pointer to the total number of nodes that
12457                make up the mesh (subset of SO)
12458                This parameter is passed as a pointer because as nodes in the mesh
12459                are visited, that number is reduced and represents when the
12460                function returns, the number of nodes that were
12461                never visited in the search.
12462                Set to NULL, if isNodeInMesh is NULL
12463 \param Method_Number (int) selector for which algorithm to use. Choose from:
12464                      0 - Straight forward implementation, slow
12465                      1 - Variation to eliminate long searches for minimum of L, much much much faster than 0, 5 times more memory.
12466 \param Path_length (float *) The distance between Nx and Ny. This value is negative if no path between Nx and Ny was found.
12467 \param N_Path (int *) Number of nodes forming the Path vector
12468 
12469 \return Path (float) A vector of N_Path node indices forming the shortest path, from Nx (Path[0]) to Ny (Path[*N_Path - 1]).
12470                   NULL is returned in case of error.
12471 
12472 \sa Graph Theory by Ronald Gould and labbook NIH-2 page 154 for path construction
12473 */
12474 /* #define LOCALDEBUG */ /* lots of debugging info. */
SUMA_Dijkstra(SUMA_SurfaceObject * SO,int Nx,int Ny,SUMA_Boolean * isNodeInMeshp,int * N_isNodeInMesh,int Method_Number,float * Lfinal,int * N_Path)12475 int * SUMA_Dijkstra (SUMA_SurfaceObject *SO, int Nx,
12476                      int Ny, SUMA_Boolean *isNodeInMeshp,
12477                      int *N_isNodeInMesh, int Method_Number,
12478                      float *Lfinal, int *N_Path)
12479 {
12480    static char FuncName[] = {"SUMA_Dijkstra"};
12481    SUMA_Boolean LocalHead = NOPE;
12482    float *L = NULL, Lmin = -1.0, DT_DIJKSTRA;
12483    double le = 0.0, de=0.0;
12484    int i, iw, iv, v, w, N_Neighb, *Path = NULL, N_loc=-1;
12485    SUMA_Boolean *isNodeInMesh=NULL;
12486    struct  timeval  start_time;
12487    SUMA_DIJKSTRA_PATH_CHAIN *DC = NULL, *DCi=NULL, *DCp=NULL;
12488    SUMA_Boolean Found = NOPE;
12489    /* variables for method 2 */
12490    int N_Lmins, *vLmins=NULL, *vLocInLmins=NULL,
12491       iLmins, ReplacingNode, ReplacedNodeLocation;
12492    float *Lmins=NULL;
12493 
12494    SUMA_ENTRY;
12495 
12496    *Lfinal = -1.0;
12497    *N_Path = 0;
12498 
12499    if (!isNodeInMeshp) {
12500       if (!(isNodeInMesh = (SUMA_Boolean *)SUMA_malloc(  sizeof(SUMA_Boolean) *
12501                                                          SO->N_Node) )) {
12502 
12503          SUMA_S_Err("Failed to allocate");
12504          goto CLEANUP;
12505       }
12506       memset((void*)isNodeInMesh, 1,  sizeof(SUMA_Boolean) * SO->N_Node);
12507       N_isNodeInMesh = &N_loc;
12508       *N_isNodeInMesh = SO->N_Node;
12509    } else {
12510       isNodeInMesh = isNodeInMeshp;
12511    }
12512 
12513    /* make sure Both Nx and Ny exist in isNodeInMesh */
12514    if (  Nx < 0 || Nx >= SO->N_Node ||
12515          Ny < 0 || Ny >= SO->N_Node ) {
12516       fprintf (SUMA_STDERR,
12517                "\aError %s: Node %d (Nx) or %d (Ny) is outside range [0..%d[ \n"
12518                , FuncName, Nx, Ny, SO->N_Node);
12519       goto CLEANUP;
12520    }
12521    if (!isNodeInMesh[Nx]) {
12522       fprintf (SUMA_STDERR,
12523                "\aError %s: Node %d (Nx) is not in mesh.\n", FuncName, Nx);
12524       goto CLEANUP;
12525    }
12526    if (!isNodeInMesh[Ny]) {
12527       fprintf (SUMA_STDERR,
12528                "\aError %s: Node %d (Ny) is not in mesh.\n", FuncName, Ny);
12529       goto CLEANUP;
12530    }
12531 
12532    if (!SO->FN) {
12533       fprintf (SUMA_STDERR,
12534                "Error %s: SO does not have FN structure.\n", FuncName);
12535       goto CLEANUP;
12536    }
12537 
12538    if (LocalHead) {
12539       /* Start timer for next function */
12540       SUMA_etime(&start_time,0);
12541    }
12542 
12543    /* allocate for chain */
12544    DC = (SUMA_DIJKSTRA_PATH_CHAIN *)SUMA_malloc(
12545                      sizeof(SUMA_DIJKSTRA_PATH_CHAIN) * SO->N_Node);
12546    if (!DC) {
12547       fprintf (SUMA_STDERR, "Error %s: Could not allocate. \n", FuncName);
12548       goto CLEANUP;
12549    }
12550 
12551    switch (Method_Number) {
12552 
12553       case 0:  /* Method 0, Brute force */
12554          /* allocate for vertices labels */
12555          L = (float *) SUMA_calloc (SO->N_Node, sizeof (float));
12556          if (!L) {
12557             fprintf (SUMA_STDERR, "Error %s: Failed to allocate.\n", FuncName);
12558             goto CLEANUP;
12559          }
12560 
12561          /* label all vertices with very large numbers,
12562          initialize path previous pointers to null */
12563          for (i=0; i < SO->N_Node; ++i) {
12564             L[i] = LARGE_NUM;
12565             DC[i].Previous = NULL;
12566          }
12567          /* label starting vertex with 0 */
12568          L[Nx] = 0.0;
12569          Lmin = 0.0;
12570          v = Nx;
12571          *Lfinal = -1.0;
12572          /* initialize path at Nx */
12573          DC[Nx].Previous = NULL;
12574          DC[Nx].node = Nx;
12575          DC[Nx].le = 0.0;
12576          DC[Nx].order = 0;
12577          *N_Path = 0;
12578          /* Brute force method */
12579          do {
12580             /* find v in Mesh / L(v) is minimal */
12581             /* this sucks up a lot of time because it is searching
12582             the entire set of SO->N_Node instead of the one that was
12583             intersected only.
12584             This can be sped up, considerably */
12585             SUMA_MIN_LOC_VEC(L, SO->N_Node, Lmin, v);
12586                   /* locates and finds the minimum of L, nodes not in mesh will
12587                   keep their large values and will not be picked*/
12588             if (!isNodeInMesh[v]) {
12589                fprintf (SUMA_STDERR,
12590                         "\aERROR %s: Dijkstra derailed. v = %d, Lmin = %f.\n"
12591                         " Try another point.", FuncName, v, Lmin);
12592                goto CLEANUP;
12593             }
12594             if (v == Ny) {
12595                if (LocalHead) fprintf (SUMA_STDERR, "%s: Done.\n", FuncName);
12596                *Lfinal = L[v];
12597                Found = YUP;
12598             } else {
12599                N_Neighb = SO->FN->N_Neighb[v];
12600                for (i=0; i < N_Neighb; ++i) {
12601                   w = SO->FN->FirstNeighb[v][i];
12602                   if (isNodeInMesh[w]) {
12603                      iw = 3*w;
12604                      iv = 3*v;
12605                      de = (double)(SO->NodeList[iw] - SO->NodeList[iv]);
12606                      le = de*de;
12607                      de = (double)(SO->NodeList[iw+1] - SO->NodeList[iv+1]);
12608                      le += de*de;
12609                      de = (double)(SO->NodeList[iw+2] - SO->NodeList[iv+2]);
12610                      le += de*de;
12611                      le = sqrt ( le );
12612                      if (L[w] > L[v] + le ) {
12613                         L[w] = L[v] + le;
12614                         /* update the path */
12615                         DCp = &(DC[v]); /* previous path */
12616                         DC[w].Previous = (void *) DCp;
12617                         DC[w].le = le;
12618                         DC[w].node = w;
12619                         DC[w].order = DCp->order + 1;
12620                      }
12621                   }
12622                }
12623 
12624                /* remove node v from isNodeInMesh and reset their distance
12625                   value to a very large one,
12626                   this way you do not have to reinitialize this variable. */
12627                isNodeInMesh[v] = NOPE;
12628                *N_isNodeInMesh -= 1;
12629                L[v] = LARGE_NUM;
12630                Found = NOPE;
12631             }
12632          } while (*N_isNodeInMesh > 0 && !Found);
12633 
12634          if (!Found) {
12635             fprintf (SUMA_STDERR,
12636                      "Error %s: No more nodes in mesh, "
12637                      "failed to reach target.\n", FuncName);
12638             goto CLEANUP;
12639          }else {
12640             if (LocalHead)
12641                fprintf (SUMA_STDERR,
12642                         "%s: Path between Nodes %d and %d is %f.\n",
12643                         FuncName, Nx, Ny, *Lfinal);
12644          }
12645 
12646 
12647          if (LocalHead) {
12648             /* stop timer */
12649             DT_DIJKSTRA = SUMA_etime(&start_time,1);
12650             fprintf (SUMA_STDERR,
12651                      "%s: Method 1- Elapsed time in function %f seconds.\n",
12652                      FuncName, DT_DIJKSTRA);
12653          }
12654 
12655          if (L) SUMA_free(L); L = NULL;
12656          break;
12657 
12658       case 1:  /********* Method 1- faster minimum searching *******************/
12659          if (LocalHead) {
12660             /* Start timer for next function */
12661             SUMA_etime(&start_time,0);
12662          }
12663 
12664          /* allocate for vertices labels and minimums vectors*/
12665          L = (float *) SUMA_calloc (SO->N_Node, sizeof (float));
12666                /* L[i] = distance to a node i*/
12667          Lmins = (float *) SUMA_calloc (SO->N_Node, sizeof (float));
12668                /* Lmins = vector of minimum calculated distances to node */
12669          vLmins = (int *) SUMA_calloc (SO->N_Node, sizeof (int));
12670                /* vLmins[i] = index (into L) of the node having a
12671                   distance Lmins[i] */
12672          vLocInLmins = (int *) SUMA_calloc (SO->N_Node, sizeof (int));
12673                /* vLocInLmin[j] = index (into Lmins) of a node having
12674                   index j (into L) */
12675 
12676          if (!L || !Lmins || !vLmins || !vLocInLmins) {
12677             fprintf (SUMA_STDERR, "Error %s: Failed to allocate.\n", FuncName);
12678             goto CLEANUP;
12679          }
12680 
12681          /* label all vertices with very large numbers and initialize
12682             vLocInLmins to -1*/
12683          for (i=0; i < SO->N_Node; ++i) {
12684             L[i] = LARGE_NUM;
12685             Lmins[i] = LARGE_NUM;
12686             vLocInLmins[i] = -1;
12687             DC[i].Previous = NULL;
12688          }
12689 
12690          /* label starting vertex with 0 */
12691          L[Nx] = 0.0;
12692          *Lfinal = -1.0;
12693 
12694          /* initialize values of vectors used to keep track of minimum
12695             values of L and their corresponding nodes */
12696          Lmins[0] = 0.0;
12697          vLmins[0] = Nx;
12698          vLocInLmins[Nx] = 0;
12699          N_Lmins = 1;
12700 
12701          /* initialize path at Nx */
12702          DC[Nx].Previous = NULL;
12703          DC[Nx].node = Nx;
12704          DC[Nx].le = 0.0;
12705          DC[Nx].order = 0;
12706          *N_Path = 0;
12707 
12708          /* method with efficient tracking of minimum */
12709          if (LocalHead)
12710             fprintf (SUMA_STDERR,
12711                      "%s: about to MIN_LOC ....N_isNodeInMesh = %d\n",
12712                      FuncName, *N_isNodeInMesh);
12713          do {
12714             /* find v in Mesh / L(v) is minimal */
12715             SUMA_MIN_LOC_VEC(Lmins, N_Lmins, Lmin, iLmins);
12716                /* locates the minimum value in Lmins vector */
12717             v = vLmins[iLmins];   /* get the node for this Lmin value */
12718             if (!isNodeInMesh[v]) {
12719                fprintf (SUMA_STDERR,"\aERROR %s: \n"
12720                                     "Dijkstra derailed. v = %d, Lmin = %f\n."
12721                                     "Try another point.", FuncName, v, Lmin);
12722                goto CLEANUP;
12723             }
12724             #ifdef LOCALDEBUG
12725                fprintf (SUMA_STDERR, "%s: Node v = %d.\n", FuncName, v);
12726             #endif
12727             if (v == Ny) {
12728                if (LocalHead) fprintf (SUMA_STDERR, "%s: Done.\n", FuncName);
12729                *Lfinal = L[v];
12730                Found = YUP;
12731             } else {
12732                N_Neighb = SO->FN->N_Neighb[v];
12733                for (i=0; i < N_Neighb; ++i) {
12734                   w = SO->FN->FirstNeighb[v][i];
12735                   if (isNodeInMesh[w]) {
12736                      iw = 3*w;
12737                      iv = 3*v;
12738                      de = (double)(SO->NodeList[iw] - SO->NodeList[iv]);
12739                      le = de*de;
12740                      de = (double)(SO->NodeList[iw+1] - SO->NodeList[iv+1]);
12741                      le += de*de;
12742                      de = (double)(SO->NodeList[iw+2] - SO->NodeList[iv+2]);
12743                      le += de*de;
12744                      le = sqrt ( le );
12745                      if (L[w] > L[v] + le ) {
12746                         #ifdef LOCALDEBUG
12747                            fprintf (SUMA_STDERR,
12748                                     "%s: L[%d]=%f > L[%d] = %f + le = %f.\n",
12749                                     FuncName, w, L[w], v, L[v], le);
12750                         #endif
12751                         L[w] = L[v] + le;
12752                         /* update the path */
12753                         DCp = &(DC[v]); /* previous path */
12754                         DC[w].Previous = (void *) DCp;
12755                         DC[w].le = le;
12756                         DC[w].node = w;
12757                         DC[w].order = DCp->order + 1;
12758 
12759                         if (vLocInLmins[w] < 0) {
12760                            #ifdef LOCALDEBUG
12761                               fprintf (SUMA_STDERR,
12762                                  "%s: adding entry for w = %d - First Hit. \n",
12763                                  FuncName, w);
12764                            #endif
12765                            Lmins[N_Lmins] = L[w];
12766                                  /* add this value to Lmins vector */
12767                            vLmins[N_Lmins] = w;
12768                                  /* store the node for this Lmins value */
12769                            vLocInLmins[w] = N_Lmins;
12770                                  /* store where that node is represented
12771                                     in Lmins */
12772                            ++N_Lmins;  /* increment N_Lmins */
12773                         } else {
12774                            #ifdef LOCALDEBUG
12775                               fprintf (SUMA_STDERR,
12776                                  "%s: modifying entry for w = %d  Second Hit.\n",                                  FuncName, w);
12777                            #endif
12778                            Lmins[vLocInLmins[w]] = L[w];
12779                                  /* update value for Lmins */
12780                         }
12781                      }else {
12782                         #ifdef LOCALDEBUG
12783                            fprintf (SUMA_STDERR,
12784                                     "%s: L[%d]=%f < L[%d] = %f + le = %f.\n",
12785                                     FuncName, w, L[w], v, L[v], le);
12786                         #endif
12787                      }
12788                   }
12789                }
12790 
12791                /* remove node v from isNodeInMesh and reset their distance
12792                   value to a very large one,
12793                   this way you do not have to reinitialize this variable. */
12794                isNodeInMesh[v] = NOPE;
12795                *N_isNodeInMesh -= 1;
12796                L[v] = LARGE_NUM;
12797                Found = NOPE;
12798 
12799                /* also remove the values (by swapping it with last element)
12800                   for this node from Lmins */
12801                #ifdef LOCALDEBUG
12802                   {
12803                      int kkk;
12804                      fprintf (SUMA_STDERR,"Lmins\tvLmins\tvLocInLmins\n");
12805                      for (kkk=0; kkk < N_Lmins; ++kkk)
12806                         fprintf (SUMA_STDERR,"%f\t%d\t%d\n",
12807                                              Lmins[kkk], vLmins[kkk],
12808                                              vLocInLmins[vLmins[kkk]] );
12809 
12810                   }
12811                #endif
12812 
12813                if (vLocInLmins[v] >= 0) { /* remove its entry if there is one */
12814                   #ifdef LOCALDEBUG
12815                      fprintf (SUMA_STDERR,
12816                               "%s: removing node v = %d. N_Lmins = %d\n",
12817                               FuncName,  v, N_Lmins);
12818                   #endif
12819                   --N_Lmins;
12820                   ReplacingNode = vLmins[N_Lmins];
12821                   ReplacedNodeLocation = vLocInLmins[v];
12822                   Lmins[vLocInLmins[v]] = Lmins[N_Lmins];
12823                   vLmins[vLocInLmins[v]] = vLmins[N_Lmins];
12824                   vLocInLmins[ReplacingNode] = ReplacedNodeLocation;
12825                   vLocInLmins[v] = -1;
12826                   Lmins[N_Lmins] = LARGE_NUM;
12827                }
12828             }
12829          } while (*N_isNodeInMesh > 0 && !Found);
12830 
12831          if (!Found) {
12832             fprintf (SUMA_STDERR,
12833                      "Error %s: No more nodes in mesh, "
12834                      "failed to reach target %d. NLmins = %d\n",
12835                      FuncName, Ny, N_Lmins);
12836             goto CLEANUP;
12837          }else {
12838             if (LocalHead)
12839                fprintf (SUMA_STDERR,
12840                         "%s: Path between Nodes %d and %d is %f.\n",
12841                         FuncName, Nx, Ny, *Lfinal);
12842          }
12843 
12844 
12845          if (LocalHead) {
12846             /* stop timer */
12847             DT_DIJKSTRA = SUMA_etime(&start_time,1);
12848             fprintf (SUMA_STDERR,
12849                      "%s: Method 2- Elapsed time in function %f seconds.\n",
12850                      FuncName, DT_DIJKSTRA);
12851          }
12852 
12853          if (L) SUMA_free(L); L = NULL;
12854          if (Lmins) SUMA_free(Lmins); Lmins = NULL;
12855          if (vLmins) SUMA_free(vLmins); vLmins = NULL;
12856          if (vLocInLmins) SUMA_free(vLocInLmins); vLocInLmins = NULL;
12857          break;   /********** Method 1- faster minimum searching **************/
12858       default:
12859          fprintf (SUMA_STDERR,
12860                   "Error %s: No such method (%d).\n",
12861                   FuncName, Method_Number);
12862          goto CLEANUP;
12863          break;
12864    }
12865 
12866    /* now reconstruct the path */
12867    *N_Path = DC[Ny].order+1;
12868    Path = (int *) SUMA_calloc (*N_Path, sizeof(int));
12869    if (!Path) {
12870       fprintf (SUMA_STDERR, "Error %s: Failed to allocate.\n", FuncName);
12871       goto CLEANUP;
12872    }
12873 
12874    DCi = &(DC[Ny]);
12875    iv = *N_Path - 1;
12876    Path[iv] = Ny;
12877    if (iv > 0) {
12878       do {
12879          --iv;
12880          DCp = (SUMA_DIJKSTRA_PATH_CHAIN *) DCi->Previous;
12881          Path[iv] = DCp->node;
12882          DCi = DCp;
12883       } while (DCi->Previous);
12884    }
12885 
12886    if (iv != 0) {
12887       fprintf (SUMA_STDERR,
12888                "Error %s: iv = %d. This should not be.\n",
12889                FuncName, iv);
12890    }
12891 
12892    CLEANUP:
12893       if (L) SUMA_free(L);
12894       if (Lmins) SUMA_free(Lmins);
12895       if (vLmins)  SUMA_free(vLmins);
12896       if (vLocInLmins)   SUMA_free(vLocInLmins);
12897       if (DC) SUMA_free(DC);
12898       if (!isNodeInMeshp) SUMA_free(isNodeInMesh);
12899 
12900    SUMA_RETURN (Path);
12901 }
12902 
12903 /* A version with the same call format of SUMA_Dijkstra
12904 but uses SUMA_Dijkstra_generic instead
12905 You can easily compare SUMA_Dijkstra_usegen to
12906                        SUMA_Dijkstra by replacing
12907       SUMA_Dijkstra in SurfDist and rerunning the examples.
12908 */
SUMA_Dijkstra_usegen(SUMA_SurfaceObject * SO,int Nx,int Ny,SUMA_Boolean * isNodeInMeshp,int * N_isNodeInMesh,int Method_Number,float * Lfinal,int * N_Path)12909 int * SUMA_Dijkstra_usegen (SUMA_SurfaceObject *SO, int Nx,
12910                      int Ny, SUMA_Boolean *isNodeInMeshp,
12911                      int *N_isNodeInMesh, int Method_Number,
12912                      float *Lfinal, int *N_Path)
12913 {
12914    return(SUMA_Dijkstra_generic (SO->N_Node,
12915                      SO->NodeList, SO->NodeDim, 0,
12916                      SO->FN->N_Neighb, SO->FN->FirstNeighb, NULL,
12917                      Nx, Ny,
12918                      isNodeInMeshp,
12919                      N_isNodeInMesh, Method_Number,
12920                      Lfinal, N_Path, 1));
12921 }
12922 
12923 
12924 /*!
12925 \brief Converts a path formed by a series of connected nodes to a series of edges
12926    ePath = SUMA_NodePath_to_EdgePath (EL, Path, N_Path, N_Edge);
12927    \param EL (SUMA_EDGE_LIST *) Pointer to edge list structure
12928    \param Path (int *) vector of node indices forming a path.
12929                        Sequential nodes in Path must be connected on the surface mesh.
12930    \param N_Path (int) number of nodes in the path
12931    \param N_Edge (int *) pointer to integer that will contain the number of edges in the path
12932                         usually equal to N_Path if path is a loop (closed) or N_Path - 1.
12933                         0 if function fails.
12934    \param ePath (int *) pointer to vector containing indices of edges forming the path.
12935                         The indices are into EL->EL and represent the first occurence of the
12936                         edge between Path[i] and Path[i+1].
12937                         NULL if trouble is encountered.
12938 
12939    \sa SUMA_NodePath_to_EdgePath_Inters
12940    \sa Path S in labbook NIH-2 page 153
12941 */
SUMA_NodePath_to_EdgePath(SUMA_EDGE_LIST * EL,int * Path,int N_Path,int * N_Edge)12942 int *SUMA_NodePath_to_EdgePath (SUMA_EDGE_LIST *EL, int *Path, int N_Path,
12943                                 int *N_Edge)
12944 {
12945    static char FuncName[]={"SUMA_NodePath_to_EdgePath"};
12946    int *ePath = NULL, i, i0;
12947    SUMA_Boolean LocalHead = NOPE;
12948 
12949    SUMA_ENTRY;
12950 
12951 
12952   *N_Edge = 0;
12953    ePath = (int *) SUMA_calloc(N_Path, sizeof(int));
12954    if (!ePath) {
12955       fprintf (SUMA_STDERR, "Error %s: Failed to allocate.\n", FuncName);
12956       SUMA_RETURN (NULL);
12957    }
12958 
12959    for (i=1; i<N_Path; ++i) {
12960       i0 = Path[i-1];
12961       /* find the location of the edge between i0 and i1 */
12962       ePath[i-1] = SUMA_FindEdge (EL, i0, Path[i]);
12963       if (ePath[i-1] < 0) { /* error */
12964          fprintf (SUMA_STDERR, "Error %s: Failed in SUMA_FindEdge.\n", FuncName);
12965          SUMA_free(ePath);
12966          *N_Edge = 0;
12967          SUMA_RETURN (NULL);
12968       }else {
12969          ++(*N_Edge);
12970       }
12971    }
12972 
12973    SUMA_RETURN (ePath);
12974 }
12975 
12976 /*!
12977 \brief determines whether to edges are identical or not. Recall
12978 that an edge can be represented multiple times in SO->EL, once for
12979 each triangle that uses it. Two edges are the same if and only if
12980 EL->EL[E1][0] == EL->EL[E2][0] && EL->EL[E1][1] == EL->EL[E2][1]
12981 
12982 ans = SUMA_isSameEdge ( EL, E1, E2);
12983 
12984 \param EL (SUMA_EDGE_LIST *) Edge List structure.
12985 \param E1 (int) edge index
12986 \param E2 (int) edge index
12987 \return ans (SUMA_Boolean) YUP/NOPE
12988 */
12989 
SUMA_isSameEdge(SUMA_EDGE_LIST * EL,int E1,int E2)12990 SUMA_Boolean SUMA_isSameEdge (SUMA_EDGE_LIST *EL, int E1, int E2)
12991 {
12992    static char FuncName[]={"SUMA_isSameEdge"};
12993 
12994    SUMA_ENTRY;
12995 
12996    if (EL->EL[E1][0] == EL->EL[E2][0] && EL->EL[E1][1] == EL->EL[E2][1]) {
12997       SUMA_RETURN (YUP);
12998    } else {
12999       SUMA_RETURN (NOPE);
13000    }
13001 
13002 }
13003 
13004 /*!
13005 \brief This function determines the strip of triangles necessary to go from one node to another
13006 along intersected edges.
13007 tPath = SUMA_IntersectionStrip (SO, SPI,
13008             int *nPath, int N_nPath, float *dinters, float dmax, int *N_tPath)
13009 \param SO (SUMA_SurfaceObject *) pointer to Surface Object structure
13010 \param SPI (SUMA_SURF_PLANE_INTERSECT *) pointer surface/plane intersection structure
13011 \param nPath (int *) series of nodes forming the Dijkstra path between nodes Nx (nPath[0] and NynPath[N_nPath-1])
13012 \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
13013 \param dinters (float *) pointer sum of distances between intersection points on intersected edges for all triangles in tPath.
13014                This distance is a better approximation for the distance along the cortical surface than the distance obtained
13015                along the shortest path.
13016 \param dmax (float) distance beyond which to quit searching. Usually this distance is slightly larger than the distance
13017                   along the path returned by SUMA_Dijkstra but dinters should always be less than the distance along the shortest path.
13018 \param N_tPath (int *) pointer to the number of triangle indices in tPath
13019 \return tPath (int*) pointer to vector containing the indices of triangles travelled from
13020          nPath[0] to nPath[N_nPath] (or vice versa if nPath[0] = SO->N_Node-1).
13021 
13022 NOTE: Although the number of elements in tPath may be small, the number of elements allocated for is SO->N_FaceSet
13023 Make sure you free tPath when you are done with it.
13024 
13025 NOTE: This function can be used to create a node path formed by intersection points along edges but that is not implemented yet.
13026 
13027 \sa SUMA_Surf_Plane_Intersect
13028 \sa SUMA_Dijkstra
13029 \sa SUMA_FromIntEdgeToIntEdge
13030 
13031 */
SUMA_IntersectionStrip(SUMA_SurfaceObject * SO,SUMA_SURF_PLANE_INTERSECT * SPI,int * nPath,int N_nPath,float * dinters,float dmax,int * N_tPath)13032 int * SUMA_IntersectionStrip (SUMA_SurfaceObject *SO,
13033             SUMA_SURF_PLANE_INTERSECT *SPI,
13034             int *nPath, int N_nPath, float *dinters, float dmax, int *N_tPath)
13035 {
13036    static char FuncName[]={"SUMA_IntersectionStrip"};
13037    int *tPath1 = NULL, *tPath2 = NULL, Incident[50], N_Incident, Nx = -1,
13038       Ny = -1, Tri = -1, Tri1 = -1, istart, n2 = -1, n3 = -1, E1, E2, cnt,
13039       N_tPath1, N_tPath2;
13040    float d1, d2;
13041    SUMA_Boolean *Visited = NULL, Found, LocalHead = NOPE;
13042 
13043    SUMA_ENTRY;
13044 
13045    /* find the edge containing the 1st 2 nodes of the Dijkstra path */
13046    /* find the triangle that contains the edge formed by the 1st 2 nodes of
13047       the Dijkstra path and is intersected by the plane*/
13048    Tri1 = -1;
13049    if (LocalHead) {
13050       fprintf (SUMA_STDERR,
13051                "%s: Looking for a triangle containing nodes [%d %d].\n",
13052                FuncName, nPath[0], nPath[1]);
13053    }
13054 
13055    Found = SUMA_Get_Incident(nPath[0], nPath[1],
13056                              SO->EL, Incident, &N_Incident, 1, 0);
13057    if (!Found) {
13058       /* no such triangle, get a triangle that contains
13059          nPath[0] and is intersected */
13060       fprintf (SUMA_STDERR, "%s: No triangle contains nodes [%d %d].\n",
13061                FuncName, nPath[0], nPath[1]);
13062       if (nPath[0] == SO->N_Node - 1) {
13063          fprintf (SUMA_STDERR,
13064                   "Warning %s: 1st node is last node of surface, "
13065                   "traversing path backwards.\n", FuncName);
13066          Nx = nPath[N_nPath - 1];
13067          Ny = nPath[0];
13068       }else {
13069          Nx = nPath[0];
13070          Ny = nPath[N_nPath - 1];
13071       }
13072       istart = SO->EL->ELloc[Nx];
13073       /* find an edge containing the first node and
13074          belonging to an intersected triangle */
13075       Found = NOPE;
13076       while (SO->EL->EL[istart][0] == Nx && !Found) {
13077          Tri = SO->EL->ELps[istart][1];
13078          if (SPI->isTriHit[Tri]) {
13079             Found = YUP;
13080             Tri1 = Tri;
13081          }
13082          ++istart;
13083       }
13084    }else {
13085       Nx = nPath[0];
13086       Ny = nPath[N_nPath - 1];
13087 
13088       /* find which of these triangles was intersected */
13089       if (LocalHead) {
13090          fprintf (SUMA_STDERR,
13091                   "%s: Found %d triangles containing nodes [%d %d].\n",
13092                   FuncName, N_Incident, nPath[0], nPath[1]);
13093          for (cnt = 0; cnt < N_Incident; ++cnt)
13094             fprintf (SUMA_STDERR, "%d isHit %d\n",
13095                      Incident[cnt], SPI->isTriHit[Incident[cnt]]);
13096          fprintf (SUMA_STDERR, "\n");
13097       }
13098       Found = NOPE;
13099       cnt = 0;
13100       while (cnt < N_Incident && !Found) {
13101          if (SPI->isTriHit[Incident[cnt]]) {
13102             Found = YUP;
13103             Tri1 = Incident[cnt];
13104          }
13105          ++cnt;
13106       }
13107    }
13108 
13109    if (!Found) {
13110       fprintf (SUMA_STDERR,
13111                "Error %s: Starting Edge could not be found.\n", FuncName);
13112       SUMA_RETURN (NULL);
13113    }else if (LocalHead) {
13114       fprintf (SUMA_STDERR, "%s: Starting with triangle %d.\n", FuncName, Tri1);
13115    }
13116 
13117    /* found starting triangle edge, begin with side 1 */
13118    if (SO->FaceSetList[3*Tri1] == Nx) {
13119       n2 = SO->FaceSetList[3*Tri1+1];
13120       n3 = SO->FaceSetList[3*Tri1+2];
13121    } else if (SO->FaceSetList[3*Tri1+1] == Nx) {
13122       n2 = SO->FaceSetList[3*Tri1];
13123       n3 = SO->FaceSetList[3*Tri1+2];
13124    } else if (SO->FaceSetList[3*Tri1+2] == Nx) {
13125       n2 = SO->FaceSetList[3*Tri1];
13126       n3 = SO->FaceSetList[3*Tri1+1];
13127    } else {
13128       fprintf (SUMA_STDERR,
13129                "Error %s: Triangle %d does not contain Nx %d.\n",
13130                FuncName, Tri1, Nx);
13131       SUMA_RETURN (NULL);
13132    }
13133 
13134 
13135 
13136    E1 = SUMA_FindEdgeInTri (SO->EL, Nx, n2, Tri1);
13137    if (!SPI->isEdgeInters[E1]) {
13138       E1 = SUMA_FindEdgeInTri (SO->EL, Nx, n3, Tri1);
13139    }
13140    /* now choose E2 such that E2 is also intersected */
13141    if (!SUMA_isSameEdge (SO->EL, SO->EL->Tri_limb[Tri1][0], E1) &&
13142                      SPI->isEdgeInters[SO->EL->Tri_limb[Tri1][0]]) {
13143       E2 = SO->EL->Tri_limb[Tri1][0];
13144    }else if (!SUMA_isSameEdge (SO->EL, SO->EL->Tri_limb[Tri1][1], E1) &&
13145                      SPI->isEdgeInters[SO->EL->Tri_limb[Tri1][1]]) {
13146       E2 = SO->EL->Tri_limb[Tri1][1];
13147    }else if (!SUMA_isSameEdge (SO->EL, SO->EL->Tri_limb[Tri1][2], E1) &&
13148                      SPI->isEdgeInters[SO->EL->Tri_limb[Tri1][2]]) {
13149       E2 = SO->EL->Tri_limb[Tri1][2];
13150    }else {
13151       fprintf (SUMA_STDERR,"Error %s: No E2 found.\n", FuncName);
13152       SUMA_RETURN (NULL);
13153    }
13154 
13155    Visited = (SUMA_Boolean *) SUMA_calloc (SO->N_FaceSet, sizeof(SUMA_Boolean));
13156    tPath1 = (int *) SUMA_calloc (SO->N_FaceSet, sizeof(int));
13157    if (!Visited || !tPath1) {
13158       fprintf (SUMA_STDERR, "Error %s: Failed to allocate.\n", FuncName);
13159       if (Visited) SUMA_free(Visited);
13160       if (tPath2) SUMA_free(tPath1);
13161       SUMA_RETURN (NULL);
13162    }
13163 
13164    N_tPath1 = 0;
13165    if (!SUMA_FromIntEdgeToIntEdge ( Tri1, E1, E2, SO->EL, SPI, Ny,
13166                                     Visited, &d1, dmax, tPath1, &N_tPath1)) {
13167       fprintf (SUMA_STDERR,
13168                "Error %s: Failed in SUMA_FromIntEdgeToIntEdge.\n", FuncName);
13169       if (Visited) SUMA_free(Visited);
13170       if (tPath2) SUMA_free(tPath1);
13171       SUMA_RETURN (NULL);
13172    }
13173 
13174    if (LocalHead) {
13175       fprintf (SUMA_STDERR, "%s: Found a distance of %f.\n\n\n", FuncName, d1);
13176    }
13177 
13178    /* Now try going in the other direction, E2->E1 */
13179    cnt = E2;
13180    E2 = E1;
13181    E1 = cnt;
13182 
13183    /* reset the values of Visited */
13184    for (cnt=0; cnt < SO->N_FaceSet; ++cnt) if (Visited[cnt]) Visited[cnt] = NOPE;
13185 
13186    tPath2 = (int *) SUMA_calloc (SO->N_FaceSet, sizeof(int));
13187    if (!Visited || !tPath2) {
13188       fprintf (SUMA_STDERR, "Error %s: Failed to allocate.\n", FuncName);
13189       if (Visited) SUMA_free(Visited);
13190       if (tPath1) SUMA_free(tPath1);
13191       if (tPath2) SUMA_free(tPath2);
13192       SUMA_RETURN (NULL);
13193    }
13194 
13195    N_tPath2 = 0;
13196    if (!SUMA_FromIntEdgeToIntEdge ( Tri1, E1, E2, SO->EL, SPI, Ny,
13197                                     Visited, &d2, dmax, tPath2, &N_tPath2)) {
13198       fprintf (SUMA_STDERR,
13199                "Error %s: Failed in SUMA_FromIntEdgeToIntEdge.\n", FuncName);
13200       if (Visited) SUMA_free(Visited);
13201       if (tPath1) SUMA_free(tPath1);
13202       if (tPath2) SUMA_free(tPath2);
13203       SUMA_RETURN (NULL);
13204    }
13205 
13206    if (Visited) SUMA_free(Visited);
13207 
13208    if (LocalHead) {
13209       fprintf (SUMA_STDERR, "%s: Found a distance of %f.\n", FuncName, d2);
13210    }
13211 
13212    if (d2 < d1) {
13213       *N_tPath = N_tPath2;
13214       *dinters = d2;
13215       if (tPath1) SUMA_free(tPath1);
13216       SUMA_RETURN (tPath2);
13217    } else {
13218       *dinters = d1;
13219       *N_tPath = N_tPath1;
13220       if (tPath2) SUMA_free(tPath2);
13221       SUMA_RETURN (tPath1);
13222    }
13223 
13224 }
13225 
13226 /*!
13227 \brief This function moves from one intersected edge to the next until a certain node is encountered or a
13228 a certain distance is exceeded. By intersected edge, I mean an edge of the surface's mesh that was
13229 intersected by a plane.
13230 
13231 ans = SUMA_FromIntEdgeToIntEdge (int Tri, int E1, int E2, SUMA_EDGE_LIST *EL, SPI, int Ny,
13232          Visited, float *d, float dmax, int *tPath, int *N_tPath);
13233 
13234 \param Tri (int) index of triangle to start with (index into SO->FaceSetList)
13235 \param E1 (int) index of edge in Tri to start from
13236 \param E2 (int) index of edge in Tri to move in the direction of (Both E1 and E2  must be intersected edges)
13237 \param EL (SUMA_EDGE_LIST *) pointer to the edge list structure, typically SO->EL
13238 \param SPI (SUMA_SURF_PLANE_INTERSECT *) pointer to structure containing intersection of plane with surface
13239 \param Ny (int) node index to stop at (index into SO->NodeList)
13240 \param Visited (SUMA_Boolean *) pointer to vector (SO->N_FaceSet elements) that keeps track of triangles visited.
13241       This vector should be all NOPE when you first call this function.
13242 \param d (float *) pointer to total distance from first intersected edge E1 to the last edge that contains E2
13243 \param dmax (float) maximum distance to go for before reversing and going in the other direction. Typically
13244          this measure should be a bit larger than the distance of a Dijkstra path although you should never get a
13245          distance that is larger than the Dijkstra path.
13246 \param tPath (int *) vector of indices of triangles visited from first edge to last edge (make sure you allocate a bundle for tPath)
13247 \param N_tPath (int *) number of elements in tPath (make sure you initialize this one to zero before you call this function )
13248 \return ans (SUMA_Boolean) YUP/NOPE, for success/failure.
13249 
13250          NOTE: This function is recursive.
13251 
13252 \sa SUMA_Surf_Plane_Intersect
13253 \sa SUMA_IntersectionStrip
13254 
13255 */
SUMA_FromIntEdgeToIntEdge(int Tri,int E1,int E2,SUMA_EDGE_LIST * EL,SUMA_SURF_PLANE_INTERSECT * SPI,int Ny,SUMA_Boolean * Visited,float * d,float dmax,int * tPath,int * N_tPath)13256 SUMA_Boolean SUMA_FromIntEdgeToIntEdge (int Tri, int E1, int E2, SUMA_EDGE_LIST *EL, SUMA_SURF_PLANE_INTERSECT *SPI, int Ny,
13257          SUMA_Boolean *Visited, float *d, float dmax, int *tPath, int *N_tPath)
13258 {  static char FuncName[]={"SUMA_FromIntEdgeToIntEdge"};
13259    int Tri2 = 0, cnt, Incident[5], N_Incident;
13260    float dx, dy, dz;
13261    SUMA_Boolean Found, LocalHead = NOPE;
13262 
13263    SUMA_ENTRY;
13264 
13265    if (Tri < 0 || E1 < 0 || E2 < 0) {
13266       fprintf (SUMA_STDERR, "Error %s: Tri (%d) or E1 (%d) or E2 (%d) is negative!\n", FuncName, Tri, E1, E2);
13267       SUMA_RETURN (NOPE);
13268    }
13269 
13270 
13271    dx = (SPI->IntersNodes[3*E2] - SPI->IntersNodes[3*E1]);
13272    dy = (SPI->IntersNodes[3*E2+1] - SPI->IntersNodes[3*E1+1]);
13273    dz = (SPI->IntersNodes[3*E2+2] - SPI->IntersNodes[3*E1+2]);
13274    if (LocalHead) {
13275       fprintf (SUMA_STDERR, "%s: Entered - Tri %d, E1 %d [%d %d], E2 %d [%d %d]\n\tdx = %f dy = %f dz = %f\n",
13276          FuncName, Tri, E1, EL->EL[E1][0], EL->EL[E1][1], E2, EL->EL[E2][0], EL->EL[E2][1], dx, dy, dz);
13277    }
13278    *d += sqrt( dx * dx + dy * dy + dz * dz);
13279 
13280    if (*d > dmax) {
13281       /* path already longer than Dijkstra path, no need to search further in this direction, get out with this d value */
13282       fprintf (SUMA_STDERR, "%s: Path longer than dmax. Returning.\n", FuncName);
13283       SUMA_RETURN (YUP);
13284    }
13285 
13286    if (EL->EL[E2][0] == Ny || EL->EL[E2][1] == Ny) {
13287       fprintf (SUMA_STDERR, "%s: Found Ny, d = %f\n", FuncName, *d);
13288       if (!Visited[Tri]) {
13289          /* add triangle to path */
13290          tPath[*N_tPath] = Tri;
13291          ++*N_tPath;
13292       }
13293       SUMA_RETURN (YUP);
13294    } else if (Visited[Tri]) {
13295       fprintf (SUMA_STDERR, "Error %s: Triangle %d already visited.\n",FuncName, Tri);
13296       SUMA_RETURN (NOPE);
13297    }
13298 
13299    /* mark triangle as visited */
13300    if (LocalHead) fprintf (SUMA_STDERR, "%s: Marking triangle %d and adding %dth element to tPath.\n", FuncName, Tri, *N_tPath);
13301    Visited[Tri] = YUP;
13302 
13303    /* add triangle to path */
13304    tPath[*N_tPath] = Tri;
13305    ++*N_tPath;
13306 
13307    /* now get the second intersected triangle, incident to E2 */
13308    if (LocalHead) fprintf (SUMA_STDERR, "%s: Searching for triangles incident to E2 %d.\n", FuncName, E2);
13309    if (!SUMA_Get_Incident(EL->EL[E2][0], EL->EL[E2][1], EL, Incident, &N_Incident, 1, 0)) {
13310       fprintf (SUMA_STDERR,"Error %s: Failed to get Incident triangles.\n", FuncName);
13311       SUMA_RETURN (NOPE);
13312    }
13313 
13314    /* find Tri2 such that Tri2 != Tri and Tri2 is an intersected triangle */
13315    cnt = 0;
13316    Found = NOPE;
13317    while (cnt < N_Incident && !Found) {
13318       if (SPI->isTriHit[Incident[cnt]] && Incident[cnt] != Tri && !Visited[Incident[cnt]]) {
13319          Found = YUP;
13320          Tri2 = Incident[cnt];
13321       }
13322       ++cnt;
13323    }
13324 
13325    if (!Found) {
13326       fprintf (SUMA_STDERR,"Error %s: Could not find next triangle.\n", FuncName);
13327       SUMA_RETURN (NOPE);
13328    }
13329 
13330    Tri = Tri2;
13331    E1 = E2;
13332 
13333    /* now find the new E2 */
13334    if (LocalHead) fprintf (SUMA_STDERR, "%s: Finding new E2.\n", FuncName);
13335 
13336    if (!SUMA_isSameEdge (EL, EL->Tri_limb[Tri][0], E1) && SPI->isEdgeInters[EL->Tri_limb[Tri][0]]) {
13337       E2 = EL->Tri_limb[Tri][0];
13338    }else if (!SUMA_isSameEdge (EL, EL->Tri_limb[Tri][1], E1) && SPI->isEdgeInters[EL->Tri_limb[Tri][1]]) {
13339       E2 = EL->Tri_limb[Tri][1];
13340    }else if (!SUMA_isSameEdge (EL, EL->Tri_limb[Tri][2], E1) && SPI->isEdgeInters[EL->Tri_limb[Tri][2]]) {
13341       E2 = EL->Tri_limb[Tri][2];
13342    }else {
13343       fprintf (SUMA_STDERR,"Error %s: No E2 found.\n", FuncName);
13344       SUMA_RETURN (NOPE);
13345    }
13346 
13347    /* call the same function again */
13348    if (!SUMA_FromIntEdgeToIntEdge (Tri, E1, E2, EL, SPI, Ny, Visited, d, dmax, tPath, N_tPath)) {
13349       fprintf (SUMA_STDERR,"Error %s: Failed in SUMA_FromIntEdgeToIntEdge.\n", FuncName);
13350       SUMA_RETURN (NOPE);
13351    }
13352 
13353    SUMA_RETURN (YUP);
13354 }
13355 
13356 
SUMA_alloc_strip(char * parent_ID)13357 SUMA_STRIP *SUMA_alloc_strip (char *parent_ID)
13358 {
13359    static char FuncName[]={"SUMA_alloc_strip"};
13360    SUMA_STRIP *strp=NULL;
13361 
13362    SUMA_ENTRY;
13363 
13364    strp = (SUMA_STRIP *)SUMA_malloc(sizeof(SUMA_STRIP));
13365    strp->Edges = (DList *)SUMA_malloc(sizeof(DList));
13366    dlist_init(strp->Edges, NULL);
13367    strp->Nodes = (DList *)SUMA_malloc(sizeof(DList));
13368    dlist_init(strp->Nodes, NULL);
13369    strp->Triangles = (DList *)SUMA_malloc(sizeof(DList));
13370    dlist_init(strp->Triangles, NULL);
13371    /* not terribly efficient for storing points!*/
13372    strp->Points = (DList *)SUMA_malloc(sizeof(DList));
13373    dlist_init(strp->Points, SUMA_freep);
13374    if (parent_ID) strp->parent_ID = SUMA_copy_string(parent_ID);
13375 
13376    SUMA_RETURN(strp);
13377 }
13378 
SUMA_free_strip(void * vstrp)13379 void SUMA_free_strip (void *vstrp)
13380 {
13381    static char FuncName[]={"SUMA_free_strip"};
13382    SUMA_STRIP *strp = (SUMA_STRIP *) vstrp;
13383    SUMA_ENTRY;
13384 
13385    if (strp) {
13386       if (strp->Edges) dlist_destroy(strp->Edges); SUMA_free(strp->Edges); strp->Edges = NULL;
13387       if (strp->Points) dlist_destroy(strp->Points); SUMA_free(strp->Points); strp->Points = NULL;
13388       if (strp->Nodes) dlist_destroy(strp->Nodes); SUMA_free(strp->Nodes); strp->Nodes = NULL;
13389       if (strp->Triangles) dlist_destroy(strp->Triangles); SUMA_free(strp->Triangles); strp->Triangles = NULL;
13390       if (strp->parent_ID) SUMA_free(strp->parent_ID); strp->parent_ID = NULL;
13391       SUMA_free(strp); strp = NULL;
13392    }
13393    SUMA_RETURNe;
13394 }
13395 
SUMA_MergeStrips(DList * striplist,SUMA_SurfaceObject * SO,char * MergeBy)13396 SUMA_Boolean SUMA_MergeStrips(DList *striplist, SUMA_SurfaceObject *SO, char *MergeBy)
13397 {
13398    static char FuncName[]={"SUMA_MergeStrips"};
13399    DListElmt *list_elm=NULL, *listnext_elm=NULL, *elm=NULL;
13400    SUMA_STRIP *strip=NULL, *stripnext=NULL;
13401    int repeat = 0, stripnext_first_edge = -1;
13402    int stripnext_last_edge = -1, strip_first_edge = -1;
13403    int strip_last_edge=-1;
13404    SUMA_Boolean ans = YUP;
13405    SUMA_Boolean LocalHead = NOPE;
13406 
13407    SUMA_ENTRY;
13408 
13409    if (!striplist || dlist_size(striplist) < 2) SUMA_RETURN(YUP); /* nothing to do here */
13410 
13411    SUMA_LHv("Have list of %d strips.\nMerging by %s\n", dlist_size(striplist), MergeBy);
13412 
13413    if (!strcmp(MergeBy,"edges")) { /* Merge by edges */
13414       do {
13415          if (!list_elm) { list_elm = listnext_elm = dlist_head(striplist); }
13416          else { list_elm = listnext_elm = dlist_next(list_elm); }
13417          strip = (SUMA_STRIP *)list_elm->data;
13418          strip_first_edge = (INT_CAST)(dlist_head(strip->Edges))->data;
13419          strip_last_edge = (INT_CAST)(dlist_tail(strip->Edges))->data;
13420          repeat = 0;
13421          do {
13422             listnext_elm = dlist_next(listnext_elm);
13423             stripnext = (SUMA_STRIP *)listnext_elm->data;
13424             stripnext_first_edge =
13425                (INT_CAST)(dlist_head(stripnext->Edges))->data;
13426             stripnext_last_edge =
13427                (INT_CAST)(dlist_tail(stripnext->Edges))->data;
13428 
13429             SUMA_LHv("Strip from edges %d to %d\n"
13430                      "Stripnext  edges %d to %d\n",
13431                      strip_first_edge, strip_last_edge,
13432                      stripnext_first_edge, stripnext_last_edge);
13433             /* any reason to merge?*/
13434             if (  strip_last_edge == stripnext_first_edge ||
13435                   SUMA_whichTri_e ( SO->EL, strip_last_edge,
13436                                     stripnext_first_edge, 1, !LocalHead)>=0) {
13437                SUMA_S_Note("Merging beginning of next to end of 1");
13438                repeat = 1;
13439                SUMA_MergeLists_Beg2_End1(stripnext->Edges, strip->Edges);
13440                SUMA_MergeLists_Beg2_End1(stripnext->Nodes, strip->Nodes);
13441                SUMA_MergeLists_Beg2_End1(stripnext->Points, strip->Points);
13442                SUMA_MergeLists_Beg2_End1(stripnext->Triangles, strip->Triangles);
13443              } else if (strip_last_edge == stripnext_last_edge ||
13444                         SUMA_whichTri_e (SO->EL, strip_last_edge,
13445                                  stripnext_last_edge, 1, !LocalHead)>=0) {
13446                SUMA_S_Note("Merging end of next to end of 1");
13447                repeat = 1;
13448                SUMA_MergeLists_End2_End1(stripnext->Edges, strip->Edges);
13449                SUMA_MergeLists_End2_End1(stripnext->Nodes, strip->Nodes);
13450                SUMA_MergeLists_End2_End1(stripnext->Points, strip->Points);
13451                SUMA_MergeLists_End2_End1(stripnext->Triangles, strip->Triangles);
13452             } else if (strip_first_edge == stripnext_last_edge ||
13453                        SUMA_whichTri_e (SO->EL, strip_first_edge,
13454                                     stripnext_last_edge, 1, !LocalHead)>=0) {
13455                SUMA_S_Note("Merging end of next to beginning of 1");
13456                repeat = 1;
13457                SUMA_MergeLists_End2_Beg1(stripnext->Edges, strip->Edges);
13458                SUMA_MergeLists_End2_Beg1(stripnext->Nodes, strip->Nodes);
13459                SUMA_MergeLists_End2_Beg1(stripnext->Points, strip->Points);
13460                SUMA_MergeLists_End2_Beg1(stripnext->Triangles, strip->Triangles);
13461             } else if (strip_first_edge == stripnext_first_edge ||
13462                        SUMA_whichTri_e (SO->EL, strip_first_edge,
13463                                     stripnext_first_edge, 1, !LocalHead)>=0) {
13464                SUMA_S_Note("Merging beginning of next to beginning of 1");
13465                repeat = 1;
13466                SUMA_MergeLists_Beg2_Beg1(stripnext->Edges, strip->Edges);
13467                SUMA_MergeLists_Beg2_Beg1(stripnext->Nodes, strip->Nodes);
13468                SUMA_MergeLists_Beg2_Beg1(stripnext->Points, strip->Points);
13469                SUMA_MergeLists_Beg2_Beg1(stripnext->Triangles, strip->Triangles);
13470             }
13471             if (repeat) { /* merger done, remove next strip */
13472                /* remove listnext_elm */
13473                dlist_remove(striplist, listnext_elm, (void*)&stripnext);
13474                SUMA_free_strip(stripnext);
13475             }
13476          } while (!repeat && listnext_elm != dlist_tail(striplist));
13477 
13478          SUMA_LHv("Now list of %d strips (repeat=%d).\n",
13479                   dlist_size(striplist), repeat);
13480 
13481       } while (!repeat && dlist_next(list_elm) != dlist_tail(striplist));
13482    } else { /* Merge by other list*/
13483       SUMA_S_Err("Not ready for this, no sir.\n");
13484       SUMA_RETURN(NOPE);
13485    }
13486 
13487    if (repeat) ans = ans * SUMA_MergeStrips(striplist, SO, MergeBy);
13488    SUMA_RETURN(ans);
13489 }
13490 
SUMA_isEdgeStripClosed(DList * edgestrip,SUMA_SurfaceObject * SO)13491 SUMA_Boolean SUMA_isEdgeStripClosed(DList *edgestrip, SUMA_SurfaceObject *SO)
13492 {
13493    static char FuncName[]={"SUMA_isEdgeStripClosed"};
13494    int e0, e1;
13495    SUMA_ENTRY;
13496 
13497    if (!edgestrip || !SO || !SO->EL) {
13498       SUMA_S_Errv("Null input edgestrip %p or SO %p or SO->EL %p\n",
13499                   edgestrip, SO, SO->EL);
13500       SUMA_RETURN(NOPE);
13501    }
13502 
13503    if (dlist_size(edgestrip) < 2) SUMA_RETURN(NOPE);
13504 
13505    e0 = (INT_CAST)((dlist_head(edgestrip))->data);
13506    e1 = (INT_CAST)((dlist_tail(edgestrip))->data);
13507    if (e0 >= SO->EL->N_EL || e1 >= SO->EL->N_EL) {
13508       SUMA_S_Errv("Edge %d or %d is >= than SO->EL->N_EL (%d)\n",
13509                   e0, e1, SO->EL->N_EL);
13510       SUMA_RETURN(NOPE);
13511    }
13512    if (  SO->EL->EL[e0][0] == SO->EL->EL[e1][0]   ||
13513          SO->EL->EL[e0][1] == SO->EL->EL[e1][0]   ||
13514          SO->EL->EL[e0][0] == SO->EL->EL[e1][1]   ||
13515          SO->EL->EL[e0][1] == SO->EL->EL[e1][1]   ) SUMA_RETURN(YUP);
13516 
13517 
13518    SUMA_RETURN(NOPE);
13519 }
13520 
13521 /*!
13522    Given a SPI structure, return the various connected paths that are
13523    formed by the intersections
13524 */
SUMA_SPI_to_EdgeStrips(SUMA_SurfaceObject * SO,SUMA_SURF_PLANE_INTERSECT * SPI)13525 DList * SUMA_SPI_to_EdgeStrips(SUMA_SurfaceObject *SO, SUMA_SURF_PLANE_INTERSECT *SPI)
13526 {
13527    static char FuncName[]={"SUMA_SPI_to_EdgeStrips"};
13528    int *Epath=NULL, N_Epath=-1;
13529    int Estart, Tstart, E0, Ec0, Ec1, Ec2, n0, n1, n2, i, j;
13530    int *Visited=NULL, Incident[5], N_Incident;
13531    int VisOrder = 0, T0;
13532    float *p4=NULL, *p0=NULL, *p1=NULL, Un, Un2, U[3], U2[3];
13533    DList *striplist=NULL;
13534    SUMA_STRIP *one_strp=NULL;
13535    SUMA_Boolean *TVisited = NULL;
13536    SUMA_Boolean LocalHead = NOPE;
13537 
13538    SUMA_ENTRY;
13539 
13540 
13541    if (!SPI || !SO->EL) { SUMA_S_Errv("NULL spi (%p) || el (%p)\n", SPI, SO->EL); SUMA_RETURN(striplist);  }
13542 
13543    /* something to keep track of what has been visited */
13544    Visited = (int *) SUMA_calloc(SO->EL->N_EL, sizeof(int));
13545    Epath = (int *)SUMA_calloc(SO->EL->N_EL, sizeof(int));
13546    TVisited = (SUMA_Boolean *) SUMA_calloc(SO->N_FaceSet, sizeof(SUMA_Boolean));
13547 
13548    if (LocalHead){
13549       FILE *fout=fopen(FuncName,"w");
13550       SUMA_LH("In debug mode, writing AAA* files to disk...");
13551       SUMA_Show_SPI(SPI, fout, SO, "AAA", &(SUMAg_SVv[0]));
13552       fclose(fout);
13553    }
13554 
13555    /* Find an intersected edge for starters */
13556    do {
13557       i = 0;
13558       Estart = -1; N_Epath = 0;
13559       do {
13560          if (!Visited[SPI->IntersEdges[i]]) Estart = SPI->IntersEdges[i];
13561          ++i;
13562       } while (i<SPI->N_IntersEdges && Estart < 0);
13563       /* Put Estart in the Epath */
13564       E0 = Estart;
13565          while (E0 >= 0) {
13566             SUMA_LHv("Now with E0=%d [nodes %d %d]\n   N_Epath = %d\n", E0, SO->EL->EL[E0][0], SO->EL->EL[E0][1], N_Epath);
13567             Epath[N_Epath] = E0; ++N_Epath;
13568             /* mark E0 as visited */
13569             Visited[E0] = VisOrder; ++VisOrder;
13570             /* Find a triangle incident to E0 and that is also intersected */
13571             if (LocalHead) fprintf (SUMA_STDERR, "%s: Searching for triangles incident to E0 %d.\n", FuncName, E0);
13572             if (!SUMA_Get_Incident(SO->EL->EL[E0][0], SO->EL->EL[E0][1], SO->EL, Incident, &N_Incident, 0, 1)) {
13573                fprintf (SUMA_STDERR,"Error %s: Failed to get Incident triangles.\n", FuncName);
13574                SUMA_FREE_DLIST(striplist);
13575                goto CLEANUP_RETURN;
13576             }
13577             if (N_Incident > 2) {
13578                SUMA_S_Err("Surface not 2 manifold. Will not proceed.\n");
13579                SUMA_FREE_DLIST(striplist);
13580                goto CLEANUP_RETURN;
13581             }
13582             T0 = -1;
13583             if (N_Incident) {
13584                if (LocalHead) {
13585                   if (N_Incident > 1) { SUMA_LHv("Have incident triangles %d and %d\n", Incident[0], Incident[1]);}
13586                   else { SUMA_LHv("Have one incident triangle %d\n", Incident[0]); }
13587                }
13588                if (SPI->isTriHit[Incident[0]] && !TVisited[Incident[0]]) T0 = Incident[0];
13589                else if (N_Incident > 1 && SPI->isTriHit[Incident[1]] && !TVisited[Incident[1]]) T0 = Incident[1];
13590                if (T0 >= 0) {
13591                   TVisited[T0] = YUP;
13592                   if (E0 == Estart) {
13593                      Tstart = T0;
13594                      SUMA_LHv("Tstart = %d\n", Tstart);
13595                   } else {
13596                      SUMA_LHv("Marked triangle %d\n", T0);
13597                   }
13598                } else {
13599                   SUMA_LHv("End of journey at edge %d\n", E0);
13600                }
13601             }
13602 
13603             E0 = -1;
13604             if (T0>=0) { /* have gun, will travel, find next edge */
13605                /* find the other interesected edge of this triangle*/
13606                n0 = SO->FaceSetList[3*T0];
13607                n1 = SO->FaceSetList[3*T0+1];
13608                n2 = SO->FaceSetList[3*T0+2];
13609                SUMA_LHv("Working triangle %d, nodes: %d %d %d\n",
13610                         T0, n0, n1, n2);
13611                /* find the two intersected edges */
13612                Ec0 = SUMA_FindEdge (SO->EL, n0, n1);
13613                Ec1 = SUMA_FindEdge (SO->EL, n0, n2);
13614                Ec2 = SUMA_FindEdge (SO->EL, n1, n2);
13615                if (!Visited[Ec0] && SPI->isEdgeInters[Ec0]) {
13616                   E0 = Ec0; /* have a new candidate */}
13617                else if (!Visited[Ec1] && SPI->isEdgeInters[Ec1]) {
13618                   E0 = Ec1; /* have a new candidate */}
13619                else if (!Visited[Ec2] && SPI->isEdgeInters[Ec2]) {
13620                   E0 = Ec2; /* have a new candidate */}
13621                else {  /* no where to go */ }
13622             }
13623          }
13624 
13625       if (N_Epath > 0) {
13626          if (!striplist) {
13627             striplist = (DList*)SUMA_malloc(sizeof(DList));
13628             dlist_init(striplist, SUMA_free_strip);
13629          }
13630          one_strp = SUMA_alloc_strip(SO->idcode_str);
13631          /* now add edge sequence to this list */
13632          for (i=0; i<N_Epath; ++i) {
13633             dlist_ins_next(one_strp->Edges, dlist_tail(one_strp->Edges),
13634                            (VOID_CAST)Epath[i]);
13635             /* here you can add the Points (xyz of intersections), if you like */
13636             p4 = (float *)SUMA_malloc(sizeof(float)*4);
13637             p4[0] = SPI->IntersNodes[3*Epath[i]];
13638             p4[1] = SPI->IntersNodes[3*Epath[i]+1];
13639             p4[2] = SPI->IntersNodes[3*Epath[i]+2];
13640             /* Store the position of the point as a fraction of the edge
13641                length from the first node forming edge */
13642             n0 = SO->EL->EL[Epath[i]][0]; n1 = SO->EL->EL[Epath[i]][1];
13643             p0 = &(SO->NodeList[3*n0]); p1 = &(SO->NodeList[3*n1]);
13644             SUMA_UNIT_VEC(p0, p1, U, Un);
13645             SUMA_UNIT_VEC(p0, p4, U2, Un2);
13646             p4[3] = Un2/Un; /* Hide it here. Forgive me Lord for I have sinned */
13647             dlist_ins_next(one_strp->Points, dlist_tail(one_strp->Points),
13648                            (void *)p4);
13649          }
13650          /* add the stip to the striplist */
13651          dlist_ins_next(striplist, dlist_tail(striplist), (void *)one_strp);
13652          one_strp = NULL; /* do not touch it anymore */
13653       }
13654    } while (Estart >=0);
13655 
13656    /* here is where you combine all the strips */
13657    if (!SUMA_MergeStrips(striplist, SO, "edges")) {
13658       SUMA_S_Err("An error occurred while merging strips!\n");
13659       SUMA_FREE_DLIST(striplist);
13660       goto CLEANUP_RETURN;
13661    }
13662 
13663    if (LocalHead) {  /* new, using list of strips */
13664       SUMA_display_edge_striplist(striplist, &(SUMAg_SVv[0]), SO, "ShowEdges, ShowConnectedPoints, ShowPoints");
13665    }
13666 
13667    CLEANUP_RETURN:
13668    if (Epath) SUMA_free(Epath); Epath = NULL;
13669    if (Visited) SUMA_free(Visited); Visited = NULL;
13670    if (TVisited) SUMA_free(TVisited); TVisited = NULL;
13671 
13672    SUMA_RETURN(striplist);
13673 }
13674 
13675 /*!
13676 \brief Converts a series of connected nodes into a series of connected triangles that were intersected by
13677 the plane.
13678 There is no guarantee that two nodes that belong to triangles intersected by the plane and part of the shortest path
13679 (as returned by SUMA_Dijkstra) for an edge that belongs to a triangle intersected by the plane. See labbook NIH-2 page
13680 158 for sketches illustrating this point. So the strip of triangles that you will get back may have holes in it since
13681 it only allows intersected triangles to be members of the path.
13682 
13683    tPath = SUMA_NodePath_to_TriPath_Inters (SO, SPI, int *nPath, int N_nPath, int *N_tPath)
13684 
13685    \param SO (SUMA_SurfaceObject *) structure containing surface object
13686    \param SPI (SUMA_SURF_PLANE_INTERSECT *) surface plane intersection structure
13687    \param nPath (int *) vector containing the shortest path defined by its nodes (output of SUMA_Dijkstra)
13688    \param N_nPath (int) number of elements in nPath
13689    \param N_tPath (int *)pointer to number of elements returned in tPath
13690    \return tPath (int *) vector of *N_tPath indices of triangles that form a strip along the shortest path.
13691 
13692    \sa SUMA_Dijkstra
13693    \sa SUMA_NodePath_to_EdgePath
13694    \sa SUMA_Surf_Plane_Intersect
13695 
13696    \sa labbook NIH-2 pages 158, and 159 for MissingTriangles
13697 */
13698 
SUMA_NodePath_to_TriPath_Inters(SUMA_SurfaceObject * SO,SUMA_SURF_PLANE_INTERSECT * SPI,int * nPath,int N_nPath,int * N_tPath)13699 int *SUMA_NodePath_to_TriPath_Inters ( SUMA_SurfaceObject *SO, SUMA_SURF_PLANE_INTERSECT *SPI, int *nPath, int N_nPath, int *N_tPath)
13700 {
13701    static char FuncName[]={"SUMA_NodePath_to_TriPath_Inters"};
13702    int *tPath = NULL, e, i, N_nc, nc[3], N_HostTri, E, j,
13703       HostTri, PrevTri, k, N1[2], N2[2], cnt, MissTri = 0, candidate;
13704    SUMA_Boolean Found, LocalHead = NOPE;
13705 
13706    SUMA_ENTRY;
13707 
13708    tPath = (int *) SUMA_calloc(2*N_nPath, sizeof(int));
13709    if (!tPath) {
13710       fprintf (SUMA_STDERR, "Error %s: Failed to allocate.\n", FuncName);
13711       SUMA_RETURN (NULL);
13712    }
13713 
13714    *N_tPath = 0;
13715    for (i=0; i < N_nPath - 1; ++i) {
13716       /* find the edge corresponding to two consecutive nodes in the path */
13717       E = SUMA_FindEdge (SO->EL, nPath[i], nPath[i+1]);
13718       if (E < 0) {
13719          fprintf (SUMA_STDERR, "Error %s: Failed in SUMA_FindEdge.\n", FuncName);
13720          SUMA_free(tPath);
13721          SUMA_RETURN(NULL);
13722       }
13723       /* find the triangles containing E and intersected by the plane */
13724       N_HostTri = SO->EL->ELps[E][2]; /* number of hosting triangles */
13725       if (N_HostTri > 2) {
13726          fprintf (SUMA_STDERR,
13727                   "Warning %s: Surface is not a surface, "
13728                   "Edge %d has more than %d hosting triangles.\n",
13729                   FuncName, E, N_HostTri);
13730       }
13731       candidate = 0;
13732       /* search for a hosting triangle that was intersected */
13733       for (j=0; j < N_HostTri; ++j) {
13734          HostTri = SO->EL->ELps[E+j][1];
13735          if (SPI->isTriHit[HostTri]) { /* a candidate for adding to the path */
13736             ++candidate;
13737             if (*N_tPath > 2*N_nPath) {
13738                fprintf (SUMA_STDERR,
13739                   "Error %s: N_tPath = %d > %d allocated.\n",
13740                   FuncName, *N_tPath, 2*N_nPath);
13741             }
13742             #if 1
13743             /* This block is an attempt to get All triangles intersected by
13744                plane AND having a node as part of the shortest path.
13745             It does not work well, probably because some of the functions
13746             called need fixing... */
13747             if (*N_tPath == 0) { /* if that is the first triangle in the path,
13748                                     add it without much fuss */
13749                tPath[*N_tPath] = HostTri; /* hosting triangle index */
13750                ++ (*N_tPath);
13751             } else { /* make sure there is continuation along edges */
13752                PrevTri = tPath[*N_tPath - 1];
13753                N_nc = SUMA_isTriLinked (&(SO->FaceSetList[3*PrevTri]),
13754                                         &(SO->FaceSetList[3*HostTri]), nc);
13755                if (!N_nc) {
13756                   fprintf (SUMA_STDERR,
13757                         "Warning %s: Triangles %d and %d are not linked.\n"
13758                         "Adding triangle %d anyway.\n",
13759                      FuncName, PrevTri, HostTri, HostTri);
13760                   /* add triangle, anyway */
13761                   tPath[*N_tPath] = HostTri;
13762                   ++ (*N_tPath);
13763                }else if (N_nc == 1) {
13764                   /* must fill triangle gap get the triangle with
13765                      the common node and common edges*/
13766                   /* first, find remaining nodes in PrevTri  */
13767                   e = 0;
13768                   for (k=0; k <3; ++k) {
13769                      if (SO->FaceSetList[3*PrevTri+k] != nc[0]) {
13770                         N1[e] = SO->FaceSetList[3*PrevTri+k]; ++e;
13771                      }
13772                   }
13773                   /* then find remaining nodes in HostTri  */
13774                   e = 0;
13775                   for (k=0; k <3; ++k) {
13776                      if (SO->FaceSetList[3*HostTri+k] != nc[0]) {
13777                         N2[e] = SO->FaceSetList[3*HostTri+k]; ++e;
13778                      }
13779                   }
13780                   /* find a triangle that has either one of the following
13781                      node combinations, in addition to nc[0]:
13782                   N1[0], N2[0] or N1[0], N2[1] or N1[1], N2[0] or N1[1], N2[1] */
13783                   Found = NOPE;
13784                   cnt = 0;
13785                   while (!Found && cnt < 4) {
13786                      switch (cnt) {
13787                         case 0:
13788                            MissTri = SUMA_whichTri (SO->EL, nc[0],
13789                                                     N1[0], N2[0], 1, 0);
13790                            if (LocalHead)
13791                               fprintf (SUMA_STDERR,
13792                "%s: looking for triangle with nodes %d and %d... Tri = %d\n",
13793                                  FuncName, N1[0], N2[0], MissTri);
13794                            break;
13795                         case 1:
13796                            MissTri = SUMA_whichTri (SO->EL, nc[0],
13797                                                     N1[0], N2[1], 1, 0);
13798                            if (LocalHead)
13799                               fprintf (SUMA_STDERR,
13800                "%s: looking for triangle with nodes %d and %d... Tri = %d\n",
13801                                  FuncName, N1[0], N2[1], MissTri);
13802                            break;
13803                         case 2:
13804                            MissTri = SUMA_whichTri (SO->EL, nc[0],
13805                                                     N1[1], N2[0], 1, 0);
13806                            if (LocalHead)
13807                               fprintf (SUMA_STDERR,
13808                "%s: looking for triangle with nodes %d and %d... Tri = %d\n",
13809                                  FuncName, N1[1], N2[0], MissTri);
13810                            break;
13811                         case 3:
13812                            MissTri = SUMA_whichTri (SO->EL, nc[0],
13813                                                     N1[1], N2[1], 1, 0);
13814                            if (LocalHead)
13815                               fprintf (SUMA_STDERR,
13816                "%s: looking for triangle with nodes %d and %d... Tri = %d\n",
13817                                  FuncName, N1[1], N2[1], MissTri);
13818                            break;
13819                      }
13820                      if (MissTri >= 0) {
13821                         Found = YUP;
13822                      }
13823                      ++cnt;
13824                   }
13825                   if (!Found) {
13826                      fprintf (SUMA_STDERR,
13827                               "Warning %s: Failed to find missing triangle.\n",
13828                               FuncName);
13829                      tPath[*N_tPath] = HostTri;
13830                      ++ (*N_tPath);
13831                   }else {
13832                      /* add the missing triangle first, then the HostTri */
13833                      tPath[*N_tPath] = MissTri;
13834                      ++ (*N_tPath);
13835                      tPath[*N_tPath] = HostTri;
13836                      ++ (*N_tPath);
13837                   }
13838                }else if (N_nc == 2) {
13839                   /* Triangles share an edge so no problem,
13840                      insert the new triangle in the path */
13841                   tPath[*N_tPath] = HostTri;
13842                   ++ (*N_tPath);
13843                }else {
13844                   fprintf (SUMA_STDERR,
13845                            "Error %s: Triangles %d and %d are identical.\n",
13846                             FuncName, PrevTri, HostTri);
13847                   SUMA_free(tPath);
13848                   SUMA_RETURN(NULL);
13849                }
13850             }
13851             #else
13852                tPath[*N_tPath] = HostTri;
13853                ++ (*N_tPath);
13854             #endif
13855          }
13856       }
13857       if (!candidate) {
13858          fprintf (SUMA_STDERR,
13859                   "\aWarning %s: Nodes %d and %d of edge %d had no "
13860                   "intersected hosting triangle.\n",
13861                   FuncName, nPath[i], nPath[i+1], E);
13862 
13863       }
13864    }
13865 
13866    SUMA_RETURN (tPath);
13867 }
13868 /*!
13869 \brief Converts a series of connected nodes into a series of connected triangles that belong to a branch.
13870 The function fails at times, picking the long instead of the short path but it is left here in case I
13871 need it in the future.
13872 
13873    tPath = SUMA_NodePath_to_TriPath_Inters_OLD (SO, Bv, Path, N_Path, N_Tri);
13874    \param SO (SUMA_SurfaceObject *) Pointer to surface object
13875    \param Bv (SUMA_TRI_BRANCH*) Pointer to tiangle branch containing nodes in path.
13876    \param Path (int *) vector of node indices forming a path.
13877                        Sequential nodes in Path must be connected on the surface mesh.
13878    \param N_Path (int) number of nodes in the path
13879    \param N_Tri (int *) pointer to integer that will contain the number of triangles in the path
13880                         0 if function fails.
13881    \return tPath (int *) pointer to vector containing indices of triangles forming the path.
13882                         The indices are into SO->FaceSetList.
13883                         NULL if trouble is encountered.
13884 
13885    \sa SUMA_NodePath_to_EdgePath
13886    \sa Path I in NIH-2, labbook page 153
13887 */
SUMA_NodePath_to_TriPath_Inters_OLD(SUMA_SurfaceObject * SO,SUMA_TRI_BRANCH * Bv,int * Path,int N_Path,int * N_Tri)13888 int *SUMA_NodePath_to_TriPath_Inters_OLD (SUMA_SurfaceObject *SO, SUMA_TRI_BRANCH *Bv, int *Path, int N_Path, int *N_Tri)
13889 {
13890    static char FuncName[]={"SUMA_NodePath_to_TriPath_Inters_OLD"};
13891    int *tPath = NULL, ilist, i0, Tri, eTri, EdgeBuf, Tri0, Tri1, Direction, i1, loc2f, iDirSet;
13892    SUMA_Boolean LocalHead = NOPE, Found = NOPE;
13893 
13894    SUMA_ENTRY;
13895 
13896 
13897   *N_Tri = 0;
13898    tPath = (int *) SUMA_calloc(Bv->N_list+1, sizeof(int));
13899    if (!tPath) {
13900       fprintf (SUMA_STDERR, "Error %s: Failed to allocate.\n", FuncName);
13901       SUMA_RETURN (NULL);
13902    }
13903 
13904    /* the first triangle should contain the first node in the path */
13905       i0 = Path[0];
13906       Tri0 = Bv->list[0];
13907       if (SO->FaceSetList[3*Tri0] != i0 && SO->FaceSetList[3*Tri0+1] != i0 && SO->FaceSetList[3*Tri0+2] != i0) {
13908          fprintf (SUMA_STDERR, "Error %s: Did not find node %d in first triangle in branch.\n", FuncName, i0);
13909          SUMA_free(tPath);
13910          *N_Tri = 0;
13911          SUMA_RETURN (NULL);
13912       }
13913 
13914 
13915    /* initiliaze first node results and look for the second node */
13916    tPath[0] = Tri0;
13917    *N_Tri = 1;
13918    Found = NOPE;
13919    ilist = 0;
13920 
13921    if (LocalHead)   fprintf(SUMA_STDERR, "%s: Going forward looking for third node\n", FuncName);
13922    if (N_Path > 2) {
13923       iDirSet = 2; /* search for third node in list, that helps determine the direction more reliably */
13924    }else {
13925       iDirSet = 1; /* settle for the second node */
13926    }
13927 
13928    ilist = 1;
13929    while (!Found && ilist < Bv->N_list) {
13930       tPath[*N_Tri] = Bv->list[ilist];
13931       if (SO->FaceSetList[3*Bv->list[ilist]] == Path[iDirSet] ||
13932          SO->FaceSetList[3*Bv->list[ilist]+1] == Path[iDirSet] ||
13933          SO->FaceSetList[3*Bv->list[ilist]+2] == Path[iDirSet]) {
13934             Found = YUP;
13935       }
13936       ++(*N_Tri);
13937       ++ilist;
13938    }
13939 
13940    if (!Found) {
13941       fprintf (SUMA_STDERR, "Error %s: Did not find next node %d in branch.\n", FuncName, Path[iDirSet]);
13942       SUMA_free(tPath);
13943       *N_Tri = 0;
13944       SUMA_RETURN (NULL);
13945    }
13946 
13947    loc2f = *N_Tri; /* number of steps to find second node in the forward direction */
13948 
13949    /* do the same in the backwards direction */
13950    tPath[0] = Tri0;
13951    *N_Tri = 1;
13952    Found = NOPE;
13953    ilist = 0;
13954 
13955    if (LocalHead) fprintf(SUMA_STDERR, "%s: Going backwards looking for third node\n", FuncName);
13956    ilist = Bv->N_list - 1;
13957    while (!Found && ilist >=  0) {
13958       tPath[*N_Tri] = Bv->list[ilist];
13959       if (LocalHead) fprintf(SUMA_STDERR, "%s: trying triangle %d for node %d.\n", FuncName, Bv->list[ilist], Path[N_Path-1]);
13960       if (SO->FaceSetList[3*Bv->list[ilist]] == Path[iDirSet] ||
13961          SO->FaceSetList[3*Bv->list[ilist]+1] == Path[iDirSet] ||
13962          SO->FaceSetList[3*Bv->list[ilist]+2] == Path[iDirSet]) {
13963             Found = YUP;
13964       }
13965       ++(*N_Tri);
13966       --ilist;
13967    }
13968 
13969    if (*N_Tri < loc2f) {
13970       /* go backwards, shorter. This is based on triangle count,
13971          it would be more accurate based on distance of intersected edge */
13972       Direction = -1;
13973    } else Direction = 1;
13974 
13975    /* now do the whole thing */
13976 
13977    tPath[0] = Tri0;
13978    *N_Tri = 1;
13979    Found = NOPE;
13980    ilist = 0;
13981    if (Direction == 1) { /* move forward until you reach the last node */
13982      if (LocalHead)   fprintf(SUMA_STDERR, "%s: Going forward, final pass \n", FuncName);
13983      ilist = 1;
13984       while (!Found && ilist < Bv->N_list) {
13985          tPath[*N_Tri] = Bv->list[ilist];
13986          if (SO->FaceSetList[3*Bv->list[ilist]] == Path[N_Path-1] ||
13987             SO->FaceSetList[3*Bv->list[ilist]+1] == Path[N_Path-1] ||
13988             SO->FaceSetList[3*Bv->list[ilist]+2] == Path[N_Path-1]) {
13989                Found = YUP;
13990          }
13991          ++(*N_Tri);
13992          ++ilist;
13993       }
13994    } else { /* move backwards */
13995       if (LocalHead) fprintf(SUMA_STDERR, "%s: Going backwards, final pass \n", FuncName);
13996       ilist = Bv->N_list - 1;
13997       while (!Found && ilist >=  0) {
13998          tPath[*N_Tri] = Bv->list[ilist];
13999          if (LocalHead) fprintf(SUMA_STDERR, "%s: trying triangle %d for node %d.\n", FuncName, Bv->list[ilist], Path[N_Path-1]);
14000          if (SO->FaceSetList[3*Bv->list[ilist]] == Path[N_Path-1] ||
14001             SO->FaceSetList[3*Bv->list[ilist]+1] == Path[N_Path-1] ||
14002             SO->FaceSetList[3*Bv->list[ilist]+2] == Path[N_Path-1]) {
14003                Found = YUP;
14004          }
14005          ++(*N_Tri);
14006          --ilist;
14007       }
14008 
14009    }
14010 
14011    if (!Found) {
14012       fprintf (SUMA_STDERR, "Error %s: Path not completed.\n", FuncName);
14013       SUMA_free(tPath);
14014       *N_Tri = 0;
14015       SUMA_RETURN (NULL);
14016    }else {
14017       if (LocalHead) {
14018          fprintf (SUMA_STDERR,"%s: Path is %d triangles long:\n", FuncName, *N_Tri);
14019          for (ilist=0; ilist< *N_Tri; ++ilist) {
14020             fprintf (SUMA_STDERR,"t%d\t", tPath[ilist]);
14021          }
14022          fprintf (SUMA_STDERR,"\n");
14023       }
14024    }
14025    SUMA_RETURN (tPath);
14026 }
14027 
14028 
SUMA_CenterOfSphere(double * p1,double * p2,double * p3,double * p4,double * c)14029 SUMA_Boolean SUMA_CenterOfSphere(double *p1, double *p2, double *p3,
14030                                  double *p4, double *c)
14031 {
14032    static char FuncName[]={"SUMA_CenterOfSphere"};
14033    double pp1[3], pp2[3], pp3[3], pp4[3];
14034    THD_dmat33  mat;
14035    double n1, n2, n3, d3;
14036    double x2Mx1, x3Mx1, x4Mx1;
14037    double y2My1, y3My1, y4My1;
14038    double z2Mz1, z3Mz1, z4Mz1;
14039    double spp1, spp2, spp3, spp4;
14040    int i = 0;
14041    SUMA_Boolean LocalHead = NOPE;
14042 
14043    SUMA_ENTRY;
14044 
14045    /* calculate doubles */
14046    for (i=0; i<3; ++i) {
14047       pp1[i] = p1[i]*p1[i];
14048       pp2[i] = p2[i]*p2[i];
14049       pp3[i] = p3[i]*p3[i];
14050       pp4[i] = p4[i]*p4[i];
14051    }
14052    spp1 = pp1[0] + pp1[1] + pp1[2];
14053    spp2 = pp2[0] + pp2[1] + pp2[2];
14054    spp3 = pp3[0] + pp3[1] + pp3[2];
14055    spp4 = pp4[0] + pp4[1] + pp4[2];
14056 
14057    /* calculate differences */
14058    x2Mx1 = p2[0] - p1[0];
14059    x3Mx1 = p3[0] - p1[0];
14060    x4Mx1 = p4[0] - p1[0];
14061    y2My1 = p2[1] - p1[1];
14062    y3My1 = p3[1] - p1[1];
14063    y4My1 = p4[1] - p1[1];
14064    z2Mz1 = p2[2] - p1[2];
14065    z3Mz1 = p3[2] - p1[2];
14066    z4Mz1 = p4[2] - p1[2];
14067 
14068 
14069    /* calculate N1 */
14070    mat.mat[0][0] = spp2 - (spp1);
14071    mat.mat[1][0] = spp3 - (spp1);
14072    mat.mat[2][0] = spp4 - (spp1);
14073    mat.mat[0][1] = y2My1;
14074    mat.mat[1][1] = y3My1;
14075    mat.mat[2][1] = y4My1;
14076    mat.mat[0][2] = z2Mz1;
14077    mat.mat[1][2] = z3Mz1;
14078    mat.mat[2][2] = z4Mz1;
14079    n1 = MAT_DET(mat);
14080    SUMA_LHv("n1=%f\n", n1);
14081 
14082    /* calculate N2 */
14083    mat.mat[0][0] = x2Mx1;
14084    mat.mat[1][0] = x3Mx1;
14085    mat.mat[2][0] = x4Mx1;
14086    mat.mat[0][1] = spp2 - (spp1);
14087    mat.mat[1][1] = spp3 - (spp1);
14088    mat.mat[2][1] = spp4 - (spp1);
14089    mat.mat[0][2] = z2Mz1;
14090    mat.mat[1][2] = z3Mz1;
14091    mat.mat[2][2] = z4Mz1;
14092    n2 = MAT_DET(mat);
14093    SUMA_LHv("n2=%f\n", n2);
14094 
14095    /* calculate N3 */
14096    mat.mat[0][0] = x2Mx1;
14097    mat.mat[1][0] = x3Mx1;
14098    mat.mat[2][0] = x4Mx1;
14099    mat.mat[0][1] = y2My1;
14100    mat.mat[1][1] = y3My1;
14101    mat.mat[2][1] = y4My1;
14102    mat.mat[0][2] = spp2 - (spp1);
14103    mat.mat[1][2] = spp3 - (spp1);
14104    mat.mat[2][2] = spp4 - (spp1);
14105    n3 = MAT_DET(mat);
14106    SUMA_LHv("n3=%f\n", n3);
14107 
14108    /* calculate D3 */
14109    mat.mat[0][0] = x2Mx1;
14110    mat.mat[1][0] = x3Mx1;
14111    mat.mat[2][0] = x4Mx1;
14112    mat.mat[0][1] = y2My1;
14113    mat.mat[1][1] = y3My1;
14114    mat.mat[2][1] = y4My1;
14115    mat.mat[0][2] = z2Mz1;
14116    mat.mat[1][2] = z3Mz1;
14117    mat.mat[2][2] = z4Mz1;
14118    d3 = MAT_DET(mat);
14119    SUMA_LHv("d3=%f\n", d3);
14120 
14121    if (d3) {
14122       /* Center */
14123       c[0] = n1/(2.0*d3);
14124       c[1] = n2/(2.0*d3);
14125       c[2] = n3/(2.0*d3);
14126       SUMA_LHv("c=[%f, %f, %f]\n", c[0], c[1], c[2]);
14127 
14128       SUMA_RETURN(YUP);
14129    } else {
14130       c[0] = 1.0; c[1] = -2.0; c[2] = 3.0;
14131       SUMA_LH("0 denominator, solution impossibile\n");
14132       SUMA_RETURN(NOPE);
14133    }
14134 }
14135 
14136 
SUMA_GetCenterOfSphereSurface(SUMA_SurfaceObject * SO,int Nquads,double * cs,double * cm)14137 SUMA_Boolean SUMA_GetCenterOfSphereSurface(SUMA_SurfaceObject *SO,
14138                                            int Nquads, double *cs, double *cm)
14139 {
14140    static char FuncName[]={"SUMA_GetCenterOfSphereSurface"};
14141    double  p1[3], p2[3], p3[3], p4[3], c[3];
14142    double *cx=NULL, *cy=NULL, *cz=NULL;
14143    int ii, nn[4], jj, Ns, nmax;
14144    int *ir = NULL, cnt;
14145    SUMA_Boolean LocalHead=NOPE;
14146 
14147    SUMA_ENTRY;
14148 
14149    c[0] = -11111.0; c[1] = -22222.0; c[2] = -33333.0;
14150    cs[0] = cs[1] = cs[2] = 0.0;
14151    if (!(ir = z_rand_order(0, SO->N_Node-1, 111111311))) {
14152       SUMA_S_Err("Failed to get randomized list");
14153       SUMA_RETURN(NOPE);
14154    } else {
14155       /* minimum number of distinct quads */
14156       nmax = (SO->N_Node-1)/4;
14157       if (Nquads < 1) Ns = SUMA_MIN_PAIR(100, nmax);
14158       else Ns = SUMA_MIN_PAIR(Nquads, nmax);
14159       cx = (double*)SUMA_malloc(sizeof(double)*Ns);
14160       cy = (double*)SUMA_malloc(sizeof(double)*Ns);
14161       cz = (double*)SUMA_malloc(sizeof(double)*Ns);
14162       cs[0] = cs[1] = cs[2] = 0.0;
14163       cnt = 0;
14164       for (jj=0; jj<Ns; ++jj) {
14165          nn[0] = ir[4*jj+0];
14166          nn[1] = ir[4*jj+1];
14167          nn[2] = ir[4*jj+2];
14168          nn[3] = ir[4*jj+3];
14169          for (ii=0; ii<3; ++ii) {
14170             p1[ii] = (double)SO->NodeList[3*nn[0]+ii];
14171             p2[ii] = (double)SO->NodeList[3*nn[1]+ii];
14172             p3[ii] = (double)SO->NodeList[3*nn[2]+ii];
14173             p4[ii] = (double)SO->NodeList[3*nn[3]+ii];
14174          }
14175          /* Find a nice center */
14176          if (SUMA_CenterOfSphere( p1, p2, p3, p4, c )) {
14177             SUMA_LHv(  "Center estimate %d:\n"
14178                            "  [%f   %f   %f]\n", jj, c[0], c[1], c[2]);
14179             for (ii=0; ii<3;++ii) {
14180                cs[ii] += c[ii];
14181             }
14182             cx[cnt] = c[0];
14183             cy[cnt] = c[1];
14184             cz[cnt] = c[2];
14185             ++cnt;
14186          }
14187       }
14188       Ns = cnt;
14189       for (ii=0; ii<3;++ii) {
14190          cs[ii] /= (double)Ns;
14191       }
14192       /* sort coords */
14193       qsort(cx, Ns, sizeof(double),
14194             (int(*) (const void *, const void *)) SUMA_compare_double);
14195       qsort(cy, Ns, sizeof(double),
14196             (int(*) (const void *, const void *)) SUMA_compare_double);
14197       qsort(cz, Ns, sizeof(double),
14198             (int(*) (const void *, const void *)) SUMA_compare_double);
14199       cm[0] = cx[Ns/2];
14200       cm[1] = cy[Ns/2];
14201       cm[2] = cz[Ns/2];
14202       SUMA_LHv(  "Average of %d center estimates:\n"
14203                  "  [%f   %f   %f]\n"
14204                  "Median of %d center estimates:\n"
14205                  "  [%f   %f   %f]\n"
14206                      , Ns, cs[0], cs[1], cs[2],
14207                        Ns, cm[0], cm[1], cm[2]);
14208       SUMA_free(cx); SUMA_free(cy); SUMA_free(cz); cx = cy = cz = NULL;
14209       if (ir) SUMA_free(ir); ir = NULL;
14210    }
14211    SUMA_RETURN(YUP);
14212 }
14213 
14214 /*  Average segment length in SO2 / S01 */
SUMA_SegmentDistortion(SUMA_SurfaceObject * SO1,SUMA_SurfaceObject * SO2)14215 float *SUMA_SegmentDistortion (SUMA_SurfaceObject *SO1, SUMA_SurfaceObject *SO2)
14216 {
14217    static char FuncName[]={"SUMA_SegmentDistortion"};
14218    float *SegDist=NULL, *p1_1, *p1_2, *p2_1, *p2_2, d_1, d_2;
14219    int i, k;
14220 
14221    SUMA_ENTRY;
14222 
14223    if (!SO1 || !SO2) { SUMA_S_Err("NULL input"); SUMA_RETURN(SegDist); }
14224    if (SO1->N_Node != SO2->N_Node) { SUMA_S_Err("input mismatch"); SUMA_RETURN(SegDist); }
14225 
14226    if (!SO1->FN) { SUMA_SurfaceMetrics(SO1, "EdgeList", NULL); }
14227    if (!SO2->FN) { SUMA_SurfaceMetrics(SO2, "EdgeList", NULL); }
14228    if (!SO1->FN || !SO2->FN) { SUMA_S_Err("Failed to calculate FN"); SUMA_RETURN(SegDist); }
14229 
14230    SegDist = (float *)SUMA_calloc(SO1->N_Node, sizeof(float));
14231 
14232    if (SO1 == SO2) {
14233       for (i=0; i<SO1->N_Node; ++i) SegDist[i] = 1.0;
14234       SUMA_RETURN(SegDist);
14235    }
14236 
14237    for (i=0; i<SO1->N_Node; ++i) {
14238       p1_1 = &(SO1->NodeList[3*i]);
14239       p1_2 = &(SO2->NodeList[3*i]);
14240       SegDist[i] = 0.0;
14241       for (k=0; k<SO1->FN->N_Neighb[i]; ++k) {
14242          p2_1 = &(SO1->NodeList[3*k]);
14243          p2_2 = &(SO2->NodeList[3*k]);
14244          SUMA_SEG_LENGTH_SQ (p1_1, p2_1, d_1);
14245          SUMA_SEG_LENGTH_SQ (p1_2, p2_2, d_2);
14246          if (d_1) SegDist[i] += sqrt(d_2 / d_1);
14247       }
14248       if (SO1->FN->N_Neighb[i]) SegDist[i] /= SO1->FN->N_Neighb[i];
14249    }
14250 
14251    SUMA_RETURN(SegDist);
14252 }
14253 
14254 /*!
14255    \brief, a function for approximate but rapid delineation of the set of nodes
14256    within a distance form a node on the surface.
14257    The approximation works via the spherical version of the surface and assumes
14258    that the distortions have a low spatial frequency.
14259    The function must be called with an 'cleanup mode' (cent = -1 ) at the end.
14260    \sa SUMA_APPROXNEIGHBORS
14261 */
14262 
SUMA_ApproxNeighbors(SUMA_SurfaceObject * SO,SUMA_SurfaceObject * SOf,int cent,float dnei,byte * nmask)14263 int SUMA_ApproxNeighbors ( SUMA_SurfaceObject *SO,
14264          SUMA_SurfaceObject *SOf, /* spherical (or someday flat) version of SO */
14265          int cent,      /* the central node*/
14266          float dnei,     /* search distance, along the surface from node cent */
14267          byte *nmask     /* to contain the nodes within rad from Cent */)
14268 {
14269    static char FuncName[]={"SUMA_ApproxNeighbors"};
14270    int N_nmask=-1;
14271    static float *SegDist = NULL;
14272    static int *mask_record=NULL;
14273    static int N_nmask_last = -2;
14274    int i;
14275    float dnei_sp, alph, rsearch;
14276    SUMA_Boolean LocalHead = NOPE;
14277 
14278    SUMA_ENTRY;
14279 
14280    if (cent == -1) {
14281       SUMA_LH("Cleanup mode");
14282       N_nmask_last = -2;
14283       if (SegDist) SUMA_free(SegDist); SegDist = NULL;
14284       if (mask_record) SUMA_free(mask_record); mask_record = NULL;
14285       SUMA_RETURN(0);
14286    }
14287 
14288    if (!SO) { SUMA_S_Err("NULL SO"); SUMA_RETURN(N_nmask); }
14289 
14290    /* Set the flat surface */
14291    if (!SOf) {
14292       if (SO->isSphere) SOf = SO;
14293    }
14294    if (!SOf) { SUMA_S_Err("Have no flat surface to work with"); SUMA_RETURN(N_nmask); }
14295 
14296    /* is this the first time this is called */
14297    if (N_nmask_last == -2) {
14298       SUMA_LH("Initializing");
14299       if (SegDist || mask_record) { SUMA_S_Err("This is not appreciated."); SUMA_RETURN(N_nmask); }
14300       SegDist = SUMA_SegmentDistortion(SO, SOf); /* this function should return a vector of 1s if SO == SOf */
14301       mask_record = (int *)SUMA_calloc(SO->N_Node,sizeof(int));
14302       N_nmask_last = -1;
14303    }
14304 
14305    if (!SegDist || !mask_record) { SUMA_S_Errv("Should not happen here (%p %p) (have you initialized?)\n", SegDist, mask_record); SUMA_RETURN(N_nmask); }
14306    if (!nmask) { SUMA_S_Err("NULL nmask"); SUMA_RETURN(N_nmask); }
14307 
14308    /* Now cleanup the previous record */
14309    for (i=0; i<N_nmask_last; ++i) { nmask[mask_record[i]] = 0; }
14310 
14311    /* Calculate the Equivalent neighborhood distance on the spherical version of the surface */
14312    dnei_sp = dnei * SegDist[cent];
14313    alph = dnei_sp / SOf->SphereRadius;
14314    rsearch = sin(alph/2.0)*2.0*SOf->SphereRadius;
14315    /* Nodes that fall within a sphere of radius rsearch and centered on cent are within dnei_sp on
14316    the sphere. We approximate that they would be within dnei mm from cent on the original surface */
14317    /* N_nmask = SUMA_nodesinsphere2( SOf->NodeList, SOf->N_Node,
14318                                   &(SOf->NodeList[3*cent]), rsearch,
14319                                   mask_record, NULL ); */
14320    SUMA_NODESINSPHERE2( SOf->NodeList, SOf->N_Node, &(SOf->NodeList[3*cent]), rsearch, mask_record, N_nmask);
14321 
14322    SUMA_LHv("Have %d nodes in mask\n", N_nmask);
14323    for (i=0; i<N_nmask; ++i) nmask[mask_record[i]] = 1;
14324    if (LocalHead && cent == 0) {
14325       int ccnt=0;
14326       char oname[100];
14327       FILE *fid=NULL;
14328       sprintf(oname, "neighb_node%d.1D.dset", cent);
14329       fid=fopen(oname,"w");
14330       fprintf(fid, "#neighbors of node %d per function ApproxNeighbors\n", cent);
14331       for (i=0; i<N_nmask; ++i) fprintf(fid, "%d\n", mask_record[i]);
14332       fclose(fid); fid = NULL;
14333       for (i=0; i<SO->N_Node; ++i) { if (nmask[i]) ++ccnt; }
14334       SUMA_LHv("Verified final mask has %d nodes (should be = %d)\n", ccnt, N_nmask);
14335    }
14336    N_nmask_last = N_nmask;
14337 
14338    SUMA_RETURN(N_nmask);
14339 }
14340 
14341 /*!
14342    create a dataset of random values
14343    N_Node (int) number of nodes on surface
14344    nc     (int) number of columns (sub-bricks)
14345    seed   (unsigned int) if 0 then seed = 123456
14346    scale  (float) if !0.0 then use it to scale output.
14347                   unscaled output is 0 to 1
14348    norm   (byte) if 1 then draw samples from a Normal distribution
14349 */
SUMA_RandomDset(int N_Node,int nc,unsigned int seed,float scale,byte norm)14350 SUMA_DSET *SUMA_RandomDset(int N_Node, int nc, unsigned int seed,
14351                            float scale, byte norm)
14352 {
14353    static char FuncName[]={"SUMA_RandomDset"};
14354    SUMA_DSET *dset = NULL;
14355    float *fr=NULL;
14356    int i;
14357 
14358    SUMA_ENTRY;
14359 
14360    if (seed == 0) seed = 123456; /* don't change that one */
14361 
14362    if (!(fr = (float *)SUMA_malloc(sizeof(float)*N_Node*nc))) {
14363       SUMA_S_Crit("Failed to mallocate"); SUMA_RETURN(NULL);
14364    }
14365    srand(seed);
14366    if (norm) {
14367       for (i=0; i<N_Node*nc; ++i) {
14368          fr[i] = (float)(SUMA_GRAN(0.,1.)); /* use a gaussian baby */
14369       }
14370    } else {
14371       for (i=0; i<N_Node*nc; ++i) {
14372          fr[i] = (float)(((double)rand()/(double)RAND_MAX));
14373       }
14374    }
14375 
14376    if (scale) for (i=0; i<N_Node*nc; ++i) fr[i] *= scale;
14377 
14378    if (!(dset = SUMA_far2dset_ns("Blurozovsky", NULL, NULL,
14379                                  &fr, N_Node, nc, 0))) {
14380       SUMA_S_Err("Failed to create random dataset");
14381       SUMA_RETURN(NULL);;
14382    }
14383    if (fr) SUMA_free(fr); fr = NULL;
14384 
14385    SUMA_RETURN(dset);
14386 }
14387 
14388 /************************** Begin QHULL Functions **************************/
14389 /*----------------------------------------------------
14390   A slightly modified version of Bob's qhull_wrap function
14391 
14392   Compute the convex hull of a bunch of 3-vectors
14393   Inputs:
14394     npt = number of vectors
14395     xyz = array of coordinates of 3-vectors;
14396           the i-th vector is stored in
14397             xyz[3*i] xyz[3*i+1] xyz[3*i+2]
14398    fliporient = 0 --> leave triangles as they come out of qhull,
14399                 1 --> flip their orientation
14400                \sa SUMA_OrientTriangles if you are not sure about flipping.
14401    qopt qhull option string. If NULL, default is for a convex hull "QJ i"
14402       Available options are:
14403          "convex_hull" --> "QJ i"
14404          Anything else gets passed to qhull
14405   Output:
14406     *ijk = pointer to malloc()-ed array of triangles;
14407            the j-th triangle is stored in
14408              ijk[3*j] ijk[3*j+1] ijk[3*j+2]
14409            where the integer index i refers to the
14410            i-th 3-vector input
14411 
14412   Return value is the number of triangles.  If this
14413   is zero, something bad happened.
14414 
14415   Example:
14416     int ntri , *tri , nvec ;
14417     float vec[something] ;
14418     ntri = SUMA_qhull_wrap( nvec , vec , &tri, 0 , NULL) ;
14419 
14420   This function just executes the Geometry Center
14421   program qhull to compute the result.  qhull
14422   should be in the user's path, or this function
14423   will fail (return 0).
14424 ------------------------------------------------------*/
14425 
SUMA_qhull_wrap(int npt,float * xyz,int ** ijk,int fliporient,char * qopt)14426 int SUMA_qhull_wrap( int npt , float * xyz , int ** ijk , int fliporient,
14427                      char *qopt)
14428 {
14429    static char FuncName[]={"SUMA_qhull_wrap"};
14430    int ii,jj , nfac , *fac ;
14431    int fd , dim=0; FILE *fp ;
14432    char qbuf[128] ;
14433    SUMA_Boolean LocalHead = NOPE;
14434 
14435    SUMA_ENTRY;
14436 
14437    dim = 3;
14438    if (!qopt) qopt = "QJ i";
14439    else {
14440       if (!strcmp(qopt,"convex_hull")) {
14441          sprintf(qopt, "QJ i");
14442       } else {
14443          SUMA_S_Notev("Have user defined qhull option of: %s\n",qopt);
14444       }
14445    }
14446 
14447    SUMA_RETURN(SUMA_q_wrap(npt, xyz, ijk, fliporient, "qhull", qopt, dim));
14448 }
14449 /*----------------------------------------------------
14450   Compute the triangulation of a bunch of 3-vectors with qdelaunay
14451   Inputs:
14452     npt = number of vectors
14453     xyz = array of coordinates of 3-vectors;
14454           the i-th vector is stored in
14455             xyz[3*i] xyz[3*i+1] xyz[3*i+2]
14456    fliporient = 0 --> leave triangles as they come out of qdelaunay,
14457                 1 --> flip their orientation
14458                \sa SUMA_OrientTriangles if you are not sure about flipping.
14459    qopt qdelaunay option string. If NULL, default is for a "QJ i"
14460       Available options are:
14461          "triangulate_xy" --> "Qt i" triangulating on X Y
14462                      coords only, input xyz still has to have a third dimension)
14463          Anything else gets passed to qdelaunay
14464   Output:
14465     *ijk = pointer to malloc()-ed array of triangles;
14466            the j-th triangle is stored in
14467              ijk[3*j] ijk[3*j+1] ijk[3*j+2]
14468            where the integer index i refers to the
14469            i-th 3-vector input
14470 
14471   Return value is the number of triangles.  If this
14472   is zero, something bad happened.
14473 
14474   Example:
14475     int ntri , *tri , nvec ;
14476     float vec[something] ;
14477     ntri = SUMA_qdelaunay_wrap( nvec , vec , &tri, 0, NULL ) ;
14478 
14479   This function just executes the Geometry Center
14480   program qdelaunay to compute the result. qdelaunay
14481   should be in the user's path, or this function
14482   will fail (return 0).
14483 ------------------------------------------------------*/
SUMA_qdelaunay_wrap(int npt,float * xyz,int ** ijk,int fliporient,char * qopt)14484 int SUMA_qdelaunay_wrap( int npt , float * xyz , int ** ijk , int fliporient,
14485                          char *qopt)
14486 {
14487    static char FuncName[]={"SUMA_qdelaunay_wrap"};
14488    int ii,jj , nfac , *fac ;
14489    int fd , dim=0; FILE *fp ;
14490    char qbuf[128] ;
14491    SUMA_Boolean LocalHead = NOPE;
14492 
14493    SUMA_ENTRY;
14494 
14495    dim = 2;
14496    if (!qopt) qopt = "Qt i";
14497    else {
14498       if (!strcmp(qopt,"triangulate_xy")) {
14499          dim = 2;
14500          sprintf(qopt, "Qt i");
14501       } else {
14502          SUMA_S_Notev("Have user defined qdelaunay option of: %s\n",qopt);
14503       }
14504    }
14505 
14506    SUMA_RETURN(SUMA_q_wrap(npt, xyz, ijk, fliporient, "qdelaunay", qopt, dim));
14507 }
14508 
SUMA_q_wrap(int npt,float * xyz,int ** ijk,int fliporient,char * qprog,char * qopt,int dim)14509 int SUMA_q_wrap( int npt , float * xyz , int ** ijk , int fliporient,
14510                  char *qprog, char *qopt, int dim)
14511 {
14512    static char FuncName[]={"SUMA_q_wrap"};
14513    int ii,jj , nfac , *fac ;
14514    int fd ;
14515    FILE *fp ;
14516    char qbuf[128] ;
14517    SUMA_Boolean LocalHead = NOPE;
14518 
14519 #ifndef DONT_USE_MKSTEMP
14520    char fname[] = "/tmp/afniXXXXXX" ;
14521 #else
14522    char *fname ;
14523 #endif
14524 
14525    SUMA_ENTRY;
14526 
14527    SUMA_LHv("qprog = %s\nqopt = %s\n", qprog, qopt);
14528 
14529    if( npt < 3 || xyz == NULL || ijk == NULL ){
14530       SUMA_S_Errv("bad inputs %d %p %p\n", npt, xyz, ijk) ;
14531       SUMA_RETURN( 0 );
14532    }
14533 
14534 #ifndef DONT_USE_MKSTEMP
14535    fd = mkstemp( fname ) ;
14536    if( fd == -1 ){
14537       SUMA_S_Err(" mkstemp fails\n");
14538       SUMA_RETURN( 0 );
14539    }
14540    fp = fdopen( fd , "w" ) ;
14541    if( fp == NULL ){
14542       SUMA_S_Err(" fdopen fails\n"); close(fd);
14543       SUMA_RETURN( 0 );
14544    }
14545 #else
14546    fname = tmpnam(NULL) ;
14547    if( fname == NULL ){
14548       SUMA_S_Err(" tmpnam fails\n");
14549       SUMA_RETURN( 0 );
14550    }
14551    fp = fopen( fname , "w" ) ;
14552    if( fp == NULL ){
14553       SUMA_S_Err(" fopen fails\n");
14554       SUMA_RETURN( 0 );
14555    }
14556 #endif
14557 
14558    fprintf(fp,"%d\n%d\n",npt, dim) ;
14559    for( ii=0 ; ii < npt ; ii++ ) {
14560       for (jj=0; jj<dim; ++jj) {
14561          fprintf(fp,"%g ",xyz[3*ii+jj]) ;
14562       }
14563       fprintf(fp,"\n");
14564    }
14565 
14566    fclose(fp) ;
14567 
14568    sprintf(qbuf,"%s %s < %s", qprog, qopt, fname) ;
14569    SUMA_LHv("Executing %s\n", qbuf);
14570    fp = popen( qbuf , "r" ) ;
14571    if( fp == NULL ){
14572       SUMA_S_Err(" popen fails\n");
14573       remove(fname);
14574       SUMA_RETURN( 0 );
14575    }
14576 
14577    jj = fscanf(fp,"%d",&nfac) ;
14578    if( jj != 1 || nfac < 1 ){
14579       SUMA_S_Err(" 1st fscanf fails\n"); pclose(fp);
14580       remove(fname);
14581       SUMA_RETURN( 0 );
14582    }
14583 
14584    fac = (int *) malloc( sizeof(int)*3*nfac ) ;
14585    if( fac == NULL ){
14586       SUMA_S_Err(" malloc fails\n"); pclose(fp);
14587       remove(fname);
14588       SUMA_RETURN( 0 );
14589    }
14590 
14591    if (fliporient) {
14592       for( ii=0 ; ii < nfac ; ii++ ){
14593          jj = fscanf(fp,"%d %d %d",fac+(3*ii+2),fac+(3*ii+1),fac+(3*ii)) ;
14594          if( jj < 3 ){
14595             SUMA_S_Errv(" fscanf fails at ii=%d\n",ii) ;
14596             pclose(fp); remove(fname); free(fac); SUMA_RETURN( 0 );
14597          }
14598       }
14599    } else {
14600       for( ii=0 ; ii < nfac ; ii++ ){
14601          jj = fscanf(fp,"%d %d %d",fac+(3*ii),fac+(3*ii+1),fac+(3*ii+2)) ;
14602          if( jj < 3 ){
14603             SUMA_S_Errv(" fscanf fails at ii=%d\n",ii) ;
14604             pclose(fp); remove(fname); free(fac); SUMA_RETURN( 0 );
14605          }
14606       }
14607    }
14608    pclose(fp); remove(fname);
14609 
14610    *ijk = fac ; SUMA_RETURN( nfac );
14611 }
14612 
14613 /************************** END QHULL Functions **************************/
14614 
14615 
14616 #if 0
14617    /************************** BEGIN Branch Functions **************************/
14618    /* these are functions that were ported to support the first version of SUMA_Surf_Plane_Intersect was was to be identical to
14619    Surf_Plane_Intersect. They are left here in case I need them in the future. */
14620 
14621                   /***
14622 
14623                   File : SUMA_FindBranch.c
14624                   Author : Ziad Saad
14625                   Date : Thu Nov 12 16:33:34 CST 1998
14626 
14627                   Purpose :
14628                       This is a C version of the matlab function SUMA_FindBranch2, check out the help
14629                      over there.
14630 
14631                       This version is supposed to be faster than the working previous one called SUMA_FindBranch.c_V1
14632                      If you want to use SUMA_FindBranch.c_V1, you need to rename it to SUMA_FindBranch.c and make the appropriate
14633                      changes in prototype.h
14634 
14635                      the working version of SUMA_FindBranch.c_V1 is in Backup010499 directory and should be used with
14636                      Surf_Plane_Intersect.c_V1
14637 
14638                   Usage :
14639 
14640 
14641                   Input paramters :
14642                             InterMat (int **) pointer to a 2D int array of dimention [IMsz, 4]
14643                            IMsz (int) number or rows in InterMat
14644                            InterNodes (float **) pointer to a 2D float array that contains the
14645                               intersection nodes XYZ coordinates, size of the array is [INsz,3]
14646                            verbose (int) verbose flag (0/1)
14647                            WBsz (int *) the number of elements in WeldedBranch
14648 
14649                   Returns :
14650                             WeldedBranch (SUMA_BRANCH *) is a pointer to structures branch that will contain
14651                               the various branches. You need to pass a pointer only, allocation for this
14652                               pointer should be done from the calling function.
14653                               see : /home/ziad/Programs/C/Z/Zlib/mystructs.h for details on
14654                               the fields of the structures branch
14655 
14656                            NOTE : The function uses static allocation for WeldedBranch
14657                            do not try to free WeldedBranch
14658 
14659 
14660 
14661                   ***/
14662                   SUMA_BRANCH * SUMA_FindBranch (int ** InterMat, int N_InterMat, float ** InterNodes, int ** NodeLoc_in_InterMat, int verbose,  int * WBsz)
14663                   {/*SUMA_FindBranch*/
14664                      int DBG , VeryFirstSeed, Seed, sz_Branch, kk;
14665                      int n_comp = 0, ntmpint, nunqrow , NodeIndex , curnode , BranchIndex;
14666                      int ntmpint2D_V2, N_vunq , brEnd1 , brEnd2 , i, k;
14667                      int tmpint2D_V2[1][2], *v, *vunq;
14668                      int *tmpint, *unqrow, iii, GotSeed;
14669                      static char FuncName[]={"SUMA_FindBranch"};
14670                      float Dprecision;
14671                      static SUMA_BRANCH * branch;
14672                      struct  timeval  start_time, tt_sub, start_time2;
14673                      float DT_WELDSUMA_BRANCH, DT_BUILDSUMA_BRANCH, DT_WELDSUMA_BRANCHONLY ,DT_FINDININTVECT, DT_VUNQ;
14674                      FILE *TimeOut;
14675                      SUMA_Boolean LocalHead = NOPE;
14676 
14677                      SUMA_ENTRY;
14678 
14679                      if (LocalHead) SUMA_disp_dmat (NodeLoc_in_InterMat, 20, 4, 1);
14680 
14681                      /* open a file to output timing info and run some stats on them */
14682                      TimeOut = fopen("FB.TimeOut","a");
14683 
14684                      DBG = 1;
14685                      Dprecision = 0.001;
14686 
14687                      VeryFirstSeed = 0;
14688 
14689                      /* Now you need to find the different branches */
14690 
14691                      Seed = VeryFirstSeed;   /* That should not matter */
14692 
14693                      /* Allocate for branch */
14694                      if (!branch)
14695                         {
14696                            branch = (SUMA_BRANCH *) SUMA_calloc(SUMA_BRANCHMAX, sizeof(SUMA_BRANCH));
14697                            if (!branch )
14698                               {
14699                                  fprintf (SUMA_STDERR, "Error %s: Could not allocate for branch", FuncName);
14700                                  SUMA_RETURN (NULL);
14701                               }
14702                         }
14703 
14704                      if (LocalHead) fprintf(SUMA_STDERR, "%s : Determining branches\n", FuncName);
14705 
14706                      /* Start timer for next function */
14707                      SUMA_etime(&start_time,0);
14708 
14709                      /* initialize the first branch */
14710                      BranchIndex = 0;
14711                      NodeIndex = 0;
14712                      branch[BranchIndex].start = Seed;
14713                      branch[BranchIndex].list[NodeIndex] = branch[BranchIndex].start;
14714                      curnode = branch[BranchIndex].start;
14715                      n_comp = N_InterMat;
14716                      ntmpint2D_V2 = 0;
14717                      while (n_comp)
14718                         {
14719                            /* see if you can find the node */
14720                            /*printf ("curnode = %d, n_comp = %d\n", curnode, n_comp);                   */
14721                            if (NodeLoc_in_InterMat[curnode][2] > -1)
14722                               {
14723                                  tmpint2D_V2[0][0] = NodeLoc_in_InterMat[curnode][2];
14724                                  tmpint2D_V2[0][1] = NodeLoc_in_InterMat[curnode][3];
14725                                  NodeLoc_in_InterMat[curnode][2] = -1;
14726                                  ntmpint2D_V2 = 1;
14727                               }
14728                            else
14729                            if (NodeLoc_in_InterMat[curnode][0] > -1)
14730                               {
14731                                  tmpint2D_V2[0][0] = NodeLoc_in_InterMat[curnode][0];
14732                                  tmpint2D_V2[0][1] = NodeLoc_in_InterMat[curnode][1];
14733                                  NodeLoc_in_InterMat[curnode][0] = -1;
14734                                  ntmpint2D_V2 = 1;
14735                               }
14736                            else
14737                               ntmpint2D_V2 = 0;
14738 
14739                            if (!ntmpint2D_V2)  /* Nothing found */
14740                               {
14741                                  /* store the last point as a stopping point */
14742                                  branch[BranchIndex].last = branch[BranchIndex].list[NodeIndex];
14743                                  branch[BranchIndex].listsz = NodeIndex + 1;
14744 
14745                                  /* start a new branch */
14746                                  /*pick any seed one that does not have a -1 entry in NodeLoc_in_InterMat*/
14747                                  iii = 0;
14748                                  GotSeed = 0;
14749                                  while (!GotSeed)
14750                                  {
14751                                     if (NodeLoc_in_InterMat[iii][2] > -1)
14752                                        {
14753 
14754                                           Seed = InterMat[NodeLoc_in_InterMat[iii][2]][NodeLoc_in_InterMat[iii][3]];
14755                                           GotSeed = 1;
14756                                        }
14757                                     else
14758                                     if (NodeLoc_in_InterMat[iii][0] > -1)
14759                                        {
14760                                           Seed = InterMat[NodeLoc_in_InterMat[iii][0]][NodeLoc_in_InterMat[iii][1]];
14761                                           GotSeed = 1;
14762                                        }
14763                                     else
14764                                        {
14765                                           ++iii;
14766                                           GotSeed = 0;
14767                                        }
14768                                  }
14769                                  ++BranchIndex;
14770                                  NodeIndex=0;
14771                                  branch[BranchIndex].start = Seed;
14772                                  branch[BranchIndex].list[NodeIndex] = branch[BranchIndex].start;
14773                                  curnode = branch[BranchIndex].start;
14774                               }
14775                            else /* That's a normal point, add it */
14776                               {
14777                                  ++NodeIndex;
14778                                  if (tmpint2D_V2[0][1]) /* take the first element */
14779                                     branch[BranchIndex].list[NodeIndex] =
14780                                        InterMat[tmpint2D_V2[0][0]][0];
14781                                     else /* take second element */
14782                                     branch[BranchIndex].list[NodeIndex] =
14783                                        InterMat[tmpint2D_V2[0][0]][1];
14784 
14785                                  /* make the new node current */
14786                                  curnode = branch[BranchIndex].list[NodeIndex];
14787 
14788                                  --n_comp;
14789                               }
14790 
14791                         }
14792 
14793                      /* now store the very last point as a stopping point */
14794 
14795                      branch[BranchIndex].last = branch[BranchIndex].list[NodeIndex];
14796                      branch[BranchIndex].listsz = NodeIndex + 1;
14797 
14798                      sz_Branch = BranchIndex + 1;
14799 
14800                      /* stop timer */
14801                      DT_BUILDSUMA_BRANCH = SUMA_etime(&start_time,1);
14802 
14803                      if (LocalHead) fprintf(SUMA_STDERR, "%s : Welding branches\n", FuncName);
14804 
14805                      /* now, if possible, link the branches together */
14806                      /* Start timer for next function */
14807                      SUMA_etime(&start_time,0);
14808 
14809                      /* initialize some variables */
14810                      v = (int *)SUMA_calloc(2*sz_Branch,sizeof(int));
14811                      if (!v)
14812                         {
14813                            fprintf (SUMA_STDERR, "Error %s: Could not allocate", FuncName);
14814                            SUMA_RETURN (NULL);
14815                         }
14816                      for (i=0;i<sz_Branch;++i)
14817                         {
14818                            v[i] = branch[i].start;
14819                            v[i+sz_Branch] = branch[i].last;
14820                         }
14821 
14822 
14823                      vunq = SUMA_UniqueInt (v, 2*sz_Branch, &N_vunq, 0);
14824 
14825                      for (i=0;i<N_vunq;++i)
14826                         {
14827                            /* find out how many time each end of a branch is used */
14828 
14829                            tmpint = SUMA_Find_inIntVect (v, 2*sz_Branch, vunq[i], &ntmpint);
14830 
14831                            if (ntmpint == 2)
14832                               {
14833                                  /*good, two branches can be joined together */
14834                                  if (tmpint[0] >= sz_Branch)
14835                                     {
14836                                        tmpint[0] = tmpint[0] - sz_Branch;
14837                                        brEnd1 = 1;
14838                                     }
14839                                  else
14840                                     brEnd1 = 0;
14841                                  if (tmpint[1] >= sz_Branch)
14842                                     {
14843                                        tmpint[1] = tmpint[1] - sz_Branch;
14844                                        brEnd2 = 1;
14845                                     }
14846                                  else
14847                                     brEnd2 = 0;
14848 
14849                                  if (tmpint[1] != tmpint[0])
14850                                     {   /*   Path is not circular, join together */
14851 
14852                                        SUMA_WeldBranches (branch, &sz_Branch, tmpint[0] ,tmpint[1] , brEnd1, brEnd2);
14853 
14854                                        for (k=0;k<sz_Branch;++k)
14855                                           {
14856                                              v[k] = branch[k].start;
14857                                              v[k+sz_Branch] = branch[k].last;
14858                                           }
14859                                     }
14860                               }
14861                            SUMA_free(tmpint);
14862                         }
14863 
14864                      /* Now go through and determine which branches are closed loops */
14865                      for (i=0;i<sz_Branch; ++i)
14866                         {
14867                            if (branch[i].start == branch[i].last)
14868                               branch[i].closed = 1;
14869                            else
14870                               branch[i].closed = 0;
14871 
14872                         }
14873 
14874 
14875                      *WBsz = sz_Branch; /* store the number of branches to SUMA_RETURN it */
14876 
14877                      /* stop timer */
14878                      DT_WELDSUMA_BRANCH = SUMA_etime(&start_time,1);
14879 
14880                      if (LocalHead) fprintf(SUMA_STDERR, "%s : Freeing allocation\n", FuncName);
14881 
14882                      SUMA_free(vunq);
14883                      SUMA_free(v);
14884 
14885                      /* Printout timing info on screen */
14886                      if (LocalHead) {
14887                         printf ("\n\t\t%s, time fractions :\n",FuncName);
14888                         printf ("\t\t\tDT_WELDSUMA_BRANCH time: %f sec\n", DT_WELDSUMA_BRANCH);
14889                         printf ("\t\t\t DT_BUILDSUMA_BRANCH percent time : %f sec\n",  DT_BUILDSUMA_BRANCH);
14890                      }
14891                      fprintf(TimeOut, "%f\t%f\n",
14892                         DT_BUILDSUMA_BRANCH, DT_WELDSUMA_BRANCH );
14893 
14894 
14895                      fclose(TimeOut);
14896                      SUMA_RETURN (branch);
14897 
14898                   }/*SUMA_FindBranch*/
14899 
14900                   /***
14901 
14902                   File : SUMA_WeldBranches.c
14903                   Author : Ziad Saad
14904                   Date : Sat Nov 14 19:30:19 CST 1998
14905 
14906                   Purpose :
14907                      mimics the function SUMA_WeldBranches.m, check out the help over there.
14908 
14909                       Except that the SUMA_RETURNeed welded branches are not in the same order as
14910                      those SUMA_RETURNeed by the matlab function
14911 
14912                   Usage :
14913                    void SUMA_WeldBranches ( BRANCH *branch, int *sz_Branch, int brIndx1, int brIndx2 , int brEnd1, int brEnd2 );
14914 
14915 
14916                   Input paramters :
14917                       branch   (BRANCH *)   a vector of structures BRANCH
14918                      sz_Branch   (int *)   pointer to the scalar containing the number of elements of branch
14919                      brIndx1   (int)   index (into branch) of the first branch to weld
14920                      brIndx2    (int)   index (into branch) of the second branch to weld
14921                      brEnd1   (int)   if 0 then weld at start of branch 1
14922                                     if 1 then weld at end of branch 1
14923                      brEnd2   (int) same as brEnd1 but for branch 2
14924 
14925                   Returns :
14926                      nothing, but what it does is weld branch1 to branch2 and puts the welded branch in the position of
14927                      min(branch1, branch2). The returned branch is always one branch shorter than the branch sent into the
14928                      function.
14929 
14930 
14931                   Support :
14932 
14933 
14934 
14935                   Side effects :
14936 
14937 
14938 
14939                   ***/
14940                   void SUMA_WeldBranches ( SUMA_BRANCH *branch, int *sz_Branch, int brIndx1, int brIndx2 , int brEnd1, int brEnd2 )
14941                   {/*SUMA_WeldBranches*/
14942                      SUMA_BRANCH tmp;
14943                      int nlst1, nlst2, k, tmpreplace, tmpmove;
14944                      static char FuncName[]={"SUMA_WeldBranches"};
14945                      SUMA_Boolean LocalHead = NOPE;
14946 
14947                      SUMA_ENTRY;
14948 
14949                      nlst1 = branch[brIndx1].listsz;
14950                      nlst2 = branch[brIndx2].listsz;
14951                      tmp.listsz = nlst1 + nlst2 - 1;
14952 
14953                      if (!brEnd1  && !brEnd2)
14954                         {
14955                            tmp.last = branch[brIndx2].last;
14956                            tmp.start = branch[brIndx1].last;
14957                            for (k= nlst1; k>0; --k)
14958                               tmp.list[nlst1-k] =  branch[brIndx1].list[k-1];
14959                            for (k=1;k<nlst2;++k) /*skip the common element */
14960                               tmp.list[nlst1+k-1] = branch[brIndx2].list[k];
14961                         }
14962                      else if (brEnd1  && brEnd2)
14963                         {
14964                            tmp.last = branch[brIndx2].start;
14965                            tmp.start = branch[brIndx1].start;
14966                            for (k= 0; k <nlst1; ++k)
14967                               tmp.list[k] = branch[brIndx1].list[k];
14968                            for (k=nlst2; k >1 ; --k)
14969                               tmp.list[nlst1+nlst2-k] = branch[brIndx2].list[k-2];
14970                         }
14971                      else if (!brEnd1 && brEnd2)
14972                         {
14973                            tmp.last = branch[brIndx2].start;
14974                            tmp.start = branch[brIndx1].last;
14975                            for (k=nlst1; k > 0; --k)
14976                               tmp.list[nlst1 - k] = branch[brIndx1].list[k-1];
14977                            for (k=nlst2; k > 1; --k)
14978                               tmp.list[nlst1+nlst2-k] = branch[brIndx2].list[k-2];
14979                         }
14980                      else if (brEnd1 && !brEnd2)
14981                         {
14982                            tmp.last = branch[brIndx2].last;
14983                            tmp.start = branch[brIndx1].start;
14984                            for (k=0;k<nlst1;++k)
14985                               tmp.list[k] = branch[brIndx1].list[k];
14986                            for (k=0;k<nlst2-1;++k)
14987                               tmp.list[nlst1+k] = branch[brIndx2].list[k+1];
14988                         }
14989 
14990                      /* decide where to put the welded branch and whether to move the last branch (or the one before it) up */
14991                      if (brIndx1 > brIndx2)
14992                         {
14993                            tmpreplace = brIndx2;
14994                            tmpmove = brIndx1;
14995                         }
14996                      else
14997                         {
14998                            tmpreplace = brIndx1;
14999                            tmpmove = brIndx2;
15000                         }
15001                      /* replace branch[tmpreplace]   with tmp */
15002                      branch[tmpreplace].start = tmp.start;
15003                      branch[tmpreplace].last = tmp.last;
15004                      branch[tmpreplace].listsz = tmp.listsz;
15005                      for(k=0;k<branch[tmpreplace].listsz;++k)
15006                         branch[tmpreplace].list[k] = tmp.list[k];
15007 
15008                      /*copy branch[sz_Branch-1] (the last branch) into position brIndx2 */
15009                      /*by now, tmpmove is definetly larger than tmpreplace*/
15010                      /* if tmpmove is not the last branch, then move the last branch up one*/
15011                      /* otherwise, no need to move anything */
15012 
15013                      if (tmpmove < *sz_Branch-1)
15014                         {
15015                            branch[tmpmove].start = branch[*sz_Branch-1].start;
15016                            branch[tmpmove].last = branch[*sz_Branch-1].last;
15017                            branch[tmpmove].listsz = branch[*sz_Branch-1].listsz;
15018                            for(k=0;k<branch[tmpmove].listsz;++k)
15019                               branch[tmpmove].list[k] = branch[*sz_Branch-1].list[k];
15020                         }
15021 
15022                      /* change the size of the branch vector */
15023                      --*sz_Branch;
15024 
15025                      SUMA_RETURNe;
15026 
15027                   }/*SUMA_WeldBranches*/
15028 
15029    /************************** END Branch Functions **************************/
15030 
15031 #endif
15032 
15033 /* Find the polygon that cuts a cube.
15034    The method is based on the Salama & Kolb algorithm
15035    "A vertex Program for Efficient Box-Plane Intersection" 2005
15036 
15037    cam   (float *): Coordinates of Camera location
15038                     Used in concert with plane to identify closest
15039                     vertex of box
15040    PlEq  (float *): Equation of slicing plane
15041    cvert (float *): Coordinates of 8 vertices forming cube
15042    p     (float *): Coordinates of 6 points forming intersection
15043                     Some points may be duplicates.
15044    Returns nhits (int): Number of unique points hit
15045    \sa SUMA_BoxSlice
15046 */
SUMA_PlaneBoxIntersect(float * cam,float * PlEq,float * cvert,float p[18])15047 int SUMA_PlaneBoxIntersect( float *cam, float *PlEq,
15048                             float *cvert, float p[18])
15049 {
15050    static char FuncName[]={"SUMA_PlaneBoxIntersect"};
15051       /* Cube vertex ordering depending on closest vertex */
15052    int vertord[8][8]={ { 0 , 4 , 1 , 3 , 7 , 5 , 2 , 6 },
15053                        { 1 , 2 , 0 , 5 , 6 , 3 , 4 , 7 },
15054                        { 2 , 6 , 3 , 1 , 5 , 7 , 0 , 4 },
15055                        { 3 , 0 , 2 , 7 , 4 , 1 , 6 , 5 },
15056                        { 4 , 7 , 5 , 0 , 3 , 6 , 1 , 2 },
15057                        { 5 , 1 , 4 , 6 , 2 , 0 , 7 , 3 },
15058                        { 6 , 5 , 7 , 2 , 1 , 4 , 3 , 0 },
15059                        { 7 , 3 , 6 , 4 , 0 , 2 , 5 , 1 } };
15060 
15061    float cam0[3] = { 1.0, 1.0, 1.0 }, *pinter, Eq3;
15062    float dd, mindist, *V0, *V1, *V2, *V3, *V4, *V5, *V6, *V7;
15063    int ii, icl, nhits=0, hit;
15064    SUMA_Boolean LocalHead = NOPE;
15065 
15066    SUMA_ENTRY;
15067 
15068    if (!cam) cam = (float *)cam0;
15069 
15070    Eq3 = PlEq[3]; /* Save original offset */
15071    SUMA_SHIFT_PLANE_TO_P(PlEq, cam); /* Shift to pass by cam */
15072 
15073    /* Find closest vertex to plane through cam */
15074    ii = 0; icl = 0;
15075    mindist = SUMA_DIST_FROM_PLANE_EQ(PlEq, cvert);
15076    while (ii<8) {
15077       pinter = cvert+3*ii;
15078       dd = SUMA_DIST_FROM_PLANE_EQ(PlEq, pinter);
15079       if (dd < mindist) {
15080          icl = ii;
15081          mindist = dd;
15082       }
15083       ++ii;
15084    }
15085    /* Put plane back where it was */
15086    PlEq[3] = Eq3;
15087 
15088    SUMA_LH("Closest vertex to %f %f %f is %d",
15089            cam[0], cam[1], cam[2], icl);
15090 
15091    V0 = cvert+3*vertord[icl][0];
15092    V1 = cvert+3*vertord[icl][1];
15093    V2 = cvert+3*vertord[icl][2];
15094    V3 = cvert+3*vertord[icl][3];
15095    V4 = cvert+3*vertord[icl][4];
15096    V5 = cvert+3*vertord[icl][5];
15097    V6 = cvert+3*vertord[icl][6];
15098    V7 = cvert+3*vertord[icl][7];
15099 
15100    /* Check for P0 intersection */
15101    pinter = p;
15102       SUMA_SEGMENT_PLANE_INTERSECT(V0, V1, PlEq, hit, pinter);
15103    if (!hit) {
15104       SUMA_SEGMENT_PLANE_INTERSECT(V1, V4, PlEq, hit, pinter);
15105    }
15106    if (!hit) {
15107       SUMA_SEGMENT_PLANE_INTERSECT(V4, V7, PlEq, hit, pinter);
15108    }
15109    if (!hit) SUMA_RETURN(nhits);
15110    ++nhits;
15111 
15112    /* Check for P2 intersection */
15113    pinter = p+6;
15114       SUMA_SEGMENT_PLANE_INTERSECT(V0, V2, PlEq, hit, pinter);
15115    if (!hit) {
15116       SUMA_SEGMENT_PLANE_INTERSECT(V2, V5, PlEq, hit, pinter);
15117    }
15118    if (!hit) {
15119       SUMA_SEGMENT_PLANE_INTERSECT(V5, V7, PlEq, hit, pinter);
15120    }
15121    if (!hit) SUMA_RETURN(nhits);
15122    ++nhits;
15123 
15124    /* Check for P4 intersection */
15125    pinter = p+12;
15126       SUMA_SEGMENT_PLANE_INTERSECT(V0, V3, PlEq, hit, pinter);
15127    if (!hit) {
15128       SUMA_SEGMENT_PLANE_INTERSECT(V3, V6, PlEq, hit, pinter);
15129    }
15130    if (!hit) {
15131       SUMA_SEGMENT_PLANE_INTERSECT(V6, V7, PlEq, hit, pinter);
15132    }
15133    if (!hit) SUMA_RETURN(nhits);
15134    ++nhits;
15135 
15136    /* Check for P1 intersection */
15137    pinter = p+3;
15138       SUMA_SEGMENT_PLANE_INTERSECT(V1, V5, PlEq, hit, pinter);
15139    if (!hit) { /* resort to P0 */
15140       pinter[0] = p[0]; pinter[1] = p[1]; pinter[2] = p[2];
15141    } else ++nhits;
15142 
15143    /* Check for P3 intersection */
15144    pinter = p+9;
15145       SUMA_SEGMENT_PLANE_INTERSECT(V2, V6, PlEq, hit, pinter);
15146    if (!hit) { /* resort to P2 */
15147       pinter[0] = p[6]; pinter[1] = p[7]; pinter[2] = p[8];
15148    } else ++nhits;
15149 
15150    /* Check for P5 intersection */
15151    pinter = p+15;
15152       SUMA_SEGMENT_PLANE_INTERSECT(V3, V4, PlEq, hit, pinter);
15153    if (!hit) { /* resort to P4 */
15154       pinter[0] = p[12]; pinter[1] = p[13]; pinter[2] = p[14];
15155    } else ++nhits;
15156 
15157    if (LocalHead) {
15158       for (ii=0; ii<6; ++ii) {
15159          fprintf(stderr,"P%d %f %f %f\n", ii, p[3*ii], p[3*ii+1], p[3*ii+2]);
15160       }
15161    }
15162 
15163    SUMA_RETURN(nhits);
15164 }
15165 
15166 /*! Slice a box along a plane.
15167    cam   (float *): Coordinates of Camera location
15168                     Used in concert with plane to identify closest
15169                     vertex of box (3x1 vals)
15170    PlEq  (float *): Equation of slicing plane (4x1 vals)
15171                     Note that PlEq[3] will not be used, slicing will
15172                     go from the farthest point on the screeen to the
15173                     closest.
15174    cvert (float *): Coordinates of 8 vertices forming cube
15175                     (8 xyz triplets -- 24x1)
15176    pv    (float *): To contain coordinates of 6 points forming intersection
15177                     for each slice. Note that some slices may
15178                     have duplicate points.
15179                     (xyz0_slc0 xyz1_slc0 ... xyz5_slc0
15180                      xyz0_slc1 xyz1_slc1 ... xyz5_slc1
15181                      ...       ...       ... ...
15182                      xyz0_slcK xyz1_slcK ... xyz5_slcK)
15183                      with K = N_slc -1
15184                      Total space allocate for P is (18*N_slcx1)
15185    hits  (int *): To contain number of hits for each slice
15186                      Total space allocated for hits (N_slcx1)
15187    PlOff  (float *): To contain the offset of the plane (PlEq[3])
15188                      at the slice.
15189    N_slc (int): Number of slices to create.
15190 
15191    Returns maximum nuber of hits across all slices (0--6).
15192    -1 for error.
15193    If only requesting PlOff (i.e. pv and hits are NULL) then
15194    1 means function ran OK. -1 means bad news.
15195 
15196    \sa SUMA_PlaneBoxIntersect()
15197 */
SUMA_PlaneBoxSlice(float * cam,float * PlEq,float * cvert,float * pv,int * hits,float * PlOff,int N_slc)15198 int SUMA_PlaneBoxSlice( float *cam, float *PlEq,
15199                         float *cvert,
15200                         float *pv, int *hits, float *PlOff, int N_slc)
15201 {
15202    static char FuncName[]={"SUMA_PlaneBoxSlice"};
15203       /* Cube vertex ordering depending on closest vertex */
15204    int vertord[8][8]={ { 0 , 4 , 1 , 3 , 7 , 5 , 2 , 6 },
15205                        { 1 , 2 , 0 , 5 , 6 , 3 , 4 , 7 },
15206                        { 2 , 6 , 3 , 1 , 5 , 7 , 0 , 4 },
15207                        { 3 , 0 , 2 , 7 , 4 , 1 , 6 , 5 },
15208                        { 4 , 7 , 5 , 0 , 3 , 6 , 1 , 2 },
15209                        { 5 , 1 , 4 , 6 , 2 , 0 , 7 , 3 },
15210                        { 6 , 5 , 7 , 2 , 1 , 4 , 3 , 0 },
15211                        { 7 , 3 , 6 , 4 , 0 , 2 , 5 , 1 } };
15212 
15213    float *pinter, *p, Eqor3, Eqcl3, Eqfr3, *Vfr, *Vcl;
15214    float dd, mindist, maxdist, *V0, *V1, *V2, *V3, *V4, *V5, *V6, *V7, stp;
15215    int ii, icl, ifr, nhits=0, hit, nn, maxhits = -1, OnlyPlOff=0;
15216    SUMA_Boolean LocalHead = NOPE;
15217 
15218    SUMA_ENTRY;
15219 
15220    if (!cam || !PlEq || !cvert) SUMA_RETURN(-1);
15221 
15222    if ((pv && !hits) || (!pv && hits)) {
15223       SUMA_S_Err("Must have both or none of pv and hits");
15224       SUMA_RETURN(-1);
15225    }
15226    if (!pv && !hits && !PlOff) {
15227       SUMA_S_Err("Nothing to do!");
15228       SUMA_RETURN(-1);
15229    }
15230    if (!pv && !hits) {
15231       OnlyPlOff = 1;
15232    }
15233 
15234    Eqor3 = PlEq[3]; /* Save original offset */
15235    SUMA_SHIFT_PLANE_TO_P(PlEq, cam); /* Shift to pass by cam */
15236 
15237    /* Find closest vertex and farthest vertices to plane through cam */
15238    ii = 0; icl = 0; ifr = 0;
15239    mindist = maxdist = SUMA_DIST_FROM_PLANE_EQ(PlEq, cvert);
15240    while (ii<8) {
15241       pinter = cvert+3*ii;
15242       dd = SUMA_DIST_FROM_PLANE_EQ(PlEq, pinter);
15243       if (dd < mindist) {
15244          icl = ii;
15245          mindist = dd;
15246       }
15247       if (dd > maxdist) {
15248          ifr = ii;
15249          maxdist = dd;
15250       }
15251       ++ii;
15252    }
15253 
15254    SUMA_LH("Closest  vertex to %f %f %f is %d"
15255            "Farthest vertex to %f %f %f is %d",
15256            cam[0], cam[1], cam[2], icl,
15257            cam[0], cam[1], cam[2], ifr);
15258 
15259 
15260    V0 = cvert+3*vertord[icl][0];
15261    V1 = cvert+3*vertord[icl][1];
15262    V2 = cvert+3*vertord[icl][2];
15263    V3 = cvert+3*vertord[icl][3];
15264    V4 = cvert+3*vertord[icl][4];
15265    V5 = cvert+3*vertord[icl][5];
15266    V6 = cvert+3*vertord[icl][6];
15267    V7 = cvert+3*vertord[icl][7];
15268 
15269    /* Shift plane to closest point */
15270    Vcl = cvert+3*vertord[icl][0];
15271    SUMA_SHIFT_PLANE_TO_P(PlEq, Vcl);
15272    Eqcl3 = PlEq[3];
15273    /* Shift plane to farthest point */
15274    Vfr = cvert+3*vertord[ifr][0];
15275    SUMA_SHIFT_PLANE_TO_P(PlEq, Vfr);
15276    Eqfr3 = PlEq[3];
15277    /* Compute the step */
15278    stp = (Eqcl3 - Eqfr3)/N_slc;
15279 
15280    if (OnlyPlOff) {
15281       for (nn=0; nn<N_slc; ++nn) {
15282          PlEq[3]+=stp;
15283          PlOff[nn] = PlEq[3];
15284       }
15285       SUMA_RETURN(1);
15286    }
15287 
15288    for (nn=0; nn<N_slc; ++nn) {
15289       PlEq[3]+=stp;
15290 
15291       /* Record plane location */
15292       if (PlOff) PlOff[nn] = PlEq[3];
15293 
15294       nhits = 0;
15295 
15296       /* Check for P0 intersection */
15297       p = pv+18*nn;
15298       pinter = p;
15299          SUMA_SEGMENT_PLANE_INTERSECT(V0, V1, PlEq, hit, pinter);
15300       if (!hit) {
15301          SUMA_SEGMENT_PLANE_INTERSECT(V1, V4, PlEq, hit, pinter);
15302       }
15303       if (!hit) {
15304          SUMA_SEGMENT_PLANE_INTERSECT(V4, V7, PlEq, hit, pinter);
15305       }
15306       if (!hit) { hits[nn]=nhits; continue; }
15307       ++nhits;
15308 
15309       /* Check for P2 intersection */
15310       pinter = p+6;
15311          SUMA_SEGMENT_PLANE_INTERSECT(V0, V2, PlEq, hit, pinter);
15312       if (!hit) {
15313          SUMA_SEGMENT_PLANE_INTERSECT(V2, V5, PlEq, hit, pinter);
15314       }
15315       if (!hit) {
15316          SUMA_SEGMENT_PLANE_INTERSECT(V5, V7, PlEq, hit, pinter);
15317       }
15318       if (!hit) { hits[nn]=nhits; continue; }
15319       ++nhits;
15320 
15321       /* Check for P4 intersection */
15322       pinter = p+12;
15323          SUMA_SEGMENT_PLANE_INTERSECT(V0, V3, PlEq, hit, pinter);
15324       if (!hit) {
15325          SUMA_SEGMENT_PLANE_INTERSECT(V3, V6, PlEq, hit, pinter);
15326       }
15327       if (!hit) {
15328          SUMA_SEGMENT_PLANE_INTERSECT(V6, V7, PlEq, hit, pinter);
15329       }
15330       if (!hit) { hits[nn]=nhits; continue; }
15331       ++nhits;
15332 
15333       /* Check for P1 intersection */
15334       pinter = p+3;
15335          SUMA_SEGMENT_PLANE_INTERSECT(V1, V5, PlEq, hit, pinter);
15336       if (!hit) { /* resort to P0 */
15337          pinter[0] = p[0]; pinter[1] = p[1]; pinter[2] = p[2];
15338       } else ++nhits;
15339 
15340       /* Check for P3 intersection */
15341       pinter = p+9;
15342          SUMA_SEGMENT_PLANE_INTERSECT(V2, V6, PlEq, hit, pinter);
15343       if (!hit) { /* resort to P2 */
15344          pinter[0] = p[6]; pinter[1] = p[7]; pinter[2] = p[8];
15345       } else ++nhits;
15346 
15347       /* Check for P5 intersection */
15348       pinter = p+15;
15349          SUMA_SEGMENT_PLANE_INTERSECT(V3, V4, PlEq, hit, pinter);
15350       if (!hit) { /* resort to P4 */
15351          pinter[0] = p[12]; pinter[1] = p[13]; pinter[2] = p[14];
15352       } else ++nhits;
15353 
15354       if (LocalHead) {
15355          for (ii=0; ii<6; ++ii) {
15356             fprintf(stderr,"P%d %f %f %f\n", ii, p[3*ii], p[3*ii+1], p[3*ii+2]);
15357          }
15358       }
15359 
15360       hits[nn] = nhits;
15361    }
15362    maxhits = -1;
15363    for (nn=0; nn<N_slc; ++nn) if (maxhits < hits[nn]) maxhits = hits[nn];
15364    SUMA_RETURN(maxhits);
15365 }
15366 
SUMA_AllocMaskEval_Params(void)15367 SUMA_MASK_EVAL_PARAMS *SUMA_AllocMaskEval_Params(void)
15368 {
15369    static char FuncName[]={"SUMA_AllocMaskEval_Params"};
15370    SUMA_MASK_EVAL_PARAMS *mep = NULL;
15371    int i;
15372 
15373    SUMA_ENTRY;
15374 
15375    mep = (SUMA_MASK_EVAL_PARAMS *)
15376                SUMA_calloc(1,sizeof(SUMA_MASK_EVAL_PARAMS));
15377    mep->N_vals = 0;
15378    memset(mep->varcol, 0,26*4*sizeof(byte));
15379    memset(mep->varsused,0,26*sizeof(byte));
15380    for (i=0; i<26; ++i) mep->varsmdo[i][0]='\0';
15381    mep->mdoused[0] = '\0';
15382    mep->allvarsineq[0]='\0';
15383    mep->marr = NULL;
15384    mep->expr=NULL;
15385    SUMA_RETURN(mep);
15386 }
15387 
SUMA_FreeMaskEval_Params(SUMA_MASK_EVAL_PARAMS * mep)15388 SUMA_MASK_EVAL_PARAMS *SUMA_FreeMaskEval_Params(SUMA_MASK_EVAL_PARAMS *mep)
15389 {
15390    static char FuncName[]={"SUMA_FreeMaskEval_Params"};
15391    int i = 0;
15392 
15393    SUMA_ENTRY;
15394 
15395    if (!mep) SUMA_RETURN(NULL);
15396    if (mep->marr) {
15397       for (i=0; i<26; ++i) SUMA_ifree(mep->marr);
15398       SUMA_ifree(mep->marr);
15399    }
15400 
15401    SUMA_ifree(mep->expr);
15402    SUMA_free(mep);
15403 
15404    SUMA_RETURN(NULL);
15405 }
15406 
SUMA_PrepMaskEval_Params(char * expr,int N_vals,SUMA_MASK_EVAL_PARAMS ** mepp)15407 SUMA_Boolean SUMA_PrepMaskEval_Params(char *expr, int N_vals,
15408                                       SUMA_MASK_EVAL_PARAMS **mepp)
15409 {
15410    static char FuncName[]={"SUMA_PrepMaskEval_Params"};
15411    int ido, ivar, i, n;
15412    SUMA_ALL_DO *ado=NULL;
15413    SUMA_MaskDO *mdo=NULL;
15414    char *c=NULL, exptmp[128]={""};
15415    SUMA_MASK_EVAL_PARAMS *mep=NULL;
15416    SUMA_Boolean LocalHead = NOPE;
15417 
15418    SUMA_ENTRY;
15419 
15420    if (N_vals <= 0 || !mepp || !expr) {
15421       SUMA_RETURN(NOPE);
15422    }
15423    if (*mepp) {
15424       mep = *mepp;
15425    } else {
15426       mep =
15427       *mepp = mep;
15428    }
15429 
15430    if (!strcmp(expr,"AND") || !strcmp(expr,"OR")) {
15431       memset(mep->varsused, 0, 26*sizeof(byte));
15432       n = 0;
15433       for (ido=0; ido<SUMAg_N_DOv; ++ido) {
15434          ado = (SUMA_ALL_DO *)SUMAg_DOv[ido].OP;
15435          if (ado->do_type == MASK_type &&
15436              !MDO_IS_SHADOW((SUMA_MaskDO *)ado)) {
15437             mdo = (SUMA_MaskDO *)ado;
15438             ivar = mdo->varname[0]-'a';
15439             if (ivar >=0 && ivar < 26 && !mep->varsused[ivar]) {
15440                mep->varsused[ivar] = 1;
15441                exptmp[n++] = mdo->varname[0];
15442                if (expr[0]=='A') exptmp[n++] = '&';
15443                else exptmp[n++] = '|';
15444             }
15445          }
15446       }
15447       if (n > 1 && (exptmp[n-1] == '|' || exptmp[n-1] == '&')) {
15448          exptmp[n-1] = '\0';
15449       } else exptmp[n] = '\0';
15450       SUMA_STRING_REPLACE(mep->expr, expr);
15451       SUMA_LH("Expression now:%s ", mep->expr);
15452    } else {
15453       SUMA_STRING_REPLACE(mep->expr, expr);
15454    }
15455 
15456    /* What variables are needed? */
15457    c = expr;
15458    memset(mep->varsused, 0, 26*sizeof(byte));
15459    memset(mep->varcol, 0, 26*4*sizeof(byte));
15460    while (*c != '\0') {
15461       if (*c >= 'a' && *c <='z') mep->varsused[*c-'a']=1;
15462       ++c;
15463    }
15464    mep->allvarsineq[0]='\0';
15465    for (n=0,i=0; i<26; ++i) {
15466       if (mep->varsused[i]) mep->allvarsineq[n]=i+'a';
15467    }
15468    mep->varsused[i]='\0';
15469 
15470    /* How does N_vals square with what was available */
15471    if (N_vals != mep->N_vals) {
15472       /* Must cleanup */
15473       if (mep->marr) {
15474          for (i=0; i<26; ++i) SUMA_ifree(mep->marr[i]);
15475       }
15476       mep->N_vals = N_vals;
15477    }
15478 
15479    if (!mep->marr) {
15480       mep->marr = (byte**)SUMA_calloc(26, sizeof(byte*));
15481    }
15482    /* Now allocate for what we need, free what we don't */
15483    for (i=0; i<26; ++i) {
15484       if (mep->varsused[i]) {
15485          if (!mep->marr[i]) {
15486             mep->marr[i] = (byte *)SUMA_calloc(mep->N_vals, sizeof(byte));
15487          } else {
15488             memset(mep->marr[i], 0, mep->N_vals*sizeof(byte));
15489          }
15490          if (!mep->marr[i]){
15491             SUMA_S_Err("Failure of love or compassion.");
15492             SUMA_RETURN(NOPE);
15493          }
15494       } else {
15495          /* save space */
15496          if (mep->marr[i]) SUMA_ifree(mep->marr[i]);
15497       }
15498    }
15499 
15500    mep->mdoused[0] = '\0';
15501 
15502    for (ido=0; ido<SUMAg_N_DOv; ++ido) {
15503       ado = (SUMA_ALL_DO *)SUMAg_DOv[ido].OP;
15504       if (ado->do_type == MASK_type &&
15505           !MDO_IS_SHADOW((SUMA_MaskDO *)ado)) {
15506          mdo = (SUMA_MaskDO *)ado;
15507          ivar = mdo->varname[0]-'a';
15508          if (ivar >=0 && ivar < 26) {
15509             if ( mep->varsused[ivar]) {
15510                SUMA_LH("TDO %s, var. %c in use in %s\n",
15511                         ADO_LABEL(ado), mdo->varname[0], expr);
15512                strcpy(mep->varsmdo[ivar], ADO_ID(ado));
15513                if (!strstr(mep->mdoused, ADO_ID(ado)))
15514                                  strcat(mep->mdoused,ADO_ID(ado));
15515                mep->varcol[4*ivar+0] =
15516                   (byte)SUMA_MIN_PAIR(255,mdo->dcolv[0]*255);
15517                mep->varcol[4*ivar+1] =
15518                   (byte)SUMA_MIN_PAIR(255,mdo->dcolv[1]*255);
15519                mep->varcol[4*ivar+2] =
15520                   (byte)SUMA_MIN_PAIR(255,mdo->dcolv[2]*255);
15521                mep->varcol[4*ivar+3] =
15522                   (byte)SUMA_MIN_PAIR(255,mdo->dcolv[3]*255);
15523             } else {
15524                mep->varsmdo[ivar][0]='\0';
15525             }
15526          } else {
15527             SUMA_S_Warn("Bad variable name: %s", mdo->varname);
15528          }
15529       }
15530    }
15531 
15532    SUMA_RETURN(YUP);
15533 }
15534 
15535 
SUMA_TractMasksIntersect(SUMA_TractDO * TDO,char * expr)15536 int SUMA_TractMasksIntersect(SUMA_TractDO *TDO, char *expr)
15537 {
15538    static char FuncName[]={"SUMA_TractMasksIntersect"};
15539    int ido, kk=0, N_OneMask=0;
15540    byte *OneMask=NULL;
15541    SUMA_ALL_DO *ado=NULL;
15542 
15543    SUMA_Boolean LocalHead = NOPE;
15544 
15545    SUMA_ENTRY;
15546 
15547    if (!expr || expr[0]=='\0') expr = "or";
15548    if (!TDO) SUMA_RETURN(-1);
15549 
15550    if (TDO->MaskStateID == SUMAg_CF->X->MaskStateID) {
15551       SUMA_LH("MaskStateID matches, nothing to update");
15552       SUMA_RETURN(TDO->N_tmask);
15553    }
15554 
15555    if (!strcasecmp(expr,"or")) {
15556       SUMA_LH("OR combo");
15557       if (TDO->tmask) memset(TDO->tmask, 0, sizeof(byte)*TDO_N_TRACTS(TDO));
15558       TDO->N_tmask = 0;
15559       SUMA_ifree(TDO->tcols); TDO->usetcols = 0;
15560       for (ido=0; ido<SUMAg_N_DOv; ++ido) {
15561          ado = (SUMA_ALL_DO *)SUMAg_DOv[ido].OP;
15562          if (ado->do_type == MASK_type &&
15563              !MDO_IS_SHADOW((SUMA_MaskDO *)ado)) {
15564             SUMA_LH("Computing intersection with %s", ADO_LABEL(ado));
15565             TDO->N_tmask += SUMA_TractMaskIntersect(TDO, (SUMA_MaskDO *)ado,
15566                                                &TDO->tmask);
15567             SUMA_LH("Now have %d tracts in mask", TDO->N_tmask);
15568          }
15569       }
15570       TDO->MaskStateID = SUMAg_CF->X->MaskStateID;
15571       SUMA_RETURN(TDO->N_tmask);
15572    } else if (!strcasecmp(expr,"and")) {
15573       SUMA_LH("AND combo");
15574       TDO->N_tmask = 0;
15575       SUMA_ifree(TDO->tcols); TDO->usetcols = 0;
15576       OneMask = (byte *)SUMA_calloc(TDO_N_TRACTS(TDO), sizeof(byte));
15577       if (!OneMask) {
15578          SUMA_S_Crit("Failed to allocate");
15579          SUMA_RETURN(0);
15580       }
15581       for (ido=0; ido<SUMAg_N_DOv; ++ido) {
15582          ado = (SUMA_ALL_DO *)SUMAg_DOv[ido].OP;
15583          if (ado->do_type == MASK_type &&
15584              !MDO_IS_SHADOW((SUMA_MaskDO *)ado)) {
15585             SUMA_LH("Computing intersection with %s", ADO_LABEL(ado));
15586             N_OneMask = SUMA_TractMaskIntersect(TDO,
15587                                                 (SUMA_MaskDO *)ado, &OneMask);
15588             if (N_OneMask > 0) {
15589                /* something is there */
15590                if (!TDO->tmask) { /* Steal the pointer */
15591                   TDO->tmask = OneMask; OneMask = NULL;
15592                } else { /* Do the and operation */
15593                   for (kk=0; kk<TDO_N_TRACTS(TDO); ++kk) {
15594                      if (TDO->tmask[kk] && OneMask[kk]) TDO->tmask[kk]=1;
15595                      else TDO->tmask[kk]=0;
15596                   }
15597                   memset(OneMask, 0, sizeof(byte)*TDO_N_TRACTS(TDO));
15598                }
15599             } else {/* Nothing to keep, get out NOW */
15600                if (TDO->tmask) {
15601                   memset(TDO->tmask, 0, sizeof(byte)*TDO_N_TRACTS(TDO));
15602                }
15603                if (OneMask != TDO->tmask) SUMA_ifree(OneMask);
15604                TDO->N_tmask = 0;
15605                SUMA_RETURN(TDO->N_tmask);
15606             }
15607          }
15608       }
15609       /* Count values in the mask */
15610       TDO->N_tmask = 0;
15611       for (kk=0; kk<TDO_N_TRACTS(TDO); ++kk) {
15612          if (TDO->tmask[kk]) ++TDO->N_tmask;
15613       }
15614       SUMA_ifree(OneMask);
15615       TDO->MaskStateID = SUMAg_CF->X->MaskStateID;
15616       SUMA_RETURN(TDO->N_tmask);
15617    } else {
15618       /* initialize the expression params */
15619       SUMA_LH("Initializing Mask Eval Params");
15620       if (!SUMA_PrepMaskEval_Params(expr, TDO_N_TRACTS(TDO), &(TDO->mep))) {
15621          SUMA_S_Err("Failed to prep eval params");
15622          SUMA_RETURN(0);
15623       }
15624 
15625       /* Now fill up the intersection masks */
15626       N_OneMask=0;
15627       for (kk=0; kk<26; ++kk) {
15628          if (TDO->mep->varsused[kk]) {
15629             if (!(ado = SUMA_whichADOg(TDO->mep->varsmdo[kk]))) {
15630                SUMA_SLP_Err("Variable %c has no mask associated with it."
15631                             "Expression usage turned off",
15632                             'a'+kk);
15633                SUMA_Set_UseMaskEval(0, 0, 1);
15634                if (LocalHead) SUMA_DUMP_TRACE("Bad Equation");
15635                SUMA_RETURN(0);
15636             }
15637             SUMA_LH("Computing intersection with %s", ADO_LABEL(ado));
15638             N_OneMask += SUMA_TractMaskIntersect(TDO,
15639                               (SUMA_MaskDO *)ado, &(TDO->mep->marr[kk]));
15640          }
15641       }
15642 
15643       if (N_OneMask) {
15644          /* Evaluate the expression, etc, etc, */
15645          SUMA_LH("Evaluating expression at all tracts");
15646          if (!TDO->tcols)
15647             TDO->tcols = (byte *)SUMA_calloc(TDO_N_TRACTS(TDO)*4, sizeof(byte));
15648          TDO->usetcols = 1;
15649          if (!SUMA_bool_mask_eval(TDO_N_TRACTS(TDO), 26, TDO->mep->marr,
15650                                   expr, TDO->tmask, TDO->mep->varcol,
15651                                   TDO->tcols, NULL)) {
15652             SUMA_S_Err("Failed to evaluate expr");
15653             SUMA_RETURN(0);
15654          }
15655          TDO->N_tmask = 0;
15656          for (kk=0; kk<TDO_N_TRACTS(TDO); ++kk) {
15657             if (TDO->tmask[kk]) ++TDO->N_tmask;
15658          }
15659 
15660          TDO->MaskStateID = SUMAg_CF->X->MaskStateID;
15661          SUMA_RETURN(TDO->N_tmask);
15662       } else {
15663          SUMA_LH("Nothing at all?");
15664          SUMA_RETURN(TDO->N_tmask);
15665       }
15666 
15667    }
15668 
15669    SUMA_RETURN(TDO->N_tmask);
15670 }
15671 
15672 
15673 /* Find tracts intersecting a mask.
15674    TDO (SUMA_TractDO *) The tract object for which intersection is to be
15675                         computed
15676    MDO (SUMA_MaskDO *) The mask
15677    IsInp (byte **) Pointer to contain the TDO_N_TRACTS(TDO) byte values
15678                    such that if (*InInp[kk]) then tract kk intersected the
15679                    mask object.
15680                    If *InInp == NULL, the function allocates a new pointer,
15681                    otherwise it uses what the user passed. This way, if a
15682                    tract has been marked as intersected by a previous
15683                    MDO, it is not revisited here.
15684    The function retuns the number of NEW intersections encountered in this call.
15685 */
SUMA_TractMaskIntersect(SUMA_TractDO * TDO,SUMA_MaskDO * MDO,byte ** IsInp)15686 int SUMA_TractMaskIntersect(SUMA_TractDO *TDO, SUMA_MaskDO *MDO, byte **IsInp)
15687 {
15688    static char FuncName[]={"SUMA_TractMaskIntersect"};
15689    byte *IsIn=NULL;
15690    int issp = 0, knet, io3, io, n, k, id, N_pts, N_IsIn = -1, itt1;
15691    TAYLOR_BUNDLE *tb=NULL;
15692    TAYLOR_TRACT *tt=NULL;
15693    float t0, t1, t2;
15694    SUMA_Boolean LocalHead = NOPE;
15695 
15696    SUMA_ENTRY;
15697 
15698    if (!TDO || !MDO || !IsInp) SUMA_RETURN(N_IsIn);
15699    if (MDO_IS_SHADOW(MDO)) SUMA_RETURN(0);
15700 
15701    N_IsIn = 0;
15702    IsIn = *IsInp; /* can still be NULL ... */
15703    if (MDO_IS_BOX(MDO) || MDO_IS_SPH(MDO)) {
15704       if (MDO_IS_SPH(MDO)) issp=1;
15705       else issp = 0;
15706       for (io=0; io<MDO->N_obj; ++io) {
15707       SUMA_LH("Mask %s %s (%d): Cen=%f %f %f, Hdim=%f %f %f",
15708                MDO->mtype, ADO_LABEL((SUMA_ALL_DO *)MDO), io,
15709                MDO->cen[0], MDO->cen[1], MDO->cen[2],
15710                MDO->hdim[0], MDO->hdim[1], MDO->hdim[2]);
15711       io3 = 3*io;
15712       for (knet=0; knet<TDO->net->N_tbv; ++knet) {
15713          tb = TDO->net->tbv[knet];
15714          for (n=0; tb && n<tb->N_tracts; ++n) {
15715             tt = tb->tracts+n;
15716             N_pts = TRACT_NPTS(tt);
15717             itt1 = Network_TB_to_1T(TDO->net, n, knet);
15718             if (!IsIn || IsIn[itt1]==0) {
15719             for (k=0; k < N_pts; ++k) {
15720                /* relative distance to center */
15721                id = 3 * k;
15722                t0 = SUMA_ABS(tt->pts[id] - MDO->cen[io3]);
15723                if ((MDO->hdim[io3] - t0) > 0) {
15724                   t1 =  SUMA_ABS(tt->pts[id+1] - MDO->cen[io3+1]);
15725                   if ((MDO->hdim[io3+1] - t1) > 0) {
15726                      t2 = SUMA_ABS(tt->pts[id+2] - MDO->cen[io3+2]);
15727                      if ((MDO->hdim[io3+2] - t2) > 0) {
15728                         #if 0
15729                         if (n == 41 && knet == 2) {
15730                            fprintf(SUMA_STDERR,
15731                                          "This In: %f %f %f (P%d T%d B%d) for \n"
15732                                          "cube cen %f %f %f hdims %f %f %f\n"
15733                                          "Deltas %f %f %f, Mag:  %f\n",
15734                                    tt->pts[id], tt->pts[id+1],tt->pts[id+2],
15735                                    k, n, knet,
15736                               MDO->cen[io3], MDO->cen[io3+1], MDO->cen[io3+2],
15737                            MDO->hdim[io3], MDO->hdim[io3+1], MDO->hdim[io3+2],
15738                            t0, t1, t2, sqrt(t0*t0+t1*t1+t2*t2));
15739                         }
15740                         #endif
15741                         /* Still need to verify the radius for sphere*/
15742                         if (!issp || (sqrt(t0*t0+t1*t1+t2*t2)<MDO->hdim[io3])) {
15743                            if (!IsIn) {
15744                               if (!(IsIn = (byte *)SUMA_calloc(
15745                                           TDO_N_TRACTS(TDO), sizeof(byte)))) {
15746                                  SUMA_S_Err("Failed to allocate for %d tracts",
15747                                              TDO_N_TRACTS(TDO));
15748                                  SUMA_RETURN(N_IsIn);
15749                               }
15750                               *IsInp = IsIn;
15751                            }
15752                            IsIn[itt1] = 1; ++N_IsIn;
15753                            break;
15754                         }
15755                      }
15756                   }
15757                }
15758             } /* For each point */
15759             } /* If tract unflagged */
15760          } /* For each tract */
15761       } /* For each bundle */
15762       } /* For each mask object */
15763       SUMA_LH("%d/%d tracts of %s in %s %s\n",
15764               N_IsIn, TDO_N_TRACTS(TDO),
15765               ADO_LABEL((SUMA_ALL_DO *)TDO),
15766               MDO->mtype,
15767               ADO_LABEL((SUMA_ALL_DO *)MDO));
15768       SUMA_RETURN(N_IsIn);
15769    } else {
15770       SUMA_S_Err("Not ready for mask type %s", MDO->mtype);
15771       SUMA_RETURN(N_IsIn);
15772    }
15773    SUMA_RETURN(N_IsIn);
15774 }
15775