GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/nrrd/axis.c Lines: 237 626 37.9 %
Date: 2017-05-26 Branches: 158 543 29.1 %

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
#include "nrrd.h"
25
#include "privateNrrd.h"
26
27
/* ------------------------------------------------------------ */
28
29
void
30
_nrrdAxisInfoInit(NrrdAxisInfo *axis) {
31
  int dd;
32
33
78640
  if (axis) {
34
39320
    axis->size = 0;
35
39320
    axis->spacing = axis->thickness = AIR_NAN;
36
39320
    axis->min = axis->max = AIR_NAN;
37
707760
    for (dd=0; dd<NRRD_SPACE_DIM_MAX; dd++) {
38
314560
      axis->spaceDirection[dd] = AIR_NAN;
39
    }
40
39320
    axis->center = nrrdCenterUnknown;
41
39320
    axis->kind = nrrdKindUnknown;
42
39320
    axis->label = (char *)airFree(axis->label);
43
39320
    axis->units = (char *)airFree(axis->units);
44
39320
  }
45
39320
}
46
47
void
48
_nrrdAxisInfoNewInit(NrrdAxisInfo *axis) {
49
50
19456
  if (axis) {
51
9728
    axis->label = NULL;
52
9728
    axis->units = NULL;
53
9728
    _nrrdAxisInfoInit(axis);
54
9728
  }
55
9728
}
56
57
/* ------------------------------------------------------------ */
58
59
/*
60
******** nrrdKindIsDomain
61
**
62
** returns non-zero for kinds (from nrrdKind* enum) that are domain
63
** axes, or independent variable axes, or resample-able axes, all
64
** different ways of describing the same thing
65
*/
66
int
67
nrrdKindIsDomain(int kind) {
68
69
168
  return (nrrdKindDomain == kind
70
56
          || nrrdKindSpace == kind
71
88
          || nrrdKindTime == kind);
72
}
73
74
/*
75
******** nrrdKindSize
76
**
77
** returns suggested size (length) of an axis with the given kind, or,
78
** 0 if either (1) there is no suggested size because the axis is the
79
** kind of an independent or domain variable or (2) the kind is invalid
80
*/
81
unsigned int
82
nrrdKindSize(int kind) {
83
  static const char me[]="nrrdKindSize";
84
  unsigned int ret;
85
86
20170
  if (!( AIR_IN_OP(nrrdKindUnknown, kind, nrrdKindLast) )) {
87
    /* they gave us invalid or unknown kind */
88
3875
    return 0;
89
  }
90
91







6210
  switch (kind) {
92
  case nrrdKindDomain:
93
  case nrrdKindSpace:
94
  case nrrdKindTime:
95
  case nrrdKindList:
96
  case nrrdKindPoint:
97
  case nrrdKindVector:
98
  case nrrdKindCovariantVector:
99
  case nrrdKindNormal:
100
    ret = 0;
101
6090
    break;
102
  case nrrdKindStub:
103
  case nrrdKindScalar:
104
    ret = 1;
105
8
    break;
106
  case nrrdKindComplex:
107
  case nrrdKind2Vector:
108
    ret = 2;
109
    break;
110
  case nrrdKind3Color:
111
  case nrrdKindRGBColor:
112
  case nrrdKindHSVColor:
113
  case nrrdKindXYZColor:
114
    ret = 3;
115
    break;
116
  case nrrdKind4Color:
117
  case nrrdKindRGBAColor:
118
    ret = 4;
119
    break;
120
  case nrrdKind3Vector:
121
  case nrrdKind3Normal:
122
    ret = 3;
123
    break;
124
  case nrrdKind4Vector:
125
  case nrrdKindQuaternion:
126
    ret = 4;
127
    break;
128
  case nrrdKind2DSymMatrix:
129
    ret = 3;
130
    break;
131
  case nrrdKind2DMaskedSymMatrix:
132
    ret = 4;
133
    break;
134
  case nrrdKind2DMatrix:
135
    ret = 4;
136
    break;
137
  case nrrdKind2DMaskedMatrix:
138
    ret = 5;
139
    break;
140
  case nrrdKind3DSymMatrix:
141
    ret = 6;
142
16
    break;
143
  case nrrdKind3DMaskedSymMatrix:
144
    ret = 7;
145
96
    break;
146
  case nrrdKind3DMatrix:
147
    ret = 9;
148
    break;
149
  case nrrdKind3DMaskedMatrix:
150
    ret = 10;
151
    break;
152
  default:
153
    fprintf(stderr, "%s: PANIC: nrrdKind %d not implemented!\n", me, kind);
154
    ret = UINT_MAX;
155
  }
156
157
6210
  return ret;
158
10085
}
159
160
/*
161
** _nrrdKindAltered:
162
**
163
** implements logic for how kind should be updated when samples
164
** along the axis are altered
165
*/
166
int
167
_nrrdKindAltered(int kindIn, int resampling) {
168
  int kindOut;
169
170
112
  if (nrrdStateKindNoop) {
171
    kindOut = nrrdKindUnknown;
172
    /* HEY: setting the kindOut to unknown is arguably not a no-op.
173
       It is more like pointedly and stubbornly simplistic. So maybe
174
       nrrdStateKindNoop could be renamed .. */
175
  } else {
176
56
    if (nrrdKindIsDomain(kindIn)
177

88
        || (0 == nrrdKindSize(kindIn) && !resampling)) {
178
      kindOut = kindIn;
179
32
    } else {
180
      kindOut = nrrdKindUnknown;
181
    }
182
  }
183
56
  return kindOut;
184
}
185
186
/*
187
** _nrrdAxisInfoCopy
188
**
189
** HEY: we have a void return even though this function potentially
190
** involves calling airStrdup!!
191
*/
192
void
193
_nrrdAxisInfoCopy(NrrdAxisInfo *dest, const NrrdAxisInfo *src, int bitflag) {
194
  int ii;
195
196
2264
  if (!(NRRD_AXIS_INFO_SIZE_BIT & bitflag)) {
197
676
    dest->size = src->size;
198
676
  }
199
1132
  if (!(NRRD_AXIS_INFO_SPACING_BIT & bitflag)) {
200
1132
    dest->spacing = src->spacing;
201
1132
  }
202
1132
  if (!(NRRD_AXIS_INFO_THICKNESS_BIT & bitflag)) {
203
1132
    dest->thickness = src->thickness;
204
1132
  }
205
1132
  if (!(NRRD_AXIS_INFO_MIN_BIT & bitflag)) {
206
1020
    dest->min = src->min;
207
1020
  }
208
1132
  if (!(NRRD_AXIS_INFO_MAX_BIT & bitflag)) {
209
1020
    dest->max = src->max;
210
1020
  }
211
1132
  if (!(NRRD_AXIS_INFO_SPACEDIRECTION_BIT & bitflag)) {
212
20376
    for (ii=0; ii<NRRD_SPACE_DIM_MAX; ii++) {
213
9056
      dest->spaceDirection[ii] = src->spaceDirection[ii];
214
    }
215
  }
216
1132
  if (!(NRRD_AXIS_INFO_CENTER_BIT & bitflag)) {
217
1132
    dest->center = src->center;
218
1132
  }
219
1132
  if (!(NRRD_AXIS_INFO_KIND_BIT & bitflag)) {
220
1132
    dest->kind = src->kind;
221
1132
  }
222
1132
  if (!(NRRD_AXIS_INFO_LABEL_BIT & bitflag)) {
223
1132
    if (dest->label != src->label) {
224
4
      dest->label = (char *)airFree(dest->label);
225
4
      dest->label = (char *)airStrdup(src->label);
226
4
    }
227
  }
228
1132
  if (!(NRRD_AXIS_INFO_UNITS_BIT & bitflag)) {
229
1132
    if (dest->units != src->units) {
230
      dest->units = (char *)airFree(dest->units);
231
      dest->units = (char *)airStrdup(src->units);
232
    }
233
  }
234
235
  return;
236
1132
}
237
238
/*
239
******** nrrdAxisInfoCopy()
240
**
241
** For copying all the per-axis peripheral information.  Takes a
242
** permutation "map"; map[d] tells from which axis in input should the
243
** output axis d copy its information.  The length of this permutation
244
** array is nout->dim.  If map is NULL, the identity permutation is
245
** assumed.  If map[i]==-1 for any i in [0,dim-1], then nothing is
246
** copied into axis i of output.  The "bitflag" field controls which
247
** per-axis fields will NOT be copied; if bitflag==0, then all fields
248
** are copied.  The value of bitflag should be |'s of NRRD_AXIS_INFO_*
249
** defines.
250
**
251
** Decided to Not use Biff, since many times map will be NULL, in
252
** which case the only error is getting a NULL nrrd, or an invalid map
253
** permutation, which will probably be unlikely given the contexts in
254
** which this is called.  For the paranoid, the integer return value
255
** indicates error.
256
**
257
** Sun Feb 27 21:12:57 EST 2005: decided to allow nout==nin, so now
258
** use a local array of NrrdAxisInfo as buffer.
259
*/
260
int
261
nrrdAxisInfoCopy(Nrrd *nout, const Nrrd *nin, const int *axmap, int bitflag) {
262
782
  NrrdAxisInfo axisBuffer[NRRD_DIM_MAX];
263
  const NrrdAxisInfo *axis;
264
  unsigned int from, axi;
265
266
391
  if (!(nout && nin)) {
267
    return 1;
268
  }
269
391
  if (axmap) {
270
1552
    for (axi=0; axi<nout->dim; axi++) {
271
584
      if (-1 == axmap[axi]) {
272
        continue;
273
      }
274

1152
      if (!AIR_IN_CL(0, axmap[axi], (int)nin->dim-1)) {
275
        return 3;
276
      }
277
    }
278
  }
279
391
  if (nout == nin) {
280
    /* copy axis info to local buffer */
281
    for (axi=0; axi<nin->dim; axi++) {
282
      _nrrdAxisInfoNewInit(axisBuffer + axi);
283
      _nrrdAxisInfoCopy(axisBuffer + axi, nin->axis + axi, bitflag);
284
    }
285
    axis = axisBuffer;
286
  } else {
287
391
    axis = nin->axis;
288
  }
289
3014
  for (axi=0; axi<nout->dim; axi++) {
290

1700
    if (axmap && -1 == axmap[axi]) {
291
      /* for this axis, we don't touch a thing */
292
      continue;
293
    }
294
2792
    from = axmap ? (unsigned int)axmap[axi] : axi;
295
1108
    _nrrdAxisInfoCopy(nout->axis + axi, axis + from, bitflag);
296
1108
  }
297
391
  if (nout == nin) {
298
    /* free dynamically allocated stuff */
299
    for (axi=0; axi<nin->dim; axi++) {
300
      _nrrdAxisInfoInit(axisBuffer + axi);
301
    }
302
  }
303
391
  return 0;
304
391
}
305
306
/*
307
******** nrrdAxisInfoSet_nva()
308
**
309
** Simple means of setting fields of the axis array in the nrrd.
310
**
311
** type to pass for third argument:
312
**           nrrdAxisInfoSize: size_t*
313
**        nrrdAxisInfoSpacing: double*
314
**      nrrdAxisInfoThickness: double*
315
**            nrrdAxisInfoMin: double*
316
**            nrrdAxisInfoMax: double*
317
** nrrdAxisInfoSpaceDirection: double (*var)[NRRD_SPACE_DIM_MAX]
318
**         nrrdAxisInfoCenter: int*
319
**           nrrdAxisInfoKind: int*
320
**          nrrdAxisInfoLabel: char**
321
**          nrrdAxisInfoUnits: char**
322
**
323
** Note that in the case of nrrdAxisInfoSpaceDirection, we only access
324
** spaceDim elements of info.V[ai] (so caller can allocate it for less
325
** than NRRD_SPACE_DIM_MAX if they know what they're doing)
326
*/
327
void
328
nrrdAxisInfoSet_nva(Nrrd *nrrd, int axInfo, const void *_info) {
329
  _nrrdAxisInfoSetPtrs info;
330
  int exists;
331
  unsigned int ai, si, minsi;
332
333
1536
  if (!( nrrd
334

2304
         && AIR_IN_CL(1, nrrd->dim, NRRD_DIM_MAX)
335

1536
         && AIR_IN_OP(nrrdAxisInfoUnknown, axInfo, nrrdAxisInfoLast)
336
768
         && _info )) {
337
    return;
338
  }
339
768
  info.P = _info;
340
341
5656
  for (ai=0; ai<nrrd->dim; ai++) {
342


4120
    switch (axInfo) {
343
    case nrrdAxisInfoSize:
344
1766
      nrrd->axis[ai].size = info.ST[ai];
345
1766
      break;
346
    case nrrdAxisInfoSpacing:
347
      nrrd->axis[ai].spacing = info.D[ai];
348
      break;
349
    case nrrdAxisInfoThickness:
350
      nrrd->axis[ai].thickness = info.D[ai];
351
      break;
352
    case nrrdAxisInfoMin:
353
6
      nrrd->axis[ai].min = info.D[ai];
354
6
      break;
355
    case nrrdAxisInfoMax:
356
6
      nrrd->axis[ai].max = info.D[ai];
357
6
      break;
358
    case nrrdAxisInfoSpaceDirection:
359
      /* we won't allow setting an invalid direction */
360
125
      exists = AIR_EXISTS(info.V[ai][0]);
361
125
      minsi = nrrd->spaceDim;
362
1000
      for (si=0; si<nrrd->spaceDim; si++) {
363
375
        nrrd->axis[ai].spaceDirection[si] = info.V[ai][si];
364
375
        if (exists ^ AIR_EXISTS(info.V[ai][si])) {
365
          minsi = 0;
366
          break;
367
        }
368
      }
369
1500
      for (si=minsi; si<NRRD_SPACE_DIM_MAX; si++) {
370
625
        nrrd->axis[ai].spaceDirection[si] = AIR_NAN;
371
      }
372
      break;
373
    case nrrdAxisInfoCenter:
374
122
      nrrd->axis[ai].center = info.I[ai];
375
122
      break;
376
    case nrrdAxisInfoKind:
377
32
      nrrd->axis[ai].kind = info.I[ai];
378
32
      break;
379
    case nrrdAxisInfoLabel:
380
3
      nrrd->axis[ai].label = (char *)airFree(nrrd->axis[ai].label);
381
3
      nrrd->axis[ai].label = (char *)airStrdup(info.CP[ai]);
382
3
      break;
383
    case nrrdAxisInfoUnits:
384
      nrrd->axis[ai].units = (char *)airFree(nrrd->axis[ai].units);
385
      nrrd->axis[ai].units = (char *)airStrdup(info.CP[ai]);
386
      break;
387
    }
388
  }
389
768
  if (nrrdAxisInfoSpaceDirection == axInfo) {
390
1076
    for (ai=nrrd->dim; ai<NRRD_DIM_MAX; ai++) {
391
8982
      for (si=0; si<NRRD_SPACE_DIM_MAX; si++) {
392
3992
        nrrd->axis[ai].spaceDirection[si] = AIR_NAN;
393
      }
394
    }
395
  }
396
768
  return;
397
768
}
398
399
/*
400
******** nrrdAxisInfoSet_va()
401
**
402
** var args front-end for nrrdAxisInfoSet_nva
403
**
404
** types to pass, one for each dimension:
405
**           nrrdAxisInfoSize: size_t
406
**        nrrdAxisInfoSpacing: double
407
**      nrrdAxisInfoThickness: double
408
**            nrrdAxisInfoMin: double
409
**            nrrdAxisInfoMax: double
410
** nrrdAxisInfoSpaceDirection: double*
411
**         nrrdAxisInfoCenter: int
412
**           nrrdAxisInfoKind: int
413
**          nrrdAxisInfoLabel: char*
414
**          nrrdAxisInfoUnits: char*
415
*/
416
void
417
nrrdAxisInfoSet_va(Nrrd *nrrd, int axInfo, ...) {
418
174
  NRRD_TYPE_BIGGEST *buffer[NRRD_DIM_MAX];
419
  _nrrdAxisInfoSetPtrs info;
420
  unsigned int ai, si;
421
87
  va_list ap;
422
87
  double *dp, svec[NRRD_DIM_MAX][NRRD_SPACE_DIM_MAX];
423
424
87
  if (!( nrrd
425

261
         && AIR_IN_CL(1, nrrd->dim, NRRD_DIM_MAX)
426
87
         && AIR_IN_OP(nrrdAxisInfoUnknown, axInfo, nrrdAxisInfoLast) )) {
427
    return;
428
  }
429
430
87
  info.P = buffer;
431
87
  va_start(ap, axInfo);
432
740
  for (ai=0; ai<nrrd->dim; ai++) {
433


566
    switch (axInfo) {
434
    case nrrdAxisInfoSize:
435
12
      info.ST[ai] = va_arg(ap, size_t);
436
      /*
437
      printf("!%s: got int[%d] = %d\n", "nrrdAxisInfoSet", d, info.I[ai]);
438
      */
439
4
      break;
440
    case nrrdAxisInfoSpaceDirection:
441
366
      dp = va_arg(ap, double*);  /* punting on using info enum */
442
      /*
443
      printf("!%s: got dp = %lu\n", "nrrdAxisInfoSet",
444
             (unsigned long)(dp));
445
      */
446
976
      for (si=0; si<nrrd->spaceDim; si++) {
447
        /* nrrd->axis[ai].spaceDirection[si] = dp[si]; */
448
366
        svec[ai][si] = dp[si];
449
      }
450
1464
      for (si=nrrd->spaceDim; si<NRRD_SPACE_DIM_MAX; si++) {
451
        /* nrrd->axis[ai].spaceDirection[si] = AIR_NAN; */
452
610
        svec[ai][si] = dp[si];
453
      }
454
      break;
455
    case nrrdAxisInfoCenter:
456
    case nrrdAxisInfoKind:
457
462
      info.I[ai] = va_arg(ap, int);
458
      /*
459
      printf("!%s: got int[%d] = %d\n",
460
             "nrrdAxisInfoSet", d, info.I[ai]);
461
      */
462
154
      break;
463
    case nrrdAxisInfoSpacing:
464
    case nrrdAxisInfoThickness:
465
    case nrrdAxisInfoMin:
466
    case nrrdAxisInfoMax:
467
      info.D[ai] = va_arg(ap, double);
468
      /*
469
      printf("!%s: got double[%d] = %g\n",
470
             "nrrdAxisInfoSet", d, info.D[ai]);
471
      */
472
      break;
473
    case nrrdAxisInfoLabel:
474
      /* we DO NOT do the airStrdup() here because this pointer value is
475
         just going to be handed to nrrdAxisInfoSet_nva(), which WILL do the
476
         airStrdup(); we're not violating the rules for axis labels */
477
9
      info.CP[ai] = va_arg(ap, char *);
478
      /*
479
      printf("!%s: got char*[%d] = |%s|\n",
480
             "nrrdAxisInfoSet", d, info.CP[ai]);
481
      */
482
3
      break;
483
    case nrrdAxisInfoUnits:
484
      /* see not above */
485
      info.CP[ai] = va_arg(ap, char *);
486
      break;
487
    }
488
  }
489
87
  va_end(ap);
490
491
87
  if (nrrdAxisInfoSpaceDirection != axInfo) {
492
    /* now set the quantities which we've gotten from the var args */
493
49
    nrrdAxisInfoSet_nva(nrrd, axInfo, info.P);
494
49
  } else {
495
38
    nrrdAxisInfoSet_nva(nrrd, axInfo, svec);
496
  }
497
498
87
  return;
499
87
}
500
501
/*
502
******** nrrdAxisInfoGet_nva()
503
**
504
** get any of the axis fields into an array
505
**
506
** Note that getting axes labels involves implicitly allocating space
507
** for them, due to the action of airStrdup().  The user is
508
** responsible for free()ing these strings when done with them.
509
**
510
** type to pass for third argument:
511
**           nrrdAxisInfoSize: size_t*
512
**        nrrdAxisInfoSpacing: double*
513
**      nrrdAxisInfoThickness: double*
514
**            nrrdAxisInfoMin: double*
515
**            nrrdAxisInfoMax: double*
516
** nrrdAxisInfoSpaceDirection: double (*var)[NRRD_SPACE_DIM_MAX]
517
**         nrrdAxisInfoCenter: int*
518
**           nrrdAxisInfoKind: int*
519
**          nrrdAxisInfoLabel: char**
520
**          nrrdAxisInfoUnits: char**
521
*/
522
void
523
nrrdAxisInfoGet_nva(const Nrrd *nrrd, int axInfo, void *_info) {
524
  _nrrdAxisInfoGetPtrs info;
525
  unsigned int ai, si;
526
527
48924
  if (!( nrrd
528

73386
         && AIR_IN_CL(1, nrrd->dim, NRRD_DIM_MAX)
529
24462
         && AIR_IN_OP(nrrdAxisInfoUnknown, axInfo, nrrdAxisInfoLast) )) {
530
    return;
531
  }
532
533
24462
  info.P = _info;
534
196568
  for (ai=0; ai<nrrd->dim; ai++) {
535


147644
    switch (axInfo) {
536
    case nrrdAxisInfoSize:
537
13445
      info.ST[ai] = nrrd->axis[ai].size;
538
13445
      break;
539
    case nrrdAxisInfoSpacing:
540
10053
      info.D[ai] = nrrd->axis[ai].spacing;
541
10053
      break;
542
    case nrrdAxisInfoThickness:
543
10053
      info.D[ai] = nrrd->axis[ai].thickness;
544
10053
      break;
545
    case nrrdAxisInfoMin:
546
10059
      info.D[ai] = nrrd->axis[ai].min;
547
10059
      break;
548
    case nrrdAxisInfoMax:
549
10059
      info.D[ai] = nrrd->axis[ai].max;
550
10059
      break;
551
    case nrrdAxisInfoSpaceDirection:
552
      for (si=0; si<nrrd->spaceDim; si++) {
553
        info.V[ai][si] = nrrd->axis[ai].spaceDirection[si];
554
      }
555
      for (si=nrrd->spaceDim; si<NRRD_SPACE_DIM_MAX; si++) {
556
        info.V[ai][si] = AIR_NAN;
557
      }
558
      break;
559
    case nrrdAxisInfoCenter:
560
10100
      info.I[ai] = nrrd->axis[ai].center;
561
10100
      break;
562
    case nrrdAxisInfoKind:
563
10053
      info.I[ai] = nrrd->axis[ai].kind;
564
10053
      break;
565
    case nrrdAxisInfoLabel:
566
      /* note airStrdup()! */
567
      info.CP[ai] = airStrdup(nrrd->axis[ai].label);
568
      break;
569
    case nrrdAxisInfoUnits:
570
      /* note airStrdup()! */
571
      info.CP[ai] = airStrdup(nrrd->axis[ai].units);
572
      break;
573
    }
574
  }
575
24462
  if (nrrdAxisInfoSpaceDirection == axInfo) {
576
    for (ai=nrrd->dim; ai<NRRD_DIM_MAX; ai++) {
577
      for (si=0; si<NRRD_SPACE_DIM_MAX; si++) {
578
        info.V[ai][si] = AIR_NAN;
579
      }
580
    }
581
  }
582
24462
  return;
583
24462
}
584
585
/*
586
** types to pass, one for each dimension:
587
**           nrrdAxisInfoSize: size_t*
588
**        nrrdAxisInfoSpacing: double*
589
**      nrrdAxisInfoThickness: double*
590
**            nrrdAxisInfoMin: double*
591
**            nrrdAxisInfoMax: double*
592
** nrrdAxisInfoSpaceDirection: double*
593
**         nrrdAxisInfoCenter: int*
594
**           nrrdAxisInfoKind: int*
595
**          nrrdAxisInfoLabel: char**
596
**          nrrdAxisInfoUnits: char**
597
*/
598
void
599
nrrdAxisInfoGet_va(const Nrrd *nrrd, int axInfo, ...) {
600
  void *buffer[NRRD_DIM_MAX], *ptr;
601
  _nrrdAxisInfoGetPtrs info;
602
  unsigned int ai, si;
603
  va_list ap;
604
  double svec[NRRD_DIM_MAX][NRRD_SPACE_DIM_MAX];
605
606
  if (!( nrrd
607
         && AIR_IN_CL(1, nrrd->dim, NRRD_DIM_MAX)
608
         && AIR_IN_OP(nrrdAxisInfoUnknown, axInfo, nrrdAxisInfoLast) )) {
609
    return;
610
  }
611
612
  if (nrrdAxisInfoSpaceDirection != axInfo) {
613
    info.P = buffer;
614
    nrrdAxisInfoGet_nva(nrrd, axInfo, info.P);
615
  } else {
616
    nrrdAxisInfoGet_nva(nrrd, axInfo, svec);
617
  }
618
619
  va_start(ap, axInfo);
620
  for (ai=0; ai<nrrd->dim; ai++) {
621
    ptr = va_arg(ap, void*);
622
    /*
623
    printf("!%s(%d): ptr = %lu\n",
624
           "nrrdAxisInfoGet", d, (unsigned long)ptr);
625
    */
626
    switch (axInfo) {
627
    case nrrdAxisInfoSize:
628
      *((size_t*)ptr) = info.ST[ai];
629
      break;
630
    case nrrdAxisInfoSpacing:
631
    case nrrdAxisInfoThickness:
632
    case nrrdAxisInfoMin:
633
    case nrrdAxisInfoMax:
634
      *((double*)ptr) = info.D[ai];
635
      /* printf("!%s: got double[%d] = %lg\n", "nrrdAxisInfoGet", d,
636
       *((double*)ptr)); */
637
      break;
638
    case nrrdAxisInfoSpaceDirection:
639
      for (si=0; si<nrrd->spaceDim; si++) {
640
        ((double*)ptr)[si] = svec[ai][si];
641
      }
642
      for (si=nrrd->spaceDim; si<NRRD_SPACE_DIM_MAX; si++) {
643
        ((double*)ptr)[si] = AIR_NAN;
644
      }
645
      break;
646
    case nrrdAxisInfoCenter:
647
    case nrrdAxisInfoKind:
648
      *((int*)ptr) = info.I[ai];
649
      /* printf("!%s: got int[%d] = %d\n",
650
         "nrrdAxisInfoGet", d, *((int*)ptr)); */
651
      break;
652
    case nrrdAxisInfoLabel:
653
    case nrrdAxisInfoUnits:
654
      /* we DO NOT do the airStrdup() here because this pointer value just
655
         came from nrrdAxisInfoGet_nva(), which already did the airStrdup() */
656
      *((char**)ptr) = info.CP[ai];
657
      /* printf("!%s: got char*[%d] = |%s|\n", "nrrdAxisInfoSet", d,
658
       *((char**)ptr)); */
659
      break;
660
    }
661
  }
662
  va_end(ap);
663
664
  return;
665
}
666
667
/*
668
** _nrrdCenter()
669
**
670
** for nrrdCenterCell and nrrdCenterNode, return will be the same
671
** as input.  Converts nrrdCenterUnknown into nrrdDefaultCenter,
672
** and then clamps to (nrrdCenterUnknown+1, nrrdCenterLast-1).
673
**
674
** Thus, this ALWAYS returns nrrdCenterNode or nrrdCenterCell
675
** (as long as those are the only two centering schemes).
676
*/
677
int
678
_nrrdCenter(int center) {
679
680
339
  center =  (nrrdCenterUnknown == center
681
113
             ? nrrdDefaultCenter
682
             : center);
683
339
  center = AIR_CLAMP(nrrdCenterUnknown+1, center, nrrdCenterLast-1);
684
113
  return center;
685
}
686
687
int
688
_nrrdCenter2(int center, int defCenter) {
689
690
  center =  (nrrdCenterUnknown == center
691
             ? defCenter
692
             : center);
693
  center = AIR_CLAMP(nrrdCenterUnknown+1, center, nrrdCenterLast-1);
694
  return center;
695
}
696
697
698
/*
699
******** nrrdAxisInfoPos()
700
**
701
** given a nrrd, an axis, and a (floating point) index space position,
702
** return the position implied the axis's min, max, and center
703
** Does the opposite of nrrdAxisIdx().
704
**
705
** does not use biff
706
*/
707
double
708
nrrdAxisInfoPos(const Nrrd *nrrd, unsigned int ax, double idx) {
709
  int center;
710
  size_t size;
711
  double min, max;
712
713

3
  if (!( nrrd && ax <= nrrd->dim-1 )) {
714
    return AIR_NAN;
715
  }
716
1
  center = _nrrdCenter(nrrd->axis[ax].center);
717
1
  min = nrrd->axis[ax].min;
718
1
  max = nrrd->axis[ax].max;
719
1
  size = nrrd->axis[ax].size;
720
721
3
  return NRRD_POS(center, min, max, size, idx);
722
1
}
723
724
/*
725
******** nrrdAxisInfoIdx()
726
**
727
** given a nrrd, an axis, and a (floating point) world space position,
728
** return the index implied the axis's min, max, and center.
729
** Does the opposite of nrrdAxisPos().
730
**
731
** does not use biff
732
*/
733
double
734
nrrdAxisInfoIdx(const Nrrd *nrrd, unsigned int ax, double pos) {
735
  int center;
736
  size_t size;
737
  double min, max;
738
739
  if (!( nrrd && ax <= nrrd->dim-1 )) {
740
    return AIR_NAN;
741
  }
742
  center = _nrrdCenter(nrrd->axis[ax].center);
743
  min = nrrd->axis[ax].min;
744
  max = nrrd->axis[ax].max;
745
  size = nrrd->axis[ax].size;
746
747
  return NRRD_IDX(center, min, max, size, pos);
748
}
749
750
/*
751
******** nrrdAxisInfoPosRange()
752
**
753
** given a nrrd, an axis, and two (floating point) index space positions,
754
** return the range of positions implied the axis's min, max, and center
755
** The opposite of nrrdAxisIdxRange()
756
*/
757
void
758
nrrdAxisInfoPosRange(double *loP, double *hiP,
759
                     const Nrrd *nrrd, unsigned int ax,
760
                     double loIdx, double hiIdx) {
761
  int center, flip = 0;
762
  size_t size;
763
  double min, max, tmp;
764
765

336
  if (!( loP && hiP && nrrd && ax <= nrrd->dim-1 )) {
766
    if (loP) *loP = AIR_NAN;
767
    if (hiP) *hiP = AIR_NAN;
768
    return;
769
  }
770
112
  center = _nrrdCenter(nrrd->axis[ax].center);
771
112
  min = nrrd->axis[ax].min;
772
112
  max = nrrd->axis[ax].max;
773
112
  size = nrrd->axis[ax].size;
774
775
112
  if (loIdx > hiIdx) {
776
    flip = 1;
777
    tmp = loIdx; loIdx = hiIdx; hiIdx = tmp;
778
  }
779

224
  if (nrrdCenterCell == center) {
780
224
    *loP = AIR_AFFINE(0, loIdx, size, min, max);
781
112
    *hiP = AIR_AFFINE(0, hiIdx+1, size, min, max);
782
112
  } else {
783
    *loP = AIR_AFFINE(0, loIdx, size-1, min, max);
784
    *hiP = AIR_AFFINE(0, hiIdx, size-1, min, max);
785
  }
786
112
  if (flip) {
787
    tmp = *loP; *loP = *hiP; *hiP = tmp;
788
  }
789
790
112
  return;
791
112
}
792
793
/*
794
******** nrrdAxisInfoIdxRange()
795
**
796
** given a nrrd, an axis, and two (floating point) world space positions,
797
** return the range of index space implied the axis's min, max, and center
798
** The opposite of nrrdAxisPosRange().
799
**
800
** Actually- there are situations where sending an interval through
801
** nrrdAxisIdxRange -> nrrdAxisPosRange -> nrrdAxisIdxRange
802
** such as in cell centering, when the range of positions given does
803
** not even span one sample.  Such as:
804
** axis->size = 4, axis->min = -4, axis->max = 4, loPos = 0, hiPos = 1
805
** --> nrrdAxisIdxRange == (2, 1.5) --> nrrdAxisPosRange == (2, -1)
806
** The basic problem is that because of the 0.5 offset inherent in
807
** cell centering, there are situations where (in terms of the arguments
808
** to nrrdAxisIdxRange()) loPos < hiPos, but *loP > *hiP.
809
*/
810
void
811
nrrdAxisInfoIdxRange(double *loP, double *hiP,
812
                     const Nrrd *nrrd, unsigned int ax,
813
                     double loPos, double hiPos) {
814
  int center, flip = 0;
815
  size_t size;
816
  double min, max, tmp;
817
818
  if (!( loP && hiP && nrrd && ax <= nrrd->dim-1 )) {
819
    *loP = *hiP = AIR_NAN;
820
    return;
821
  }
822
  center = _nrrdCenter(nrrd->axis[ax].center);
823
  min = nrrd->axis[ax].min;
824
  max = nrrd->axis[ax].max;
825
  size = nrrd->axis[ax].size;
826
827
  if (loPos > hiPos) {
828
    flip = 1;
829
    tmp = loPos; loPos = hiPos; hiPos = tmp;
830
  }
831
  if (nrrdCenterCell == center) {
832
    if (min < max) {
833
      *loP = AIR_AFFINE(min, loPos, max, 0, size);
834
      *hiP = AIR_AFFINE(min, hiPos, max, -1, size-1);
835
    } else {
836
      *loP = AIR_AFFINE(min, loPos, max, -1, size-1);
837
      *hiP = AIR_AFFINE(min, hiPos, max, 0, size);
838
    }
839
  } else {
840
    *loP = AIR_AFFINE(min, loPos, max, 0, size-1);
841
    *hiP = AIR_AFFINE(min, hiPos, max, 0, size-1);
842
  }
843
  if (flip) {
844
    tmp = *loP; *loP = *hiP; *hiP = tmp;
845
  }
846
847
  return;
848
}
849
850
void
851
nrrdAxisInfoSpacingSet(Nrrd *nrrd, unsigned int ax) {
852
  int sign;
853
  double min, max, tmp;
854
855
  if (!( nrrd && ax <= nrrd->dim-1 )) {
856
    return;
857
  }
858
859
  min = nrrd->axis[ax].min;
860
  max = nrrd->axis[ax].max;
861
  if (!( AIR_EXISTS(min) && AIR_EXISTS(max) )) {
862
    /* there's no actual basis on which to set the spacing information,
863
       but we have to set it something, so here goes .. */
864
    nrrd->axis[ax].spacing = nrrdDefaultSpacing;
865
    return;
866
  }
867
868
  if (min > max) {
869
    tmp = min; min = max; max = tmp;
870
    sign = -1;
871
  } else {
872
    sign = 1;
873
  }
874
875
  /* the skinny */
876
  nrrd->axis[ax].spacing = NRRD_SPACING(_nrrdCenter(nrrd->axis[ax].center),
877
                                        min, max, nrrd->axis[ax].size);
878
  nrrd->axis[ax].spacing *= sign;
879
880
  return;
881
}
882
883
void
884
nrrdAxisInfoMinMaxSet(Nrrd *nrrd, unsigned int ax, int defCenter) {
885
  int center;
886
  double spacing;
887
888
  if (!( nrrd && ax <= nrrd->dim-1 )) {
889
    return;
890
  }
891
892
  center = _nrrdCenter2(nrrd->axis[ax].center, defCenter);
893
  spacing = nrrd->axis[ax].spacing;
894
  if (!AIR_EXISTS(spacing))
895
    spacing = nrrdDefaultSpacing;
896
  if (nrrdCenterCell == center) {
897
    nrrd->axis[ax].min = 0;
898
    nrrd->axis[ax].max = spacing*AIR_CAST(double, nrrd->axis[ax].size);
899
  } else {
900
    nrrd->axis[ax].min = 0;
901
    nrrd->axis[ax].max = spacing*AIR_CAST(double, nrrd->axis[ax].size - 1);
902
  }
903
904
  return;
905
}
906
/* ---- BEGIN non-NrrdIO */
907
908
/*
909
** not using the value comparators in accessors.c because of their
910
** slightly strange behavior WRT infinity (+inf < -42).  This code
911
** may eventually warrant wider availability, for now its here but
912
** accessible to nrrd files via privateNrrd.h
913
*/
914
int
915
_nrrdDblcmp(double aa, double bb) {
916
  int nna, nnb, ret;
917
918
3884
  nna = AIR_EXISTS(aa) || !airIsNaN(aa);
919
2904
  nnb = AIR_EXISTS(bb) || !airIsNaN(bb);
920
980
  if (nna && nnb) {
921
    /* both either exist or are an infinity */
922
108
    ret = (aa < bb
923
           ? -1
924
36
           : (aa > bb
925
              ? 1
926
              : 0));
927
36
  } else {
928
    /* one or the other is NaN */
929
2832
    ret = (nna < nnb
930
           ? -1
931
944
           : (nna > nnb
932
              ? 1
933
              : 0));
934
  }
935
980
  return ret;
936
}
937
938
/*
939
******** nrrdAxisInfoCompare
940
**
941
** compares all fields in the NrrdAxisInfoCompare
942
**
943
** See comment about logic of return value above nrrdCompare()
944
**
945
** NOTE: the structure of this code is very similar to that of
946
** nrrdCompare, and any improvements here should be reflected there
947
*/
948
int
949
nrrdAxisInfoCompare(const NrrdAxisInfo *axisA, const NrrdAxisInfo *axisB,
950
                    int *differ, char explain[AIR_STRLEN_LARGE]) {
951
  static const char me[]="nrrdAxisInfoCompare";
952
  unsigned int saxi;
953
954
40
  if (!(axisA && axisB && differ)) {
955
    biffAddf(NRRD, "%s: got NULL pointer (%p, %p, or %p)", me,
956
             AIR_CVOIDP(axisA), AIR_CVOIDP(axisB), AIR_VOIDP(differ));
957
    return 1;
958
  }
959
960
20
  if (explain) {
961
20
    strcpy(explain, "");
962
20
  }
963
20
  if (axisA->size != axisB->size) {
964
    char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL];
965
    *differ = axisA->size < axisB->size ? -1 : 1;
966
    if (explain) {
967
      sprintf(explain, "axisA->size=%s %s axisB->size=%s",
968
              airSprintSize_t(stmp1, axisA->size),
969
              *differ < 0 ? "<" : ">",
970
              airSprintSize_t(stmp2, axisB->size));
971
    }
972
    return 0;
973
  }
