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