1 /*
2   Teem: Tools to process and visualize scientific data and images             .
3   Copyright (C) 2012, 2011, 2010, 2009  University of Chicago
4   Copyright (C) 2008, 2007, 2006, 2005  Gordon Kindlmann
5   Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998  University of Utah
6 
7   This library is free software; you can redistribute it and/or
8   modify it under the terms of the GNU Lesser General Public License
9   (LGPL) as published by the Free Software Foundation; either
10   version 2.1 of the License, or (at your option) any later version.
11   The terms of redistributing and/or modifying this software also
12   include exceptions to the LGPL that facilitate static linking.
13 
14   This library is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17   Lesser General Public License for more details.
18 
19   You should have received a copy of the GNU Lesser General Public License
20   along with this library; if not, write to Free Software Foundation, Inc.,
21   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22 */
23 
24 #include "ten.h"
25 #include "privateTen.h"
26 
27 #define TEN_FIBER_INCR 512
28 
29 /*
30 ** _tenFiberProbe
31 **
32 ** The job here is to probe at (world space) "wPos" and then set:
33 **   tfx->fiberTen
34 **   tfx->fiberEval (all 3 evals)
35 **   tfx->fiberEvec (all 3 eigenvectors)
36 **   if (tfx->stop & (1 << tenFiberStopAniso): tfx->fiberAnisoStop
37 **
38 ** In the case of non-single-tensor tractography, we do so based on
39 ** ten2Which (when at the seedpoint) or
40 **
41 ** Note that for performance reasons, a non-zero return value
42 ** (indicating error) and the associated use of biff, is only possible
43 ** if seedProbe is non-zero, the reason being that problems can be
44 ** detected at the seedpoint, and won't arise after the seedpoint.
45 **
46 ** Errors from gage are indicated by *gageRet, which includes leaving
47 ** the domain of the volume, which is used to terminate fibers.
48 **
49 ** Our use of tfx->seedEvec (shared with _tenFiberAlign), as well as that
50 ** of tfx->lastDir and tfx->lastDirSet, could stand to have further
51 ** debugging and documentation ...
52 */
53 int
_tenFiberProbe(tenFiberContext * tfx,int * gageRet,double wPos[3],int seedProbe)54 _tenFiberProbe(tenFiberContext *tfx, int *gageRet,
55                double wPos[3], int seedProbe) {
56   static const char me[]="_tenFiberProbe";
57   double iPos[3];
58   int ret = 0;
59   double tens2[2][7];
60 
61   gageShapeWtoI(tfx->gtx->shape, iPos, wPos);
62   *gageRet = gageProbe(tfx->gtx, iPos[0], iPos[1], iPos[2]);
63 
64   if (tfx->verbose > 2) {
65     fprintf(stderr, "%s(%g,%g,%g, %s): hi ----- %s\n", me,
66             iPos[0], iPos[1], iPos[2], seedProbe ? "***TRUE***" : "false",
67             tfx->useDwi ? "using DWIs" : "");
68   }
69 
70   if (!tfx->useDwi) {
71     /* normal single-tensor tracking */
72     TEN_T_COPY(tfx->fiberTen, tfx->gageTen);
73     ELL_3V_COPY(tfx->fiberEval, tfx->gageEval);
74     ELL_3M_COPY(tfx->fiberEvec, tfx->gageEvec);
75     if (tfx->stop & (1 << tenFiberStopAniso)) {
76       tfx->fiberAnisoStop = tfx->gageAnisoStop[0];
77     }
78     if (seedProbe) {
79       ELL_3V_COPY(tfx->seedEvec, tfx->fiberEvec);
80     }
81   } else { /* tracking in DWIs */
82     if (tfx->verbose > 2 && seedProbe) {
83       fprintf(stderr, "%s:   fiber type = %s\n", me,
84               airEnumStr(tenDwiFiberType, tfx->fiberType));
85     }
86     switch (tfx->fiberType) {
87       double evec[2][9], eval[2][3];
88     case tenDwiFiberType1Evec0:
89       if (tfx->mframeUse) {
90         double matTmpA[9], matTmpB[9];
91         TEN_T2M(matTmpA, tfx->gageTen);
92         ELL_3M_MUL(matTmpB, tfx->mframe, matTmpA);
93         ELL_3M_MUL(matTmpA, matTmpB, tfx->mframeT);
94         TEN_M2T(tfx->fiberTen, matTmpA);
95         tfx->fiberTen[0] = tfx->gageTen[0];
96       } else {
97         TEN_T_COPY(tfx->fiberTen, tfx->gageTen);
98       }
99       tenEigensolve_d(tfx->fiberEval, tfx->fiberEvec, tfx->fiberTen);
100       if (tfx->stop & (1 << tenFiberStopAniso)) {
101         double tmp;
102         tmp = tenAnisoTen_d(tfx->fiberTen, tfx->anisoStopType);
103         tfx->fiberAnisoStop = AIR_CLAMP(0, tmp, 1);
104       }
105       if (seedProbe) {
106         ELL_3V_COPY(tfx->seedEvec, tfx->fiberEvec);
107       }
108       break;
109     case tenDwiFiberType2Evec0:
110       /* Estimate principal diffusion direction of each tensor */
111       if (tfx->mframeUse) {
112         /* Transform both the tensors */
113         double matTmpA[9], matTmpB[9];
114 
115         TEN_T2M(matTmpA, tfx->gageTen2 + 0);
116         ELL_3M_MUL(matTmpB, tfx->mframe, matTmpA);
117         ELL_3M_MUL(matTmpA, matTmpB, tfx->mframeT);
118         TEN_M2T(tens2[0], matTmpA);
119         /* new eigen values and vectors */
120         tenEigensolve_d(eval[0], evec[0], tens2[0]);
121 
122         TEN_T2M(matTmpA, tfx->gageTen2 + 7);
123         ELL_3M_MUL(matTmpB, tfx->mframe, matTmpA);
124         ELL_3M_MUL(matTmpA, matTmpB, tfx->mframeT);
125         TEN_M2T(tens2[1], matTmpA);
126         tenEigensolve_d(eval[1], evec[1], tens2[1]);
127       } else {
128         tenEigensolve_d(eval[0], evec[0], tfx->gageTen2 + 0);
129         tenEigensolve_d(eval[1], evec[1], tfx->gageTen2 + 7);
130       }
131 
132       /* set ten2Use */
133       if (seedProbe) {  /* we're on the *very* 1st probe per tract,
134                            at the seed pt */
135         ELL_3V_COPY(tfx->seedEvec, evec[tfx->ten2Which]);
136         tfx->ten2Use = tfx->ten2Which;
137         if (tfx->verbose > 2) {
138           fprintf(stderr, "%s: ** ten2Use == ten2Which == %d\n", me,
139                   tfx->ten2Use);
140         }
141       } else {
142         double *lastVec, dot[2];
143 
144         if (!tfx->lastDirSet) {
145           /* we're on some probe of the first step */
146           lastVec = tfx->seedEvec;
147         } else {
148           /* we're past the first step */
149           /* Arish says: "Bug len has not been initialized and don't think
150              its needed". The first part is not a problem; "len" is in the
151              *output* argument of ELL_3V_NORM.  The second part seems to be
152              true, even though Gordon can't currently see why! */
153           /* ELL_3V_NORM(tfx->lastDir, tfx->lastDir, len); */
154           lastVec = tfx->lastDir;
155         }
156         dot[0] = ELL_3V_DOT(lastVec, evec[0]);
157         dot[1] = ELL_3V_DOT(lastVec, evec[1]);
158         if (dot[0] < 0) {
159           dot[0] *= -1;
160           ELL_3M_SCALE(evec[0], -1, evec[0]);
161         }
162         if (dot[1] < 0) {
163           dot[1] *= -1;
164           ELL_3M_SCALE(evec[1], -1, evec[1]);
165         }
166         tfx->ten2Use = (dot[0] > dot[1]) ? 0 : 1;
167         if (tfx->verbose > 2) {
168           fprintf(stderr, "%s(%g,%g,%g): dot[0,1] = %f, %f -> use %u\n",
169                   me, wPos[0], wPos[1], wPos[2], dot[0], dot[1],
170                   tfx->ten2Use );
171         }
172       }
173 
174       /* based on ten2Use, set the rest of the needed fields */
175       if (tfx->mframeUse) {
176         TEN_T_COPY(tfx->fiberTen, tens2[tfx->ten2Use]);
177       } else {
178         TEN_T_COPY(tfx->fiberTen, tfx->gageTen2 + 7*(tfx->ten2Use));
179       }
180       tfx->fiberTen[0] = tfx->gageTen2[0];   /* copy confidence */
181       ELL_3V_COPY(tfx->fiberEval, eval[tfx->ten2Use]);
182       ELL_3M_COPY(tfx->fiberEvec, evec[tfx->ten2Use]);
183       if (tfx->stop & (1 << tenFiberStopAniso)) {
184         double tmp;
185         tmp = tenAnisoEval_d(tfx->fiberEval, tfx->anisoStopType);
186         tfx->fiberAnisoStop = AIR_CLAMP(0, tmp, 1);
187         /* HEY: what about speed? */
188       } else {
189         tfx->fiberAnisoStop = AIR_NAN;
190       }
191       break;
192     default:
193       biffAddf(TEN, "%s: %s %s (%d) unimplemented!", me,
194               tenDwiFiberType->name,
195               airEnumStr(tenDwiFiberType, tfx->fiberType), tfx->fiberType);
196       ret = 1;
197     } /* switch (tfx->fiberType) */
198   }
199   if (tfx->verbose > 2) {
200     fprintf(stderr, "%s: fiberEvec = %g %g %g\n", me,
201             tfx->fiberEvec[0], tfx->fiberEvec[1], tfx->fiberEvec[2]);
202   }
203 
204   return ret;
205 }
206 
207 int
_tenFiberStopCheck(tenFiberContext * tfx)208 _tenFiberStopCheck(tenFiberContext *tfx) {
209   static const char me[]="_tenFiberStopCheck";
210 
211   if (tfx->numSteps[tfx->halfIdx] >= TEN_FIBER_NUM_STEPS_MAX) {
212     fprintf(stderr, "%s: numSteps[%d] exceeded sanity check value of %d!!\n",
213             me, tfx->halfIdx, TEN_FIBER_NUM_STEPS_MAX);
214     fprintf(stderr, "%s: Check fiber termination conditions, or recompile "
215             "with a larger value for TEN_FIBER_NUM_STEPS_MAX\n", me);
216     return tenFiberStopNumSteps;
217   }
218   if (tfx->stop & (1 << tenFiberStopConfidence)) {
219     if (tfx->fiberTen[0] < tfx->confThresh) {
220       return tenFiberStopConfidence;
221     }
222   }
223   if (tfx->stop & (1 << tenFiberStopRadius)) {
224     if (tfx->radius < tfx->minRadius) {
225       return tenFiberStopRadius;
226     }
227   }
228   if (tfx->stop & (1 << tenFiberStopAniso)) {
229     if (tfx->fiberAnisoStop  < tfx->anisoThresh) {
230       return tenFiberStopAniso;
231     }
232   }
233   if (tfx->stop & (1 << tenFiberStopNumSteps)) {
234     if (tfx->numSteps[tfx->halfIdx] > tfx->maxNumSteps) {
235       return tenFiberStopNumSteps;
236     }
237   }
238   if (tfx->stop & (1 << tenFiberStopLength)) {
239     if (tfx->halfLen[tfx->halfIdx] >= tfx->maxHalfLen) {
240       return tenFiberStopLength;
241     }
242   }
243   if (tfx->useDwi
244       && tfx->stop & (1 << tenFiberStopFraction)
245       && tfx->gageTen2) { /* not all DWI fiber types use gageTen2 */
246     double fracUse;
247     fracUse = (0 == tfx->ten2Use
248                ? tfx->gageTen2[7]
249                : 1 - tfx->gageTen2[7]);
250     if (fracUse < tfx->minFraction) {
251       return tenFiberStopFraction;
252     }
253   }
254   return 0;
255 }
256 
257 void
_tenFiberAlign(tenFiberContext * tfx,double vec[3])258 _tenFiberAlign(tenFiberContext *tfx, double vec[3]) {
259   static const char me[]="_tenFiberAlign";
260   double scale, dot;
261 
262   if (tfx->verbose > 2) {
263     fprintf(stderr, "%s: hi %s (lds %d):\t%g %g %g\n", me,
264             !tfx->lastDirSet ? "**" : "  ",
265             tfx->lastDirSet, vec[0], vec[1], vec[2]);
266   }
267   if (!(tfx->lastDirSet)) {
268     dot = ELL_3V_DOT(tfx->seedEvec, vec);
269     /* this is the first step (or one of the intermediate steps
270        for RK) in this fiber half; 1st half follows the
271        eigenvector determined at seed point, 2nd goes opposite */
272     if (tfx->verbose > 2) {
273       fprintf(stderr, "!%s: dir=%d, dot=%g\n", me, tfx->halfIdx, dot);
274     }
275     if (!tfx->halfIdx) {
276       /* 1st half */
277       scale = dot < 0 ? -1 : 1;
278     } else {
279       /* 2nd half */
280       scale = dot > 0 ? -1 : 1;
281     }
282   } else {
283     dot = ELL_3V_DOT(tfx->lastDir, vec);
284     /* we have some history in this fiber half */
285     scale = dot < 0 ? -1 : 1;
286   }
287   ELL_3V_SCALE(vec, scale, vec);
288   if (tfx->verbose > 2) {
289     fprintf(stderr, "!%s: scl = %g -> \t%g %g %g\n",
290             me, scale, vec[0], vec[1], vec[2]);
291   }
292   return;
293 }
294 
295 /*
296 ** parm[0]: lerp between 1 and the stuff below
297 ** parm[1]: "t": (parm[1],0) is control point between (0,0) and (1,1)
298 ** parm[2]: "d": parabolic blend between parm[1]-parm[2] and parm[1]+parm[2]
299 */
300 void
_tenFiberAnisoSpeed(double * step,double xx,double parm[3])301 _tenFiberAnisoSpeed(double *step, double xx, double parm[3]) {
302   double aa, dd, tt, yy;
303 
304   tt = parm[1];
305   dd = parm[2];
306   aa = 1.0/(DBL_EPSILON + 4*dd*(1.0-tt));
307   yy = xx - tt + dd;
308   xx = (xx < tt - dd
309         ? 0
310         : (xx < tt + dd
311            ? aa*yy*yy
312            : (xx - tt)/(1 - tt)));
313   xx = AIR_LERP(parm[0], 1, xx);
314   ELL_3V_SCALE(step, xx, step);
315 }
316 
317 /*
318 ** -------------------------------------------------------------------
319 ** -------------------------------------------------------------------
320 ** The _tenFiberStep_* routines are responsible for putting a step into
321 ** the given step[] vector.  Without anisoStepSize, this should be
322 ** UNIT LENGTH, with anisoStepSize, its scaled by that anisotropy measure
323 */
324 void
_tenFiberStep_Evec(tenFiberContext * tfx,double step[3])325 _tenFiberStep_Evec(tenFiberContext *tfx, double step[3]) {
326 
327   /* fiberEvec points to the correct gage answer based on fiberType */
328   ELL_3V_COPY(step, tfx->fiberEvec + 3*0);
329   _tenFiberAlign(tfx, step);
330   if (tfx->anisoSpeedType) {
331     _tenFiberAnisoSpeed(step, tfx->fiberAnisoSpeed,
332                         tfx->anisoSpeedFunc);
333   }
334 }
335 
336 void
_tenFiberStep_TensorLine(tenFiberContext * tfx,double step[3])337 _tenFiberStep_TensorLine(tenFiberContext *tfx, double step[3]) {
338   double cl, evec0[3], vout[3], vin[3], len;
339 
340   ELL_3V_COPY(evec0, tfx->fiberEvec + 3*0);
341   _tenFiberAlign(tfx, evec0);
342 
343   if (tfx->lastDirSet) {
344     ELL_3V_COPY(vin, tfx->lastDir);
345     TEN_T3V_MUL(vout, tfx->fiberTen, tfx->lastDir);
346     ELL_3V_NORM(vout, vout, len);
347     _tenFiberAlign(tfx, vout);  /* HEY: is this needed? */
348   } else {
349     ELL_3V_COPY(vin, evec0);
350     ELL_3V_COPY(vout, evec0);
351   }
352 
353   /* HEY: should be using one of the tenAnisoEval[] functions */
354   cl = (tfx->fiberEval[0] - tfx->fiberEval[1])/(tfx->fiberEval[0] + 0.00001);
355 
356   ELL_3V_SCALE_ADD3(step,
357                     cl, evec0,
358                     (1-cl)*(1-tfx->wPunct), vin,
359                     (1-cl)*tfx->wPunct, vout);
360   /* _tenFiberAlign(tfx, step); */
361   ELL_3V_NORM(step, step, len);
362   if (tfx->anisoSpeedType) {
363     _tenFiberAnisoSpeed(step, tfx->fiberAnisoSpeed,
364                         tfx->anisoSpeedFunc);
365   }
366 }
367 
368 void
_tenFiberStep_PureLine(tenFiberContext * tfx,double step[3])369 _tenFiberStep_PureLine(tenFiberContext *tfx, double step[3]) {
370   static const char me[]="_tenFiberStep_PureLine";
371 
372   AIR_UNUSED(tfx);
373   AIR_UNUSED(step);
374   fprintf(stderr, "%s: sorry, unimplemented!\n", me);
375 }
376 
377 void
_tenFiberStep_Zhukov(tenFiberContext * tfx,double step[3])378 _tenFiberStep_Zhukov(tenFiberContext *tfx, double step[3]) {
379   static const char me[]="_tenFiberStep_Zhukov";
380 
381   AIR_UNUSED(tfx);
382   AIR_UNUSED(step);
383   fprintf(stderr, "%s: sorry, unimplemented!\n", me);
384 }
385 
386 void (*
387 _tenFiberStep[TEN_FIBER_TYPE_MAX+1])(tenFiberContext *, double *) = {
388   NULL,
389   _tenFiberStep_Evec,
390   _tenFiberStep_Evec,
391   _tenFiberStep_Evec,
392   _tenFiberStep_TensorLine,
393   _tenFiberStep_PureLine,
394   _tenFiberStep_Zhukov
395 };
396 
397 /*
398 ** -------------------------------------------------------------------
399 ** -------------------------------------------------------------------
400 ** The _tenFiberIntegrate_* routines must assume that
401 ** _tenFiberProbe(tfx, tfx->wPos, AIR_FALSE) has just been called
402 */
403 
404 int
_tenFiberIntegrate_Euler(tenFiberContext * tfx,double forwDir[3])405 _tenFiberIntegrate_Euler(tenFiberContext *tfx, double forwDir[3]) {
406 
407   _tenFiberStep[tfx->fiberType](tfx, forwDir);
408   ELL_3V_SCALE(forwDir, tfx->stepSize, forwDir);
409   return 0;
410 }
411 
412 int
_tenFiberIntegrate_Midpoint(tenFiberContext * tfx,double forwDir[3])413 _tenFiberIntegrate_Midpoint(tenFiberContext *tfx, double forwDir[3]) {
414   double loc[3], half[3];
415   int gret;
416 
417   _tenFiberStep[tfx->fiberType](tfx, half);
418   ELL_3V_SCALE_ADD2(loc, 1, tfx->wPos, 0.5*tfx->stepSize, half);
419   _tenFiberProbe(tfx, &gret, loc, AIR_FALSE); if (gret) return 1;
420   _tenFiberStep[tfx->fiberType](tfx, forwDir);
421   ELL_3V_SCALE(forwDir, tfx->stepSize, forwDir);
422   return 0;
423 }
424 
425 int
_tenFiberIntegrate_RK4(tenFiberContext * tfx,double forwDir[3])426 _tenFiberIntegrate_RK4(tenFiberContext *tfx, double forwDir[3]) {
427   double loc[3], k1[3], k2[3], k3[3], k4[3], c1, c2, c3, c4, h;
428   int gret;
429 
430   h = tfx->stepSize;
431   c1 = h/6.0; c2 = h/3.0; c3 = h/3.0; c4 = h/6.0;
432 
433   _tenFiberStep[tfx->fiberType](tfx, k1);
434   ELL_3V_SCALE_ADD2(loc, 1, tfx->wPos, 0.5*h, k1);
435   _tenFiberProbe(tfx, &gret, loc, AIR_FALSE); if (gret) return 1;
436   _tenFiberStep[tfx->fiberType](tfx, k2);
437   ELL_3V_SCALE_ADD2(loc, 1, tfx->wPos, 0.5*h, k2);
438   _tenFiberProbe(tfx, &gret, loc, AIR_FALSE); if (gret) return 1;
439   _tenFiberStep[tfx->fiberType](tfx, k3);
440   ELL_3V_SCALE_ADD2(loc, 1, tfx->wPos, h, k3);
441   _tenFiberProbe(tfx, &gret, loc, AIR_FALSE); if (gret) return 1;
442   _tenFiberStep[tfx->fiberType](tfx, k4);
443 
444   ELL_3V_SET(forwDir,
445              c1*k1[0] + c2*k2[0] + c3*k3[0] + c4*k4[0],
446              c1*k1[1] + c2*k2[1] + c3*k3[1] + c4*k4[1],
447              c1*k1[2] + c2*k2[2] + c3*k3[2] + c4*k4[2]);
448 
449   return 0;
450 }
451 
452 int (*
453 _tenFiberIntegrate[TEN_FIBER_INTG_MAX+1])(tenFiberContext *tfx, double *) = {
454   NULL,
455   _tenFiberIntegrate_Euler,
456   _tenFiberIntegrate_Midpoint,
457   _tenFiberIntegrate_RK4
458 };
459 
460 /*
461 ** modified body of previous tenFiberTraceSet, in order to
462 ** permit passing the nval for storing desired probed values
463 */
464 static int
_fiberTraceSet(tenFiberContext * tfx,Nrrd * nval,Nrrd * nfiber,double * buff,unsigned int halfBuffLen,unsigned int * startIdxP,unsigned int * endIdxP,double seed[3])465 _fiberTraceSet(tenFiberContext *tfx, Nrrd *nval, Nrrd *nfiber,
466                double *buff, unsigned int halfBuffLen,
467                unsigned int *startIdxP, unsigned int *endIdxP,
468                double seed[3]) {
469   static const char me[]="_fiberTraceSet";
470   airArray *fptsArr[2],      /* airArrays of backward (0) and forward (1)
471                                 fiber points */
472     *pansArr[2];             /* airArrays of backward (0) and forward (1)
473                                 probed values */
474   double *fpts[2],           /* arrays storing forward and backward
475                                 fiber points */
476     *pans[2],                /* arrays storing forward and backward
477                                 probed values */
478     tmp[3],
479     iPos[3],
480     currPoint[3],
481     forwDir[3],
482     *fiber,                  /* array of both forward and backward points,
483                                 when finished */
484     *valOut;                 /* same for probed values */
485   const double *pansP;       /* pointer to gage's probed values */
486 
487   int gret, whyStop, buffIdx, fptsIdx, pansIdx, outIdx, oldStop, keepfiber;
488   unsigned int i, pansLen;
489   airArray *mop;
490   airPtrPtrUnion appu;
491 
492   if (!(tfx)) {
493     biffAddf(TEN, "%s: got NULL pointer", me);
494     return 1;
495   }
496   if (nval) {
497     if (!tfx->fiberProbeItem) {
498       biffAddf(TEN, "%s: want to record probed values but no item set", me);
499       return 1;
500     }
501     pansLen = gageAnswerLength(tfx->gtx, tfx->pvl, tfx->fiberProbeItem);
502     pansP = gageAnswerPointer(tfx->gtx, tfx->pvl, tfx->fiberProbeItem);
503   } else {
504     pansLen = 0;
505     pansP = NULL;
506   }
507   /*
508   fprintf(stderr, "!%s: =========================== \n", me);
509   fprintf(stderr, "!%s: \n", me);
510   fprintf(stderr, "!%s: item %d -> pansLen = %u\n", me,
511           tfx->fiberProbeItem, pansLen);
512   fprintf(stderr, "!%s: \n", me);
513   fprintf(stderr, "!%s:  =========================== \n", me);
514   */
515 
516   /* HEY: a hack to preserve the state inside tenFiberContext so that
517      we have fewer side effects (tfx->maxNumSteps may still be set) */
518   oldStop = tfx->stop;
519   if (!nfiber) {
520     if (!( buff && halfBuffLen > 0 && startIdxP && startIdxP )) {
521       biffAddf(TEN, "%s: need either non-NULL nfiber or fpts buffer info", me);
522       return 1;
523     }
524     if (tenFiberStopSet(tfx, tenFiberStopNumSteps, halfBuffLen)) {
525       biffAddf(TEN, "%s: error setting new fiber stop", me);
526       return 1;
527     }
528   }
529 
530   /* initialize the quantities which describe the fiber halves */
531   tfx->halfLen[0] = tfx->halfLen[1] = 0.0;
532   tfx->numSteps[0] = tfx->numSteps[1] = 0;
533   tfx->whyStop[0] = tfx->whyStop[1] = tenFiberStopUnknown;
534   /*
535   fprintf(stderr, "!%s: try probing once, at seed %g %g %g\n", me,
536           seed[0], seed[1], seed[2]);
537   */
538   /* try probing once, at seed point */
539   if (tfx->useIndexSpace) {
540     gageShapeItoW(tfx->gtx->shape, tmp, seed);
541   } else {
542     ELL_3V_COPY(tmp, seed);
543   }
544   if (_tenFiberProbe(tfx, &gret, tmp, AIR_TRUE)) {
545     biffAddf(TEN, "%s: first _tenFiberProbe failed", me);
546     return 1;
547   }
548   if (gret) {
549     if (gageErrBoundsSpace != tfx->gtx->errNum) {
550       biffAddf(TEN, "%s: gage problem on first _tenFiberProbe: %s (%d)",
551               me, tfx->gtx->errStr, tfx->gtx->errNum);
552       return 1;
553     } else {
554       /* the problem on the first probe was that it was out of bounds,
555          which is not a catastrophe; its handled the same as below */
556       tfx->whyNowhere = tenFiberStopBounds;
557       if (nval) {
558         nrrdEmpty(nval);
559       }
560       if (nfiber) {
561         nrrdEmpty(nfiber);
562       } else {
563         *startIdxP = *endIdxP = 0;
564       }
565       return 0;
566     }
567   }
568 
569   /* see if we're doomed (tract dies before it gets anywhere)  */
570   /* have to fake out the possible radius check, since at this point
571      there is no radius of curvature; this will always pass */
572   tfx->radius = DBL_MAX;
573   if ((whyStop = _tenFiberStopCheck(tfx))) {
574     /* stopped immediately at seed point, but that's not an error */
575     tfx->whyNowhere = whyStop;
576     if (nval) {
577       nrrdEmpty(nval);
578     }
579     if (nfiber) {
580       nrrdEmpty(nfiber);
581     } else {
582       *startIdxP = *endIdxP = 0;
583     }
584     return 0;
585   } else {
586     /* did not immediately halt */
587     tfx->whyNowhere = tenFiberStopUnknown;
588   }
589 
590   /* airMop{Error,Okay}() can safely be called on NULL */
591   mop = (nfiber || nval) ? airMopNew() : NULL;
592 
593   for (tfx->halfIdx=0; tfx->halfIdx<=1; tfx->halfIdx++) {
594     if (nval) {
595       appu.d = &(pans[tfx->halfIdx]);
596       pansArr[tfx->halfIdx] = airArrayNew(appu.v, NULL,
597                                           pansLen*sizeof(double),
598                                           TEN_FIBER_INCR);
599       airMopAdd(mop, pansArr[tfx->halfIdx],
600                 (airMopper)airArrayNuke, airMopAlways);
601     } else {
602       pansArr[tfx->halfIdx] = NULL;
603     }
604     pansIdx = -1;
605     if (nfiber) {
606       appu.d = &(fpts[tfx->halfIdx]);
607       fptsArr[tfx->halfIdx] = airArrayNew(appu.v, NULL,
608                                           3*sizeof(double), TEN_FIBER_INCR);
609       airMopAdd(mop, fptsArr[tfx->halfIdx],
610                 (airMopper)airArrayNuke, airMopAlways);
611       buffIdx = -1;
612     } else {
613       fptsArr[tfx->halfIdx] = NULL;
614       fpts[tfx->halfIdx] = NULL;
615       buffIdx = halfBuffLen;
616     }
617     fptsIdx = -1;
618     tfx->halfLen[tfx->halfIdx] = 0;
619     if (tfx->useIndexSpace) {
620       ELL_3V_COPY(iPos, seed);
621       gageShapeItoW(tfx->gtx->shape, tfx->wPos, iPos);
622     } else {
623       /*
624       fprintf(stderr, "!%s(A): %p %p %p\n", me,
625               tfx->gtx->shape, iPos, seed);
626       */
627       gageShapeWtoI(tfx->gtx->shape, iPos, seed);
628       ELL_3V_COPY(tfx->wPos, seed);
629     }
630     /* have to initially pass the possible radius check in
631        _tenFiberStopCheck(); this will always pass */
632     tfx->radius = DBL_MAX;
633     ELL_3V_SET(tfx->lastDir, 0, 0, 0);
634     tfx->lastDirSet = AIR_FALSE;
635     for (tfx->numSteps[tfx->halfIdx] = 0;
636          AIR_TRUE;
637          tfx->numSteps[tfx->halfIdx]++) {
638       _tenFiberProbe(tfx, &gret, tfx->wPos, AIR_FALSE);
639       if (gret) {
640         /* even if gageProbe had an error OTHER than going out of bounds,
641            we're not going to report it any differently here, alas */
642         tfx->whyStop[tfx->halfIdx] = tenFiberStopBounds;
643         /*
644         fprintf(stderr, "!%s: A tfx->whyStop[%d] = %s\n", me, tfx->halfIdx,
645                 airEnumStr(tenFiberStop, tfx->whyStop[tfx->halfIdx]));
646         */
647         break;
648       }
649       if ((whyStop = _tenFiberStopCheck(tfx))) {
650         if (tenFiberStopNumSteps == whyStop) {
651           /* we stopped along this direction because
652              tfx->numSteps[tfx->halfIdx] exceeded tfx->maxNumSteps.
653              Okay.  But tfx->numSteps[tfx->halfIdx] is supposed to be
654              a record of how steps were (successfully) taken.  So we
655              need to decrementing before moving on ... */
656           tfx->numSteps[tfx->halfIdx]--;
657         }
658         tfx->whyStop[tfx->halfIdx] = whyStop;
659         /*
660         fprintf(stderr, "!%s: B tfx->whyStop[%d] = %s\n", me, tfx->halfIdx,
661                 airEnumStr(tenFiberStop, tfx->whyStop[tfx->halfIdx]));
662         */
663         break;
664       }
665       if (tfx->useIndexSpace) {
666         /*
667         fprintf(stderr, "!%s(B): %p %p %p\n", me,
668                 tfx->gtx->shape, iPos, tfx->wPos);
669         */
670         gageShapeWtoI(tfx->gtx->shape, iPos, tfx->wPos);
671         ELL_3V_COPY(currPoint, iPos);
672       } else {
673         ELL_3V_COPY(currPoint, tfx->wPos);
674       }
675       if (nval) {
676         pansIdx = airArrayLenIncr(pansArr[tfx->halfIdx], 1);
677         /* HEY: speed this up */
678         memcpy(pans[tfx->halfIdx] + pansLen*pansIdx, pansP,
679                pansLen*sizeof(double));
680         /*
681         fprintf(stderr, "!%s: (dir %d) saving to %d: %g @ (%g,%g,%g)\n", me,
682                 tfx->halfIdx, pansIdx, pansP[0],
683                 currPoint[0], currPoint[1], currPoint[2]);
684         */
685       }
686       if (nfiber) {
687         fptsIdx = airArrayLenIncr(fptsArr[tfx->halfIdx], 1);
688         ELL_3V_COPY(fpts[tfx->halfIdx] + 3*fptsIdx, currPoint);
689       } else {
690         ELL_3V_COPY(buff + 3*buffIdx, currPoint);
691         /*
692         fprintf(stderr, "!%s: (dir %d) saving to %d pnt %g %g %g\n", me,
693                 tfx->halfIdx, buffIdx,
694                 currPoint[0], currPoint[1], currPoint[2]);
695         */
696         buffIdx += !tfx->halfIdx ? -1 : 1;
697       }
698       /* forwDir is set by this to point to the next fiber point */
699       if (_tenFiberIntegrate[tfx->intg](tfx, forwDir)) {
700         tfx->whyStop[tfx->halfIdx] = tenFiberStopBounds;
701         /*
702         fprintf(stderr, "!%s: C tfx->whyStop[%d] = %s\n", me, tfx->halfIdx,
703                 airEnumStr(tenFiberStop, tfx->whyStop[tfx->halfIdx]));
704         */
705         break;
706       }
707       /*
708       fprintf(stderr, "!%s: forwDir = %g %g %g\n", me,
709               forwDir[0], forwDir[1], forwDir[2]);
710       */
711       if (tfx->stop & (1 << tenFiberStopRadius)) {
712         /* some more work required to compute radius of curvature */
713         double svec[3], dvec[3], SS, DD, dlen; /* sum,diff length squared */
714         /* tfx->lastDir and forwDir are not normalized to unit-length */
715         if (tfx->lastDirSet) {
716           ELL_3V_ADD2(svec, tfx->lastDir, forwDir);
717           ELL_3V_SUB(dvec, tfx->lastDir, forwDir);
718           SS = ELL_3V_DOT(svec, svec);
719           DD = ELL_3V_DOT(dvec, dvec);
720           /* Sun Nov 2 00:04:05 EDT 2008: GLK can't recover how he
721              derived this, and can't see why it would be corrrect,
722              even though it seems to work correctly...
723              tfx->radius = sqrt(SS*(SS+DD)/DD)/4;
724           */
725           dlen = sqrt(DD);
726           tfx->radius = dlen ? (SS + DD)/(4*dlen) : DBL_MAX;
727         } else {
728           tfx->radius = DBL_MAX;
729         }
730       }
731       /*
732       if (!tfx->lastDirSet) {
733         fprintf(stderr, "!%s: now setting lastDirSet to (%g,%g,%g)\n", me,
734                 forwDir[0], forwDir[1], forwDir[2]);
735       }
736       */
737       ELL_3V_COPY(tfx->lastDir, forwDir);
738       tfx->lastDirSet = AIR_TRUE;
739       ELL_3V_ADD2(tfx->wPos, tfx->wPos, forwDir);
740       tfx->halfLen[tfx->halfIdx] += ELL_3V_LEN(forwDir);
741     }
742   }
743 
744   keepfiber = AIR_TRUE;
745   if ((tfx->stop & (1 << tenFiberStopStub))
746       && (2 == fptsArr[0]->len + fptsArr[1]->len)) {
747     /* seed point was actually valid, but neither half got anywhere,
748        and the user has set tenFiberStopStub, so we report this as
749        a non-starter, via tfx->whyNowhere. */
750     tfx->whyNowhere = tenFiberStopStub;
751     keepfiber = AIR_FALSE;
752   }
753   if ((tfx->stop & (1 << tenFiberStopMinNumSteps))
754       && (fptsArr[0]->len + fptsArr[1]->len < tfx->minNumSteps)) {
755     /* whole fiber didn't have enough steps */
756     tfx->whyNowhere = tenFiberStopMinNumSteps;
757     keepfiber = AIR_FALSE;
758   }
759   if ((tfx->stop & (1 << tenFiberStopMinLength))
760       && (tfx->halfLen[0] + tfx->halfLen[1] < tfx->minWholeLen)) {
761     /* whole fiber wasn't long enough */
762     tfx->whyNowhere = tenFiberStopMinLength;
763     keepfiber = AIR_FALSE;
764   }
765   if (!keepfiber) {
766     /* for the curious, tfx->whyStop[0,1], tfx->numSteps[0,1], and
767        tfx->halfLen[1,2] remain set, from above */
768     if (nval) {
769       nrrdEmpty(nval);
770     }
771     if (nfiber) {
772       nrrdEmpty(nfiber);
773     } else {
774       *startIdxP = *endIdxP = 0;
775     }
776   } else {
777     if (nval) {
778       if (nrrdMaybeAlloc_va(nval, nrrdTypeDouble, 2,
779                             AIR_CAST(size_t, pansLen),
780                             AIR_CAST(size_t, (pansArr[0]->len
781                                               + pansArr[1]->len - 1)))) {
782         biffMovef(TEN, NRRD, "%s: couldn't allocate probed value nrrd", me);
783         airMopError(mop); return 1;
784       }
785       valOut = AIR_CAST(double*, nval->data);
786       outIdx = 0;
787       /* HEY: speed up memcpy */
788       for (i=pansArr[0]->len-1; i>=1; i--) {
789         memcpy(valOut + pansLen*outIdx, pans[0] + pansLen*i,
790                pansLen*sizeof(double));
791         outIdx++;
792       }
793       for (i=0; i<=pansArr[1]->len-1; i++) {
794         memcpy(valOut + pansLen*outIdx, pans[1] + pansLen*i,
795                pansLen*sizeof(double));
796         outIdx++;
797       }
798     }
799     if (nfiber) {
800       if (nrrdMaybeAlloc_va(nfiber, nrrdTypeDouble, 2,
801                             AIR_CAST(size_t, 3),
802                             AIR_CAST(size_t, (fptsArr[0]->len
803                                               + fptsArr[1]->len - 1)))) {
804         biffMovef(TEN, NRRD, "%s: couldn't allocate fiber nrrd", me);
805         airMopError(mop); return 1;
806       }
807       fiber = AIR_CAST(double*, nfiber->data);
808       outIdx = 0;
809       for (i=fptsArr[0]->len-1; i>=1; i--) {
810         ELL_3V_COPY(fiber + 3*outIdx, fpts[0] + 3*i);
811         outIdx++;
812       }
813       for (i=0; i<=fptsArr[1]->len-1; i++) {
814         ELL_3V_COPY(fiber + 3*outIdx, fpts[1] + 3*i);
815         outIdx++;
816       }
817     } else {
818       *startIdxP = halfBuffLen - tfx->numSteps[0];
819       *endIdxP = halfBuffLen + tfx->numSteps[1];
820     }
821   }
822 
823   tfx->stop = oldStop;
824   airMopOkay(mop);
825   return 0;
826 }
827 
828 /*
829 ******** tenFiberTraceSet
830 **
831 ** slightly more flexible API for fiber tracking than tenFiberTrace
832 **
833 ** EITHER: pass a non-NULL nfiber, and NULL, 0, NULL, NULL for
834 ** the following arguments, and things are the same as with tenFiberTrace:
835 ** data inside the nfiber is allocated, and the tract vertices are copied
836 ** into it, having been stored in dynamically allocated airArrays
837 **
838 ** OR: pass a NULL nfiber, and a buff allocated for 3*(2*halfBuffLen + 1)
839 ** (note the "+ 1" !!!) doubles.  The fiber tracking on each half will stop
840 ** at halfBuffLen points. The given seedpoint will be stored in
841 ** buff[0,1,2 + 3*halfBuffLen].  The linear (1-D) indices for the end of
842 ** the first tract half, and the end of the second tract half, will be set in
843 ** *startIdxP and *endIdxP respectively (this does not include a multiply
844 ** by 3)
845 **
846 ** it is worth pointing out here that internally, all tractography is done
847 ** in gage's world space, regardless of tfx->useIndexSpace.  The conversion
848 ** from/to index is space (if tfx->useIndexSpace is non-zero) is only done
849 ** for seedpoints and when fiber vertices are saved out, respectively.
850 **
851 ** As of Sun Aug  1 20:40:55 CDT 2010 this is just a wrapper around
852 ** _fiberTraceSet; this will probably change in Teem 2.0
853 */
854 int
tenFiberTraceSet(tenFiberContext * tfx,Nrrd * nfiber,double * buff,unsigned int halfBuffLen,unsigned int * startIdxP,unsigned int * endIdxP,double seed[3])855 tenFiberTraceSet(tenFiberContext *tfx, Nrrd *nfiber,
856                  double *buff, unsigned int halfBuffLen,
857                  unsigned int *startIdxP, unsigned int *endIdxP,
858                  double seed[3]) {
859   static const char me[]="tenFiberTraceSet";
860 
861   if (_fiberTraceSet(tfx, NULL, nfiber, buff, halfBuffLen,
862                      startIdxP, endIdxP, seed)) {
863     biffAddf(TEN, "%s: problem", me);
864     return 1;
865   }
866 
867   return 0;
868 }
869 
870 /*
871 ******** tenFiberTrace
872 **
873 ** takes a starting position in index or world space, depending on the
874 ** value of tfx->useIndexSpace
875 */
876 int
tenFiberTrace(tenFiberContext * tfx,Nrrd * nfiber,double seed[3])877 tenFiberTrace(tenFiberContext *tfx, Nrrd *nfiber, double seed[3]) {
878   static const char me[]="tenFiberTrace";
879 
880   if (_fiberTraceSet(tfx, NULL, nfiber, NULL, 0, NULL, NULL, seed)) {
881     biffAddf(TEN, "%s: problem computing tract", me);
882     return 1;
883   }
884 
885   return 0;
886 }
887 
888 /*
889 ******** tenFiberDirectionNumber
890 **
891 ** NOTE: for the time being, a return of zero indicates an error, not
892 ** that we're being clever and detect that the seedpoint is in such
893 ** isotropy that no directions are possible (though such cleverness
894 ** will hopefully be implemented soon)
895 */
896 unsigned int
tenFiberDirectionNumber(tenFiberContext * tfx,double seed[3])897 tenFiberDirectionNumber(tenFiberContext *tfx, double seed[3]) {
898   static const char me[]="tenFiberDirectionNumber";
899   unsigned int ret;
900 
901   if (!(tfx && seed)) {
902     biffAddf(TEN, "%s: got NULL pointer", me);
903     return 0;
904   }
905 
906   /* HEY: eventually this stuff will be specific to the seedpoint ... */
907 
908   if (tfx->useDwi) {
909     switch (tfx->fiberType) {
910     case tenDwiFiberType1Evec0:
911       ret = 1;
912       break;
913     case tenDwiFiberType2Evec0:
914       ret = 2;
915       break;
916     case tenDwiFiberType12BlendEvec0:
917       biffAddf(TEN, "%s: sorry, type %s not yet implemented", me,
918               airEnumStr(tenDwiFiberType, tenDwiFiberType12BlendEvec0));
919       ret = 0;
920       break;
921     default:
922       biffAddf(TEN, "%s: type %d unknown!", me, tfx->fiberType);
923       ret = 0;
924       break;
925     }
926   } else {
927     /* not using DWIs */
928     ret = 1;
929   }
930 
931   return ret;
932 }
933 
934 /*
935 ******** tenFiberSingleTrace
936 **
937 ** fiber tracing API that uses new tenFiberSingle, as well as being
938 ** aware of multi-direction tractography
939 **
940 ** NOTE: this will not try any cleverness in setting "num"
941 ** according to whether the seedpoint is a non-starter
942 */
943 int
tenFiberSingleTrace(tenFiberContext * tfx,tenFiberSingle * tfbs,double seed[3],unsigned int which)944 tenFiberSingleTrace(tenFiberContext *tfx, tenFiberSingle *tfbs,
945                     double seed[3], unsigned int which) {
946   static const char me[]="tenFiberSingleTrace";
947 
948   if (!(tfx && tfbs && seed)) {
949     biffAddf(TEN, "%s: got NULL pointer", me);
950     return 1;
951   }
952 
953   /* set input fields in tfbs */
954   ELL_3V_COPY(tfbs->seedPos, seed);
955   tfbs->dirIdx = which;
956   /* not our job to set tfbx->dirNum ... */
957 
958   /* set tfbs->nvert */
959   /* no harm in setting this even when there are no multiple fibers */
960   tfx->ten2Which = which;
961   if (_fiberTraceSet(tfx, (tfx->fiberProbeItem ? tfbs->nval : NULL),
962                      tfbs->nvert, NULL, 0, NULL, NULL, seed)) {
963     biffAddf(TEN, "%s: problem computing tract", me);
964     return 1;
965   }
966 
967   /* set other fields based on tfx output */
968   tfbs->halfLen[0] = tfx->halfLen[0];
969   tfbs->halfLen[1] = tfx->halfLen[1];
970   tfbs->seedIdx = tfx->numSteps[0];
971   tfbs->stepNum[0] = tfx->numSteps[0];
972   tfbs->stepNum[1] = tfx->numSteps[1];
973   tfbs->whyStop[0] = tfx->whyStop[0];
974   tfbs->whyStop[1] = tfx->whyStop[1];
975   tfbs->whyNowhere = tfx->whyNowhere;
976 
977   return 0;
978 }
979 
980 typedef union {
981   tenFiberSingle **f;
982   void **v;
983 } fiberunion;
984 
985 /* uses biff */
986 tenFiberMulti *
tenFiberMultiNew()987 tenFiberMultiNew() {
988   static const char me[]="tenFiberMultiNew";
989   tenFiberMulti *ret;
990   fiberunion tfu;
991 
992   ret = AIR_CAST(tenFiberMulti *, calloc(1, sizeof(tenFiberMulti)));
993   if (ret) {
994     ret->fiber = NULL;
995     ret->fiberNum = 0;
996     tfu.f = &(ret->fiber);
997     ret->fiberArr = airArrayNew(tfu.v, &(ret->fiberNum),
998                                 sizeof(tenFiberSingle), 512 /* incr */);
999     if (ret->fiberArr) {
1000       airArrayStructCB(ret->fiberArr,
1001                        AIR_CAST(void (*)(void *), tenFiberSingleInit),
1002                        AIR_CAST(void (*)(void *), tenFiberSingleDone));
1003     } else {
1004       biffAddf(TEN, "%s: couldn't create airArray", me);
1005       return NULL;
1006     }
1007   } else {
1008     biffAddf(TEN, "%s: couldn't create tenFiberMulti", me);
1009     return NULL;
1010   }
1011   return ret;
1012 }
1013 
1014 int
tenFiberMultiCheck(airArray * arr)1015 tenFiberMultiCheck(airArray *arr) {
1016   static const char me[]="tenFiberMultiCheck";
1017 
1018   if (!arr) {
1019     biffAddf(TEN, "%s: got NULL pointer", me);
1020     return 1;
1021   }
1022   if (sizeof(tenFiberSingle) != arr->unit) {
1023     biffAddf(TEN, "%s: given airArray cannot be for fibers", me);
1024     return 1;
1025   }
1026   if (!(AIR_CAST(void (*)(void *), tenFiberSingleInit) == arr->initCB
1027         && AIR_CAST(void (*)(void *), tenFiberSingleDone) == arr->doneCB)) {
1028     biffAddf(TEN, "%s: given airArray not set up with fiber callbacks", me);
1029     return 1;
1030   }
1031   return 0;
1032 }
1033 
1034 tenFiberMulti *
tenFiberMultiNix(tenFiberMulti * tfm)1035 tenFiberMultiNix(tenFiberMulti *tfm) {
1036 
1037   if (tfm) {
1038     airArrayNuke(tfm->fiberArr);
1039     airFree(tfm);
1040   }
1041   return NULL;
1042 }
1043 
1044 /*
1045 ******** tenFiberMultiTrace
1046 **
1047 ** does tractography for a list of seedpoints
1048 **
1049 ** tfml has been returned from tenFiberMultiNew()
1050 */
1051 int
tenFiberMultiTrace(tenFiberContext * tfx,tenFiberMulti * tfml,const Nrrd * _nseed)1052 tenFiberMultiTrace(tenFiberContext *tfx, tenFiberMulti *tfml,
1053                    const Nrrd *_nseed) {
1054   static const char me[]="tenFiberMultiTrace";
1055   airArray *mop;
1056   const double *seedData;
1057   double seed[3];
1058   unsigned int seedNum, seedIdx, fibrNum, dirNum, dirIdx;
1059   Nrrd *nseed;
1060 
1061   if (!(tfx && tfml && _nseed)) {
1062     biffAddf(TEN, "%s: got NULL pointer", me);
1063     return 1;
1064   }
1065   if (tenFiberMultiCheck(tfml->fiberArr)) {
1066     biffAddf(TEN, "%s: problem with fiber array", me);
1067     return 1;
1068   }
1069   if (!(2 == _nseed->dim && 3 == _nseed->axis[0].size)) {
1070     biffAddf(TEN, "%s: seed list should be a 2-D (not %u-D) "
1071             "3-by-X (not %u-by-X) array", me, _nseed->dim,
1072             AIR_CAST(unsigned int, _nseed->axis[0].size));
1073     return 1;
1074   }
1075 
1076   mop = airMopNew();
1077 
1078   seedNum = _nseed->axis[1].size;
1079   if (nrrdTypeDouble == _nseed->type) {
1080     seedData = AIR_CAST(const double *, _nseed->data);
1081   } else {
1082     nseed = nrrdNew();
1083     airMopAdd(mop, nseed, AIR_CAST(airMopper, nrrdNuke), airMopAlways);
1084     if (nrrdConvert(nseed, _nseed, nrrdTypeDouble)) {
1085       biffMovef(TEN, NRRD, "%s: couldn't convert seed list", me);
1086       return 1;
1087     }
1088     seedData = AIR_CAST(const double *, nseed->data);
1089   }
1090 
1091   /* HEY: the correctness of the use of the airArray here is quite subtle */
1092   fibrNum = 0;
1093   for (seedIdx=0; seedIdx<seedNum; seedIdx++) {
1094     dirNum = tenFiberDirectionNumber(tfx, seed);
1095     if (!dirNum) {
1096       biffAddf(TEN, "%s: couldn't learn dirNum at seed (%g,%g,%g)", me,
1097               seed[0], seed[1], seed[2]);
1098       return 1;
1099     }
1100     for (dirIdx=0; dirIdx<dirNum; dirIdx++) {
1101       if (tfx->verbose > 1) {
1102         fprintf(stderr, "%s: dir %u/%u on seed %u/%u; len %u; # %u\n",
1103                 me, dirIdx, dirNum, seedIdx, seedNum,
1104                 tfml->fiberArr->len, fibrNum);
1105       }
1106       /* tfml->fiberArr->len can never be < fibrNum */
1107       if (tfml->fiberArr->len == fibrNum) {
1108         airArrayLenIncr(tfml->fiberArr, 1);
1109       }
1110       ELL_3V_COPY(tfml->fiber[fibrNum].seedPos, seedData + 3*seedIdx);
1111       tfml->fiber[fibrNum].dirIdx = dirIdx;
1112       tfml->fiber[fibrNum].dirNum = dirNum;
1113       ELL_3V_COPY(seed, seedData + 3*seedIdx);
1114       if (tenFiberSingleTrace(tfx, &(tfml->fiber[fibrNum]), seed, dirIdx)) {
1115         biffAddf(TEN, "%s: trouble on seed (%g,%g,%g) %u/%u, dir %u/%u", me,
1116                 seed[0], seed[1], seed[2], seedIdx, seedNum, dirIdx, dirNum);
1117         return 1;
1118       }
1119       if (tfx->verbose) {
1120         if (tenFiberStopUnknown == tfml->fiber[fibrNum].whyNowhere) {
1121           fprintf(stderr, "%s: (%g,%g,%g) ->\n"
1122                   "   steps = %u,%u; len = %g,%g; whyStop = %s,%s\n",
1123                   me, seed[0], seed[1], seed[2],
1124                   tfml->fiber[fibrNum].stepNum[0],
1125                   tfml->fiber[fibrNum].stepNum[1],
1126                   tfml->fiber[fibrNum].halfLen[0],
1127                   tfml->fiber[fibrNum].halfLen[1],
1128                   airEnumStr(tenFiberStop, tfml->fiber[fibrNum].whyStop[0]),
1129                   airEnumStr(tenFiberStop, tfml->fiber[fibrNum].whyStop[1]));
1130         } else {
1131           fprintf(stderr, "%s: (%g,%g,%g) -> whyNowhere: %s\n",
1132                   me, seed[0], seed[1], seed[2],
1133                   airEnumStr(tenFiberStop, tfml->fiber[fibrNum].whyNowhere));
1134         }
1135       }
1136       fibrNum++;
1137     }
1138   }
1139   /* if the airArray got to be its length only because of the work above,
1140      then the following will be a no-op.  Otherwise, via the callbacks,
1141      it will clear out the tenFiberSingle's that we didn't create here */
1142   airArrayLenSet(tfml->fiberArr, fibrNum);
1143 
1144   airMopOkay(mop);
1145   return 0;
1146 }
1147 
1148 static int
_fiberMultiExtract(tenFiberContext * tfx,Nrrd * nval,limnPolyData * lpld,tenFiberMulti * tfml)1149 _fiberMultiExtract(tenFiberContext *tfx, Nrrd *nval,
1150                    limnPolyData *lpld, tenFiberMulti *tfml) {
1151   static const char me[]="_fiberMultiExtract";
1152   unsigned int seedIdx, vertTotalNum, fiberNum, fiberIdx, vertTotalIdx,
1153     pansLen, pvNum;
1154   double *valOut;
1155 
1156   if (!(tfx && (lpld || nval) && tfml)) {
1157     biffAddf(TEN, "%s: got NULL pointer", me);
1158     return 1;
1159   }
1160   if (tenFiberMultiCheck(tfml->fiberArr)) {
1161     biffAddf(TEN, "%s: problem with fiber array", me);
1162     return 1;
1163   }
1164   if (nval) {
1165     if (!tfx->fiberProbeItem) {
1166       biffAddf(TEN, "%s: want probed values but no item set", me);
1167       return 1;
1168     }
1169     pansLen = gageAnswerLength(tfx->gtx, tfx->pvl, tfx->fiberProbeItem);
1170   } else {
1171     pansLen = 0;
1172   }
1173   /*
1174   fprintf(stderr, "!%s: =========================== \n", me);
1175   fprintf(stderr, "!%s: \n", me);
1176   fprintf(stderr, "!%s: item %d -> pansLen = %u\n", me,
1177           tfx->fiberProbeItem, pansLen);
1178   fprintf(stderr, "!%s: \n", me);
1179   fprintf(stderr, "!%s:  =========================== \n", me);
1180   */
1181 
1182   /* we have to count the real fibers that went somewhere, excluding
1183      fibers that went nowhere (counted in tfml->fiberNum) */
1184   vertTotalNum = 0;
1185   fiberNum = 0;
1186   pvNum = 0;
1187   for (seedIdx=0; seedIdx<tfml->fiberArr->len; seedIdx++) {
1188     tenFiberSingle *tfs;
1189     tfs = tfml->fiber + seedIdx;
1190     if (!(tenFiberStopUnknown == tfs->whyNowhere)) {
1191       continue;
1192     }
1193     if (nval) {
1194       if (tfs->nval) {
1195         if (!(2 == tfs->nval->dim
1196               && pansLen == tfs->nval->axis[0].size
1197               && tfs->nvert->axis[1].size == tfs->nval->axis[1].size)) {
1198           biffAddf(TEN, "%s: fiber[%u]->nval seems wrong", me, seedIdx);
1199           return 1;
1200         }
1201         pvNum++;
1202       }
1203     }
1204     vertTotalNum += tfs->nvert->axis[1].size;
1205     fiberNum++;
1206   }
1207   if (nval && pvNum != fiberNum) {
1208     biffAddf(TEN, "%s: pvNum %u != fiberNum %u", me, pvNum, fiberNum);
1209     return 1;
1210   }
1211 
1212   if (nval) {
1213     if (nrrdMaybeAlloc_va(nval, nrrdTypeDouble, 2,
1214                           AIR_CAST(size_t, pansLen),
1215                           AIR_CAST(size_t, vertTotalNum))) {
1216       biffMovef(TEN, NRRD, "%s: couldn't allocate output", me);
1217       return 1;
1218     }
1219     valOut = AIR_CAST(double *, nval->data);
1220   } else {
1221     valOut = NULL;
1222   }
1223   if (lpld) {
1224     if (limnPolyDataAlloc(lpld, 0, /* no extra per-vertex info */
1225                           vertTotalNum, vertTotalNum, fiberNum)) {
1226       biffMovef(TEN, LIMN, "%s: couldn't allocate output", me);
1227       return 1;
1228     }
1229   }
1230 
1231   fiberIdx = 0;
1232   vertTotalIdx = 0;
1233   for (seedIdx=0; seedIdx<tfml->fiberArr->len; seedIdx++) {
1234     double *vert, *pans;
1235     unsigned int vertIdx, vertNum;
1236     tenFiberSingle *tfs;
1237     tfs = tfml->fiber + seedIdx;
1238     if (!(tenFiberStopUnknown == tfs->whyNowhere)) {
1239       continue;
1240     }
1241     vertNum = tfs->nvert->axis[1].size;
1242     pans = (nval
1243             ? AIR_CAST(double*, tfs->nval->data)
1244             : NULL);
1245     vert = (lpld
1246             ? AIR_CAST(double*, tfs->nvert->data)
1247             : NULL);
1248     for (vertIdx=0; vertIdx<vertNum; vertIdx++) {
1249       if (lpld) {
1250         ELL_3V_COPY_TT(lpld->xyzw + 4*vertTotalIdx, float, vert + 3*vertIdx);
1251         (lpld->xyzw + 4*vertTotalIdx)[3] = 1.0;
1252         lpld->indx[vertTotalIdx] = vertTotalIdx;
1253       }
1254       if (nval) {
1255         /* HEY speed up memcpy */
1256         memcpy(valOut + pansLen*vertTotalIdx,
1257                pans + pansLen*vertIdx,
1258                pansLen*sizeof(double));
1259       }
1260       vertTotalIdx++;
1261     }
1262     if (lpld) {
1263       lpld->type[fiberIdx] = limnPrimitiveLineStrip;
1264       lpld->icnt[fiberIdx] = vertNum;
1265     }
1266     fiberIdx++;
1267   }
1268 
1269   return 0;
1270 }
1271 
1272 /*
1273 ******** tenFiberMultiPolyData
1274 **
1275 ** converts tenFiberMulti to polydata.
1276 **
1277 ** currently the tenFiberContext *tfx arg is not used, but it will
1278 ** probably be needed in the future as the way that parameters to the
1279 ** polydata creation process are passed.
1280 */
1281 int
tenFiberMultiPolyData(tenFiberContext * tfx,limnPolyData * lpld,tenFiberMulti * tfml)1282 tenFiberMultiPolyData(tenFiberContext *tfx,
1283                       limnPolyData *lpld, tenFiberMulti *tfml) {
1284   static const char me[]="tenFiberMultiPolyData";
1285 
1286   if (_fiberMultiExtract(tfx, NULL, lpld, tfml)) {
1287     biffAddf(TEN, "%s: problem", me);
1288     return 1;
1289   }
1290   return 0;
1291 }
1292 
1293 
1294 int
tenFiberMultiProbeVals(tenFiberContext * tfx,Nrrd * nval,tenFiberMulti * tfml)1295 tenFiberMultiProbeVals(tenFiberContext *tfx,
1296                        Nrrd *nval, tenFiberMulti *tfml) {
1297   static const char me[]="tenFiberMultiProbeVals";
1298 
1299   if (_fiberMultiExtract(tfx, nval, NULL, tfml)) {
1300     biffAddf(TEN, "%s: problem", me);
1301     return 1;
1302   }
1303   return 0;
1304 }
1305