1 |
|
|
/* |
2 |
|
|
Teem: Tools to process and visualize scientific data and images . |
3 |
|
|
Copyright (C) 2013, 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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 * |
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 |
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 * |
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 |
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 |
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 |
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 |
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 |
|
|
} |