GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/bin/vprobe.c Lines: 0 324 0.0 %
Date: 2017-05-26 Branches: 0 152 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 <stdio.h>
26
27
#include <teem/biff.h>
28
#include <teem/hest.h>
29
#include <teem/nrrd.h>
30
#include <teem/gage.h>
31
#include <teem/ten.h>
32
#include <teem/meet.h>
33
34
static const char *probeInfo =
35
  ("Shows off the functionality of the gage library. "
36
   "Uses gageProbe() to query various kinds of volumes "
37
   "to learn various measured or derived quantities. "
38
   "Can set environment variable TEEM_VPROBE_HACK_ZI "
39
   "to limit probing to a single z slice.");
40
41
int
42
main(int argc, const char *argv[]) {
43
  gageKind *kind;
44
  const char *me;
45
  char *whatS, *err, *outS, *stackReadFormat, *stackSaveFormat;
46
  hestParm *hparm;
47
  hestOpt *hopt = NULL;
48
  NrrdKernelSpec *k00, *k11, *k22, *kSS, *kSSblur;
49
  int what, E=0, renorm, SSuniform, SSoptim, verbose, zeroZ,
50
    orientationFromSpacing, SSnormd;
51
  unsigned int iBaseDim, oBaseDim, axi, numSS, ninSSIdx, seed;
52
  const double *answer;
53
  Nrrd *nin, *nout, **ninSS=NULL;
54
  Nrrd *ngrad=NULL, *nbmat=NULL;
55
  size_t ai, ansLen, idx, xi, yi, zi, six, siy, siz, sox, soy, soz;
56
  double bval=0, gmc, rangeSS[2], wrlSS, idxSS=AIR_NAN,
57
    dsix, dsiy, dsiz, dsox, dsoy, dsoz;
58
  gageContext *ctx;
59
  gagePerVolume *pvl=NULL;
60
  double t0, t1, x, y, z, scale[3], rscl[3], min[3], maxOut[3], maxIn[3];
61
  airArray *mop;
62
  unsigned int hackZi, *skip, skipNum;
63
  double (*ins)(void *v, size_t I, double d);
64
  gageStackBlurParm *sbp;
65
66
  char hackKeyStr[]="TEEM_VPROBE_HACK_ZI", *hackValStr;
67
  int otype, hackSet;
68
  char stmp[4][AIR_STRLEN_SMALL];
69
70
  me = argv[0];
71
  /* parse environment variables first, in case they break nrrdDefault*
72
     or nrrdState* variables in a way that nrrdSanity() should see */
73
  nrrdDefaultGetenv();
74
  nrrdStateGetenv();
75
  /* no harm done in making sure we're sane */
76
  if (!nrrdSanity()) {
77
    fprintf(stderr, "******************************************\n");
78
    fprintf(stderr, "******************************************\n");
79
    fprintf(stderr, "\n");
80
    fprintf(stderr, "  %s: nrrd sanity check FAILED.\n", me);
81
    fprintf(stderr, "\n");
82
    fprintf(stderr, "  This means that either nrrd can't work on this "
83
            "platform, or (more likely)\n");
84
    fprintf(stderr, "  there was an error in the compilation options "
85
            "and variable definitions\n");
86
    fprintf(stderr, "  for how Teem was built here.\n");
87
    fprintf(stderr, "\n");
88
    fprintf(stderr, "  %s\n", err = biffGetDone(NRRD));
89
    fprintf(stderr, "\n");
90
    fprintf(stderr, "******************************************\n");
91
    fprintf(stderr, "******************************************\n");
92
    free(err);
93
    return 1;
94
  }
95
96
  mop = airMopNew();
97
  hparm = hestParmNew();
98
  airMopAdd(mop, hparm, AIR_CAST(airMopper, hestParmFree), airMopAlways);
99
  hparm->elideSingleOtherType = AIR_TRUE;
100
  hestOptAdd(&hopt, "i", "nin", airTypeOther, 1, 1, &nin, NULL,
101
             "input volume", NULL, NULL, nrrdHestNrrd);
102
  hestOptAdd(&hopt, "k", "kind", airTypeOther, 1, 1, &kind, NULL,
103
             "\"kind\" of volume (\"scalar\", \"vector\", "
104
             "\"tensor\", or \"dwi\")",
105
             NULL, NULL, meetHestGageKind);
106
  hestOptAdd(&hopt, "v", "verbosity", airTypeInt, 1, 1, &verbose, "1",
107
             "verbosity level");
108
  hestOptAdd(&hopt, "q", "query", airTypeString, 1, 1, &whatS, NULL,
109
             "the quantity (scalar, vector, or matrix) to learn by probing");
110
  hestOptAdd(&hopt, "s", "sclX sclY sxlZ", airTypeDouble, 3, 3, scale,
111
             "1.0 1.0 1.0",
112
             "scaling factor for resampling on each axis "
113
             "(>1.0 : supersampling)");
114
  hestOptAdd(&hopt, "k00", "kern00", airTypeOther, 1, 1, &k00,
115
             "tent", "kernel for gageKernel00",
116
             NULL, NULL, nrrdHestKernelSpec);
117
  hestOptAdd(&hopt, "k11", "kern11", airTypeOther, 1, 1, &k11,
118
             "cubicd:1,0", "kernel for gageKernel11",
119
             NULL, NULL, nrrdHestKernelSpec);
120
  hestOptAdd(&hopt, "k22", "kern22", airTypeOther, 1, 1, &k22,
121
             "cubicdd:1,0", "kernel for gageKernel22",
122
             NULL, NULL, nrrdHestKernelSpec);
123
  hestOptAdd(&hopt, "seed", "N", airTypeUInt, 1, 1, &seed, "42",
124
             "RNG seed; mostly for debugging");
125
  hestOptAdd(&hopt, "zz", "bool", airTypeBool, 1, 1, &zeroZ, "false",
126
             "enable \"zeroZ\" behavior in gage that partially "
127
             "implements working with 3D images as if they are 2D");
128
129
  hestOptAdd(&hopt, "ssn", "SS #", airTypeUInt, 1, 1, &numSS,
130
             "0", "how many scale-space samples to evaluate, or, "
131
             "0 to turn-off all scale-space behavior");
132
  hestOptAdd(&hopt, "ssr", "scale range", airTypeDouble, 2, 2, rangeSS,
133
             "nan nan", "range of scales in scale-space");
134
  hestOptAdd(&hopt, "ssrf", "SS read format", airTypeString, 1, 1,
135
             &stackReadFormat, "",
136
             "printf-style format (including a \"%u\") for the "
137
             "filenames from which to *read* "
138
             "pre-blurred volumes computed for the stack");
139
  hestOptAdd(&hopt, "sssf", "SS save format", airTypeString, 1, 1,
140
             &stackSaveFormat, "",
141
             "printf-style format (including a \"%u\") for the "
142
             "filenames in which to *save* "
143
             "pre-blurred volumes computed for the stack");
144
  hestOptAdd(&hopt, "ssw", "SS pos", airTypeDouble, 1, 1, &wrlSS, "0",
145
             "\"world\"-space position (true sigma) "
146
             "at which to sample in scale-space");
147
  hestOptAdd(&hopt, "kssb", "kernel", airTypeOther, 1, 1, &kSSblur,
148
             "dgauss:1,5", "blurring kernel, to sample scale space",
149
             NULL, NULL, nrrdHestKernelSpec);
150
  hestOptAdd(&hopt, "kssr", "kernel", airTypeOther, 1, 1, &kSS,
151
             "hermite", "kernel for reconstructing from scale space samples",
152
             NULL, NULL, nrrdHestKernelSpec);
153
  hestOptAdd(&hopt, "ssu", NULL, airTypeInt, 0, 0, &SSuniform, NULL,
154
             "do uniform samples along sigma, and not (by default) "
155
             "samples according to the effective diffusion scale");
156
  hestOptAdd(&hopt, "sso", NULL, airTypeInt, 0, 0, &SSoptim, NULL,
157
             "if not using \"-ssu\", use pre-computed optimal "
158
             "sigmas when possible");
159
  hestOptAdd(&hopt, "ssnd", NULL, airTypeInt, 0, 0, &SSnormd, NULL,
160
             "normalize derivatives by scale");
161
162
  hestOptAdd(&hopt, "rn", NULL, airTypeInt, 0, 0, &renorm, NULL,
163
             "renormalize kernel weights at each new sample location. "
164
             "\"Accurate\" kernels don't need this; doing it always "
165
             "makes things go slower");
166
  hestOptAdd(&hopt, "gmc", "min gradmag", airTypeDouble, 1, 1, &gmc,
167
             "0.0", "For curvature-based queries, use zero when gradient "
168
             "magnitude is below this");
169
  hestOptAdd(&hopt, "ofs", "ofs", airTypeInt, 0, 0, &orientationFromSpacing,
170
             NULL, "If only per-axis spacing is available, use that to "
171
             "contrive full orientation info");
172
  hestOptAdd(&hopt, "t", "type", airTypeEnum, 1, 1, &otype, "float",
173
             "type of output volume", NULL, nrrdType);
174
  hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, "-",
175
             "output volume");
