GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/pull/trace.c Lines: 0 605 0.0 %
Date: 2017-05-26 Branches: 0 436 0.0 %

Line Branch Exec Source
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
25
#include "pull.h"
26
#include "privatePull.h"
27
28
pullTrace *
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->nstab = nrrdNew();
39
    ret->norin = nrrdNew();
40
    ret->seedIdx = 0;
41
    ret->whyStop[0] = ret->whyStop[1] = pullTraceStopUnknown;
42
    ret->whyStopCFail[0] = ret->whyStopCFail[1] = pullConstraintFailUnknown;
43
    ret->whyNowhere = pullTraceStopUnknown;
44
    ret->whyNowhereCFail = pullConstraintFailUnknown;
45
  }
46
  return ret;
47
}
48
49
pullTrace *
50
pullTraceNix(pullTrace *pts) {
51
52
  if (pts) {
53
    nrrdNuke(pts->nvert);
54
    nrrdNuke(pts->nstrn);
55
    nrrdNuke(pts->nstab);
56
    nrrdNuke(pts->norin);
57
    free(pts);
58
  }
59
  return NULL;
60
}
61
62
void
63
pullTraceStability(double *spcStab,
64
                   double *oriStab,
65
                   const double pos0[4],
66
                   const double pos1[4],
67
                   const double ori0[3],
68
                   const double ori1[3],
69
                   double sigma0,
70
                   const pullContext *pctx) {
71
  double sc, stb, dx, ds, diff[4];
72
73
  ELL_4V_SUB(diff, pos1, pos0);
74
  dx = ELL_3V_LEN(diff)/(pctx->voxelSizeSpace);
75
  sc = (pos0[3] + pos1[3])/2;
76
  if (pctx->flag.scaleIsTau) {
77
    sc = gageSigOfTau(sc);
78
  }
79
  sc += sigma0;
80
  dx /= sc;
81
  ds = diff[3];
82
  stb = atan2(ds, dx)/(AIR_PI/2); /* [-1,1]: 0 means least stable */
83
  stb = AIR_ABS(stb);      /* [0,1]: 0 means least stable */
84
  *spcStab = AIR_CLAMP(0, stb, 1);
85
  /*
86
  static double maxvelo = 0;
87
  if (1 && vv > maxvelo) {
88
    char me[]="pullTraceStability";
89
    printf("\n%s: [%g,%g,%g,%g] - [%g,%g,%g,%g] = [%g,%g,%g,%g]\n", me,
90
           pos0[0], pos0[1], pos0[2], pos0[3],
91
           pos1[0], pos1[1], pos1[2], pos1[3],
92
           diff[0], diff[1], diff[2], diff[3]);
93
    printf("%s: dx = %g -> %g; ds = %g\n", me, dx0, dx, ds);
94
    printf("%s: vv = atan2(%g,%g)/pi = %g -> %g > %g\n", me, ds, dx, vv0, vv, maxvelo);
95
    maxvelo = vv;
96
  }
97
  */
98
  if (ori0 && ori1) {
99
    /* dori = delta in orientation */
100
    double dori = ell_3v_angle_d(ori0, ori1);
101
    /* dori in [0,pi]; 0 and pi mean no change */
102
    if (dori > AIR_PI/2) {
103
      dori -= AIR_PI;
104
    }
105
    /* dori in [-pi/2,pi/2]; 0 means no change; +-pi/2 means most change */
106
    dori = AIR_ABS(dori);
107
    /* dori in [0,pi/2]; 0 means no change; pi/2 means most change */
108
    *oriStab = atan2(ds, dori)/(AIR_PI/2);
109
    /* *oriStab in [0,1]; 0 means stable, 1 means not stable */
110
  } else {
111
    *oriStab = 0;
112
  }
113
  return;
114
}
115
116
int
117
_pullConstrTanSlap(pullContext *pctx, pullPoint *point,
118
                   double tlen,
119
                   /* input+output */ double toff[3],
120
                   /* output */ int *constrFailP) {
121
  static const char me[]="_pullConstrTanSlap";
122
  double pos0[4], tt;
123
124
  if (!(pctx && point && toff && constrFailP)) {
125
    biffAddf(PULL, "%s: got NULL pointer", me);
126
    return 1;
127
  }
128
  if (pctx->flag.zeroZ) {
129
    toff[2] = 0;
130
  }
131
  ELL_3V_NORM(toff, toff, tt);
132
  if (!(tlen > 0 && tt > 0)) {
133
    biffAddf(PULL, "%s: tlen %g or |toff| %g not positive", me, tlen, tt);
134
    return 1;
135
  }
136
  ELL_4V_COPY(pos0, point->pos); /* pos0[3] should stay put; will test at end */
137
138
  ELL_3V_SCALE_ADD2(point->pos, 1, pos0, tlen, toff);
139
  if (_pullConstraintSatisfy(pctx->task[0], point, 0 /* travmax */, constrFailP)) {
140
    biffAddf(PULL, "%s: trouble", me);
141
    ELL_4V_COPY(point->pos, pos0); return 1;
142
  }
143
  /* save offset to new position */
144
  ELL_3V_SUB(toff, point->pos, pos0);
145
146
  if (pos0[3] != point->pos[3]) {
147
    biffAddf(PULL, "%s: point->pos[3] %g was changed (from %g)",
148
             me, point->pos[3], pos0[3]);
149
    ELL_4V_COPY(point->pos, pos0); return 1;
150
  }
151
  ELL_4V_COPY(point->pos, pos0);
152
  return 0;
153
}
154
155
int
156
_pullConstrOrientFind(pullContext *pctx, pullPoint *point,
157
                      int normalfind, /* find normal two 2D surface,
158
                                         else find tangent to 1D curve */
159
                      double tlen,
160
                      const double tdir0[3], /* if non-NULL, try using this direction */
161
                      /* output */
162
                      double dir[3],
163
                      int *constrFailP) {
164
  static const char me[]="_pullConstrOrientFind";
165
  double tt;
166
167
#define SLAP(LEN, DIR)                                                  \
168
  /* fprintf(stderr, "!%s: SLAP %g %g %g -->", me, (DIR)[0], (DIR)[1], (DIR)[2]); */  \
169
  if (_pullConstrTanSlap(pctx, point, (LEN), (DIR), constrFailP)) {     \
170
    biffAddf(PULL, "%s: looking for tangent, starting with (%g,%g,%g)", \
171
             me, (DIR)[0], (DIR)[1], (DIR)[2]);                         \
172
    return 1;                                                           \
173
  }                                                                     \
174
  if (*constrFailP) {                                                   \
175
    /* unsuccessful in finding tangent, but not a biff error */         \
176
    return 0;                                                           \
177
  }                                                                     \
178
  /* fprintf(stderr, " %g %g %g\n", (DIR)[0], (DIR)[1], (DIR)[2]); */
179
180
  if (!(pctx && point && constrFailP)) {
181
    biffAddf(PULL, "%s: got NULL pointer", me);
182
    return 1;
183
  }
184
  *constrFailP = pullConstraintFailUnknown;
185
  if (normalfind) {
186
    double tan0[3], tan1[3];
187
    /* looking for a surface normal */
188
    if (tdir0) { /* have something to start with */
189
      ell_3v_perp_d(tan0, tdir0);
190
      ELL_3V_CROSS(tan1, tan0, tdir0);
191
      SLAP(tlen, tan1);
192
      SLAP(tlen, tan0);
193
      ELL_3V_CROSS(dir, tan1, tan0);
194
    } else { /* have to start from scratch */
195
      double tns[9] = {1,0,0,  0,1,0,  0,0,1};
196
      SLAP(tlen, tns+0);
197
      SLAP(tlen, tns+3);
198
      SLAP(tlen, tns+6);
199
      ell_3m_1d_nullspace_d(dir, tns);
200
    }
201
  } else {
202
    /* looking for a curve tangent */
203
    if (tdir0) { /* have something to start with */
204
      ELL_3V_COPY(dir, tdir0);
205
      SLAP(tlen, dir);
206
      /* SLAP(tlen, dir);   (didn't have much effect, in one test) */
207
    } else { /* have to start from scratch */
208
      double tX[3] = {1,0,0}, tY[3] = {0,1,0}, tZ[3] = {0,0,1};
209
      SLAP(tlen, tX);
210
      SLAP(tlen, tY);
211
      if (ELL_3V_DOT(tX, tY) < 0) { ELL_3V_SCALE(tY, -1, tY); }
212
      if (pctx->flag.zeroZ) {
213
        ELL_3V_SET(tZ, 0, 0, 0);
214
      } else {
215
        SLAP(tlen, tZ);
216
        if (ELL_3V_DOT(tX, tZ) < 0) { ELL_3V_SCALE(tZ, -1, tZ); }
217
        if (ELL_3V_DOT(tY, tZ) < 0) { ELL_3V_SCALE(tY, -1, tZ); }
218
      }
219
      ELL_3V_ADD3(dir, tX, tY, tZ);
220
    }
221
  }
222
  ELL_3V_NORM(dir, dir, tt);
223
  if (!(tt > 0)) {
224
    biffAddf(PULL, "%s: computed direction is zero (%g)?", me, tt);
225
    return 1;
226
  }
227
  return 0;
228
}
229
230
/*
231
******** pullTraceSet
232
**
233
** computes a single trace, according to the given parameters,
234
** and store it in the pullTrace
235
int recordStrength: should strength be recorded along trace
236
double scaleDelta: discrete step along scale
237
double halfScaleWin: how far, along scale, trace should go in each direction
238
unsigned int arrIncr: increment for storing position (maybe strength)
239
const double _seedPos[4]: starting position
240
*/
241
int
242
pullTraceSet(pullContext *pctx, pullTrace *pts,
243
             int recordStrength,
244
             double scaleDelta, double halfScaleWin,
245
             double orientTestLen,
246
             unsigned int arrIncr,
247
             const double _seedPos[4]) {
248
  static const char me[]="pullTraceSet";
249
  pullPoint *point;
250
  airArray *mop, *trceArr[2], *hstrnArr[2], *horinArr[2];
251
  double *trce[2], ssrange[2], *vert, *hstrn[2], *horin[2], *strn, *orin, *stab,
252
    seedPos[4], polen, porin[3];
253
  int constrFail;
254
  unsigned int dirIdx, lentmp, tidx, oidx, vertNum;
255
256
  if (!( pctx && pts && _seedPos )) {
257
    biffAddf(PULL, "%s: got NULL pointer", me);
258
    return 1;
259
  }
260
  if (!( AIR_EXISTS(scaleDelta) && scaleDelta > 0.0 )) {
261
    biffAddf(PULL, "%s: need existing scaleDelta > 0 (not %g)",
262
             me, scaleDelta);
263
    return 1;
264
  }
265
  if (!( halfScaleWin > 0 )) {
266
    biffAddf(PULL, "%s: need halfScaleWin > 0", me);
267
    return 1;
268
  }
269
  if (!(pctx->constraint)) {
270
    biffAddf(PULL, "%s: given context doesn't have constraint set", me);
271
    return 1;
272
  }
273
  pts->fdim = _pullConstraintDim(pctx);
274
  if (0 > pts->fdim) {
275
    biffAddf(PULL, "%s: couldn't learn dimension of feature", me);
276
    return 1;
277
  }
278
  if (pts->fdim == 2 && pctx->flag.zeroZ) {
279
    biffAddf(PULL, "%s: can't have feature dim 2 with zeroZ", me);
280
    return 1;
281
  }
282
  if (!( AIR_EXISTS(orientTestLen) && orientTestLen >= 0 )) {
283
    biffAddf(PULL, "%s: need non-negative orientTestLen (not %g)\n",
284
             me, orientTestLen);
285
    return 1;
286
  }
287
  if (pts->fdim && !orientTestLen) {
288
    /* not really an error */
289
    /*
290
    fprintf(stderr, "\n\n%s: WARNING: have a %d-D feature, but not "
291
            "measuring its orientation\n\n\n", me, pts->fdim);
292
    */
293
  }
294
  /* The test for "should I measure orientation"
295
     is "if (pts->fdim && orientTestLen)" */
296
  if (recordStrength && !pctx->ispec[pullInfoStrength]) {
297
    biffAddf(PULL, "%s: want to record strength but %s not set in context",
298
             me, airEnumStr(pullInfo, pullInfoStrength));
299
    return 1;
300
  }
301
  if (pullConstraintScaleRange(pctx, ssrange)) {
302
    biffAddf(PULL, "%s: trouble getting scale range", me);
303
    return 1;
304
  }
305
306
  /* re-initialize termination descriptions (in case of trace re-use) */
307
  pts->whyStop[0] = pts->whyStop[1] = pullTraceStopUnknown;
308
  pts->whyStopCFail[0] = pts->whyStopCFail[1] = pullConstraintFailUnknown;
309
  pts->whyNowhere = pullTraceStopUnknown;
310
  pts->whyNowhereCFail = pullConstraintFailUnknown;
311
312
  /* enforce zeroZ */
313
  ELL_4V_COPY(seedPos, _seedPos);
314
  if (pctx->flag.zeroZ) {
315
    seedPos[2] = 0.0;
316
  }
317
318
  /* save seedPos in any case */
319
  ELL_4V_COPY(pts->seedPos, seedPos);
320
321
  mop = airMopNew();
322
  point = pullPointNew(pctx); /* we'll want to decrement idtagNext later */
323
  airMopAdd(mop, point, (airMopper)pullPointNix, airMopAlways);
324
325
  ELL_4V_COPY(point->pos, seedPos);
326
  /*
327
  if (pctx->verbose) {
328
    fprintf(stderr, "%s: trying at seed=(%g,%g,%g,%g)\n", me,
329
            seedPos[0], seedPos[1], seedPos[2], seedPos[3]);
330
  }
331
  */
332
  /* The termination of the trace due to low stablity is handled here
333
     (or could be), not by _pullConstraintSatisfy, so we set 0 for the
334
     travelMax arg of _pullConstraintSatisfy (no travel limit) */
335
  if (_pullConstraintSatisfy(pctx->task[0], point,
336
                             0 /* travmax */, &constrFail)) {
337
    biffAddf(PULL, "%s: constraint sat on seed point", me);
338
    airMopError(mop);
339
    return 1;
340
  }
341
  /*
342
  if (pctx->verbose) {
343
    fprintf(stderr, "%s: seed=(%g,%g,%g,%g) -> %s (%g,%g,%g,%g)\n", me,
344
            seedPos[0], seedPos[1], seedPos[2], seedPos[3],
345
            constrFail ? "!NO!" : "(yes)",
346
            point->pos[0] - seedPos[0], point->pos[1] - seedPos[1],
347
            point->pos[2] - seedPos[2], point->pos[3] - seedPos[3]);
348
  }
349
  */
350
  if (constrFail) {
351
    pts->whyNowhere = pullTraceStopConstrFail;
352
    airMopOkay(mop);
353
    /* pctx->idtagNext -= 1; / * HACK * / */
354
    return 0;
355
  }
356
  if (point->status & PULL_STATUS_EDGE_BIT) {
357
    pts->whyNowhere = pullTraceStopVolumeEdge;
358
    airMopOkay(mop);
359
    /* pctx->idtagNext -= 1; / * HACK * / */
360
    return 0;
361
  }
362
  if (pctx->flag.zeroZ && point->pos[2] != 0) {
363
    biffAddf(PULL, "%s: zeroZ violated (a)", me);
364
    airMopError(mop);
365
    return 1;
366
  }
367
368
  /* else constraint sat worked at seed point; we have work to do */
369
  if (pts->fdim && orientTestLen) {
370
    /* learn orientation at seed point */
371
    polen = (orientTestLen
372
             *pctx->voxelSizeSpace
373
             /* if used, the effect of this this last (unprincipled) factor
374
                is gradually increase the test distance with scale
375
                *(1 + gageTauOfSig(_pullSigma(pctx, point->pos))) */ );
376
    double pos0[4], dp[4];
377
    int cf;
378
    ELL_4V_COPY(pos0, point->pos);
379
380
    if (_pullConstrOrientFind(pctx, point, pts->fdim == 2,
381
                              polen, NULL /* no initial guess */, porin, &cf)) {
382
      biffAddf(PULL, "%s: trying to find orientation at seed", me);
383
      airMopError(mop);
384
      return 1;
385
    }
386
    ELL_4V_SUB(dp, pos0, point->pos);
387
    /*
388
    fprintf(stderr, "!%s: cf = %d (%s)\n", me, cf, airEnumStr(pullConstraintFail, cf));
389
    fprintf(stderr, "!%s: (fdim=%u) pos=[%g,%g,%g,%g] polen=%g porin=[%g,%g,%g] |%g|\n",
390
            me, pts->fdim,
391
            point->pos[0], point->pos[1], point->pos[2], point->pos[3],
392
            polen, porin[0], porin[1], porin[2], ELL_4V_LEN(dp));
393
    */
394
    if (cf) {
395
      pts->whyNowhere = pullTraceStopConstrFail;
396
      pts->whyNowhereCFail = cf;
397
      airMopOkay(mop);
398
      /* pctx->idtagNext -= 1; / * HACK * / */
399
      return 0;
400
    }
401
  } else {
402
    /* either feature is 0D points, or don't care about orientation */
403
    polen = AIR_NAN;
404
    ELL_3V_SET(porin, AIR_NAN, AIR_NAN, AIR_NAN);
405
  }
406
407
  for (dirIdx=0; dirIdx<2; dirIdx++) {
408
    trceArr[dirIdx] = airArrayNew((void**)(trce + dirIdx), NULL,
409
                                  4*sizeof(double), arrIncr);
410
    airMopAdd(mop, trceArr[dirIdx], (airMopper)airArrayNuke, airMopAlways);
411
    if (recordStrength) {
412
      hstrnArr[dirIdx] = airArrayNew((void**)(hstrn + dirIdx), NULL,
413
                                     sizeof(double), arrIncr);
414
      airMopAdd(mop, hstrnArr[dirIdx], (airMopper)airArrayNuke, airMopAlways);
415
    } else {
416
      hstrnArr[dirIdx] = NULL;
417
      hstrn[dirIdx] = NULL;
418
    }
419
    if (pts->fdim && orientTestLen) {
420
      horinArr[dirIdx] = airArrayNew((void**)(horin + dirIdx), NULL,
421
                                     3*sizeof(double), arrIncr);
422
      airMopAdd(mop, horinArr[dirIdx], (airMopper)airArrayNuke, airMopAlways);
423
    } else {
424
      horinArr[dirIdx] = NULL;
425
      horin[dirIdx] = NULL;
426
    }
427
  }
428
  for (dirIdx=0; dirIdx<2; dirIdx++) {
429
    unsigned int step;
430
    double dscl;
431
    dscl = (!dirIdx ? -1 : +1)*scaleDelta;
432
    step = 0;
433
    while (1) {
434
      if (!step) {
435
        /* first step in both directions requires special tricks */
436
        if (0 == dirIdx) {
437
          /* this is done once, at the very start */
438
          /* save constraint sat of seed point */
439
          tidx = airArrayLenIncr(trceArr[0], 1);
440
          ELL_4V_COPY(trce[0] + 4*tidx, point->pos);
441
          if (recordStrength) {
442
            airArrayLenIncr(hstrnArr[0], 1);
443
            hstrn[0][0] = pullPointScalar(pctx, point, pullInfoStrength,
444
                                          NULL, NULL);
445
          }
446
          if (pts->fdim && orientTestLen) {
447
            airArrayLenIncr(horinArr[0], 1);
448
            ELL_3V_COPY(horin[0] + 3*0, porin);
449
          }
450
        } else {
451
          /* re-set position from constraint sat of seed pos */
452
          ELL_4V_COPY(point->pos, trce[0] + 4*0);
453
          if (pts->fdim && orientTestLen) {
454
            ELL_3V_COPY(porin, horin[0] + 3*0);
455
          }
456
        }
457
      }
458
      /* nudge position along scale */
459
      point->pos[3] += dscl;
460
      if (!AIR_IN_OP(ssrange[0], point->pos[3], ssrange[1])) {
461
        /* if we've stepped outside the range of scale for the volume
462
           containing the constraint manifold, we're done */
463
        pts->whyStop[dirIdx] = pullTraceStopBounds;
464
        break;
465
      }
466
      if (AIR_ABS(point->pos[3] - seedPos[3]) > halfScaleWin) {
467
        /* we've moved along scale as far as allowed */
468
        pts->whyStop[dirIdx] = pullTraceStopLength;
469
        break;
470
      }
471
      /* re-assert constraint */
472
      /*
473
      fprintf(stderr, "%s(%u): pos = %g %g %g %g.... \n", me,
474
              point->idtag, point->pos[0], point->pos[1],
475
              point->pos[2], point->pos[3]);
476
      */
477
      if (_pullConstraintSatisfy(pctx->task[0], point,
478
                                 0 /* travmax */, &constrFail)) {
479
        biffAddf(PULL, "%s: dir %u, step %u", me, dirIdx, step);
480
        airMopError(mop);
481
        return 1;
482
      }
483
      /*
484
      if (pctx->verbose) {
485
        fprintf(stderr, "%s(%u): ... %s(%d); pos = %g %g %g %g\n", me,
486
                point->idtag,
487
                constrFail ? "FAIL" : "(ok)",
488
                constrFail, point->pos[0], point->pos[1],
489
                point->pos[2], point->pos[3]);
490
      }
491
      */
492
      if (point->status & PULL_STATUS_EDGE_BIT) {
493
        pts->whyStop[dirIdx] = pullTraceStopVolumeEdge;
494
        break;
495
      }
496
      if (constrFail) {
497
        /* constraint sat failed; no error, we're just done
498
           with stepping for this direction */
499
        pts->whyStop[dirIdx] = pullTraceStopConstrFail;
500
        pts->whyStopCFail[dirIdx] = constrFail;
501
        break;
502
      }
503
      if (pctx->flag.zeroZ && point->pos[2] != 0) {
504
        biffAddf(PULL, "%s: zeroZ violated (b)", me);
505
        airMopError(mop);
506
        return 1;
507
      }
508
      if (pts->fdim && orientTestLen) {
509
        if (_pullConstrOrientFind(pctx, point, pts->fdim == 2,
510
                                  polen, porin, porin, &constrFail)) {
511
          biffAddf(PULL, "%s: at dir %u, step %u", me, dirIdx, step);
512
          airMopError(mop);
513
          return 1;
514
        }
515
      }
516
      if (trceArr[dirIdx]->len >= 2) {
517
        /* see if we're moving too fast, by comparing with previous point */
518
        /* actually, screw that */
519
      }
520
      /* else save new point on trace */
521
      tidx = airArrayLenIncr(trceArr[dirIdx], 1);
522
      ELL_4V_COPY(trce[dirIdx] + 4*tidx, point->pos);
523
      if (recordStrength) {
524
        tidx = airArrayLenIncr(hstrnArr[dirIdx], 1);
525
        hstrn[dirIdx][tidx] = pullPointScalar(pctx, point, pullInfoStrength,
526
                                              NULL, NULL);
527
      }
528
      if (pts->fdim && orientTestLen) {
529
        tidx = airArrayLenIncr(horinArr[dirIdx], 1);
530
        ELL_3V_COPY(horin[dirIdx] + 3*tidx, porin);
531
      }
532
      step++;
533
    }
534
  }
535
536
  /* transfer trace halves to pts->nvert */
537
  vertNum = trceArr[0]->len + trceArr[1]->len;
538
  if (0 == vertNum || 1 == vertNum || 2 == vertNum) {
539
    pts->whyNowhere = pullTraceStopStub;
540
    airMopOkay(mop);
541
    /* pctx->idtagNext -= 1; / * HACK * / */
542
    return 0;
543
  }
544
545
  if (nrrdMaybeAlloc_va(pts->nvert, nrrdTypeDouble, 2,
546
                        AIR_CAST(size_t, 4),
547
                        AIR_CAST(size_t, vertNum))
548
      || nrrdMaybeAlloc_va(pts->nstab, nrrdTypeDouble, 2,
549
                           AIR_CAST(size_t, 2),
550
                           AIR_CAST(size_t, vertNum))) {
551
    biffMovef(PULL, NRRD, "%s: allocating output", me);
552
    airMopError(mop);
553
    return 1;
554
  }
555
  if (recordStrength) {
556
    /* doing slicing is a simple form of allocation */
557
    if (nrrdSlice(pts->nstrn, pts->nvert, 0 /* axis */, 0 /* pos */)) {
558
      biffMovef(PULL, NRRD, "%s: allocating strength output", me);
559
      airMopError(mop);
560
      return 1;
561
    }
562
  }
563
  if (pts->fdim && orientTestLen) {
564
    /* cropping just to allocate */
565
    size_t cmin[2] = {0, 0}, cmax[2] = {2, pts->nvert->axis[1].size-1};
566
    if (nrrdCrop(pts->norin, pts->nvert, cmin, cmax)) {
567
      biffMovef(PULL, NRRD, "%s: allocating orientation output", me);
568
      airMopError(mop);
569
      return 1;
570
    }
571
  }
572
  vert = AIR_CAST(double *, pts->nvert->data);
573
  if (recordStrength) {
574
    strn = AIR_CAST(double *, pts->nstrn->data);
575
  } else {
576
    strn = NULL;
577
  }
578
  if (pts->fdim && orientTestLen) {
579
    orin = AIR_CAST(double *, pts->norin->data);
580
  } else {
581
    orin = NULL;
582
  }
583
  stab = AIR_CAST(double *, pts->nstab->data);
584
  lentmp = trceArr[0]->len;
585
  oidx = 0;
586
  for (tidx=0; tidx<lentmp; tidx++) {
587
    ELL_4V_COPY(vert + 4*oidx, trce[0] + 4*(lentmp - 1 - tidx));
588
    if (strn) {
589
      strn[oidx] = hstrn[0][lentmp - 1 - tidx];
590
    }
591
    if (orin) {
592
      ELL_3V_COPY(orin + 3*oidx, horin[0] + 3*(lentmp - 1 - tidx));
593
    }
594
    oidx++;
595
  }
596
  /* the last index written to (before oidx++) was the seed index */
597
  pts->seedIdx = oidx-1;
598
  lentmp = trceArr[1]->len;
599
  for (tidx=0; tidx<lentmp; tidx++) {
600
    ELL_4V_COPY(vert + 4*oidx, trce[1] + 4*tidx);
601
    if (strn) {
602
      strn[oidx] = hstrn[1][tidx];
603
    }
604
    if (orin) {
605
      ELL_3V_COPY(orin + 3*oidx, horin[1] + 3*tidx);
606
    }
607
    oidx++;
608
  }
609
  lentmp = pts->nstab->axis[1].size;
610
  if (1 == lentmp) {
611
    stab[0 + 2*0] = 0.0;
612
    stab[1 + 2*0] = 0.0;
613
  } else {
614
    for (tidx=0; tidx<lentmp; tidx++) {
615
      double *pA, *pB, *p0, *p1, *p2, *rA, *rB, *r0=NULL, *r1=NULL, *r2=NULL;
616
      p0 = vert + 4*(tidx-1);
617
      p1 = vert + 4*tidx;
618
      p2 = vert + 4*(tidx+1);
619
      if (orin) {
620
        r0 = orin + 3*(tidx-1);
621
        r1 = orin + 3*tidx;
622
        r2 = orin + 3*(tidx+1);
623
      }
624
      if (!tidx) {
625
        /* first */
626
        pA = p1; rA = r1;
627
        pB = p2; rB = r2;
628
      } else if (tidx < lentmp-1) {
629
        /* middle */
630
        pA = p0; rA = r0;
631
        pB = p2; rB = r2;
632
      } else {
633
        /* last */
634
        pA = p0; rA = r0;
635
        pB = p1; rB = r1;
636
      }
637
      pullTraceStability(stab + 0 + 2*tidx, stab + 1 + 2*tidx,
638
                         pA, pB, rA, rB, 0.5 /* sigma0 */, pctx);
639
    }
640
  }
641
642
  airMopOkay(mop);
643
  /* pctx->idtagNext -= 1; / * HACK * / */
644
  return 0;
645
}
646
647
typedef union {
648
  pullTrace ***trace;
649
  void **v;
650
} blahblahUnion;
651
652
pullTraceMulti *
653
pullTraceMultiNew(void) {
654
  /* static const char me[]="pullTraceMultiNew"; */
655
  pullTraceMulti *ret;
656
  blahblahUnion bbu;
657
658
  ret = AIR_CALLOC(1, pullTraceMulti);
659
  if (ret) {
660
    ret->trace = NULL;
661
    ret->traceNum = 0;
662
    ret->traceArr = airArrayNew((bbu.trace = &(ret->trace), bbu.v),
663
                                &(ret->traceNum), sizeof(pullTrace*),
664
                                _PULL_TRACE_MULTI_INCR);
665
    airArrayPointerCB(ret->traceArr,
666
                      NULL, /* because we get handed pullTrace structs
667
                               that have already been allocated
668
                               (and then we own them) */
669
                      (void *(*)(void *))pullTraceNix);
670
  }
671
  return ret;
672
}
673
674
int
675
pullTraceMultiAdd(pullTraceMulti *mtrc, pullTrace *trc, int *addedP) {
676
  static const char me[]="pullTraceMultiAdd";
677
  unsigned int indx;
678
679
  if (!(mtrc && trc && addedP)) {
680
    biffAddf(PULL, "%s: got NULL pointer", me);
681
    return 1;
682
  }
683
  if (!(trc->nvert->data && trc->nvert->axis[1].size >= 3)) {
684
    /*  for now getting a stub trace is not an error
685
    biffAddf(PULL, "%s: got stub trace", me);
686
    return 1; */
687
    *addedP = AIR_FALSE;
688
    return 0;
689
  }
690
  if (!(trc->nstab->data
691
        && trc->nstab->axis[1].size == trc->nvert->axis[1].size)) {
692
    biffAddf(PULL, "%s: stab data inconsistent", me);
693
    return 1;
694
  }
695
  *addedP = AIR_TRUE;
696
  indx = airArrayLenIncr(mtrc->traceArr, 1);
697
  if (!mtrc->trace) {
698
    biffAddf(PULL, "%s: alloc error", me);
699
    return 1;
700
  }
701
  mtrc->trace[indx] = trc;
702
  return 0;
703
}
704
705
int
706
pullTraceMultiPlotAdd(Nrrd *nplot, const pullTraceMulti *mtrc,
707
                      const Nrrd *nfilt, int strengthUse,
708
                      int smooth, int flatWght,
709
                      unsigned int trcIdxMin, unsigned int trcNum,
710
                      Nrrd *nmaskedpos,
711
                      const Nrrd *nmask) {
712
  static const char me[]="pullTraceMultiPlotAdd";
713
  double ssRange[2], vRange[2], *plot;
714
  unsigned int sizeS, sizeT, trcIdx, trcIdxMax;
715
  int *filt;
716
  airArray *mop;
717
  Nrrd *nsmst; /* smoothed stability */
718
  airArray *mposArr;
719
  double *mpos;
720
  unsigned char *mask;
721
722
  if (!(nplot && mtrc)) {
723
    biffAddf(PULL, "%s: got NULL pointer", me);
724
    return 1;
725
  }
726
  if (nrrdCheck(nplot)) {
727
    biffMovef(PULL, NRRD, "%s: trouble with nplot", me);
728
    return 1;
729
  }
730
  if (nfilt) {
731
    if (nrrdCheck(nfilt)) {
732
      biffMovef(PULL, NRRD, "%s: trouble with nfilt", me);
733
      return 1;
734
    }
735
    if (!(1 == nfilt->dim && nrrdTypeInt == nfilt->type)) {
736
      biffAddf(PULL, "%s: didn't get 1-D array of %s (got %u-D of %s)", me,
737
               airEnumStr(nrrdType, nrrdTypeInt), nfilt->dim,
738
               airEnumStr(nrrdType, nfilt->type));
739
      return 1;
740
    }
741
  }
742
  if (!(2 == nplot->dim && nrrdTypeDouble == nplot->type)) {
743
    biffAddf(PULL, "%s: didn't get 2-D array of %s (got %u-D of %s)", me,
744
             airEnumStr(nrrdType, nrrdTypeDouble), nplot->dim,
745
             airEnumStr(nrrdType, nplot->type));
746
    return 1;
747
  }
748
  if (!(trcIdxMin < mtrc->traceNum)) {
749
    biffAddf(PULL, "%s: trcIdxMin %u not < traceNum %u", me,
750
             trcIdxMin, mtrc->traceNum);
751
    return 1;
752
  }
753
  if (trcNum) {
754
    trcIdxMax = trcIdxMin + trcNum-1;
755
    if (!(trcIdxMax < mtrc->traceNum)) {
756
      biffAddf(PULL, "%s: trcIdxMax %u = %u+%u-1 not < traceNum %u", me,
757
               trcIdxMax, trcIdxMin, trcNum, mtrc->traceNum);
758
      return 1;
759
    }
760
  } else {
761
    trcIdxMax = mtrc->traceNum-1;
762
  }
763
  if (nmaskedpos || nmask) {
764
    if (!( nmaskedpos && nmask )) {
765
      biffAddf(PULL, "%s: need either both or neither of nmaskedpos (%p)"
766
               "and nmask (%p)", me, nmaskedpos, nmask);
767
      return 1;
768
    }
769
    if (!( 2 == nmask->dim
770
           && nrrdTypeUChar == nmask->type
771
           && nplot->axis[0].size == nmask->axis[0].size
772
           && nplot->axis[1].size == nmask->axis[1].size )) {
773
      biffAddf(PULL, "%s: got trace mask but wanted "
774
               "2-D %s %u-by-%u (not %u-D %s %u-by-%u)\n", me,
775
               airEnumStr(nrrdType, nrrdTypeUChar),
776
               AIR_CAST(unsigned int, nplot->axis[0].size),
777
               AIR_CAST(unsigned int, nplot->axis[1].size),
778
               nmask->dim,
779
               airEnumStr(nrrdType, nmask->type),
780
               AIR_CAST(unsigned int, nmask->axis[0].size),
781
               AIR_CAST(unsigned int, nmask->axis[1].size));
782
      return 1;
783
    }
784
    mask = AIR_CAST(unsigned char *, nmask->data);
785
  } else {
786
    mask = NULL;
787
  }
788
789
  ssRange[0] = nplot->axis[0].min;
790
  ssRange[1] = nplot->axis[0].max;
791
  vRange[0] = nplot->axis[1].min;
792
  vRange[1] = nplot->axis[1].max;
793
  if (!( AIR_EXISTS(ssRange[0]) && AIR_EXISTS(ssRange[1]) &&
794
         AIR_EXISTS(vRange[0]) && AIR_EXISTS(vRange[1]) )) {
795
    biffAddf(PULL, "%s: need both axis 0 (%g,%g) and 1 (%g,%g) min,max", me,
796
             ssRange[0], ssRange[1], vRange[0], vRange[1]);
797
    return 1;
798
  }
799
  if (1 != vRange[0]) {
800
    biffAddf(PULL, "%s: expected vRange[0] == 1 not %g", me, vRange[0]);
801
    return 1;
802
  }
803
  mop = airMopNew();
804
805
  mpos = NULL;
806
  if (nmaskedpos && nmask) {
807
    nrrdEmpty(nmaskedpos);
808
    mposArr = airArrayNew((void**)(&mpos), NULL,
809
                          4*sizeof(double), 512 /* HEY */);
810
  } else {
811
    mposArr = NULL;
812
  }
813
814
  nsmst = nrrdNew();
815
  airMopAdd(mop, nsmst, (airMopper)nrrdNuke, airMopAlways);
816
  plot = AIR_CAST(double *, nplot->data);
817
  filt = (nfilt
818
          ? AIR_CAST(int *, nfilt->data)
819
          : NULL);
820
  sizeS = AIR_CAST(unsigned int, nplot->axis[0].size);
821
  sizeT = AIR_CAST(unsigned int, nplot->axis[1].size);
822
  for (trcIdx=trcIdxMin; trcIdx<=trcIdxMax; trcIdx++) {
823
    int pntIdx, pntNum;
824
    const pullTrace *trc;
825
    const double *vert, *stab, *strn;
826
    unsigned int maskInCount;
827
    double maskInPos[4];
828
829
    if (filt && !filt[trcIdx]) {
830
      continue;
831
    }
832
    trc = mtrc->trace[trcIdx];
833
    if (pullTraceStopStub == trc->whyNowhere) {
834
      continue;
835
    }
836
    if (strengthUse && !(trc->nstrn && trc->nstrn->data)) {
837
      biffAddf(PULL, "%s: requesting strength-based weighting, but don't have "
838
               "strength info in trace %u", me, trcIdx);
839
      airMopError(mop); return 1;
840
    }
841
    pntNum = AIR_CAST(int, trc->nvert->axis[1].size);
842
    vert = AIR_CAST(double *, trc->nvert->data);
843
    stab = AIR_CAST(double *, trc->nstab->data);
844
    if (smooth > 0) {
845
      double *smst;
846
      if (nrrdCopy(nsmst, trc->nstab)) {
847
        biffMovef(PULL, NRRD, "%s: trouble w/ trace %u", me, trcIdx);
848
        airMopError(mop); return 1;
849
      }
850
      smst = AIR_CAST(double *, nsmst->data);
851
      for (pntIdx=0; pntIdx<pntNum; pntIdx++) {
852
        int ii, jj;
853
        double ss, ww, ws;
854
        ss = ws = 0;
855
        for (jj=-smooth; jj<=smooth; jj++) {
856
          ii = pntIdx+jj;
857
          ii = AIR_CLAMP(0, ii, pntNum-1);
858
          ww = nrrdKernelBSpline3->eval1_d(AIR_AFFINE(-smooth-1, jj,
859
                                                      smooth+1,-2,2), NULL);
860
          ws += ww;
861
          ss += ww*stab[0 + 2*ii]*stab[1 + 2*ii];
862
        }
863
        smst[pntIdx] = ss/ws;
864
      }
865
      /* now redirect stab */
866
      stab = smst;
867
    }
868
    strn = AIR_CAST(double *, (strengthUse && trc->nstrn
869
                               ? trc->nstrn->data : NULL));
870
    /* would be nice to get some graphical indication of this */
871
    fprintf(stderr, "!%s: trace %u in [%u,%u]: %u points; stops = %s(%s) | %s(%s)\n",
872
            me, trcIdx, trcIdxMin, trcIdxMax, pntNum,
873
            airEnumStr(pullTraceStop, trc->whyStop[0]),
874
            (pullTraceStopConstrFail == trc->whyStop[0]
875
             ? airEnumStr(pullConstraintFail, trc->whyStopCFail[0])
876
             : ""),
877
            airEnumStr(pullTraceStop, trc->whyStop[1]),
878
            (pullTraceStopConstrFail == trc->whyStop[1]
879
             ? airEnumStr(pullConstraintFail, trc->whyStopCFail[1])
880
             : ""));
881
    /* */
882
883
    if (mask) {
884
      maskInCount = 0;
885
      ELL_4V_SET(maskInPos, 0, 0, 0, 0);
886
    }
887
    for (pntIdx=0; pntIdx<pntNum; pntIdx++) {
888
      const double *pp;
889
      double add, ww;
890
      unsigned int sidx, vidx;
891
      pp = vert + 4*pntIdx;
892
      if (!(AIR_IN_OP(ssRange[0], pp[3], ssRange[1]))) {
893
        continue;
894
      }
895
      if (flatWght > 0) {
896
        if (!pntIdx || pntIdx == pntNum-1) {
897
          continue;
898
        }
899
      } else if (flatWght < 0) {
900
        /* HACK: only show the seed point */
901
        if (AIR_CAST(unsigned int, pntIdx) != trc->seedIdx) {
902
          continue;
903
        }
904
      }
905
      sidx = airIndex(ssRange[0], pp[3], ssRange[1], sizeS);
906
      vidx = airIndexClamp(1, stab[0 + 2*pntIdx]*stab[1 + 2*pntIdx], 0, sizeT);
907
      add = strn ? strn[pntIdx] : 1;
908
      if (flatWght > 0) {
909
        double dx = ( ((vert + 4*(pntIdx+1))[3] - (vert + 4*(pntIdx-1))[3])
910
                      / (ssRange[1] - ssRange[0]) );
911
        /*
912
        double dx = ( ((vert + 4*(pntIdx+1))[3] - pp[3])
913
                      / (ssRange[1] - ssRange[0]) );
914
        */
915
        double dy = (stab[0 + 2*(pntIdx+1)]*stab[1 + 2*(pntIdx+1)]
916
                     - stab[0 + 2*(pntIdx-1)]*stab[1 + 2*(pntIdx-1)]);
917
        ww = dx/sqrt(dx*dx + dy*dy);
918
      } else {
919
        ww = 1;
920
      }
921
      plot[sidx + sizeS*vidx] += AIR_MAX(0, ww*add);
922
      if (mask && mask[sidx + sizeS*vidx] > 200) {
923
        ELL_4V_ADD2(maskInPos, maskInPos, pp);
924
        maskInCount ++;
925
      }
926
    }
927
    if (mask && maskInCount) {
928
      unsigned int mpi = airArrayLenIncr(mposArr, 1);
929
      ELL_4V_SCALE(mpos + 4*mpi, 1.0/maskInCount, maskInPos);
930
    }
931
  }
932
  if (mask && mposArr->len) {
933
    if (nrrdMaybeAlloc_va(nmaskedpos, nrrdTypeDouble, 2,
934
                          AIR_CAST(size_t, 4),
935
                          AIR_CAST(size_t, mposArr->len))) {
936
      biffAddf(PULL, "%s: couldn't allocate masked pos", me);
937
      airMopError(mop); return 1;
938
    }
939
    memcpy(nmaskedpos->data, mposArr->data,
940
           4*(mposArr->len)*sizeof(double));
941
  }
942
  airMopOkay(mop);
943
  return 0;
944
}
945
946
static size_t
947
nsizeof(const Nrrd *nrrd) {
948
  return (nrrd
949
          ? nrrdElementSize(nrrd)*nrrdElementNumber(nrrd)
950
          : 0);
951
}
952
953
size_t
954
pullTraceMultiSizeof(const pullTraceMulti *mtrc) {
955
  size_t ret;
956
  unsigned int ti;
957
958
  if (!mtrc) {
959
    return 0;
960
  }
961
  ret = 0;
962
  for (ti=0; ti<mtrc->traceNum; ti++) {
963
    ret += sizeof(pullTrace);
964
    ret += nsizeof(mtrc->trace[ti]->nvert);
965
    ret += nsizeof(mtrc->trace[ti]->nstrn);
966
    ret += nsizeof(mtrc->trace[ti]->nstab);
967
  }
968
  ret += sizeof(pullTrace*)*(mtrc->traceArr->size);
969
  return ret;
970
}
971
972
pullTraceMulti *
973
pullTraceMultiNix(pullTraceMulti *mtrc) {
974
975
  if (mtrc) {
976
    airArrayNuke(mtrc->traceArr);
977
    free(mtrc);
978
  }
979
  return NULL;
980
}
981
982
983
#define PULL_MTRC_MAGIC "PULLMTRC0001"
984
#define DEMARK_STR "======"
985
986
static int
987
tracewrite(FILE *file, const pullTrace *trc, unsigned int ti) {
988
  static const char me[]="tracewrite";
989
990
  /*
991
  this was used to get ascii coordinates for a trace,
992
  to help isolate (via emacs) one trace from a saved multi-trace
993
  NrrdIoState *nio = nrrdIoStateNew();
994
  nio->encoding = nrrdEncodingAscii;
995
  */
996
997
  fprintf(file, "%s %u\n", DEMARK_STR, ti);
998
  ell_4v_print_d(file, trc->seedPos);
999
#define WRITE(FF) \
1000
  if (trc->FF && trc->FF->data) { \
1001
    if (nrrdWrite(file, trc->FF, NULL /* nio */ )) {        \
1002
      biffMovef(PULL, NRRD, "%s: trouble with " #FF , me); \
1003
      return 1; \
1004
    } \
1005
  } else { \
1006
    fprintf(file, "NULL"); \
1007
  } \
1008
  fprintf(file, "\n")
1009
1010
  fprintf(file, "nrrds: vert strn stab = %d %d %d\n",
1011
          trc->nvert && trc->nvert->data,
1012
          trc->nstrn && trc->nstrn->data,
1013
          trc->nstab && trc->nstab->data);
1014
  WRITE(nvert);
1015
  WRITE(nstrn);
1016
  WRITE(nstab);
1017
  fprintf(file, "%u\n", trc->seedIdx);
1018
  fprintf(file, "%s %s %s\n",
1019
          airEnumStr(pullTraceStop, trc->whyStop[0]),
1020
          airEnumStr(pullTraceStop, trc->whyStop[1]),
1021
          airEnumStr(pullTraceStop, trc->whyNowhere));
1022
#undef WRITE
1023
  return 0;
1024
}
1025
1026
int
1027
pullTraceMultiWrite(FILE *file, const pullTraceMulti *mtrc) {
1028
  static const char me[]="pullTraceMultiWrite";
1029
  unsigned int ti;
1030
1031
  if (!(file && mtrc)) {
1032
    biffAddf(PULL, "%s: got NULL pointer", me);
1033
    return 1;
1034
  }
1035
  fprintf(file, "%s\n", PULL_MTRC_MAGIC);
1036
  fprintf(file, "%u traces\n", mtrc->traceNum);
1037
1038
  for (ti=0; ti<mtrc->traceNum; ti++) {
1039
    if (tracewrite(file, mtrc->trace[ti], ti)) {
1040
      biffAddf(PULL, "%s: trace %u/%u", me, ti, mtrc->traceNum);
1041
      return 1;
1042
    }
1043
  }
1044
  return 0;
1045
}
1046
1047
static int
1048
traceread(pullTrace *trc, FILE *file, unsigned int _ti) {
1049
  static const char me[]="traceread";
1050
  char line[AIR_STRLEN_MED], name[AIR_STRLEN_MED];
1051
  unsigned int ti, lineLen;
1052
  int stops[3], hackhack, vertHN, strnHN, stabHN; /* HN == have nrrd */
1053
1054
  sprintf(name, "separator");
1055
  lineLen = airOneLine(file, line, AIR_STRLEN_MED);
1056
  if (!lineLen) {
1057
    biffAddf(PULL, "%s: didn't get %s line", me, name);
1058
    return 1;
1059
  }
1060
  if (1 != sscanf(line, DEMARK_STR " %u", &ti)) {
1061
    biffAddf(PULL, "%s: \"%s\" doesn't look like %s line", me, line, name);
1062
    return 1;
1063
  }
1064
  if (ti != _ti) {
1065
    biffAddf(PULL, "%s: read trace index %u but wanted %u", me, ti, _ti);
1066
    return 1;
1067
  }
1068
  sprintf(name, "seed pos");
1069
  lineLen = airOneLine(file, line, AIR_STRLEN_MED);
1070
  if (!lineLen) {
1071
    biffAddf(PULL, "%s: didn't get %s line", me, name);
1072
    return 1;
1073
  }
1074
  if (4 != sscanf(line, "%lg %lg %lg %lg", trc->seedPos + 0,
1075
                  trc->seedPos + 1, trc->seedPos + 2, trc->seedPos + 3)) {
1076
    biffAddf(PULL, "%s: couldn't parse %s line \"%s\" as 4 doubles",
1077
             me, name, line);
1078
    return 1;
1079
  }
1080
  sprintf(name, "have nrrds");
1081
  lineLen = airOneLine(file, line, AIR_STRLEN_MED);
1082
  if (!lineLen) {
1083
    biffAddf(PULL, "%s: didn't get %s line", me, name);
1084
    return 1;
1085
  }
1086
  if (3 != sscanf(line, "nrrds: vert strn stab = %d %d %d",
1087
                  &vertHN, &strnHN, &stabHN)) {
1088
    biffAddf(PULL, "%s: couldn't parse %s line", me, name);
1089
    return 1;
1090
  }
1091
#define READ(FF) \
1092
  if (FF##HN) {                         \
1093
    if (nrrdRead(trc->n##FF, file, NULL)) {        \
1094
      biffMovef(PULL, NRRD, "%s: trouble with " #FF , me); \
1095
      return 1; \
1096
    } \
1097
    fgetc(file); \
1098
  } else {                                      \
1099
    airOneLine(file, line, AIR_STRLEN_MED); \
1100
  }
1101
  hackhack = nrrdStateVerboseIO;  /* should be fixed in Teem v2 */
1102
  nrrdStateVerboseIO = 0;
1103
  READ(vert);
1104
  READ(strn);
1105
  READ(stab);
1106
  nrrdStateVerboseIO = hackhack;
1107
1108
  sprintf(name, "seed idx");
1109
  lineLen = airOneLine(file, line, AIR_STRLEN_MED);
1110
  if (!lineLen) {
1111
    biffAddf(PULL, "%s: didn't get %s line", me, name);
1112
    return 1;
1113
  }
1114
  if (1 != sscanf(line, "%u", &(trc->seedIdx))) {
1115
    biffAddf(PULL, "%s: didn't parse uint from %s line \"%s\"",
1116
             me, name, line);
1117
    return 1;
1118
  }
1119
  sprintf(name, "stops");
1120
  lineLen = airOneLine(file, line, AIR_STRLEN_MED);
1121
  if (!lineLen) {
1122
    biffAddf(PULL, "%s: didn't get %s line", me, name);
1123
    return 1;
1124
  }
1125
  if (3 != airParseStrE(stops, line, " ", 3, pullTraceStop)) {
1126
    biffAddf(PULL, "%s: didn't see 3 %s on %s line \"%s\"", me,
1127
             pullTraceStop->name, name, line);
1128
    return 1;
1129
  }
1130
1131
  return 0;
1132
}
1133
int
1134
pullTraceMultiRead(pullTraceMulti *mtrc, FILE *file) {
1135
  static const char me[]="pullTraceMultiRead";
1136
  char line[AIR_STRLEN_MED], name[AIR_STRLEN_MED];
1137
  unsigned int lineLen, ti, tnum;
1138
  pullTrace *trc;
1139
1140
  if (!(mtrc && file)) {
1141
    biffAddf(PULL, "%s: got NULL pointer", me);
1142
    return 1;
1143
  }
1144
  airArrayLenSet(mtrc->traceArr, 0);
1145
  sprintf(name, "magic");
1146
  lineLen = airOneLine(file, line, AIR_STRLEN_MED);
1147
  if (!lineLen) {
1148
    biffAddf(PULL, "%s: didn't get %s line", me, name);
1149
    return 1;
1150
  }
1151
  if (strcmp(line, PULL_MTRC_MAGIC)) {
1152
    biffAddf(PULL, "%s: %s line \"%s\" not expected \"%s\"",
1153
             me, name, line, PULL_MTRC_MAGIC);
1154
    return 1;
1155
  }
1156
1157
  sprintf(name, "# of traces");
1158
  lineLen = airOneLine(file, line, AIR_STRLEN_MED);
1159
  if (!lineLen) {
1160
    biffAddf(PULL, "%s: didn't get %s line", me, name);
1161
    return 1;
1162
  }
1163
  if (1 != sscanf(line, "%u traces", &tnum)) {
1164
    biffAddf(PULL, "%s: \"%s\" doesn't look like %s line", me, line, name);
1165
    return 1;
1166
  }
1167
  for (ti=0; ti<tnum; ti++) {
1168
    int added;
1169
    trc = pullTraceNew();
1170
    if (traceread(trc, file, ti)) {
1171
      biffAddf(PULL, "%s: on trace %u/%u", me, ti, tnum);
1172
      return 1;
1173
    }
1174
    if (pullTraceMultiAdd(mtrc, trc, &added)) {
1175
      biffAddf(PULL, "%s: adding trace %u/%u", me, ti, tnum);
1176
      return 1;
1177
    }
1178
    if (!added) {
1179
      trc = pullTraceNix(trc);
1180
    }
1181
  }
1182
1183
  return 0;
1184
}