974
975
#define DOUBLE_COMPARE(VAL, STR)                                        \
976
  *differ = _nrrdDblcmp(axisA->VAL, axisB->VAL);                        \
977
  if (*differ) {                                                        \
978
    if (explain) {                                                      \
979
      sprintf(explain, "axisA->%s %.17g %s axisB->%s %.17g",            \
980
              STR, axisA->VAL, *differ < 0 ? "<" : ">",                 \
981
              STR, axisB->VAL);                                         \
982
    }                                                                   \
983
    return 0;                                                           \
984
  }
985
986

20
  DOUBLE_COMPARE(spacing, "spacing");
987

20
  DOUBLE_COMPARE(thickness, "thickness");
988

20
  DOUBLE_COMPARE(min, "min");
989

20
  DOUBLE_COMPARE(max, "max");
990
360
  for (saxi=0; saxi<NRRD_SPACE_DIM_MAX; saxi++) {
991
160
    char stmp[AIR_STRLEN_SMALL];
992
160
    sprintf(stmp, "spaceDirection[%u]", saxi);
993

160
    DOUBLE_COMPARE(spaceDirection[saxi], stmp);
994
320
  }
995
#undef DOUBLE_COMPARE
996
997
20
  if (axisA->center != axisB->center) {
998
    *differ = axisA->center < axisB->center ? -1 : 1;
999
    if (explain) {
1000
      sprintf(explain, "axisA->center %s %s axisB->center %s",
1001
              airEnumStr(nrrdCenter, axisA->center),
1002
              *differ < 0 ? "<" : ">",
1003
              airEnumStr(nrrdCenter, axisB->center));
1004
    }
1005
    return 0;
1006
  }