176
  hestParseOrDie(hopt, argc-1, argv+1, hparm,
177
                 me, probeInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE);
178
  airMopAdd(mop, hopt, AIR_CAST(airMopper, hestOptFree), airMopAlways);
179
  airMopAdd(mop, hopt, AIR_CAST(airMopper, hestParseFree), airMopAlways);
180
181
  what = airEnumVal(kind->enm, whatS);
182
  if (!what) {
183
    /* 0 indeed always means "unknown" for any gageKind */
184
    fprintf(stderr, "%s: couldn't parse \"%s\" as measure of \"%s\" volume\n",
185
            me, whatS, kind->name);
186
    hestUsage(stderr, hopt, me, hparm);
187
    hestGlossary(stderr, hopt, hparm);
188
    airMopError(mop);
189
    return 1;
190
  }
191
192
  /* special set-up required for DWI kind */
193
  if (!strcmp(TEN_DWI_GAGE_KIND_NAME, kind->name)) {
194
    if (tenDWMRIKeyValueParse(&ngrad, &nbmat, &bval, &skip, &skipNum, nin)) {
195
      airMopAdd(mop, err = biffGetDone(TEN), airFree, airMopAlways);
196
      fprintf(stderr, "%s: trouble parsing DWI info:\n%s\n", me, err);
197
      airMopError(mop); return 1;
198
    }
199
    if (skipNum) {
200
      fprintf(stderr, "%s: sorry, can't do DWI skipping in tenDwiGage", me);
201
      airMopError(mop); return 1;
202
    }
203
    /* this could stand to use some more command-line arguments */
204
    if (tenDwiGageKindSet(kind, 50, 1, bval, 0.001, ngrad, nbmat,
205
                          tenEstimate1MethodLLS,
206
                          tenEstimate2MethodQSegLLS, seed)) {
207
      airMopAdd(mop, err = biffGetDone(TEN), airFree, airMopAlways);
208
      fprintf(stderr, "%s: trouble parsing DWI info:\n%s\n", me, err);
209
      airMopError(mop); return 1;
210
    }
211
  }
