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
25 #include "pull.h"
26 #include "privatePull.h"
27
28 pullTrace *
pullTraceNew(void)29 pullTraceNew(void) {
30 pullTrace *ret;
31
32 ret = AIR_CALLOC(1, pullTrace);
33 if (ret) {
34 ret->seedPos[0] = ret->seedPos[1] = AIR_NAN;
35 ret->seedPos[2] = ret->seedPos[3] = AIR_NAN;
36 ret->nvert = nrrdNew();
37 ret->nstrn = nrrdNew();
38 ret->nvelo = nrrdNew();
39 ret->seedIdx = 0;
40 ret->whyStop[0] = ret->whyStop[1] = pullTraceStopUnknown;
41 ret->whyNowhere = pullTraceStopUnknown;
42 }
43 return ret;
44 }
45
46 pullTrace *
pullTraceNix(pullTrace * pts)47 pullTraceNix(pullTrace *pts) {
48
49 if (pts) {
50 nrrdNuke(pts->nvert);
51 nrrdNuke(pts->nstrn);
52 nrrdNuke(pts->nvelo);
53 free(pts);
54 }
55 return NULL;
56 }
57
58
59 int
pullTraceSet(pullContext * pctx,pullTrace * pts,int recordStrength,double scaleDelta,double halfScaleWin,double velocityMax,unsigned int arrIncr,const double seedPos[4])60 pullTraceSet(pullContext *pctx, pullTrace *pts,
61 int recordStrength,
62 double scaleDelta, double halfScaleWin,
63 double velocityMax, unsigned int arrIncr,
64 const double seedPos[4]) {
65 static const char me[]="pullTraceSet";
66 pullPoint *point;
67 airArray *mop, *trceArr[2], *hstrnArr[2];
68 double *trce[2], ssrange[2], *vert, *hstrn[2], *strn, *velo, travmax;
69 int constrFail;
70 unsigned int dirIdx, lentmp, tidx, oidx, vertNum;
71
72 if (!( pctx && pts && seedPos )) {
73 biffAddf(PULL, "%s: got NULL pointer", me);
74 return 1;
75 }
76 if (!( AIR_EXISTS(scaleDelta) && scaleDelta > 0.0 )) {
77 biffAddf(PULL, "%s: need existing scaleDelta > 0 (not %g)",
78 me, scaleDelta);
79 return 1;
80 }
81 if (!( halfScaleWin > 0 )) {
82 biffAddf(PULL, "%s: need halfScaleWin > 0", me);
83 return 1;
84 }
85 if (!(pctx->constraint)) {
86 biffAddf(PULL, "%s: given context doesn't have constraint set", me);
87 return 1;
88 }
89 if (recordStrength && !pctx->ispec[pullInfoStrength]) {
90 biffAddf(PULL, "%s: want to record strength but %s not set in context",
91 me, airEnumStr(pullInfo, pullInfoStrength));
92 return 1;
93 }
94 if (pullConstraintScaleRange(pctx, ssrange)) {
95 biffAddf(PULL, "%s: trouble getting scale range", me);
96 return 1;
97 }
98
99 /* re-initialize termination descriptions (in case of trace re-use) */
100 pts->whyStop[0] = pts->whyStop[1] = pullTraceStopUnknown;
101 pts->whyNowhere = pullTraceStopUnknown;
102
103 /* save seedPos in any case */
104 ELL_4V_COPY(pts->seedPos, seedPos);
105
106 mop = airMopNew();
107 point = pullPointNew(pctx); /* we'll want to decrement idtagNext later */
108 airMopAdd(mop, point, (airMopper)pullPointNix, airMopAlways);
109
110 ELL_4V_COPY(point->pos, seedPos);
111 if (_pullConstraintSatisfy(pctx->task[0], point, 2, &constrFail)) {
112 biffAddf(PULL, "%s: constraint sat on seed point", me);
113 airMopError(mop);
114 return 1;
115 }
116 /*
117 fprintf(stderr, "!%s: seed=(%g,%g,%g,%g) -> %s (%g,%g,%g,%g)\n", me,
118 seedPos[0], seedPos[1], seedPos[2], seedPos[3],
119 constrFail ? "!NO!" : "(yes)",
120 point->pos[0] - seedPos[0], point->pos[1] - seedPos[1],
121 point->pos[2] - seedPos[2], point->pos[3] - seedPos[3]);
122 */
123 if (constrFail) {
124 pts->whyNowhere = pullTraceStopConstrFail;
125 airMopOkay(mop);
126 pctx->idtagNext -= 1; /* HACK */
127 return 0;
128 }
129
130 /* else constraint sat worked at seed point; we have work to do */
131 /* travmax is passed to _pullConstraintSatisfy; the intention is
132 that constraint satisfaction should not fail because the point
133 traveled too far (so make travmax large); the limiting of the
134 trace based on velocity is handled here, not by
135 _pullConstraintSatisfy */
136 travmax = 10.0*scaleDelta*velocityMax/pctx->voxelSizeSpace;
137
138 for (dirIdx=0; dirIdx<2; dirIdx++) {
139 trceArr[dirIdx] = airArrayNew((void**)(trce + dirIdx), NULL,
140 4*sizeof(double), arrIncr);
141 airMopAdd(mop, trceArr[dirIdx], (airMopper)airArrayNuke, airMopAlways);
142 if (recordStrength) {
143 hstrnArr[dirIdx] = airArrayNew((void**)(hstrn + dirIdx), NULL,
144 sizeof(double), arrIncr);
145 airMopAdd(mop, hstrnArr[dirIdx], (airMopper)airArrayNuke, airMopAlways);
146 } else {
147 hstrnArr[dirIdx] = NULL;
148 hstrn[dirIdx] = NULL;
149 }
150 }
151 for (dirIdx=0; dirIdx<2; dirIdx++) {
152 unsigned int step;
153 double dscl;
154 dscl = (!dirIdx ? -1 : +1)*scaleDelta;
155 step = 0;
156 while (1) {
157 if (!step) {
158 /* first step in both directions requires special tricks */
159 if (0 == dirIdx) {
160 /* save constraint sat of seed point */
161 tidx = airArrayLenIncr(trceArr[0], 1);
162 ELL_4V_COPY(trce[0] + 4*tidx, point->pos);
163 if (recordStrength) {
164 tidx = airArrayLenIncr(hstrnArr[0], 1);
165 hstrn[0][0] = pullPointScalar(pctx, point, pullInfoStrength,
166 NULL, NULL);
167 }
168 } else {
169 /* re-set position from constraint sat of seed pos */
170 ELL_4V_COPY(point->pos, trce[0] + 4*0);
171 }
172 }
173 /* nudge position along scale */
174 point->pos[3] += dscl;
175 if (!AIR_IN_OP(ssrange[0], point->pos[3], ssrange[1])) {
176 /* if we've stepped outside the range of scale for the volume
177 containing the constraint manifold, we're done */
178 pts->whyStop[dirIdx] = pullTraceStopBounds;
179 break;
180 }
181 if (AIR_ABS(point->pos[3] - seedPos[3]) > halfScaleWin) {
182 /* we've moved along scale as far as allowed */
183 pts->whyStop[dirIdx] = pullTraceStopLength;
184 break;
185 }
186 /* re-assert constraint */
187 /*
188 fprintf(stderr, "%s(%u): pos = %g %g %g %g.... \n", me,
189 point->idtag, point->pos[0], point->pos[1],
190 point->pos[2], point->pos[3]);
191 */
192 if (_pullConstraintSatisfy(pctx->task[0], point,
193 travmax, &constrFail)) {
194 biffAddf(PULL, "%s: dir %u, step %u", me, dirIdx, step);
195 airMopError(mop);
196 return 1;
197 }
198 /*
199 fprintf(stderr, "%s(%u): ... %s(%d); pos = %g %g %g %g\n", me,
200 point->idtag,
201 constrFail ? "FAIL" : "(ok)",
202 constrFail, point->pos[0], point->pos[1],
203 point->pos[2], point->pos[3]);
204 */
205 if (constrFail) {
206 /* constraint sat failed; no error, we're just done
207 with stepping for this direction */
208 pts->whyStop[dirIdx] = pullTraceStopConstrFail;
209 break;
210 }
211 if (trceArr[dirIdx]->len >= 2) {
212 /* see if we're moving too fast, by comparing with previous point */
213 double pos0[3], pos1[3], diff[3], vv;
214 unsigned int ii;
215
216 ii = trceArr[dirIdx]->len-2;
217 ELL_3V_COPY(pos0, trce[dirIdx] + 4*(ii+0));
218 ELL_3V_COPY(pos1, trce[dirIdx] + 4*(ii+1));
219 ELL_3V_SUB(diff, pos1, pos0);
220 vv = ELL_3V_LEN(diff)/scaleDelta;
221 /*
222 fprintf(stderr, "%s(%u): velo %g %s velocityMax %g => %s\n", me,
223 point->idtag, vv,
224 vv > velocityMax ? ">" : "<=",
225 velocityMax,
226 vv > velocityMax ? "FAIL" : "(ok)");
227 */
228 if (vv > velocityMax) {
229 pts->whyStop[dirIdx] = pullTraceStopSpeeding;
230 break;
231 }
232 }
233 /* else save new point on trace */
234 tidx = airArrayLenIncr(trceArr[dirIdx], 1);
235 ELL_4V_COPY(trce[dirIdx] + 4*tidx, point->pos);
236 if (recordStrength) {
237 tidx = airArrayLenIncr(hstrnArr[dirIdx], 1);
238 hstrn[dirIdx][tidx] = pullPointScalar(pctx, point, pullInfoStrength,
239 NULL, NULL);
240 }
241 step++;
242 }
243 }
244
245 /* transfer trace halves to pts->nvert */
246 vertNum = trceArr[0]->len + trceArr[1]->len;
247 if (0 == vertNum || 1 == vertNum || 2 == vertNum) {
248 pts->whyNowhere = pullTraceStopStub;
249 airMopOkay(mop);
250 pctx->idtagNext -= 1; /* HACK */
251 return 0;
252 }
253
254 if (nrrdMaybeAlloc_va(pts->nvert, nrrdTypeDouble, 2,
255 AIR_CAST(size_t, 4),
256 AIR_CAST(size_t, vertNum))
257 || nrrdMaybeAlloc_va(pts->nvelo, nrrdTypeDouble, 1,
258 AIR_CAST(size_t, vertNum))) {
259 biffMovef(PULL, NRRD, "%s: allocating output", me);
260 airMopError(mop);
261 return 1;
262 }
263 if (recordStrength) {
264 if (nrrdSlice(pts->nstrn, pts->nvert, 0 /* axis */, 0 /* pos */)) {
265 biffMovef(PULL, NRRD, "%s: allocating output", me);
266 airMopError(mop);
267 return 1;
268 }
269 }
270 vert = AIR_CAST(double *, pts->nvert->data);
271 if (recordStrength) {
272 strn = AIR_CAST(double *, pts->nstrn->data);
273 } else {
274 strn = NULL;
275 }
276 velo = AIR_CAST(double *, pts->nvelo->data);
277 lentmp = trceArr[0]->len;
278 oidx = 0;
279 for (tidx=0; tidx<lentmp; tidx++) {
280 ELL_4V_COPY(vert + 4*oidx, trce[0] + 4*(lentmp - 1 - tidx));
281 if (strn) {
282 strn[oidx] = hstrn[0][lentmp - 1 - tidx];
283 }
284 oidx++;
285 }
286 /* the last index written to (before oidx++) was the seed index */
287 pts->seedIdx = oidx-1;
288 lentmp = trceArr[1]->len;
289 for (tidx=0; tidx<lentmp; tidx++) {
290 ELL_4V_COPY(vert + 4*oidx, trce[1] + 4*tidx);
291 if (strn) {
292 strn[oidx] = hstrn[1][tidx];
293 }
294 oidx++;
295 }
296 lentmp = pts->nvelo->axis[0].size;
297 if (1 == lentmp) {
298 velo[0] = 0.0;
299 } else {
300 for (tidx=0; tidx<lentmp; tidx++) {
301 double *p0, *p1, *p2, diff[3];
302 if (!tidx) {
303 /* first */
304 p1 = vert + 4*tidx;
305 p2 = vert + 4*(tidx+1);
306 ELL_3V_SUB(diff, p2, p1);
307 velo[tidx] = ELL_3V_LEN(diff)/(p2[3]-p1[3]);
308 } else if (tidx < lentmp-1) {
309 /* middle */
310 p0 = vert + 4*(tidx-1);
311 p2 = vert + 4*(tidx+1);
312 ELL_3V_SUB(diff, p2, p0);
313 velo[tidx] = ELL_3V_LEN(diff)/(p2[3]-p0[3]);
314 } else {
315 /* last */
316 p0 = vert + 4*(tidx-1);
317 p1 = vert + 4*tidx;
318 ELL_3V_SUB(diff, p1, p0);
319 velo[tidx] = ELL_3V_LEN(diff)/(p1[3]-p0[3]);
320 }
321 }
322 }
323
324 airMopOkay(mop);
325 pctx->idtagNext -= 1; /* HACK */
326 return 0;
327 }
328
329 typedef union {
330 pullTrace ***trace;
331 void **v;
332 } blahblahUnion;
333
334 pullTraceMulti *
pullTraceMultiNew(void)335 pullTraceMultiNew(void) {
336 /* static const char me[]="pullTraceMultiNew"; */
337 pullTraceMulti *ret;
338 blahblahUnion bbu;
339
340 ret = AIR_CALLOC(1, pullTraceMulti);
341 if (ret) {
342 ret->trace = NULL;
343 ret->traceNum = 0;
344 ret->traceArr = airArrayNew((bbu.trace = &(ret->trace), bbu.v),
345 &(ret->traceNum), sizeof(pullTrace*),
346 _PULL_TRACE_MULTI_INCR);
347 airArrayPointerCB(ret->traceArr,
348 NULL, /* because we get handed pullTrace structs
349 that have already been allocated
350 (and then we own them) */
351 (void *(*)(void *))pullTraceNix);
352 }
353 return ret;
354 }
355
356 int
pullTraceMultiAdd(pullTraceMulti * mtrc,pullTrace * trc,int * addedP)357 pullTraceMultiAdd(pullTraceMulti *mtrc, pullTrace *trc, int *addedP) {
358 static const char me[]="pullTraceMultiAdd";
359 unsigned int indx;
360
361 if (!(mtrc && trc && addedP)) {
362 biffAddf(PULL, "%s: got NULL pointer", me);
363 return 1;
364 }
365 if (!(trc->nvert->data && trc->nvert->axis[1].size >= 3)) {
366 /* for now getting a stub trace is not an error
367 biffAddf(PULL, "%s: got stub trace", me);
368 return 1; */
369 *addedP = AIR_FALSE;
370 return 0;
371 }
372 if (!(trc->nvelo->data
373 && trc->nvelo->axis[0].size == trc->nvert->axis[1].size)) {
374 biffAddf(PULL, "%s: velo data inconsistent", me);
375 return 1;
376 }
377 *addedP = AIR_TRUE;
378 indx = airArrayLenIncr(mtrc->traceArr, 1);
379 if (!mtrc->trace) {
380 biffAddf(PULL, "%s: alloc error", me);
381 return 1;
382 }
383 mtrc->trace[indx] = trc;
384 return 0;
385 }
386
387 int
pullTraceMultiFilterConcaveDown(Nrrd * nfilt,const pullTraceMulti * mtrc,double winLenFrac)388 pullTraceMultiFilterConcaveDown(Nrrd *nfilt, const pullTraceMulti *mtrc,
389 double winLenFrac) {
390 static const char me[]="pullTraceMultiFilterConcaveDown";
391 unsigned int ti;
392 int *filt;
393
394 if (!(nfilt && mtrc)) {
395 biffAddf(PULL, "%s: got NULL pointer (%p %p)", me,
396 AIR_VOIDP(nfilt), AIR_CVOIDP(mtrc));
397 return 1;
398 }
399 if (!(AIR_EXISTS(winLenFrac) && AIR_IN_OP(0.0, winLenFrac, 1.0))) {
400 biffAddf(PULL, "%s: winLenFrac %g doesn't exist or not in [0,1]",
401 me, winLenFrac);
402 return 1;
403 }
404 if (nrrdMaybeAlloc_va(nfilt, nrrdTypeInt, 1, mtrc->traceNum)) {
405 biffMovef(PULL, NRRD, "%s: trouble creating output", me);
406 return 1;
407 }
408 filt = AIR_CAST(int *, nfilt->data);
409 for (ti=0; ti<mtrc->traceNum; ti++) {
410 unsigned winLen;
411 const pullTrace *trc;
412 const double *velo;
413 unsigned int schange, pidx, lentmp;
414 double dv, dv0=0.0, rdv, dv1;
415
416 trc = mtrc->trace[ti];
417 lentmp = trc->nvert->axis[1].size;
418 velo = AIR_CAST(const double *, trc->nvelo->data);
419 winLen = AIR_CAST(unsigned int, winLenFrac*lentmp);
420 if (winLen < 3) {
421 continue;
422 }
423 schange = 0;
424 rdv = 0.0;
425 for (pidx=0; pidx<lentmp-1; pidx++) {
426 /* normalizing by scaleDelta isn't needed for detecting sign changes */
427 dv = velo[pidx+1] - velo[pidx];
428 if (pidx < winLen) {
429 rdv += dv;
430 } else {
431 double tmp;
432 if (pidx == winLen) {
433 dv0 = rdv;
434 }
435 tmp = rdv;
436 rdv += dv;
437 rdv -= velo[pidx-winLen+1] - velo[pidx-winLen];
438 schange += (rdv*tmp < 0);
439 }
440 }
441 dv1 = rdv;
442 filt[ti] = (1 == schange) && dv0 < 0.0 && dv1 > 0.0;
443 }
444 return 0;
445 }
446
447 int
pullTraceMultiPlotAdd(Nrrd * nplot,const pullTraceMulti * mtrc,const Nrrd * nfilt,unsigned int trcIdxMin,unsigned int trcNum)448 pullTraceMultiPlotAdd(Nrrd *nplot, const pullTraceMulti *mtrc,
449 const Nrrd *nfilt,
450 unsigned int trcIdxMin,unsigned int trcNum) {
451 static const char me[]="pullTraceMultiPlot";
452 double ssRange[2], vRange[2], velHalf, *plot;
453 unsigned int sizeS, sizeV, trcIdx, trcIdxMax;
454 int *filt;
455
456 if (!(nplot && mtrc)) {
457 biffAddf(PULL, "%s: got NULL pointer", me);
458 return 1;
459 }
460 if (nrrdCheck(nplot)) {
461 biffMovef(PULL, NRRD, "%s: trouble with nplot", me);
462 return 1;
463 }
464 if (nfilt) {
465 if (nrrdCheck(nfilt)) {
466 biffMovef(PULL, NRRD, "%s: trouble with nfilt", me);
467 return 1;
468 }
469 if (!(1 == nfilt->dim && nrrdTypeInt == nfilt->type)) {
470 biffAddf(PULL, "%s: didn't get 1-D array of %s (got %u-D of %s)", me,
471 airEnumStr(nrrdType, nrrdTypeInt), nfilt->dim,
472 airEnumStr(nrrdType, nfilt->type));
473 return 1;
474 }
475 }
476 if (!(2 == nplot->dim && nrrdTypeDouble == nplot->type)) {
477 biffAddf(PULL, "%s: didn't get 2-D array of %s (got %u-D of %s)", me,
478 airEnumStr(nrrdType, nrrdTypeDouble), nplot->dim,
479 airEnumStr(nrrdType, nplot->type));
480 return 1;
481 }
482 if (!(trcIdxMin < mtrc->traceNum)) {
483 biffAddf(PULL, "%s: trcIdxMin %u not < traceNum %u", me,
484 trcIdxMin, mtrc->traceNum);
485 return 1;
486 }
487 if (trcNum) {
488 trcIdxMax = trcIdxMin + trcNum-1;
489 if (!(trcIdxMax < mtrc->traceNum)) {
490 biffAddf(PULL, "%s: trcIdxMax %u = %u+%u-1 not < traceNum %u", me,
491 trcIdxMax, trcIdxMin, trcNum, mtrc->traceNum);
492 return 1;
493 }
494 } else {
495 trcIdxMax = mtrc->traceNum-1;
496 }
497 ssRange[0] = nplot->axis[0].min;
498 ssRange[1] = nplot->axis[0].max;
499 vRange[0] = nplot->axis[1].min;
500 vRange[1] = nplot->axis[1].max;
501 if (!( AIR_EXISTS(ssRange[0]) && AIR_EXISTS(ssRange[1]) &&
502 AIR_EXISTS(vRange[0]) && AIR_EXISTS(vRange[1]) )) {
503 biffAddf(PULL, "%s: need both axis 0 (%g,%g) and 1 (%g,%g) min,max", me,
504 ssRange[0], ssRange[1], vRange[0], vRange[1]);
505 return 1;
506 }
507 if (0 != vRange[0]) {
508 biffAddf(PULL, "%s: expected vRange[0] == 0 not %g", me, vRange[0]);
509 return 1;
510 }
511 /* HEY: this is a sneaky hack; the non-linear encoding of velocity along
512 this axis means that the max velocity is actually infinite, but this
513 seems like the least wrong way of storing this information in the place
514 where it belongs (in the output plot nrrd) instead of assuming it will
515 always be passed the same in successive calls */
516 velHalf = vRange[1]/2.0;
517 plot = AIR_CAST(double *, nplot->data);
518 filt = (nfilt
519 ? AIR_CAST(int *, nfilt->data)
520 : NULL);
521 sizeS = AIR_CAST(unsigned int, nplot->axis[0].size);
522 sizeV = AIR_CAST(unsigned int, nplot->axis[1].size);
523 for (trcIdx=trcIdxMin; trcIdx<=trcIdxMax; trcIdx++) {
524 unsigned int pntIdx, pntNum;
525 const pullTrace *trc;
526 const double *vert, *velo;
527 if (filt && !filt[trcIdx]) {
528 continue;
529 }
530 trc = mtrc->trace[trcIdx];
531 if (pullTraceStopStub == trc->whyNowhere) {
532 continue;
533 }
534 vert = AIR_CAST(double *, trc->nvert->data);
535 velo = AIR_CAST(double *, trc->nvelo->data);
536 pntNum = trc->nvert->axis[1].size;
537 for (pntIdx=0; pntIdx<pntNum; pntIdx++) {
538 const double *pp;
539 unsigned int sidx, vidx;
540 pp = vert + 4*pntIdx;
541 if (!(AIR_IN_OP(ssRange[0], pp[3], ssRange[1]))) {
542 continue;
543 }
544 if (velo[pntIdx] <= 0.0) {
545 continue;
546 }
547 sidx = airIndex(ssRange[0], pp[3], ssRange[1], sizeS);
548 /* HEY weird that Clamp is needed, but it is, as this atan()
549 does sometime return a negative value (?) */
550 vidx = airIndexClamp(0.0, atan(velo[pntIdx]/velHalf), AIR_PI/2, sizeV);
551 plot[sidx + sizeS*vidx] += 1;
552 }
553 }
554 return 0;
555 }
556
557 static size_t
nsizeof(const Nrrd * nrrd)558 nsizeof(const Nrrd *nrrd) {
559 return (nrrd
560 ? nrrdElementSize(nrrd)*nrrdElementNumber(nrrd)
561 : 0);
562 }
563
564 size_t
pullTraceMultiSizeof(const pullTraceMulti * mtrc)565 pullTraceMultiSizeof(const pullTraceMulti *mtrc) {
566 size_t ret;
567 unsigned int ti;
568
569 if (!mtrc) {
570 return 0;
571 }
572 ret = 0;
573 for (ti=0; ti<mtrc->traceNum; ti++) {
574 ret += sizeof(pullTrace);
575 ret += nsizeof(mtrc->trace[ti]->nvert);
576 ret += nsizeof(mtrc->trace[ti]->nstrn);
577 ret += nsizeof(mtrc->trace[ti]->nvelo);
578 }
579 ret += sizeof(pullTrace*)*(mtrc->traceArr->size);
580 return ret;
581 }
582
583 pullTraceMulti *
pullTraceMultiNix(pullTraceMulti * mtrc)584 pullTraceMultiNix(pullTraceMulti *mtrc) {
585
586 if (mtrc) {
587 airArrayNuke(mtrc->traceArr);
588 free(mtrc);
589 }
590 return NULL;
591 }
592
593
594 #define PULL_MTRC_MAGIC "PULLMTRC0001"
595 #define DEMARK_STR "======"
596
597 static int
tracewrite(FILE * file,const pullTrace * trc,unsigned int ti)598 tracewrite(FILE *file, const pullTrace *trc, unsigned int ti) {
599 static const char me[]="tracewrite";
600
601 fprintf(file, "%s %u\n", DEMARK_STR, ti);
602 ell_4v_print_d(file, trc->seedPos);
603 #define WRITE(FF) \
604 if (trc->FF && trc->FF->data) { \
605 if (nrrdWrite(file, trc->FF, NULL)) { \
606 biffMovef(PULL, NRRD, "%s: trouble with " #FF , me); \
607 return 1; \
608 } \
609 } else { \
610 fprintf(file, "NULL"); \
611 } \
612 fprintf(file, "\n")
613 fprintf(file, "nrrds: vert strn velo = %d %d %d\n",
614 trc->nvert && trc->nvert->data,
615 trc->nstrn && trc->nstrn->data,
616 trc->nvelo && trc->nvelo->data);
617 WRITE(nvert);
618 WRITE(nstrn);
619 WRITE(nvelo);
620 fprintf(file, "%u\n", trc->seedIdx);
621 fprintf(file, "%s %s %s\n",
622 airEnumStr(pullTraceStop, trc->whyStop[0]),
623 airEnumStr(pullTraceStop, trc->whyStop[1]),
624 airEnumStr(pullTraceStop, trc->whyNowhere));
625 #undef WRITE
626 return 0;
627 }
628
629 int
pullTraceMultiWrite(FILE * file,const pullTraceMulti * mtrc)630 pullTraceMultiWrite(FILE *file, const pullTraceMulti *mtrc) {
631 static const char me[]="pullTraceMultiWrite";
632 unsigned int ti;
633
634 if (!(file && mtrc)) {
635 biffAddf(PULL, "%s: got NULL pointer", me);
636 return 1;
637 }
638 fprintf(file, "%s\n", PULL_MTRC_MAGIC);
639 fprintf(file, "%u traces\n", mtrc->traceNum);
640
641 for (ti=0; ti<mtrc->traceNum; ti++) {
642 if (tracewrite(file, mtrc->trace[ti], ti)) {
643 biffAddf(PULL, "%s: trace %u/%u", me, ti, mtrc->traceNum);
644 return 1;
645 }
646 }
647 return 0;
648 }
649
650 static int
traceread(pullTrace * trc,FILE * file,unsigned int _ti)651 traceread(pullTrace *trc, FILE *file, unsigned int _ti) {
652 static const char me[]="traceread";
653 char line[AIR_STRLEN_MED], name[AIR_STRLEN_MED];
654 unsigned int ti, lineLen;
655 int stops[3], hackhack, vertHN, strnHN, veloHN; /* HN == have nrrd */
656
657 sprintf(name, "separator");
658 lineLen = airOneLine(file, line, AIR_STRLEN_MED);
659 if (!lineLen) {
660 biffAddf(PULL, "%s: didn't get %s line", me, name);
661 return 1;
662 }
663 if (1 != sscanf(line, DEMARK_STR " %u", &ti)) {
664 biffAddf(PULL, "%s: \"%s\" doesn't look like %s line", me, line, name);
665 return 1;
666 }
667 if (ti != _ti) {
668 biffAddf(PULL, "%s: read trace index %u but wanted %u", me, ti, _ti);
669 return 1;
670 }
671 sprintf(name, "seed pos");
672 lineLen = airOneLine(file, line, AIR_STRLEN_MED);
673 if (!lineLen) {
674 biffAddf(PULL, "%s: didn't get %s line", me, name);
675 return 1;
676 }
677 if (4 != sscanf(line, "%lg %lg %lg %lg", trc->seedPos + 0,
678 trc->seedPos + 1, trc->seedPos + 2, trc->seedPos + 3)) {
679 biffAddf(PULL, "%s: couldn't parse %s line \"%s\" as 4 doubles",
680 me, name, line);
681 return 1;
682 }
683 sprintf(name, "have nrrds");
684 lineLen = airOneLine(file, line, AIR_STRLEN_MED);
685 if (!lineLen) {
686 biffAddf(PULL, "%s: didn't get %s line", me, name);
687 return 1;
688 }
689 if (3 != sscanf(line, "nrrds: vert strn velo = %d %d %d",
690 &vertHN, &strnHN, &veloHN)) {
691 biffAddf(PULL, "%s: couldn't parse %s line", me, name);
692 return 1;
693 }
694 #define READ(FF) \
695 if (FF##HN) { \
696 if (nrrdRead(trc->n##FF, file, NULL)) { \
697 biffMovef(PULL, NRRD, "%s: trouble with " #FF , me); \
698 return 1; \
699 } \
700 fgetc(file); \
701 } else { \
702 airOneLine(file, line, AIR_STRLEN_MED); \
703 }
704 hackhack = nrrdStateVerboseIO; /* should be fixed in Teem v2 */
705 nrrdStateVerboseIO = 0;
706 READ(vert);
707 READ(strn);
708 READ(velo);
709 nrrdStateVerboseIO = hackhack;
710
711 sprintf(name, "seed idx");
712 lineLen = airOneLine(file, line, AIR_STRLEN_MED);
713 if (!lineLen) {
714 biffAddf(PULL, "%s: didn't get %s line", me, name);
715 return 1;
716 }
717 if (1 != sscanf(line, "%u", &(trc->seedIdx))) {
718 biffAddf(PULL, "%s: didn't parse uint from %s line \"%s\"",
719 me, name, line);
720 return 1;
721 }
722 sprintf(name, "stops");
723 lineLen = airOneLine(file, line, AIR_STRLEN_MED);
724 if (!lineLen) {
725 biffAddf(PULL, "%s: didn't get %s line", me, name);
726 return 1;
727 }
728 if (3 != airParseStrE(stops, line, " ", 3, pullTraceStop)) {
729 biffAddf(PULL, "%s: didn't see 3 %s on %s line \"%s\"", me,
730 pullTraceStop->name, name, line);
731 return 1;
732 }
733
734 return 0;
735 }
736 int
pullTraceMultiRead(pullTraceMulti * mtrc,FILE * file)737 pullTraceMultiRead(pullTraceMulti *mtrc, FILE *file) {
738 static const char me[]="pullTraceMultiRead";
739 char line[AIR_STRLEN_MED], name[AIR_STRLEN_MED];
740 unsigned int lineLen, ti, tnum;
741 pullTrace *trc;
742
743 if (!(mtrc && file)) {
744 biffAddf(PULL, "%s: got NULL pointer", me);
745 return 1;
746 }
747 airArrayLenSet(mtrc->traceArr, 0);
748 sprintf(name, "magic");
749 lineLen = airOneLine(file, line, AIR_STRLEN_MED);
750 if (!lineLen) {
751 biffAddf(PULL, "%s: didn't get %s line", me, name);
752 return 1;
753 }
754 if (strcmp(line, PULL_MTRC_MAGIC)) {
755 biffAddf(PULL, "%s: %s line \"%s\" not expected \"%s\"",
756 me, name, line, PULL_MTRC_MAGIC);
757 return 1;
758 }
759
760 sprintf(name, "# of traces");
761 lineLen = airOneLine(file, line, AIR_STRLEN_MED);
762 if (!lineLen) {
763 biffAddf(PULL, "%s: didn't get %s line", me, name);
764 return 1;
765 }
766 if (1 != sscanf(line, "%u traces", &tnum)) {
767 biffAddf(PULL, "%s: \"%s\" doesn't look like %s line", me, line, name);
768 return 1;
769 }
770 for (ti=0; ti<tnum; ti++) {
771 int added;
772 trc = pullTraceNew();
773 if (traceread(trc, file, ti)) {
774 biffAddf(PULL, "%s: on trace %u/%u", me, ti, tnum);
775 return 1;
776 }
777 if (pullTraceMultiAdd(mtrc, trc, &added)) {
778 biffAddf(PULL, "%s: adding trace %u/%u", me, ti, tnum);
779 return 1;
780 }
781 if (!added) {
782 trc = pullTraceNix(trc);
783 }
784 }
785
786 return 0;
787 }
788