1007
20
  if (axisA->kind != axisB->kind) {
1008
    *differ = axisA->kind < axisB->kind ? -1 : 1;
1009
    if (explain) {
1010
      sprintf(explain, "axisA->kind %s %s axisB->kind %s",
1011
              airEnumStr(nrrdKind, axisA->kind),
1012
              *differ < 0 ? "<" : ">",
1013
              airEnumStr(nrrdKind, axisB->kind));
1014
    }
1015
    return 0;
1016
  }
1017
20
  *differ = airStrcmp(axisA->label, axisB->label);
1018
20
  if (*differ) {
1019
    if (explain) {
1020
      /* can't safely print whole labels because of fixed-size of explain */
1021
      sprintf(explain, "axisA->label %s axisB->label",
1022
              *differ < 0 ? "<" : ">");
1023
      if (strlen(explain) + airStrlen(axisA->label)
1024
          + airStrlen(axisB->label)
1025
          + 2*strlen(" \"\" ") + 1 < AIR_STRLEN_LARGE) {
1026
        /* ok, we can print them */
1027
        sprintf(explain, "axisA->label \"%s\" %s axisB->label \"%s\"",
1028
                axisA->label ? axisA->label : "",
1029
                *differ < 0 ? "<" : ">",
1030
                axisB->label ? axisB->label : "");
1031
      }
1032
    }
1033
    return 0;
1034
  }