212
213
  /* for setting up pre-blurred scale-space samples */
214
  if (numSS) {
215
    unsigned int vi;
216
    sbp = gageStackBlurParmNew();
217
    airMopAdd(mop, sbp, (airMopper)gageStackBlurParmNix, airMopAlways);
218
    ninSS = AIR_CAST(Nrrd **, calloc(numSS, sizeof(Nrrd *)));
219
    if (!ninSS) {
220
      fprintf(stderr, "%s: couldn't allocate ninSS", me);
221
      airMopError(mop); return 1;
222
    }
223
    for (ninSSIdx=0; ninSSIdx<numSS; ninSSIdx++) {
224
      ninSS[ninSSIdx] = nrrdNew();
225
      airMopAdd(mop, ninSS[ninSSIdx], (airMopper)nrrdNuke, airMopAlways);
226
    }
227
    if (gageStackBlurParmScaleSet(sbp, numSS, rangeSS[0], rangeSS[1],
228
                                  SSuniform, SSoptim)
229
        || gageStackBlurParmKernelSet(sbp, kSSblur)
230
        || gageStackBlurParmRenormalizeSet(sbp, AIR_TRUE)
231
        || gageStackBlurParmBoundarySet(sbp, nrrdBoundaryBleed, AIR_NAN)
232
        || gageStackBlurParmVerboseSet(sbp, verbose)) {
233
      airMopAdd(mop, err = biffGetDone(GAGE), airFree, airMopAlways);
234
      fprintf(stderr, "%s: trouble with stack blur info:\n%s\n", me, err);
235
      airMopError(mop); return 1;
236
    }
237
    if (airStrlen(stackReadFormat)) {
238
      if (nrrdLoadMulti(ninSS, numSS,
239
                        stackReadFormat, 0, NULL)) {
240
        airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways);
241
        fprintf(stderr, "%s: trouble loading blurrings:\n%s\n", me, err);
242
        airMopError(mop); return 1;
243
      }
244
      if (gageStackBlurCheck(AIR_CAST(const Nrrd *const*, ninSS),
245
                             sbp, nin, kind)) {
246
        airMopAdd(mop, err = biffGetDone(GAGE), airFree, airMopAlways);
247
        fprintf(stderr, "%s: trouble:\n%s\n", me, err);
248
        airMopError(mop); return 1;
249
      }
250
    } else {
251
      if (gageStackBlur(ninSS, sbp, nin, kind)) {
252
        airMopAdd(mop, err = biffGetDone(GAGE), airFree, airMopAlways);
253
        fprintf(stderr, "%s: trouble pre-computing blurrings:\n%s\n", me, err);
254
        airMopError(mop); return 1;
255
      }
256
      if (airStrlen(stackSaveFormat) && !airStrlen(stackReadFormat)) {
257
        if (nrrdSaveMulti(stackSaveFormat,
258
                          AIR_CAST(const Nrrd *const *, ninSS),
259
                          numSS, 0, NULL)) {
260
          airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways);
261
          fprintf(stderr, "%s: trouble saving blurrings:\n%s\n", me, err);
262
          airMopError(mop); return 1;
263
        }
264
      }
