GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/ten/path.c Lines: 83 594 14.0 %
Date: 2017-05-26 Branches: 14 314 4.5 %

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 "ten.h"
26
#include "privateTen.h"
27
28
tenInterpParm *
29
tenInterpParmNew(void) {
30
  tenInterpParm *tip;
31
32
32
  tip = AIR_CAST(tenInterpParm *, malloc(sizeof(tenInterpParm)));
33
16
  if (tip) {
34
16
    tip->verbose = AIR_FALSE;
35
16
    tip->convStep = 0.2;
36
16
    tip->minNorm = 0.0;
37
16
    tip->convEps = 0.0000000001;
38
16
    tip->wghtSumEps = 0.0000001;
39
16
    tip->enableRecurse = AIR_TRUE;
40
16
    tip->maxIter = 20;
41
16
    tip->numSteps = 100;
42
16
    tip->lengthFancy = AIR_FALSE;
43
44
16
    tip->allocLen = 0;
45
16
    tip->eval = NULL;
46
16
    tip->evec = NULL;
47
16
    tip->rtIn = NULL;
48
16
    tip->rtLog = NULL;
49
16
    tip->qIn = NULL;
50
16
    tip->qBuff = NULL;
51
16
    tip->qInter = NULL;
52
53
16
    tip->numIter = 0;
54
16
    tip->convFinal = AIR_NAN;
55
16
    tip->lengthShape = AIR_NAN;
56
16
    tip->lengthOrient = AIR_NAN;
57
16
  }
58
16
  return tip;
59
}
60
61
62
63
/*
64
** handles allocating all the various buffers that are needed for QGL
65
** interpolation, so that they are repeatedly allocated and freed
66
** between calls
67
*/
68
int
69
tenInterpParmBufferAlloc(tenInterpParm *tip, unsigned int num) {
70
  static const char me[]="tenInterpParmBufferAlloc";
71
72
32
  if (0 == num) {
73
    /* user wants to free buffers for some reason */
74
    airFree(tip->eval); tip->eval = NULL;
75
    airFree(tip->evec); tip->evec = NULL;
76
    airFree(tip->rtIn); tip->rtIn = NULL;
77
    airFree(tip->rtLog); tip->rtLog = NULL;
78
    airFree(tip->qIn); tip->qIn = NULL;
79
    airFree(tip->qBuff); tip->qBuff = NULL;
80
    airFree(tip->qInter); tip->qInter = NULL;
81
    tip->allocLen = 0;
82
16
  } else if (1 == num) {
83
    biffAddf(TEN, "%s: need num >= 2 (not %u)", me, num);
84
    return 1;
85
16
  } else if (num != tip->allocLen) {
86
16
    airFree(tip->eval); tip->eval = NULL;
87
16
    airFree(tip->evec); tip->evec = NULL;
88
16
    airFree(tip->rtIn); tip->rtIn = NULL;
89
16
    airFree(tip->rtLog); tip->rtLog = NULL;
90
16
    airFree(tip->qIn); tip->qIn = NULL;
91
16
    airFree(tip->qBuff); tip->qBuff = NULL;
92
16
    airFree(tip->qInter); tip->qInter = NULL;
93
16
    tip->eval = AIR_CALLOC(3*num, double);
94
16
    tip->evec = AIR_CALLOC(9*num, double);
95
16
    tip->rtIn = AIR_CALLOC(3*num, double);
96
16
    tip->rtLog = AIR_CALLOC(3*num, double);
97
16
    tip->qIn = AIR_CALLOC(4*num, double);
98
16
    tip->qBuff = AIR_CALLOC(4*num, double);
99
16
    tip->qInter = AIR_CALLOC(num*num, double);
100

48
    if (!(tip->eval && tip->evec &&
101

32
          tip->rtIn && tip->rtLog &&
102

48
          tip->qIn && tip->qBuff && tip->qInter)) {
103
      biffAddf(TEN, "%s: didn't alloc buffers (%p,%p,%p %p %p %p %p)", me,
104
               AIR_VOIDP(tip->eval), AIR_VOIDP(tip->evec),
105
               AIR_VOIDP(tip->rtIn), AIR_VOIDP(tip->rtLog),
106
               AIR_VOIDP(tip->qIn), AIR_VOIDP(tip->qBuff),
107
               AIR_VOIDP(tip->qInter));
108
      return 1;
109
    }
110
16
    tip->allocLen = num;
111
16
  }
112
16
  return 0;
113
16
}
114
115
tenInterpParm *
116
tenInterpParmCopy(tenInterpParm *tip) {
117
  static const char me[]="tenInterpParmCopy";
118
  tenInterpParm *newtip;
119
  unsigned int num;
120
121
16
  num = tip->allocLen;
122
8
  newtip = tenInterpParmNew();
123
8
  if (newtip) {
124
8
    memcpy(newtip, tip, sizeof(tenInterpParm));
125
    /* manually set all pointers */
126
8
    newtip->allocLen = 0;
127
8
    newtip->eval = NULL;
128
8
    newtip->evec = NULL;
129
8
    newtip->rtIn = NULL;
130
8
    newtip->rtLog = NULL;
131
8
    newtip->qIn = NULL;
132
8
    newtip->qBuff = NULL;
133
8
    newtip->qInter = NULL;
134
8
    if (tenInterpParmBufferAlloc(newtip, num)) {
135
      biffAddf(TEN, "%s: trouble allocating output", me);
136
      return NULL;
137
    }
138
8
    memcpy(newtip->eval, tip->eval, 3*num*sizeof(double));
139
8
    memcpy(newtip->evec, tip->evec, 9*num*sizeof(double));
140
8
    memcpy(newtip->rtIn, tip->rtIn, 3*num*sizeof(double));
141
8
    memcpy(newtip->rtLog, tip->rtLog, 3*num*sizeof(double));
142
8
    memcpy(newtip->qIn, tip->qIn, 4*num*sizeof(double));
143
8
    memcpy(newtip->qBuff, tip->qBuff, 4*num*sizeof(double));
144
8
    memcpy(newtip->qInter, tip->qInter, num*num*sizeof(double));
145
8
  }
146
8
  return newtip;
147
8
}
148
149
tenInterpParm *
150
tenInterpParmNix(tenInterpParm *tip) {
151
152
32
  if (tip) {
153
16
    airFree(tip->eval);
154
16
    airFree(tip->evec);
155
16
    airFree(tip->rtIn);
156
16
    airFree(tip->rtLog);
157
16
    airFree(tip->qIn);
158
16
    airFree(tip->qBuff);
159
16
    airFree(tip->qInter);
160
16
    free(tip);
161
16
  }
162
16
  return NULL;
163
}
164
165
/*
166
******** tenInterpTwo_d
167
**
168
** interpolates between two tensors, in various ways
169
**
170
** this is really only used for demo purposes; its not useful for
171
** doing real work in DTI fields.  So: its okay that its slow
172
** (e.g. for tenInterpTypeQuatGeoLox{K,R}, it recomputes the
173
** eigensystems at the endpoints for every call, even though they are
174
** apt to be the same between calls.
175
**
176
** this
177
*/
178
void
179
tenInterpTwo_d(double oten[7],
180
               const double tenA[7], const double tenB[7],
181
               int ptype, double aa,
182
               tenInterpParm *tip) {
183
  static const char me[]="tenInterpTwo_d";
184
  double logA[7], logB[7], tmp1[7], tmp2[7], logMean[7],
185
    mat1[9], mat2[9], mat3[9], sqrtA[7], isqrtA[7],
186
    mean[7], sqrtB[7], isqrtB[7],
187
    oeval[3], evalA[3], evalB[3], oevec[9], evecA[9], evecB[9];
188
189
190
  if (!( oten && tenA && tenB )) {
191
    /* got NULL pointer, but not using biff */
192
    if (oten) {
193
      TEN_T_SET(oten, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN,
194
                AIR_NAN, AIR_NAN, AIR_NAN);
195
    }
196
    return;
197
  }
198
199
  switch (ptype) {
200
  case tenInterpTypeLinear:
201
    TEN_T_LERP(oten, aa, tenA, tenB);
202
    break;
203
  case tenInterpTypeLogLinear:
204
    tenLogSingle_d(logA, tenA);
205
    tenLogSingle_d(logB, tenB);
206
    TEN_T_LERP(logMean, aa, logA, logB);
207
    tenExpSingle_d(oten, logMean);
208
    break;
209
  case tenInterpTypeAffineInvariant:
210
    tenSqrtSingle_d(sqrtA, tenA);
211
    tenInv_d(isqrtA, sqrtA);
212
    TEN_T2M(mat1, tenB);
213
    TEN_T2M(mat2, isqrtA);
214
    ELL_3M_MUL(mat3, mat1, mat2);   /*  B * is(A) */
215
    ELL_3M_MUL(mat1, mat2, mat3);   /*  is(A) * B * is(A) */
216
    TEN_M2T(tmp2, mat1);
217
    tenPowSingle_d(tmp1, tmp2, aa); /*  m = (is(A) * B * is(A))^aa */
218
    TEN_T2M(mat1, tmp1);
219
    TEN_T2M(mat2, sqrtA);
220
    ELL_3M_MUL(mat3, mat1, mat2);   /*  m * sqrt(A) */
221
    ELL_3M_MUL(mat1, mat2, mat3);   /*  sqrt(A) * m * sqrt(A) */
222
    TEN_M2T(oten, mat1);
223
    oten[0] = AIR_LERP(aa, tenA[0], tenB[0]);
224
    if (tip->verbose) {
225
      fprintf(stderr, "%s:\nA= %g %g %g   %g %g  %g\n"
226
              "B = %g %g %g   %g %g  %g\n"
227
              "foo = %g %g %g   %g %g  %g\n"
228
              "bar(%g) = %g %g %g   %g %g  %g\n", me,
229
              tenA[1], tenA[2], tenA[3], tenA[4], tenA[5], tenA[6],
230
              tenB[1], tenB[2], tenB[3], tenB[4], tenB[5], tenB[6],
231
              tmp1[1], tmp1[2], tmp1[3], tmp1[4], tmp1[5], tmp1[6],
232
              aa, oten[1], oten[2], oten[3], oten[4], oten[5], oten[6]);
233
    }
234
    break;
235
  case tenInterpTypeWang:
236
    /* HEY: this seems to be broken */
237
    TEN_T_LERP(mean, aa, tenA, tenB);    /* "A" = mean */
238
    tenLogSingle_d(logA, tenA);
239
    tenLogSingle_d(logB, tenB);
240
    TEN_T_LERP(logMean, aa, logA, logB); /* "B" = logMean */
241
    tenSqrtSingle_d(sqrtB, logMean);
242
    tenInv_d(isqrtB, sqrtB);
243
    TEN_T2M(mat1, mean);
244
    TEN_T2M(mat2, isqrtB);
245
    ELL_3M_MUL(mat3, mat1, mat2);
246
    ELL_3M_MUL(mat1, mat2, mat3);
247
    TEN_M2T(tmp1, mat1);
248
    tenSqrtSingle_d(oten, tmp1);
249
    oten[0] = AIR_LERP(aa, tenA[0], tenB[0]);
250
    break;
251
  case tenInterpTypeQuatGeoLoxK:
252
  case tenInterpTypeQuatGeoLoxR:
253
    tenEigensolve_d(evalA, evecA, tenA);
254
    tenEigensolve_d(evalB, evecB, tenB);
255
    if (tenInterpTypeQuatGeoLoxK == ptype) {
256
      tenQGLInterpTwoEvalK(oeval, evalA, evalB, aa);
257
    } else {
258
      tenQGLInterpTwoEvalR(oeval, evalA, evalB, aa);
259
    }
260
    tenQGLInterpTwoEvec(oevec, evecA, evecB, aa);
261
    tenMakeSingle_d(oten, AIR_LERP(aa, tenA[0], tenB[0]), oeval, oevec);
262
    break;
263
  case tenInterpTypeGeoLoxK:
264
  case tenInterpTypeGeoLoxR:
265
  case tenInterpTypeLoxK:
266
  case tenInterpTypeLoxR:
267
    /* (currently) no closed-form expression for these */
268
    TEN_T_SET(oten, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN,
269
              AIR_NAN, AIR_NAN, AIR_NAN);
270
    break;
271
  case tenInterpTypeRThetaPhiLinear:
272
    if (1) {
273
      double rtpA[3], rtpB[3], rtpM[3], eval[3], tenM[7];
274
      tenEigensolve_d(eval, NULL, tenA);
275
      tenTripleConvertSingle_d(rtpA, tenTripleTypeRThetaPhi,
276
                               eval, tenTripleTypeEigenvalue);
277
      tenEigensolve_d(eval, NULL, tenB);
278
      tenTripleConvertSingle_d(rtpB, tenTripleTypeRThetaPhi,
279
                               eval, tenTripleTypeEigenvalue);
280
      TEN_T_LERP(tenM, aa, tenA, tenB);
281
      tenEigensolve_d(eval, oevec, tenM);
282
      ELL_3V_LERP(rtpM, aa, rtpA, rtpB);
283
      tenTripleConvertSingle_d(oeval, tenTripleTypeEigenvalue,
284
                               rtpM, tenTripleTypeRThetaPhi);
285
    }
286
    tenMakeSingle_d(oten, AIR_LERP(aa, tenA[0], tenB[0]), oeval, oevec);
287
    break;
288
  default:
289
    /* error */
290
    TEN_T_SET(oten, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN,
291
              AIR_NAN, AIR_NAN, AIR_NAN);
292
    break;
293
  }
294
  return;
295
}
296
297
/*
298
** this NON-optionally uses biff
299
**
300
** for simplicity, a pre-allocated tenInterpParm MUST be passed,
301
** regardless of the interpolation requested
302
*/
303
int
304
tenInterpN_d(double tenOut[7],
305
             const double *tenIn, const double *wght,
306
             unsigned int num, int ptype, tenInterpParm *tip) {
307
  static const char me[]="tenInterpN_d";
308
  unsigned int ii;
309
  double ww, cc, tenErr[7], tmpTen[7], wghtSum, eval[3], evec[9], rtp[3];
310
311
  TEN_T_SET(tenErr, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN,
312
            AIR_NAN, AIR_NAN, AIR_NAN);
313
  /* wght can be NULL ==> equal 1/num weight for all */
314
  if (!(tenOut && tenIn && tip)) {
315
    biffAddf(TEN, "%s: got NULL pointer", me);
316
    return 1;
317
  }
318
  if (!( num >= 2 )) {
319
    biffAddf(TEN, "%s: need num >= 2 (not %u)", me, num);
320
    TEN_T_COPY(tenOut, tenErr); return 1;
321
  }
322
  if (airEnumValCheck(tenInterpType, ptype)) {
323
    biffAddf(TEN, "%s: invalid %s %d", me, tenInterpType->name, ptype);
324
    TEN_T_COPY(tenOut, tenErr); return 1;
325
  }
326
  wghtSum = 0;
327
  for (ii=0; ii<num; ii++) {
328
    ww = wght ? wght[ii] : 1.0/num;
329
    wghtSum += ww;
330
  }
331
  if (!( AIR_IN_CL(1 - tip->wghtSumEps, wghtSum, 1 + tip->wghtSumEps) )) {
332
    biffAddf(TEN, "%s: wght sum %g not within %g of 1.0", me,
333
             wghtSum, tip->wghtSumEps);
334
    TEN_T_COPY(tenOut, tenErr); return 1;
335
  }
336
337
  switch (ptype) {
338
  case tenInterpTypeLinear:
339
    TEN_T_SET(tenOut, 0,   0, 0, 0,   0, 0,   0);
340
    cc = 0;
341
    for (ii=0; ii<num; ii++) {
342
      ww = wght ? wght[ii] : 1.0/num;
343
      TEN_T_SCALE_INCR(tenOut, ww, tenIn + 7*ii);
344
      cc += ww*(tenIn + 7*ii)[0];
345
    }
346
    tenOut[0] = cc;
347
    break;
348
  case tenInterpTypeLogLinear:
349
    TEN_T_SET(tenOut, 0,   0, 0, 0,   0, 0,   0);
350
    cc = 0;
351
    for (ii=0; ii<num; ii++) {
352
      ww = wght ? wght[ii] : 1.0/num;
353
      tenLogSingle_d(tmpTen, tenIn + 7*ii);
354
      TEN_T_SCALE_INCR(tenOut, ww, tmpTen);
355
      cc += ww*(tenIn + 7*ii)[0];
356
    }
357
    tenOut[0] = cc;
358
    TEN_T_COPY(tmpTen, tenOut);
359
    tenExpSingle_d(tenOut, tmpTen);
360
    break;
361
  case tenInterpTypeAffineInvariant:
362
  case tenInterpTypeWang:
363
    biffAddf(TEN, "%s: sorry, not implemented", me);
364
    TEN_T_COPY(tenOut, tenErr); return 1;
365
    break;
366
  case tenInterpTypeGeoLoxK:
367
  case tenInterpTypeGeoLoxR:
368
  case tenInterpTypeLoxK:
369
  case tenInterpTypeLoxR:
370
    biffAddf(TEN, "%s: %s doesn't support averaging multiple tensors", me,
371
             airEnumStr(tenInterpType, ptype));
372
    TEN_T_COPY(tenOut, tenErr); return 1;
373
    break;
374
  case tenInterpTypeQuatGeoLoxK:
375
  case tenInterpTypeQuatGeoLoxR:
376
    if (tenInterpParmBufferAlloc(tip, num)) {
377
      biffAddf(TEN, "%s: trouble getting buffers", me);
378
      TEN_T_COPY(tenOut, tenErr); return 1;
379
    } else {
380
      cc = 0;
381
      for (ii=0; ii<num; ii++) {
382
        tenEigensolve_d(tip->eval + 3*ii, tip->evec + 9*ii, tenIn + 7*ii);
383
        ww = wght ? wght[ii] : 1.0/num;
384
        cc += ww*(tenIn + 7*ii)[0];
385
      }
386
      if (_tenQGLInterpNEval(eval, tip->eval, wght, num, ptype, tip)
387
          || _tenQGLInterpNEvec(evec, tip->evec, wght, num, tip)) {
388
        biffAddf(TEN, "%s: trouble computing", me);
389
        TEN_T_COPY(tenOut, tenErr); return 1;
390
      }
391
      tenMakeSingle_d(tenOut, cc, eval, evec);
392
    }
393
    break;
394
  case tenInterpTypeRThetaPhiLinear:
395
    TEN_T_SET(tmpTen, 0,   0, 0, 0,   0, 0,   0);
396
    ELL_3V_SET(rtp, 0, 0, 0);
397
    for (ii=0; ii<num; ii++) {
398
      double tmpeval[3], tmprtp[3];
399
      tenEigensolve_d(tmpeval, NULL, tenIn + 7*ii);
400
      tenTripleConvertSingle_d(tmprtp, tenTripleTypeRThetaPhi,
401
                               tmpeval, tenTripleTypeEigenvalue);
402
      ww = wght ? wght[ii] : 1.0/num;
403
      TEN_T_SCALE_INCR(tmpTen, ww, tenIn + 7*ii);
404
      ELL_3V_SCALE_INCR(rtp, ww, tmprtp);
405
    }
406
    tenEigensolve_d(eval, evec, tmpTen); /* only care about evec */
407
    tenTripleConvertSingle_d(eval, tenTripleTypeEigenvalue,
408
                             rtp, tenTripleTypeRThetaPhi);
409
    tenMakeSingle_d(tenOut, tmpTen[0], eval, evec);
410
    break;
411
  default:
412
    biffAddf(TEN, "%s: sorry, interp type %s (%d) not implemented",
413
             me, airEnumStr(tenInterpType, ptype), ptype);
414
    TEN_T_COPY(tenOut, tenErr);
415
    return 1;
416
  }
417
418
  return 0;
419
}
420
421
int
422
_tenInterpGeoLoxRelaxOne(Nrrd *nodata, Nrrd *ntdata, Nrrd *nigrtdata,
423
                       unsigned int ii, int rotnoop, double scl,
424
                       tenInterpParm *tip) {
425
  static const char me[]="_tenInterpGeoLoxRelaxOne";
426
  double *tdata, *odata, *igrtdata, *tt[5], *igrt[5][6], d02[7], d24[7],
427
    len02, len24, tmp, tng[7], correct, update[7];
428
  unsigned int jj;
429
430
  if (tip->verbose) {
431
    fprintf(stderr, "---- %u --> %u %u %u %u %u\n", ii,
432
            2*ii - 2, 2*ii - 1, 2*ii, 2*ii + 1, 2*ii + 2);
433
  }
434
  tdata = AIR_CAST(double *, ntdata->data);
435
  odata = AIR_CAST(double *, nodata->data);
436
  tt[0] = tdata + 7*(2*ii - 2);
437
  tt[1] = tdata + 7*(2*ii - 1); /* unused */
438
  tt[2] = tdata + 7*(2*ii + 0);
439
  tt[3] = tdata + 7*(2*ii + 1); /* unused */
440
  tt[4] = tdata + 7*(2*ii + 2);
441
  igrtdata = AIR_CAST(double *, nigrtdata->data);
442
  for (jj=0; jj<6; jj++) {
443
    igrt[0][jj] = igrtdata + 7*(jj + 6*(2*ii - 2)); /* unused */
444
    igrt[1][jj] = igrtdata + 7*(jj + 6*(2*ii - 1));
445
    igrt[2][jj] = igrtdata + 7*(jj + 6*(2*ii + 0));
446
    igrt[3][jj] = igrtdata + 7*(jj + 6*(2*ii + 1));
447
    igrt[4][jj] = igrtdata + 7*(jj + 6*(2*ii + 2)); /* unused */
448
  }
449
450
  /* re-align [1] and [3] bases relative to [2] */
451
  /* HEY: should I be worrying about aligning the mode normal
452
     when it had to be computed from eigenvectors? */
453
  for (jj=3; jj<6; jj++) {
454
    if (TEN_T_DOT(igrt[1][jj], igrt[2][jj]) < 0) {
455
      TEN_T_SCALE(igrt[1][jj], -1, igrt[1][jj]);
456
    }
457
    if (TEN_T_DOT(igrt[3][jj], igrt[2][jj]) < 0) {
458
      TEN_T_SCALE(igrt[3][jj], -1, igrt[1][jj]);
459
    }
460
  }
461
462
  TEN_T_SUB(tng, tt[4], tt[0]);
463
  tmp = 1.0/TEN_T_NORM(tng);
464
  TEN_T_SCALE(tng, tmp, tng);
465
466
  TEN_T_SUB(d02, tt[2], tt[0]);
467
  TEN_T_SUB(d24, tt[4], tt[2]);
468
  TEN_T_SET(update, 1,   0, 0, 0,   0, 0,   0);
469
  for (jj=0; jj<(rotnoop ? 3u : 6u); jj++) {
470
    len02 = TEN_T_DOT(igrt[1][jj], d02);
471
    len24 = TEN_T_DOT(igrt[3][jj], d24);
472
    correct = (len24 - len02)/2;
473
    TEN_T_SCALE_INCR(update, correct*scl, igrt[2][jj]);
474
    if (tip->verbose) {
475
      fprintf(stderr, "igrt[1][%u] = %g %g %g   %g %g   %g\n", jj,
476
              igrt[1][jj][1], igrt[1][jj][2], igrt[1][jj][3],
477
              igrt[1][jj][4], igrt[1][jj][5], igrt[1][jj][6]);
478
      fprintf(stderr, "igrt[3][%u] = %g %g %g   %g %g   %g\n", jj,
479
              igrt[3][jj][1], igrt[3][jj][2], igrt[3][jj][3],
480
              igrt[3][jj][4], igrt[3][jj][5], igrt[3][jj][6]);
481
      fprintf(stderr, "(jj=%u) len = %g %g --> (d = %g) "
482
              "update = %g %g %g     %g %g   %g\n",
483
              jj, len02, len24,
484
              TEN_T_DOT(igrt[2][0], update),
485
              update[1], update[2], update[3],
486
              update[4], update[5], update[6]);
487
    }
488
  }
489
  if (rotnoop) {
490
    double avg[7], diff[7], len;
491
    TEN_T_LERP(avg, 0.5, tt[0], tt[4]);
492
    TEN_T_SUB(diff, avg, tt[2]);
493
    for (jj=0; jj<3; jj++) {
494
      len = TEN_T_DOT(igrt[2][jj], diff);
495
      TEN_T_SCALE_INCR(diff, -len, igrt[2][jj]);
496
    }
497
    TEN_T_SCALE_INCR(update, scl*0.2, diff);  /* HEY: scaling is a hack */
498
    if (tip->verbose) {
499
      fprintf(stderr, "(rotnoop) (d = %g) "
500
              "update = %g %g %g     %g %g   %g\n",
501
              TEN_T_DOT(igrt[2][0], update),
502
              update[1], update[2], update[3],
503
              update[4], update[5], update[6]);
504
    }
505
  }
506
  /*
507
  TEN_T_SUB(d02, tt[2], tt[0]);
508
  TEN_T_SUB(d24, tt[4], tt[2]);
509
  len02 = TEN_T_DOT(tng, d02);
510
  len24 = TEN_T_DOT(tng, d24);
511
  correct = (len24 - len02);
512
  TEN_T_SCALE_INCR(update, scl*correct, tng);
513
  */
514
515
  if (!TEN_T_EXISTS(update)) {
516
    biffAddf(TEN, "%s: computed non-existent update (step-size too big?)", me);
517
    return 1;
518
  }
519
520
  TEN_T_ADD(odata + 7*(2*ii + 0), tt[2], update);
521
522
  return 0;
523
}
524
525
void
526
_tenInterpGeoLoxIGRT(double *igrt, double *ten, int useK, int rotNoop,
527
                   double minnorm) {
528
  /* static const char me[]="_tenInterpGeoLoxIGRT"; */
529
  double eval[3], evec[9];
530
531
  if (useK) {
532
    tenInvariantGradientsK_d(igrt + 7*0, igrt + 7*1, igrt + 7*2, ten, minnorm);
533
  } else {
534
    tenInvariantGradientsR_d(igrt + 7*0, igrt + 7*1, igrt + 7*2, ten, minnorm);
535
  }
536
  if (rotNoop) {
537
    /* these shouldn't be used */
538
    TEN_T_SET(igrt + 7*3, 1, AIR_NAN, AIR_NAN, AIR_NAN,
539
              AIR_NAN, AIR_NAN, AIR_NAN);
540
    TEN_T_SET(igrt + 7*4, 1, AIR_NAN, AIR_NAN, AIR_NAN,
541
              AIR_NAN, AIR_NAN, AIR_NAN);
542
    TEN_T_SET(igrt + 7*5, 1, AIR_NAN, AIR_NAN, AIR_NAN,
543
              AIR_NAN, AIR_NAN, AIR_NAN);
544
  } else {
545
    tenEigensolve_d(eval, evec, ten);
546
    tenRotationTangents_d(igrt + 7*3, igrt + 7*4, igrt + 7*5, evec);
547
  }
548
  return;
549
}
550
551
/*
552
** if "doubling" is non-zero, this assumes that the real
553
** vertices are on the even-numbered indices:
554
** (0   1   2   3   4)
555
**  0   2   4   6   8 --> size=9 --> NN=4
556
**    1   3   5   7
557
*/
558
double
559
tenInterpPathLength(Nrrd *ntt, int doubleVerts, int fancy, int shape) {
560
  double *tt, len, diff[7], *tenA, *tenB;
561
  unsigned int ii, NN;
562
563
  tt = AIR_CAST(double *, ntt->data);
564
  if (doubleVerts) {
565
    NN = AIR_CAST(unsigned int, (ntt->axis[1].size-1)/2);
566
  } else {
567
    NN = AIR_CAST(unsigned int, ntt->axis[1].size-1);
568
  }
569
  len = 0;
570
  for (ii=0; ii<NN; ii++) {
571
    if (doubleVerts) {
572
      tenA = tt + 7*2*(ii + 1);
573
      tenB = tt + 7*2*(ii + 0);
574
    } else {
575
      tenA = tt + 7*(ii + 1);
576
      tenB = tt + 7*(ii + 0);
577
    }
578
    TEN_T_SUB(diff, tenA, tenB);
579
    if (fancy) {
580
      double mean[7], igrt[7*6], dot, incr;
581
      unsigned int lo, hi;
582
583
      TEN_T_LERP(mean, 0.5, tenA, tenB);
584
      _tenInterpGeoLoxIGRT(igrt, mean, AIR_FALSE, AIR_FALSE, 0.0);
585
      if (shape) {
586
        lo = 0;
587
        hi = 2;
588
      } else {
589
        lo = 3;
590
        hi = 5;
591
      }
592
      incr = 0;
593
      for (ii=lo; ii<=hi; ii++) {
594
        dot = TEN_T_DOT(igrt + 7*ii, diff);
595
        incr += dot*dot;
596
      }
597
      len += sqrt(incr);
598
    } else {
599
      len += TEN_T_NORM(diff);
600
    }
601
  }
602
  return len;
603
}
604
605
double
606
_tenPathSpacingEqualize(Nrrd *nout, Nrrd *nin) {
607
  /* static const char me[]="_tenPathSpacingEqualize"; */
608
  double *in, *out, len, diff[7],
609
    lenTotal,  /* total length of input */
610
    lenStep,   /* correct length on input polyline between output vertices */
611
    lenIn,     /* length along input processed so far */
612
    lenHere,   /* length of segment associated with current input index */
613
    lenRmn,    /* length along past input segments as yet unmapped to output */
614
    *tenHere, *tenNext;
615
  unsigned int idxIn, idxOut, NN;
616
617
  in = AIR_CAST(double *, nin->data);
618
  out = AIR_CAST(double *, nout->data);
619
  NN = (nin->axis[1].size-1)/2;
620
  lenTotal = tenInterpPathLength(nin, AIR_TRUE, AIR_FALSE, AIR_FALSE);
621
  lenStep = lenTotal/NN;
622
  /*
623
  fprintf(stderr, "!%s: lenTotal/NN = %g/%u = %g = lenStep\n", me,
624
          lenTotal, NN, lenStep);
625
  */
626
  TEN_T_COPY(out + 7*2*(0 + 0), in + 7*2*(0 + 0));
627
  lenIn = lenRmn = 0;
628
  idxOut = 1;
629
  for (idxIn=0; idxIn<NN; idxIn++) {
630
    tenNext = in + 7*2*(idxIn + 1);
631
    tenHere = in + 7*2*(idxIn + 0);
632
    TEN_T_SUB(diff, tenNext, tenHere);
633
    lenHere = TEN_T_NORM(diff);
634
    /*
635
    fprintf(stderr, "!%s(%u): %g + %g >(%s)= %g\n", me, idxIn,
636
            lenRmn, lenHere,
637
            (lenRmn + lenHere >= lenStep ? "yes" : "no"),
638
            lenStep);
639
    */
640
    if (lenRmn + lenHere >= lenStep) {
641
      len = lenRmn + lenHere;
642
      while (len > lenStep) {
643
        len -= lenStep;
644
        /*
645
        fprintf(stderr, "!%s(%u): len = %g -> %g\n", me, idxIn,
646
                len + lenStep, len);
647
        */
648
        TEN_T_AFFINE(out + 7*(2*idxOut + 0),
649
                     lenHere, len, 0, tenHere, tenNext);
650
        /*
651
        fprintf(stderr, "!%s(%u): out[%u] ~ %g\n", me, idxIn, idxOut,
652
                AIR_AFFINE(lenHere, len, 0, 0, 1));
653
        */
654
        idxOut++;
655
      }
656
      lenRmn = len;
657
    } else {
658
      lenRmn += lenHere;
659
      /*
660
      fprintf(stderr, "!%s(%u):   (==> lenRmn = %g -> %g)\n", me, idxIn,
661
              lenRmn - lenHere, lenRmn);
662
      */
663
    }
664
    /* now lenRmn < lenStep */
665
    lenIn += lenHere;
666
  }
667
  /* copy very last one in case we didn't get to it somehow */
668
  TEN_T_COPY(out + 7*2*(NN + 0), in + 7*2*(NN + 0));
669
670
  /* fill in vertex mid-points */
671
  for (idxOut=0; idxOut<NN; idxOut++) {
672
    TEN_T_LERP(out + 7*(2*idxOut + 1),
673
               0.5, out + 7*(2*idxOut + 0), out + 7*(2*idxOut + 2));
674
  }
675
  return lenTotal;
676
}
677
678
int
679
_tenInterpGeoLoxPolyLine(Nrrd *ngeod, unsigned int *numIter,
680
                         const double tenA[7], const double tenB[7],
681
                         unsigned int NN, int useK, int rotnoop,
682
                         tenInterpParm *tip) {
683
  static const char me[]="_tenInterpGeoLoxPolyLine";
684
  Nrrd *nigrt, *ntt, *nss, *nsub;
685
  double *igrt, *geod, *tt, len, newlen;
686
  unsigned int ii;
687
  airArray *mop;
688
689
  if (!(ngeod && numIter && tenA && tenB)) {
690
    biffAddf(TEN, "%s: got NULL pointer", me);
691
    return 1;
692
  }
693
  if (!(NN >= 2)) {
694
    biffAddf(TEN, "%s: # steps %u too small", me, NN);
695
    return 1;
696
  }
697
698
  mop = airMopNew();
699
  ntt = nrrdNew();
700
  airMopAdd(mop, ntt, (airMopper)nrrdNuke, airMopAlways);
701
  nss = nrrdNew();
702
  airMopAdd(mop, nss, (airMopper)nrrdNuke, airMopAlways);
703
  nigrt = nrrdNew();
704
  airMopAdd(mop, nigrt, (airMopper)nrrdNuke, airMopAlways);
705
  nsub = nrrdNew();
706
  airMopAdd(mop, nsub, (airMopper)nrrdNuke, airMopAlways);
707
  if (nrrdMaybeAlloc_va(ngeod, nrrdTypeDouble, 2,
708
                        AIR_CAST(size_t, 7),
709
                        AIR_CAST(size_t, NN+1))
710
      || nrrdMaybeAlloc_va(ntt, nrrdTypeDouble, 2,
711
                           AIR_CAST(size_t, 7),
712
                           AIR_CAST(size_t, 2*NN + 1))
713
      || nrrdMaybeAlloc_va(nigrt, nrrdTypeDouble, 3,
714
                           AIR_CAST(size_t, 7),
715
                           AIR_CAST(size_t, 6),
716
                           AIR_CAST(size_t, 2*NN + 1))) {
717
    biffMovef(TEN, NRRD, "%s: couldn't allocate output", me);
718
    airMopError(mop); return 1;
719
  }
720
  geod = AIR_CAST(double *, ngeod->data);
721
  tt = AIR_CAST(double *, ntt->data);
722
  igrt = AIR_CAST(double *, nigrt->data);
723
724
  *numIter = 0;
725
  if (NN > 14 && tip->enableRecurse) {
726
    unsigned int subIter;
727
    int E;
728
    NrrdResampleContext *rsmc;
729
    double kparm[3] = {1.0, 0.0, 0.5};
730
    /* recurse and find geodesic with smaller number of vertices */
731
    if (_tenInterpGeoLoxPolyLine(nsub, &subIter, tenA, tenB,
732
                                 NN/2, useK, rotnoop, tip)) {
733
      biffAddf(TEN, "%s: problem with recursive call", me);
734
      airMopError(mop); return 1;
735
    }
736
    /* upsample coarse geodesic to higher resolution */
737
    rsmc = nrrdResampleContextNew();
738
    airMopAdd(mop, rsmc, (airMopper)nrrdResampleContextNix, airMopAlways);
739
    E = AIR_FALSE;
740
    if (!E) E |= nrrdResampleDefaultCenterSet(rsmc, nrrdCenterNode);
741
    if (!E) E |= nrrdResampleInputSet(rsmc, nsub);
742
    if (!E) E |= nrrdResampleKernelSet(rsmc, 0, NULL, NULL);
743
    if (!E) E |= nrrdResampleKernelSet(rsmc, 1, nrrdKernelTent, kparm);
744
    if (!E) E |= nrrdResampleSamplesSet(rsmc, 1, 2*NN + 1);
745
    if (!E) E |= nrrdResampleRangeFullSet(rsmc, 1);
746
    if (!E) E |= nrrdResampleBoundarySet(rsmc, nrrdBoundaryBleed);
747
    if (!E) E |= nrrdResampleTypeOutSet(rsmc, nrrdTypeDefault);
748
    if (!E) E |= nrrdResampleRenormalizeSet(rsmc, AIR_TRUE);
749
    if (!E) E |= nrrdResampleExecute(rsmc, ntt);
750
    if (E) {
751
      biffMovef(TEN, NRRD, "%s: problem upsampling course solution", me);
752
      airMopError(mop); return 1;
753
    }
754
    *numIter += subIter;
755
  } else {
756
    /* initialize the path, including all the segment midpoints */
757
    for (ii=0; ii<=2*NN; ii++) {
758
      TEN_T_AFFINE(tt + 7*ii, 0, ii, 2*NN, tenA, tenB);
759
    }
760
  }
761
  for (ii=0; ii<=2*NN; ii++) {
762
    _tenInterpGeoLoxIGRT(igrt + 7*6*ii, tt + 7*ii, useK, rotnoop,
763
                         tip->minNorm);
764
  }
765
  nrrdCopy(nss, ntt);
766
767
  newlen = tenInterpPathLength(ntt, AIR_TRUE, AIR_FALSE, AIR_FALSE);
768
  do {
769
    unsigned int lo, hi;
770
    int dd;
771
    len = newlen;
772
    if (0 == *numIter % 2) {
773
      lo = 1;
774
      hi = NN;
775
      dd = 1;
776
    } else {
777
      lo = NN-1;
778
      hi = 0;
779
      dd = -1;
780
    }
781
    if (tip->verbose) {
782
      fprintf(stderr, "%s: ======= iter = %u (NN=%u)\n", me, *numIter, NN);
783
    }
784
    for (ii=lo; ii!=hi; ii+=dd) {
785
      double sclHack;
786
      sclHack = ii*4.0/NN - ii*ii*4.0/NN/NN;
787
      if (_tenInterpGeoLoxRelaxOne(nss, ntt, nigrt, ii, rotnoop,
788
                                   sclHack*tip->convStep, tip)) {
789
        biffAddf(TEN, "%s: problem on vert %u, iter %u\n", me, ii, *numIter);
790
        return 1;
791
      }
792
    }
793
    newlen = _tenPathSpacingEqualize(ntt, nss);
794
    /* try doing this less often */
795
    for (ii=0; ii<=2*NN; ii++) {
796
      _tenInterpGeoLoxIGRT(igrt + 7*6*ii, tt + 7*ii, useK, rotnoop,
797
                           tip->minNorm);
798
    }
799
    *numIter += 1;
800
  } while ((0 == tip->maxIter || *numIter < tip->maxIter)
801
           && 2*AIR_ABS(newlen - len)/(newlen + len) > tip->convEps);
802
803
  /* copy final result to output */
804
  for (ii=0; ii<=NN; ii++) {
805
    TEN_T_COPY(geod + 7*ii, tt + 7*2*ii);
806
  }
807
  /* values from outer-most recursion will stick */
808
  tip->numIter = *numIter;
809
  tip->convFinal = 2*AIR_ABS(newlen - len)/(newlen + len);
810
811
  airMopOkay(mop);
812
  return 0;
813
}
814
815
int
816
tenInterpTwoDiscrete_d(Nrrd *nout,
817
                       const double tenA[7], const double tenB[7],
818
                       int ptype, unsigned int num,
819
                       tenInterpParm *_tip) {
820
  static const char me[]="tenInterpTwoDiscrete_d";
821
  double *out;
822
  unsigned int ii;
823
  airArray *mop;
824
  tenInterpParm *tip;
825
826
  if (!nout) {
827
    biffAddf(TEN, "%s: got NULL pointer", me);
828
    return 1;
829
  }
830
  if (airEnumValCheck(tenInterpType, ptype)) {
831
    biffAddf(TEN, "%s: path type %d not a valid %s", me, ptype,
832
            tenInterpType->name);
833
    return 1;
834
  }
835
836
  mop = airMopNew();
837
  if (_tip) {
838
    tip = _tip;
839
  } else {
840
    tip = tenInterpParmNew();
841
    airMopAdd(mop, tip, (airMopper)tenInterpParmNix, airMopAlways);
842
  }
843
  if (!( num >= 2 )) {
844
    biffAddf(TEN, "%s: need num >= 2 (not %u)", me, num);
845
    airMopError(mop); return 1;
846
  }
847
  if (nrrdMaybeAlloc_va(nout, nrrdTypeDouble, 2,
848
                        AIR_CAST(size_t, 7),
849
                        AIR_CAST(size_t, num))) {
850
    biffMovef(TEN, NRRD, "%s: trouble allocating output", me);
851
    airMopError(mop); return 1;
852
  }
853
  out = AIR_CAST(double *, nout->data);
854
855
  if (ptype == tenInterpTypeLinear
856
      || ptype == tenInterpTypeLogLinear
857
      || ptype == tenInterpTypeAffineInvariant
858
      || ptype == tenInterpTypeWang
859
      || ptype == tenInterpTypeQuatGeoLoxK
860
      || ptype == tenInterpTypeQuatGeoLoxR
861
      || ptype == tenInterpTypeRThetaPhiLinear) {
862
    /* we have fast ways of doing interpolation
863
       between two tensors for these path types */
864
    for (ii=0; ii<num; ii++) {
865
      /* yes, this is often doing a lot of needless recomputations. */
866
      tenInterpTwo_d(out + 7*ii, tenA, tenB,
867
                     ptype, (double)ii/(num-1), tip);
868
    }
869
  } else if (ptype == tenInterpTypeGeoLoxK
870
             || ptype == tenInterpTypeGeoLoxR
871
             || ptype == tenInterpTypeLoxK
872
             || ptype == tenInterpTypeLoxR) {
873
    /* we have slow iterative code for these */
874
    unsigned int numIter;
875
    int useK, rotnoop;
876
877
    useK = (tenInterpTypeGeoLoxK == ptype
878
            || tenInterpTypeLoxK == ptype);
879
    rotnoop = (tenInterpTypeGeoLoxK == ptype
880
               || tenInterpTypeGeoLoxR == ptype);
881
    fprintf(stderr, "!%s: useK = %d, rotnoop = %d\n", me, useK, rotnoop);
882
    if (_tenInterpGeoLoxPolyLine(nout, &numIter,
883
                                 tenA, tenB,
884
                                 num, useK, rotnoop, tip)) {
885
      biffAddf(TEN, "%s: trouble finding path", me);
886
      airMopError(mop); return 1;
887
    }
888
  } else {
889
    biffAddf(TEN, "%s: sorry, interp for path %s not implemented", me,
890
             airEnumStr(tenInterpType, ptype));
891
    airMopError(mop); return 1;
892
  }
893
894
  airMopOkay(mop);
895
  return 0;
896
}
897
898
double
899
tenInterpDistanceTwo_d(const double tenA[7], const double tenB[7],
900
                    int ptype, tenInterpParm *_tip) {
901
  static const char me[]="tenInterpDistanceTwo_d";
902
  char *err;
903
  tenInterpParm *tip;
904
  airArray *mop;
905
  double ret, diff[7], logA[7], logB[7], invA[7], det, siA[7],
906
    mat1[9], mat2[9], mat3[9], logDiff[7];
907
  Nrrd *npath;
908
909
  if (!( tenA && tenB && !airEnumValCheck(tenInterpType, ptype) )) {
910
    return AIR_NAN;
911
  }
912
913
  mop = airMopNew();
914
  switch (ptype) {
915
  case tenInterpTypeLinear:
916
    TEN_T_SUB(diff, tenA, tenB);
917
    ret = TEN_T_NORM(diff);
918
    break;
919
  case tenInterpTypeLogLinear:
920
    tenLogSingle_d(logA, tenA);
921
    tenLogSingle_d(logB, tenB);
922
    TEN_T_SUB(diff, logA, logB);
923
    ret = TEN_T_NORM(diff);
924
    break;
925
  case tenInterpTypeAffineInvariant:
926
    TEN_T_INV(invA, tenA, det);
927
    tenSqrtSingle_d(siA, invA);
928
    TEN_T2M(mat1, tenB);
929
    TEN_T2M(mat2, siA);
930
    ell_3m_mul_d(mat3, mat1, mat2);
931
    ell_3m_mul_d(mat1, mat2, mat3);
932
    TEN_M2T(diff, mat1);
933
    tenLogSingle_d(logDiff, diff);
934
    ret = TEN_T_NORM(logDiff);
935
    break;
936
  case tenInterpTypeGeoLoxK:
937
  case tenInterpTypeGeoLoxR:
938
  case tenInterpTypeLoxK:
939
  case tenInterpTypeLoxR:
940
  case tenInterpTypeQuatGeoLoxK:
941
  case tenInterpTypeQuatGeoLoxR:
942
    npath = nrrdNew();
943
    airMopAdd(mop, npath, (airMopper)nrrdNuke, airMopAlways);
944
    if (_tip) {
945
      tip = _tip;
946
    } else {
947
      tip = tenInterpParmNew();
948
      airMopAdd(mop, tip, (airMopper)tenInterpParmNix, airMopAlways);
949
    }
950
    if (tenInterpTwoDiscrete_d(npath, tenA, tenB, ptype,
951
                               tip->numSteps, tip)) {
952
      airMopAdd(mop, err = biffGetDone(TEN), airFree, airMopAlways);
953
      fprintf(stderr, "%s: trouble computing path:\n%s\n", me, err);
954
      airMopError(mop); return AIR_NAN;
955
    }
956
    ret = tenInterpPathLength(npath, AIR_FALSE, AIR_FALSE, AIR_FALSE);
957
    if (tip->lengthFancy) {
958
      tip->lengthShape = tenInterpPathLength(npath, AIR_FALSE,
959
                                             AIR_TRUE, AIR_TRUE);
960
      tip->lengthOrient = tenInterpPathLength(npath, AIR_FALSE,
961
                                              AIR_TRUE, AIR_FALSE);
962
    }
963
    break;
964
  case tenInterpTypeWang:
965
  default:
966
    fprintf(stderr, "%s: unimplemented %s %d!!!!\n", me,
967
            tenInterpType->name, ptype);
968
    ret = AIR_NAN;
969
    break;
970
  }
971
972
  airMopOkay(mop);
973
  return ret;
974
}
975
976
/*
977
** actually, the input nrrds don't have to be 3D ...
978
*/
979
int
980
tenInterpMulti3D(Nrrd *nout, const Nrrd *const *nin, const double *wght,
981
                 unsigned int ninLen, int ptype, tenInterpParm *_tip) {
982
  static const char me[]="tenInterpMulti3D";
983
  unsigned int ninIdx;
984
  size_t II, NN;
985
  double (*lup)(const void *, size_t), (*ins)(void *, size_t, double),
986
    *tbuff;
987
  tenInterpParm *tip;
988
  airArray *mop;
989
990
  /* allow NULL wght, to signify equal weighting */
991
  if (!(nout && nin)) {
992
    biffAddf(TEN, "%s: got NULL pointer", me);
993
    return 1;
994
  }
995
  if (!( ninLen > 0 )) {
996
    biffAddf(TEN, "%s: need at least 1 nin, not 0", me);
997
    return 1;
998
  }
999
  if (airEnumValCheck(tenInterpType, ptype)) {
1000
    biffAddf(TEN, "%s: invalid %s %d", me,
1001
             tenInterpType->name, ptype);
1002
    return 1;
1003
  }
1004
1005
  if (tenTensorCheck(nin[0], nrrdTypeDefault, AIR_FALSE, AIR_TRUE)) {
1006
    biffAddf(TEN, "%s: first nrrd not a tensor array", me);
1007
    return 1;
1008
  }
1009
  if (!( nrrdTypeFloat == nin[0]->type ||
1010
         nrrdTypeDouble == nin[0]->type )) {
1011
    biffAddf(TEN, "%s: need type %s or %s (not %s) in first nrrd", me,
1012
             airEnumStr(nrrdType, nrrdTypeFloat),
1013
             airEnumStr(nrrdType, nrrdTypeDouble),
1014
             airEnumStr(nrrdType, nin[0]->type));
1015
    return 1;
1016
  }
1017
  for (ninIdx=1; ninIdx<ninLen; ninIdx++) {
1018
    if (tenTensorCheck(nin[ninIdx], nrrdTypeDefault, AIR_FALSE, AIR_TRUE)) {
1019
      biffAddf(TEN, "%s: nin[%u] not a tensor array", me, ninIdx);
1020
      return 1;
1021
    }
1022
    if (!nrrdSameSize(nin[0], nin[ninIdx], AIR_TRUE)) {
1023
      biffMovef(TEN, NRRD, "%s: nin[0] doesn't match nin[%u]", me, ninIdx);
1024
      return 1;
1025
    }
1026
    if (nin[0]->type != nin[ninIdx]->type) {
1027
      biffAddf(TEN, "%s: nin[0] type (%s) != nin[%u] type (%s)", me,
1028
               airEnumStr(nrrdType, nin[0]->type),
1029
               ninIdx, airEnumStr(nrrdType, nin[ninIdx]->type));
1030
      return 1;
1031
    }
1032
  }
1033
1034
  mop = airMopNew();
1035
  if (nrrdCopy(nout, nin[0])) {
1036
    biffMovef(TEN, NRRD, "%s: couldn't initialize output", me);
1037
    airMopError(mop); return 1;
1038
  }
1039
  if (_tip) {
1040
    tip = _tip;
1041
  } else {
1042
    tip = tenInterpParmNew();
1043
    airMopAdd(mop, tip, (airMopper)tenInterpParmNix, airMopAlways);
1044
  }
1045
  tbuff = AIR_CAST(double *, calloc(7*ninLen, sizeof(double)));
1046
  if (!tbuff) {
1047
    biffAddf(TEN, "%s: couldn't allocate tensor buff", me);
1048
    airMopError(mop); return 1;
1049
  }
1050
  ins = nrrdDInsert[nin[0]->type];
1051
  lup = nrrdDLookup[nin[0]->type];
1052
  NN = nrrdElementNumber(nin[0])/7;
1053
  for (II=0; II<NN; II++) {
1054
    double tenOut[7];
1055
    unsigned int tt;
1056
    for (ninIdx=0; ninIdx<ninLen; ninIdx++) {
1057
      for (tt=0; tt<7; tt++) {
1058
        tbuff[tt + 7*ninIdx] = lup(nin[ninIdx]->data, tt + 7*II);
1059
      }
1060
    }
1061
    if (tenInterpN_d(tenOut, tbuff, wght, ninLen, ptype, tip)) {
1062
      char stmp[AIR_STRLEN_SMALL];
1063
      biffAddf(TEN, "%s: trouble on sample %s", me,
1064
               airSprintSize_t(stmp, II));
1065
      airMopError(mop); return 1;
1066
    }
1067
    for (tt=0; tt<7; tt++) {
1068
      ins(nout->data, tt + 7*II, tenOut[tt]);
1069
    }
1070
  }
1071
1072
  airMopOkay(mop);
1073
  return 0;
1074
}