1035
20
  *differ = airStrcmp(axisA->units, axisB->units);
1036
20
  if (*differ) {
1037
    if (explain) {
1038
      /* can't print whole string because of fixed-size of explain */
1039
      sprintf(explain, "axisA->units %s axisB->units",
1040
              *differ < 0 ? "<" : ">");
1041
    }
1042
    return 0;
1043
  }
1044
1045
20
  return 0;
1046
20
}
1047
/* ---- END non-NrrdIO */
1048
1049
/*
1050
******** nrrdDomainAxesGet
1051
**
1052
** Based on the per-axis "kind" field, learns which are the domain
1053
** (resample-able) axes of an image, in other words, the axes which
1054
** correspond to independent variables.  The return value is the
1055
** number of domain axes, and that many values are set in the given
1056
** axisIdx[] array
1057
**
1058
** NOTE: this takes a wild guess that an unset (nrrdKindUnknown) kind
1059
** is a domain axis.
1060
*/
1061
unsigned int
1062
nrrdDomainAxesGet(const Nrrd *nrrd, unsigned int axisIdx[NRRD_DIM_MAX]) {
1063
  unsigned int domAxi, axi;
1064
1065
  if (!( nrrd && axisIdx )) {
1066
    return 0;
1067
  }
1068
  domAxi = 0;
1069
  for (axi=0; axi<nrrd->dim; axi++) {
1070
    if (nrrdKindUnknown == nrrd->axis[axi].kind
1071
        || nrrdKindIsDomain(nrrd->axis[axi].kind)) {
1072
      axisIdx[domAxi++] = axi;
1073
    }
1074
  }
1075
  return domAxi;
1076
}
1077
1078
int
1079
_nrrdSpaceVecExists(const Nrrd *nrrd, unsigned int axi) {
1080
  unsigned int sai;
1081
  int ret;
1082
1083

8328
  if (!( nrrd && axi < nrrd->dim && nrrd->spaceDim )) {
1084
    ret = AIR_FALSE;
1085
  } else {
1086
    ret = AIR_TRUE;
1087
16656
    for (sai=0; sai<nrrd->spaceDim; sai++) {
1088
6246
      ret &= AIR_EXISTS(nrrd->axis[axi].spaceDirection[sai]);
1089
    }
1090
  }
1091
2082
  return ret;
1092
}
1093
1094
unsigned int
1095
nrrdSpatialAxesGet(const Nrrd *nrrd, unsigned int axisIdx[NRRD_DIM_MAX]) {
1096
  unsigned int spcAxi, axi;
1097
1098
  if (!( nrrd && axisIdx && nrrd->spaceDim)) {
1099
    return 0;
1100
  }
1101
  spcAxi = 0;
1102
  for (axi=0; axi<nrrd->dim; axi++) {
1103
    if (_nrrdSpaceVecExists(nrrd, axi)) {
1104
      axisIdx[spcAxi++] = axi;
1105
    }
1106
  }
1107
  return spcAxi;
1108
}
1109
1110
/*
1111
******** nrrdRangeAxesGet
1112
**
1113
** Based on the per-axis "kind" field, learns which are the range
1114
** (non-resample-able) axes of an image, in other words, the axes
1115
** which correspond to dependent variables.  The return value is the
1116
** number of range axes; that number of values are set in the given
1117
** axisIdx[] array
1118
**
1119
** Note: this really is as simple as returning the complement of the
1120
** axis selected by nrrdDomainAxesGet()
1121
*/
1122
unsigned int
1123
nrrdRangeAxesGet(const Nrrd *nrrd, unsigned int axisIdx[NRRD_DIM_MAX]) {
1124
  unsigned int domNum, domIdx[NRRD_DIM_MAX], rngAxi, axi, ii, isDom;
1125
1126
  if (!( nrrd && axisIdx )) {
1127
    return 0;
1128
  }
1129
  domNum = nrrdDomainAxesGet(nrrd, domIdx);
1130
  rngAxi = 0;
1131
  for (axi=0; axi<nrrd->dim; axi++) {
1132
    isDom = AIR_FALSE;
1133
    for (ii=0; ii<domNum; ii++) {   /* yes, inefficient */
1134
      isDom |= axi == domIdx[ii];
1135
    }
1136
    if (!isDom) {
1137
      axisIdx[rngAxi++] = axi;
1138
    }
1139
  }
1140
  return rngAxi;
1141
}
1142
1143
unsigned int
1144
nrrdNonSpatialAxesGet(const Nrrd *nrrd, unsigned int axisIdx[NRRD_DIM_MAX]) {
1145
  unsigned int spcNum, spcIdx[NRRD_DIM_MAX], nspAxi, axi, ii, isSpc;
1146
1147
  if (!( nrrd && axisIdx )) {
1148
    return 0;
1149
  }
1150
  /* HEY: copy and paste, should refactor with above */
1151
  spcNum = nrrdSpatialAxesGet(nrrd, spcIdx);
1152
  nspAxi = 0;
1153
  for (axi=0; axi<nrrd->dim; axi++) {
1154
    isSpc = AIR_FALSE;
1155
    for (ii=0; ii<spcNum; ii++) {   /* yes, inefficient */
1156
      isSpc |= axi == spcIdx[ii];
1157
    }
1158
    if (!isSpc) {
1159
      axisIdx[nspAxi++] = axi;
1160
    }
1161
  }
1162
  return nspAxi;
1163
}
1164
1165
1166
/*
1167
******** nrrdSpacingCalculate
1168
**
1169
** Determine nrrdSpacingStatus, and whatever can be calculated about
1170
** spacing for a given axis.  Takes a nrrd, an axis, a double pointer
1171
** (for returning a scalar), a space vector, and an int pointer for
1172
** returning the known length of the space vector.
1173
**
1174
** The behavior of what has been set by the function is determined by
1175
** the return value, which takes values from the nrrdSpacingStatus*
1176
** enum, as follows:
1177
**
1178
** returned status value:            what it means, and what it set
1179
** ---------------------------------------------------------------------------
1180
** nrrdSpacingStatusUnknown          Something about the given arguments is
1181
**                                   invalid.
1182
**                                   *spacing = NaN,
1183
**                                   vector = all NaNs
1184
**
1185
** nrrdSpacingStatusNone             There is no spacing info at all:
1186
**                                   *spacing = NaN,
1187
**                                   vector = all NaNs
1188
**
1189
** nrrdSpacingStatusScalarNoSpace    There is no surrounding space, but the
1190
**                                   axis's spacing was known.
1191
**                                   *spacing = axis->spacing,
1192
**                                   vector = all NaNs
1193
**
1194
** nrrdSpacingStatusScalarWithSpace  There *is* a surrounding space, but the
1195
**                                   given axis does not live in that space,
1196
**                                   because it has no space direction.  Caller
1197
**                                   may want to think about what's going on.
1198
**                                   *spacing = axis->spacing,
1199
**                                   vector = all NaNs
1200
**
1201
** nrrdSpacingStatusDirection        There is a surrounding space, in which
1202
**                                   this axis has a direction V:
1203
**                                   *spacing = |V| (length of direction),
1204
**                                   vector = V/|V| (normalized direction)
1205
**                                   NOTE: it is still possible for both
1206
**                                   *spacing and vector to be all NaNs!!
1207
*/
1208
int
1209
nrrdSpacingCalculate(const Nrrd *nrrd, unsigned int ax,
1210
                     double *spacing, double vector[NRRD_SPACE_DIM_MAX]) {
1211
  int ret;
1212
1213

6246
  if (!( nrrd && spacing && vector
1214
2082
         && ax <= nrrd->dim-1
1215
4164
         && !_nrrdCheck(nrrd, AIR_FALSE, AIR_FALSE) )) {
1216
    /* there's a problem with the arguments.  Note: the _nrrdCheck()
1217
       call does not check on non-NULL-ity of nrrd->data */
1218
    ret = nrrdSpacingStatusUnknown;
1219
    if (spacing) {
1220
      *spacing = AIR_NAN;
1221
    }
1222
    if (vector) {
1223
      nrrdSpaceVecSetNaN(vector);
1224
    }
1225
  } else {
1226

4164
    if (AIR_EXISTS(nrrd->axis[ax].spacing)) {
1227
2082
      if (nrrd->spaceDim > 0) {
1228
        ret = nrrdSpacingStatusScalarWithSpace;
1229
      } else {
1230
        ret = nrrdSpacingStatusScalarNoSpace;
1231
      }
1232
      *spacing = nrrd->axis[ax].spacing;
1233
      nrrdSpaceVecSetNaN(vector);
1234
    } else {
1235

4164
      if (nrrd->spaceDim > 0 && _nrrdSpaceVecExists(nrrd, ax)) {
1236
        ret = nrrdSpacingStatusDirection;
1237
4164
        *spacing = nrrdSpaceVecNorm(nrrd->spaceDim,
1238
2082
                                    nrrd->axis[ax].spaceDirection);
1239
2082
        nrrdSpaceVecScale(vector, 1.0/(*spacing),
1240
                          nrrd->axis[ax].spaceDirection);
1241
2082
      } else {
1242
        ret = nrrdSpacingStatusNone;
1243
        *spacing = AIR_NAN;
1244
        nrrdSpaceVecSetNaN(vector);
1245
      }
1246
    }
1247
  }
1248
2082
  return ret;
1249
}
1250
1251
int
1252
nrrdOrientationReduce(Nrrd *nout, const Nrrd *nin,
1253
                      int setMinsFromOrigin) {
1254
  static const char me[]="nrrdOrientationReduce";
1255
  unsigned int spatialAxisNum, spatialAxisIdx[NRRD_DIM_MAX], saxii;
1256
  NrrdAxisInfo *axis;
1257
1258
  if (!(nout && nin)) {
1259
    biffAddf(NRRD, "%s: got NULL pointer", me);
1260
    return 1;
1261
  }
1262
1263
  if (nout != nin) {
1264
    if (nrrdCopy(nout, nin)) {
1265
      biffAddf(NRRD, "%s: trouble doing initial copying", me);
1266
      return 1;
1267
    }
1268
  }
1269
  if (!nout->spaceDim) {
1270
    /* we're done! */
1271
    return 0;
1272
  }
1273
  spatialAxisNum = nrrdSpatialAxesGet(nout, spatialAxisIdx);
1274
  for (saxii=0; saxii<spatialAxisNum; saxii++) {
1275
    axis = nout->axis + spatialAxisIdx[saxii];
1276
    axis->spacing = nrrdSpaceVecNorm(nout->spaceDim,
1277
                                     axis->spaceDirection);
1278
    if (setMinsFromOrigin) {
1279
      axis->min = (saxii < nout->spaceDim
1280
                   ? nout->spaceOrigin[saxii]
1281
                   : AIR_NAN);
1282
    }
1283
  }
1284
  nrrdSpaceSet(nout, nrrdSpaceUnknown);
1285
1286
  return 0;
1287
}
1288
1289
/*
1290
******** nrrdMetaData
1291
**
1292
** The brains of "unu dnorm" (for Diderot normalization): put all meta-data
1293
** of a nrrd into some simpler canonical form.
1294
**
1295
** This function probably doesn't belong in this file, but it is kind
1296
** the opposite of nrrdOrientationReduce (above), so here it is
1297
*/
1298
int
1299
nrrdMetaDataNormalize(Nrrd *nout, const Nrrd *nin,
1300
                      int version,
1301
                      int trivialOrient,
1302
                      int permuteComponentAxisFastest,
1303
                      int recenterGrid,
1304
                      double sampleSpacing,
1305
                      int *lostMeasurementFrame) {
1306
  static const char me[]="nrrdMetaDataNormalize";
1307
  size_t size[NRRD_DIM_MAX];
1308
  int kindIn, kindOut, haveMM, gotmf;
1309
  unsigned int kindAxis, axi, si, sj;
1310
  Nrrd *ntmp;
1311
  airArray *mop;
1312
1313
  if (!(nout && nin)) {
1314
    biffAddf(NRRD, "%s: got NULL pointer", me);
1315
    return 1;
1316
  }
1317
  if (airEnumValCheck(nrrdMetaDataCanonicalVersion, version)) {
1318
    biffAddf(NRRD, "%s: version %d not valid %s", me,
1319
             version, nrrdMetaDataCanonicalVersion->name);
1320
    return 1;
1321
  }
1322
  if (nrrdMetaDataCanonicalVersionAlpha != version) {
1323
    biffAddf(NRRD, "%s: sorry, %s %s not implemented (only %s)", me,
1324
             nrrdMetaDataCanonicalVersion->name,
1325
             airEnumStr(nrrdMetaDataCanonicalVersion, version),
1326
             airEnumStr(nrrdMetaDataCanonicalVersion,
1327
                        nrrdMetaDataCanonicalVersionAlpha));
1328
    return 1;
1329
  }
1330
1331
  if (_nrrdCheck(nin, AIR_FALSE /* checkData */, AIR_TRUE /* useBiff */)) {
1332
    biffAddf(NRRD, "%s: basic check failed", me);
1333
    return 1;
1334
  }
1335
  /* but can't deal with block type */
1336
  if (nrrdTypeBlock == nin->type) {
1337
    biffAddf(NRRD, "%s: can only have scalar types (not %s)", me,
1338
             airEnumStr(nrrdType, nrrdTypeBlock));
1339
    return 1;
1340
  }
1341
1342
  /* look at all per-axis kinds */
1343
  /* see if there's a range kind, verify that there's only one */
1344
  /* set haveMM */
1345
  haveMM = AIR_TRUE;
1346
  kindIn = nrrdKindUnknown;
1347
  kindAxis = 0; /* only means something if kindIn != nrrdKindUnknown */
1348
  for (axi=0; axi<nin->dim; axi++) {
1349
    if (nrrdKindUnknown == nin->axis[axi].kind
1350
        || nrrdKindIsDomain(nin->axis[axi].kind)) {
1351
      haveMM &= AIR_EXISTS(nin->axis[axi].min);
1352
      haveMM &= AIR_EXISTS(nin->axis[axi].max);
1353
    } else {
1354
      if (nrrdKindUnknown != kindIn) {
1355
        biffAddf(NRRD, "%s: got non-domain kind %s on axis %u, but already "
1356
                 "have kind %s on previous axis %u", me,
1357
                 airEnumStr(nrrdKind, nin->axis[axi].kind), axi,
1358
                 airEnumStr(nrrdKind, kindIn), kindAxis);
1359
        return 1;
1360
      }
1361
      kindIn = nin->axis[axi].kind;
1362
      kindAxis = axi;
1363
    }
1364
  }
1365
1366
  if (nrrdKindUnknown != kindIn && kindAxis) {
1367
    /* have a non-domain axis, and it isn't the fastest */
1368
    if (permuteComponentAxisFastest) {
1369
      if (nout == nin) {
1370
        biffAddf(NRRD, "%s: can't permute non-domain axis %u (kind %s) "
1371
                 "to axis 0 with nout == nin", me,
1372
                 kindAxis, airEnumStr(nrrdKind, kindIn));
1373
        return 1;
1374
      }
1375
      biffAddf(NRRD, "%s: sorry, permuting non-domain axis %u (kind %s) "
1376
               "to axis 0 not yet implemented", me,
1377
               kindAxis, airEnumStr(nrrdKind, kindIn));
1378
      return 1;
1379
    } else {
1380
      /* caller thinks its okay for non-domain axis to be on
1381
         something other than fastest axis */
1382
      if (nrrdMetaDataCanonicalVersionAlpha == version) {
1383
        biffAddf(NRRD, "%s: (%s) non-domain axis %u (kind %s) "
1384
                 "must be fastest axis", me,
1385
                 airEnumStr(nrrdMetaDataCanonicalVersion, version),
1386
                 kindAxis, airEnumStr(nrrdKind, kindIn));
1387
        return 1;
1388
      }
1389
      /* maybe with nrrdMetaDataCanonicalVersionAlpha != version
1390
         it is okay to have non-domain axis on non-fastest axis? */
1391
    }
1392
  }
1393
1394
  /* HEY: would be nice to handle a stub "scalar" axis by deleting it */
1395
1396
  /* see if the non-domain kind is something we can interpret as a tensor */
1397
  if (nrrdKindUnknown != kindIn) {
1398
    switch (kindIn) {
1399
      /* ======= THESE are the kinds that we can possibly output ======= */
1400
    case nrrdKind2Vector:
1401
    case nrrdKind3Vector:
1402
    case nrrdKind4Vector:
1403
    case nrrdKind2DSymMatrix:
1404
    case nrrdKind2DMatrix:
1405
    case nrrdKind3DSymMatrix:
1406
    case nrrdKind3DMatrix:
1407
      /* =============================================================== */
1408
      kindOut = kindIn;
1409
      break;
1410
      /* Some other kinds are mapped to those above */
1411
    case nrrdKind3Color:
1412
    case nrrdKindRGBColor:
1413
      kindOut = nrrdKind3Vector;
1414
      break;
1415
    case nrrdKind4Color:
1416
    case nrrdKindRGBAColor:
1417
      kindOut = nrrdKind4Vector;
1418
      break;
1419
    default:
1420
      biffAddf(NRRD, "%s: got non-conforming kind %s on axis %u", me,
1421
               airEnumStr(nrrdKind, kindIn), kindAxis);
1422
      return 1;
1423
    }
1424
  } else {
1425
    /* kindIn is nrrdKindUnknown, so its a simple scalar image,
1426
       and that's what the output will be too; kindOut == nrrdKindUnknown
1427
       is used in the code below to say "its a scalar image" */
1428
    kindOut = nrrdKindUnknown;
1429
  }
1430
1431
  /* initialize output by copying meta-data from nin to ntmp */
1432
  mop = airMopNew();
1433
  ntmp = nrrdNew();
1434
  airMopAdd(mop, ntmp, (airMopper)nrrdNix, airMopAlways);
1435
  /* HEY this is doing the work of a shallow copy, which isn't
1436
     available in the API.  You can pass nrrdCopy() a nin with NULL
1437
     nin->data, which implements a shallow copy, but we can't set
1438
     nin->data=NULL here because of const correctness */
1439
  nrrdAxisInfoGet_nva(nin, nrrdAxisInfoSize, size);
1440
  if (nrrdWrap_nva(ntmp, NULL, nin->type, nin->dim, size)) {
1441
    biffAddf(NRRD, "%s: couldn't wrap buffer nrrd around NULL", me);
1442
    airMopError(mop); return 1;
1443
  }
1444
  /* so ntmp->data == NULL */
1445
  nrrdAxisInfoCopy(ntmp, nin, NULL, NRRD_AXIS_INFO_SIZE_BIT);
1446
  if (nrrdBasicInfoCopy(ntmp, nin, NRRD_BASIC_INFO_DATA_BIT)) {
1447
    biffAddf(NRRD, "%s: trouble copying basic info", me);
1448
    airMopError(mop); return 1;
1449
  }
1450
1451
  /* no comments */
1452
  nrrdCommentClear(ntmp);
1453
1454
  /* no measurement frame */
1455
  gotmf = AIR_FALSE;
1456
  for (si=0; si<NRRD_SPACE_DIM_MAX; si++) {
1457
    for (sj=0; sj<NRRD_SPACE_DIM_MAX; sj++) {
1458
      gotmf |= AIR_EXISTS(ntmp->measurementFrame[si][sj]);
1459
    }
1460
  }
1461
  if (lostMeasurementFrame) {
1462
    *lostMeasurementFrame = gotmf;
1463
  }
1464
  for (si=0; si<NRRD_SPACE_DIM_MAX; si++) {
1465
    for (sj=0; sj<NRRD_SPACE_DIM_MAX; sj++) {
1466
      ntmp->measurementFrame[si][sj] = AIR_NAN;
1467
    }
1468
  }
1469
1470
  /* no key/value pairs */
1471
  nrrdKeyValueClear(ntmp);
1472
1473
  /* no content field */
1474
  ntmp->content = airFree(ntmp->content);
1475
1476
  /* normalize domain kinds to "space" */
1477
  /* HEY: if Diderot supports time-varying fields, this will have to change */
1478
  /* turn off centers (current Diderot semantics don't expose centering) */
1479
  /* turn off thickness */
1480
  /* turn off labels and units */
1481
  for (axi=0; axi<ntmp->dim; axi++) {
1482
    if (nrrdKindUnknown == kindOut) {
1483
      ntmp->axis[axi].kind = nrrdKindSpace;
1484
    } else {
1485
      ntmp->axis[axi].kind = (kindAxis == axi
1486
                              ? kindOut
1487
                              : nrrdKindSpace);
1488
    }
1489
    ntmp->axis[axi].center = nrrdCenterUnknown;
1490
    ntmp->axis[axi].thickness = AIR_NAN;
1491
    ntmp->axis[axi].label = airFree(ntmp->axis[axi].label);
1492
    ntmp->axis[axi].units = airFree(ntmp->axis[axi].units);
1493
    ntmp->axis[axi].min = AIR_NAN;
1494
    ntmp->axis[axi].max = AIR_NAN;
1495
    ntmp->axis[axi].spacing = AIR_NAN;
1496
  }
1497
1498
  /* logic of orientation definition:
1499
     If space dimension is known:
1500
        set origin to zero if not already set
1501
        set space direction to unit vector if not already set
1502
     Else if have per-axis min and max:
1503
        set spae origin and directions to communicate same intent
1504
        as original per-axis min and max and original centering
1505
     Else
1506
        set origin to zero and all space directions to units.
1507
     (It might be nice to use gage's logic for mapping from world to index,
1508
     but we have to accept a greater variety of kinds and dimensions
1509
     than gage ever has to process.)
1510
     The result is that space origin and space directions are set.
1511
     the "space" field is not used, only "spaceDim"
1512
  */
1513
  /* no named space */
1514
  ntmp->space = nrrdSpaceUnknown;
1515
  if (ntmp->spaceDim && !trivialOrient) {
1516
    int saxi = 0;
1517
    if (!nrrdSpaceVecExists(ntmp->spaceDim, ntmp->spaceOrigin)) {
1518
      nrrdSpaceVecSetZero(ntmp->spaceOrigin);
1519
    }
1520
    for (axi=0; axi<ntmp->dim; axi++) {
1521
      if (nrrdKindUnknown == kindOut || kindAxis != axi) {
1522
        /* its a domain axis of output */
1523
        if (!nrrdSpaceVecExists(ntmp->spaceDim,
1524
                                ntmp->axis[axi].spaceDirection)) {
1525
          nrrdSpaceVecSetZero(ntmp->axis[axi].spaceDirection);
1526
          ntmp->axis[axi].spaceDirection[saxi] = sampleSpacing;
1527
        }
1528
        /* else we leave existing space vector as is */
1529
        saxi++;
1530
      } else {
1531
        /* else its a range (non-domain, component) axis */
1532
        nrrdSpaceVecSetNaN(ntmp->axis[axi].spaceDirection);
1533
      }
1534
    }
1535
  } else if (haveMM && !trivialOrient) {
1536
    int saxi = 0;
1537
    size_t N;
1538
    double rng;
1539
    for (axi=0; axi<ntmp->dim; axi++) {
1540
      if (nrrdKindUnknown == kindOut || kindAxis != axi) {
1541
        /* its a domain axis of output */
1542
        nrrdSpaceVecSetZero(ntmp->axis[axi].spaceDirection);
1543
        rng = nin->axis[axi].max - nin->axis[axi].min;
1544
        if (nrrdCenterNode == nin->axis[axi].center) {
1545
          ntmp->spaceOrigin[saxi] = nin->axis[axi].min;
1546
          N = nin->axis[axi].size;
1547
          ntmp->axis[axi].spaceDirection[saxi] = rng/(N-1);
1548
        } else {
1549
          /* unknown centering treated as cell */
1550
          N = nin->axis[axi].size;
1551
          ntmp->spaceOrigin[saxi] = nin->axis[axi].min + (rng/N)/2;
1552
          ntmp->axis[axi].spaceDirection[saxi] = rng/N;
1553
        }
1554
        saxi++;
1555
      } else {
1556
        /* else its a range axis */
1557
        nrrdSpaceVecSetNaN(ntmp->axis[axi].spaceDirection);
1558
      }
1559
    }
1560
    ntmp->spaceDim = saxi;
1561
  } else {
1562
    /* either trivialOrient, or, not spaceDim and not haveMM */
1563
    int saxi = 0;
1564
    nrrdSpaceVecSetZero(ntmp->spaceOrigin);
1565
    for (axi=0; axi<ntmp->dim; axi++) {
1566
      if (nrrdKindUnknown == kindOut || kindAxis != axi) {
1567
        /* its a domain axis of output */
1568
        nrrdSpaceVecSetZero(ntmp->axis[axi].spaceDirection);
1569
        ntmp->axis[axi].spaceDirection[saxi]
1570
          = (AIR_EXISTS(nin->axis[axi].spacing)
1571
             ? nin->axis[axi].spacing
1572
             : sampleSpacing);
1573
        saxi++;
1574
      } else {
1575
        /* else its a range axis */
1576
        nrrdSpaceVecSetNaN(ntmp->axis[axi].spaceDirection);
1577
      }
1578
    }
1579
    ntmp->spaceDim = saxi;
1580
  }
1581
1582
  /* space dimension has to match the number of domain axes */
1583
  if (ntmp->dim != ntmp->spaceDim + !!kindOut) {
1584
    biffAddf(NRRD, "%s: output dim %d != spaceDim %d + %d %s%s%s%s",
1585
             me, ntmp->dim, ntmp->spaceDim, !!kindOut,
1586
             kindOut ? "for non-scalar (" : "(scalar data)",
1587
             kindOut ? airEnumStr(nrrdKind, kindOut) : "",
1588
             kindOut ? ") data" : "",
1589
             kindOut ? "" : "; a non-domain axis in the input "
1590
             "may be missing an informative \"kind\", leading to the "
1591
             "false assumption of a scalar array");
1592
    airMopError(mop); return 1;
1593
  }
1594
1595
  if (recenterGrid) {
1596
    /* sets field's origin so field is centered on the origin. capiche? */
1597
    /* this code was tacked on later than the stuff above, so its
1598
       logic could probably be moved up there, but it seems cleaner to
1599
       have it as a separate post-process */
1600
    double mean[NRRD_SPACE_DIM_MAX];
1601
    nrrdSpaceVecSetZero(mean);
1602
    for (axi=0; axi<ntmp->dim; axi++) {
1603
      if (nrrdKindUnknown == kindOut || kindAxis != axi) {
1604
        nrrdSpaceVecScaleAdd2(mean, 1.0, mean,
1605
                              0.5*(ntmp->axis[axi].size - 1),
1606
                              ntmp->axis[axi].spaceDirection);
1607
      }
1608
    }
1609
    nrrdSpaceVecScaleAdd2(mean, 1.0, mean,
1610
                          1.0, ntmp->spaceOrigin);
1611
    /* now mean is the center of the field */
1612
    nrrdSpaceVecScaleAdd2(ntmp->spaceOrigin,
1613
                          1.0, ntmp->spaceOrigin,
1614
                          -1.0, mean);
1615
  }
1616
1617
  /* with that all done, now copy from ntmp to nout */
1618
  if (nout != nin) {
1619
    /* have to copy data */
1620
    ntmp->data = nin->data;
1621
    if (nrrdCopy(nout, ntmp)) {
1622
      biffAddf(NRRD, "%s: problem copying (with data) to output", me);
1623
      airMopError(mop); return 1;
1624
    }
1625
  } else {
1626
    /* nout == nin; have to copy only meta-data, leave data as is */
1627
    void *data = nin->data;
1628
    /* ntmp->data == NULL, so this is a shallow copy */
1629
    if (nrrdCopy(nout, ntmp)) {
1630
      biffAddf(NRRD, "%s: problem copying meta-data to output", me);
1631
      airMopError(mop); return 1;
1632
    }
1633
    nout->data = data;
1634
  }
1635
1636
  airMopOkay(mop);
1637
  return 0;
1638
}
1639