265
    }
266
    /* there's actually work to do here, weirdly: gageProbe can either
267
       work in index space, or in world space, but vprobe has
268
       basically always been index-space-centric.  For doing any kind
269
       scale/stack space hacking for which vprobe is suited, its nicer
270
       to have the user-specified stack position be in the stack's
271
       "world" space, not the (potentially non-uniform) index space.
272
       So here, we have to actually replicate work that is done by
273
       _gageProbeSpace() in converting from world to index space */
274
    for (vi=0; vi<numSS-1; vi++) {
275
      if (AIR_IN_CL(sbp->sigma[vi], wrlSS, sbp->sigma[vi+1])) {
276
        idxSS = vi + AIR_AFFINE(sbp->sigma[vi], wrlSS, sbp->sigma[vi+1], 0, 1);
277
        if (verbose > 1) {
278
          fprintf(stderr, "%s: scale pos %g -> idx %g = %u + %g\n", me,
279
                  wrlSS, idxSS, vi,
280
                  AIR_AFFINE(sbp->sigma[vi], wrlSS, sbp->sigma[vi+1], 0, 1));
281
        }
282
        break;
283
      }
284
    }
285
    if (vi == numSS-1) {
286
      fprintf(stderr, "%s: scale pos %g outside range %g=%g, %g=%g\n", me,
287
              wrlSS, rangeSS[0], sbp->sigma[0],
288
              rangeSS[1], sbp->sigma[numSS-1]);
289
      airMopError(mop); return 1;
290
    }
291
  } else {
292
    ninSS = NULL;
293
    sbp = NULL;
294
  }
295
296
  /***
297
  **** Except for the gageProbe() call in the inner loop below,
298
  **** and the gageContextNix() call at the very end, all the gage
299
  **** calls which set up (and take down) the context and state are here.
300
  ***/
301
  ctx = gageContextNew();
302
  airMopAdd(mop, ctx, AIR_CAST(airMopper, gageContextNix), airMopAlways);
303
  gageParmSet(ctx, gageParmGradMagCurvMin, gmc);
304
  gageParmSet(ctx, gageParmVerbose, verbose);
305
  gageParmSet(ctx, gageParmTwoDimZeroZ, zeroZ);
306
  gageParmSet(ctx, gageParmRenormalize, renorm ? AIR_TRUE : AIR_FALSE);
307
  gageParmSet(ctx, gageParmCheckIntegrals, AIR_TRUE);
308
  gageParmSet(ctx, gageParmOrientationFromSpacing, orientationFromSpacing);
309
  E = 0;
310
  if (!E) E |= !(pvl = gagePerVolumeNew(ctx, nin, kind));
311
  if (!E) E |= gageKernelSet(ctx, gageKernel00, k00->kernel, k00->parm);
312
  if (!E) E |= gageKernelSet(ctx, gageKernel11, k11->kernel, k11->parm);
313
  if (!E) E |= gageKernelSet(ctx, gageKernel22, k22->kernel, k22->parm);
314
  if (numSS) {
315
    gagePerVolume **pvlSS;
316
    gageParmSet(ctx, gageParmStackUse, AIR_TRUE);
317
    gageParmSet(ctx, gageParmStackNormalizeDeriv, SSnormd);
318
    if (!E) E |= !(pvlSS = AIR_CAST(gagePerVolume **,
319
                                    calloc(numSS, sizeof(gagePerVolume *))));
320
    if (!E) airMopAdd(mop, pvlSS, (airMopper)airFree, airMopAlways);
321
    if (!E) E |= gageStackPerVolumeNew(ctx, pvlSS,
322
                                       AIR_CAST(const Nrrd*const*, ninSS),
323
                                       numSS, kind);
324
    if (!E) E |= gageStackPerVolumeAttach(ctx, pvl, pvlSS, sbp->sigma, numSS);
325
    if (!E) E |= gageKernelSet(ctx, gageKernelStack, kSS->kernel, kSS->parm);
326
  } else {
327
    if (!E) E |= gagePerVolumeAttach(ctx, pvl);
328
  }
329
  if (!E) E |= gageQueryItemOn(ctx, pvl, what);
330
  if (!E) E |= gageUpdate(ctx);
331
  if (E) {
332
    airMopAdd(mop, err = biffGetDone(GAGE), airFree, airMopAlways);
333
    fprintf(stderr, "%s: trouble:\n%s\n", me, err);
334
    airMopError(mop);
335
    return 1;
336
  }
337
  answer = gageAnswerPointer(ctx, pvl, what);
338
  /***
339
  **** end gage setup.
340
  ***/
341
342
  ansLen = kind->table[what].answerLength;
343
  iBaseDim = kind->baseDim;
344
  oBaseDim = 1 == ansLen ? 0 : 1;
345
  six = nin->axis[0+iBaseDim].size;
346
  siy = nin->axis[1+iBaseDim].size;
347
  siz = nin->axis[2+iBaseDim].size;
348
  dsix = AIR_CAST(double, six);
349
  dsiy = AIR_CAST(double, siy);
350
  dsiz = AIR_CAST(double, siz);
351
  sox = AIR_CAST(size_t, scale[0]*dsix);
352
  soy = AIR_CAST(size_t, scale[1]*dsiy);
353
  soz = AIR_CAST(size_t, scale[2]*dsiz);
354
  dsox = AIR_CAST(double, sox);
355
  dsoy = AIR_CAST(double, soy);
356
  dsoz = AIR_CAST(double, soz);
357
  if (verbose) {
358
    fprintf(stderr, "%s: six,y,z = %u %u %u\n", me,
359
            AIR_UINT(six),
360
            AIR_UINT(siy),
361
            AIR_UINT(siz));
362
    fprintf(stderr, "%s: sox,y,z = %u %u %u\n", me,
363
            AIR_UINT(sox),
364
            AIR_UINT(soy),
365
            AIR_UINT(soz));
366
  }
367
  rscl[0] = dsix/dsox;
368
  rscl[1] = dsiy/dsoy;
369
  rscl[2] = dsiz/dsoz;
370
371
  if (verbose) {
372
    fprintf(stderr, "%s: kernel support = %d^3 samples\n", me,
373
            2*ctx->radius);
374
    fprintf(stderr, "%s: effective scaling is %g %g %g\n", me,
375
            rscl[0], rscl[1], rscl[2]);
376
  }
377
  /* else, normal volume probing */
378
  if (ansLen > 1) {
379
    if (verbose) {
380
      fprintf(stderr, "%s: creating %s x %s x %s x %s output\n", me,
381
              airSprintSize_t(stmp[0], ansLen),
382
              airSprintSize_t(stmp[1], sox),
383
              airSprintSize_t(stmp[2], soy),
384
              airSprintSize_t(stmp[3], soz));
385
    }
386
    if (!E) E |= nrrdMaybeAlloc_va(nout=nrrdNew(), otype, 4,
387
                                   ansLen, sox, soy, soz);
388
  } else {
389
    if (verbose) {
390
      fprintf(stderr, "%s: creating %s x %s x %s output\n", me,
391
              airSprintSize_t(stmp[0], sox),
392
              airSprintSize_t(stmp[1], soy),
393
              airSprintSize_t(stmp[2], soz));
394
    }
395
    if (!E) E |= nrrdMaybeAlloc_va(nout=nrrdNew(), otype, 3,
396
                                   sox, soy, soz);
397
  }
398
  if (E) {
399
    airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways);
400
    fprintf(stderr, "%s: trouble:\n%s\n", me, err);
401
    airMopError(mop);
402
    return 1;
403
  }
404
  airMopAdd(mop, nout, AIR_CAST(airMopper, nrrdNuke), airMopAlways);
405
406
  hackSet = nrrdGetenvUInt(&hackZi, &hackValStr, hackKeyStr);
407
  if (AIR_FALSE == hackSet) {
408
    fprintf(stderr, "%s: couldn't parse value of \"%s\" (\"%s\") as uint\n",
409
            me, hackKeyStr, hackValStr);
410
    airMopError(mop);
411
    return 1;
412
  }
413
  if (AIR_TRUE == hackSet) {
414
    fprintf(stderr, "%s: %s hack on: will only measure Zi=%u\n",
415
            me, hackKeyStr, hackZi);
416
  }
417
418
  if (nrrdCenterCell == ctx->shape->center) {
419
    ELL_3V_SET(min, -0.5, -0.5, -0.5);
420
    ELL_3V_SET(maxOut, dsox-0.5, dsoy-0.5, dsoz-0.5);
421
    ELL_3V_SET(maxIn,  dsix-0.5, dsiy-0.5, dsiz-0.5);
422
  } else {
423
    ELL_3V_SET(min, 0, 0, 0);
424
    ELL_3V_SET(maxOut, dsox-1, dsoy-1, dsoz-1);
425
    ELL_3V_SET(maxIn, dsix-1, dsiy-1, dsiz-1);
426
  }
427
  t0 = airTime();
428
  ins = nrrdDInsert[nout->type];
429
  gageParmSet(ctx, gageParmVerbose, verbose/10);
430
  for (zi=0; zi<soz; zi++) {
431
    if (verbose) {
432
      if (verbose > 1) {
433
        fprintf(stderr, "z = ");
434
      }
435
      fprintf(stderr, " %s/%s",
436
              airSprintSize_t(stmp[0], zi),
437
              airSprintSize_t(stmp[1], soz-1));
438
      fflush(stderr);
439
      if (verbose > 1) {
440
        fprintf(stderr, "\n");
441
      }
442
    }
443
    if (AIR_TRUE == hackSet) {
444
      if (hackZi != zi) {
445
        continue;
446
      }
447
    }
448
449
    z = AIR_AFFINE(min[2], zi, maxOut[2], min[2], maxIn[2]);
450
    for (yi=0; yi<soy; yi++) {
451
      y = AIR_AFFINE(min[1], yi, maxOut[1], min[1], maxIn[1]);
452
      if (2 == verbose) {
453
        fprintf(stderr, " %u/%u", AIR_UINT(yi),
454
                AIR_UINT(soy));
455
        fflush(stderr);
456
      }
457
      for (xi=0; xi<sox; xi++) {
458
        if (verbose > 2) {
459
          fprintf(stderr, " (%u,%u)/(%u,%u)",
460
                  AIR_UINT(xi), AIR_UINT(yi),
461
                  AIR_UINT(sox), AIR_UINT(soy));
462
          fflush(stderr);
463
        }
464
        x = AIR_AFFINE(min[0], xi, maxOut[0], min[0], maxIn[0]);
465
        idx = xi + sox*(yi + soy*zi);
466
        E = (numSS
467
             ? gageStackProbe(ctx, x, y, z, idxSS)
468
             : gageProbe(ctx, x, y, z));
469
        if (E) {
470
          fprintf(stderr,
471
                  "%s: trouble at i=(%s,%s,%s) -> f=(%g,%g,%g):\n%s\n(%d)\n",
472
                  me, airSprintSize_t(stmp[0], xi),
473
                  airSprintSize_t(stmp[1], yi),
474
                  airSprintSize_t(stmp[2], zi), x, y, z,
475
                  ctx->errStr, ctx->errNum);
476
          airMopError(mop);
477
          return 1;
478
        }
479
        if (1 == ansLen) {
480
          ins(nout->data, idx, *answer);
481
        } else {
482
          for (ai=0; ai<=ansLen-1; ai++) {
483
            ins(nout->data, ai + ansLen*idx, answer[ai]);
484
          }
485
        }
486
      }
487
    }
488
  }
489
490
  /* HEY: this isn't actually correct in general, but is true
491
     for gageKindScl and gageKindVec */
492
  nrrdContentSet_va(nout, "probe", nin, "%s", airEnumStr(kind->enm, what));
493
494
  for (axi=0; axi<3; axi++) {
495
    /* HEY: why not using nrrdAxisInfoCopy? */
496
    nout->axis[axi+oBaseDim].label = airStrdup(nin->axis[axi+iBaseDim].label);
497
    nout->axis[axi+oBaseDim].center = ctx->shape->center;
498
    nout->axis[axi+oBaseDim].kind = nin->axis[axi+iBaseDim].kind;
499
  }
500
501
  nrrdBasicInfoCopy(nout, nin, (NRRD_BASIC_INFO_DATA_BIT
502
                                | NRRD_BASIC_INFO_TYPE_BIT
503
                                | NRRD_BASIC_INFO_BLOCKSIZE_BIT
504
                                | NRRD_BASIC_INFO_DIMENSION_BIT
505
                                | NRRD_BASIC_INFO_CONTENT_BIT
506
                                | NRRD_BASIC_INFO_SPACEORIGIN_BIT
507
                                | NRRD_BASIC_INFO_OLDMIN_BIT
508
                                | NRRD_BASIC_INFO_OLDMAX_BIT
509
                                | NRRD_BASIC_INFO_COMMENTS_BIT
510
                                | NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT));
511
  if (ctx->shape->fromOrientation) {
512
    if (nin->space) {
513
      nrrdSpaceSet(nout, nin->space);
514
    } else {
515
      nrrdSpaceDimensionSet(nout, nin->spaceDim);
516
    }
517
    nrrdSpaceVecCopy(nout->spaceOrigin, nin->spaceOrigin);
518
    for (axi=0; axi<3; axi++) {
519
      nrrdSpaceVecScale(nout->axis[axi+oBaseDim].spaceDirection,
520
                        rscl[axi],
521
                        nin->axis[axi+iBaseDim].spaceDirection);
522
      z = AIR_AFFINE(min[axi], 0, maxOut[axi], min[axi], maxIn[axi]);
523
      nrrdSpaceVecScaleAdd2(nout->spaceOrigin,
524
                            1.0, nout->spaceOrigin,
525
                            z, nin->axis[axi+iBaseDim].spaceDirection);
526
    }
527
  } else {
528
    for (axi=0; axi<3; axi++) {
529
      nout->axis[axi+oBaseDim].spacing =
530
        rscl[axi]*nin->axis[axi+iBaseDim].spacing;
531
    }
532
  }
533
534
  fprintf(stderr, "\n");
535
  t1 = airTime();
536
  fprintf(stderr, "probe rate = %g KHz\n", dsox*dsoy*dsoz/(1000.0*(t1-t0)));
537
  if (nrrdSave(outS, nout, NULL)) {
538
    airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways);
539
    fprintf(stderr, "%s: trouble saving output:\n%s\n", me, err);
540
    airMopError(mop);
541
    return 1;
542
  }
543
544
  airMopOkay(mop);
545
  return 0;
546
}