GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/gage/shape.c Lines: 104 244 42.6 %
Date: 2017-05-26 Branches: 58 154 37.7 %

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 "gage.h"
25
#include "privateGage.h"
26
27
void
28
gageShapeReset(gageShape *shape) {
29
30
  /* NOTE this guards against NULL */
31
1536
  if (shape) {
32
    /* input */
33
768
    shape->defaultCenter = gageDefDefaultCenter;
34
768
    shape->orientationFromSpacing = gageDefOrientationFromSpacing;
35
    /* output; setting this in ways that can be used to test
36
       whether the shape has been set */
37
768
    shape->center = nrrdCenterUnknown;
38
768
    shape->fromOrientation = AIR_FALSE;
39
768
    ELL_3V_SET(shape->size, 0, 0, 0);
40
768
    ELL_3V_SET(shape->spacing, AIR_NAN, AIR_NAN, AIR_NAN);
41
768
    ELL_4M_NAN_SET(shape->ItoW);
42
768
    ELL_4M_NAN_SET(shape->WtoI);
43
768
    ELL_3M_NAN_SET(shape->ItoWSubInvTransp);
44
768
    ELL_3M_NAN_SET(shape->ItoWSubInv);
45
768
  }
46
768
  return;
47
}
48
49
static void *
50
_mopShapeReset(void *_shape) {
51
  gageShape *shape;
52
53
  shape = AIR_CAST(gageShape *, _shape);
54
  gageShapeReset(shape);
55
  return NULL;
56
}
57
58
gageShape *
59
gageShapeNew() {
60
  gageShape *shape;
61
62
842
  shape = (gageShape *)calloc(1, sizeof(gageShape));
63
421
  if (shape) {
64
421
    gageShapeReset(shape);
65
421
  }
66
421
  return shape;
67
}
68
69
gageShape *
70
gageShapeCopy(const gageShape *shp) {
71
  gageShape *nhp;
72
73
148
  if ((nhp = gageShapeNew())) {
74
    /* no pointers, so this is easy */
75
74
    memcpy(nhp, shp, sizeof(gageShape));
76
74
  }
77
74
  return nhp;
78
}
79
80
gageShape *
81
gageShapeNix(gageShape *shape) {
82
83
822
  airFree(shape);
84
411
  return NULL;
85
}
86
87
static void
88
shapeUnitItoW(const gageShape *shape, double world[3],
89
              const double indx[3], const double volHalfLen[3]) {
90
  unsigned int i;
91
92
  if (nrrdCenterNode == shape->center) {
93
    for (i=0; i<=2; i++) {
94
      world[i] = NRRD_NODE_POS(-volHalfLen[i], volHalfLen[i],
95
                               shape->size[i], indx[i]);
96
    }
97
  } else {
98
    for (i=0; i<=2; i++) {
99
      world[i] = NRRD_CELL_POS(-volHalfLen[i], volHalfLen[i],
100
                               shape->size[i], indx[i]);
101
    }
102
  }
103
}
104
105
/*
106
** _gageShapeSet
107
**
108
** we are serving two masters here.  If ctx is non-NULL, we are being called
109
** from within gage, and we are to be lax or strict according to the settings
110
** of ctx->parm.requireAllSpacings and ctx->parm.requireEqualCenters.  If
111
** ctx is NULL, gageShapeSet was called, in which case we go with lax
112
** behavior (nothing "required")
113
**
114
** This function has subsumed the contents of the old gageVolumeCheck,
115
** and hence has become this weird beast- part error checker and part
116
** (gageShape) initializer.  Oh well...
117
*/
118
int
119
_gageShapeSet(const gageContext *ctx, gageShape *shape,
120
              const Nrrd *nin, unsigned int baseDim) {
121
  static const char me[]="_gageShapeSet";
122
  int ai, cx, cy, cz, statCalc[3], status, ofspc;
123
  unsigned int minsize;
124
  const NrrdAxisInfo *ax[3];
125
1388
  double vecA[4], vecB[3], vecC[3], vecD[4], matA[9],
126
    spcCalc[3], vecCalc[3][NRRD_SPACE_DIM_MAX], orig[NRRD_SPACE_DIM_MAX];
127
  airArray *mop;
128
129
  /*
130
  fprintf(stderr, "!%s: ctx = %p (%s, %s)\n", me, ctx,
131
          (ctx
132
           ? (ctx->shape->fromOrientation
133
              ? "YES from orient"
134
              : "not from orient")
135
           : "???"),
136
          (ctx
137
           ? (ctx->parm.orientationFromSpacing
138
              ? "YES ofs"
139
              : "not ofs")
140
           : "???"));
141
  */
142
  /* ------ basic error checking */
143
694
  mop = airMopNew();
144
694
  airMopAdd(mop, shape, _mopShapeReset, airMopOnError);
145
694
  if (!( shape && nin )) {
146
    biffAddf(GAGE, "%s: got NULL pointer", me);
147
    airMopError(mop); return 1;
148
  }
149
694
  if (nrrdCheck(nin)) {
150
    biffMovef(GAGE, NRRD, "%s: basic nrrd validity check failed", me);
151
    airMopError(mop); return 1;
152
  }
153
694
  if (nrrdTypeBlock == nin->type) {
154
    biffAddf(GAGE, "%s: need a non-block type nrrd", me);
155
    airMopError(mop); return 1;
156
  }
157
694
  if (!(nin->dim == 3 + baseDim)) {
158
    biffAddf(GAGE, "%s: nrrd should be %u-D, not %u-D",
159
             me, 3 + baseDim, nin->dim);
160
    airMopError(mop); return 1;
161
  }
162
694
  ax[0] = &(nin->axis[baseDim+0]);
163
694
  ax[1] = &(nin->axis[baseDim+1]);
164
694
  ax[2] = &(nin->axis[baseDim+2]);
165
166
694
  statCalc[0] = nrrdSpacingCalculate(nin, baseDim + 0,
167
694
                                     spcCalc + 0, vecCalc[0]);
168
694
  statCalc[1] = nrrdSpacingCalculate(nin, baseDim + 1,
169
694
                                     spcCalc + 1, vecCalc[1]);
170
694
  statCalc[2] = nrrdSpacingCalculate(nin, baseDim + 2,
171
694
                                     spcCalc + 2, vecCalc[2]);
172
  /* see if nrrdSpacingCalculate ever *failed* */
173
1388
  if (nrrdSpacingStatusUnknown == statCalc[0]
174
1388
      || nrrdSpacingStatusUnknown == statCalc[1]
175
1388
      || nrrdSpacingStatusUnknown == statCalc[2]) {
176
    biffAddf(GAGE, "%s: nrrdSpacingCalculate trouble on axis %d, %d, or %d",
177
             me, baseDim + 0, baseDim + 1, baseDim + 2);
178
    airMopError(mop); return 1;
179
  }
180

1388
  if (!( statCalc[0] == statCalc[1] && statCalc[1] == statCalc[2] )) {
181
    biffAddf(GAGE, "%s: inconsistent spacing information on axes "
182
             "%u (%s), %u (%s), and %u (%s)", me,
183
             baseDim + 0, airEnumDesc(nrrdSpacingStatus, statCalc[0]),
184
             baseDim + 1, airEnumDesc(nrrdSpacingStatus, statCalc[1]),
185
             baseDim + 2, airEnumDesc(nrrdSpacingStatus, statCalc[2]));
186
    airMopError(mop); return 1;
187
  }
188
  /* this simplifies reasoning in the code that follows */
189
  status = statCalc[0];
190
  /* zero spacing would be problematic */
191

694
  if (0 == spcCalc[0] && 0 == spcCalc[1] && 0 == spcCalc[2]) {
192
    biffAddf(GAGE, "%s: spacings (%g,%g,%g) for axes %d,%d,%d not all "
193
             "non-zero", me, spcCalc[1], spcCalc[1], spcCalc[2],
194
             baseDim+0, baseDim+1, baseDim+2);
195
    airMopError(mop); return 1;
196
  }
197
198
  /* error checking based on status */
199
694
  if (nrrdSpacingStatusScalarWithSpace == status) {
200
    biffAddf(GAGE, "%s: sorry, can't handle per-axis spacing that isn't part "
201
             "of a surrounding world space (%s)",
202
             me, airEnumStr(nrrdSpacingStatus, status));
203
    airMopError(mop); return 1;
204
  }
205
  /* we no longer allow a nrrd to come in with no spacing info at all */
206
694
  if (nrrdSpacingStatusNone == status) {
207
    biffAddf(GAGE, "%s: sorry, need some spacing info for spatial axes "
208
             "%u, %u, %u", me,
209
             baseDim+0, baseDim+1, baseDim+2);
210
    airMopError(mop); return 1;
211
  }
212
  /* actually, there shouldn't be any other options for spacing status
213
     besides these too; this is just being careful */
214
694
  if (!( nrrdSpacingStatusDirection == status
215
694
         || nrrdSpacingStatusScalarNoSpace == status )) {
216
    biffAddf(GAGE, "%s: sorry, can only handle spacing status %d (%s) "
217
             "or %d (%s), not %d (%s)", me,
218
             nrrdSpacingStatusDirection,
219
             airEnumStr(nrrdSpacingStatus, nrrdSpacingStatusDirection),
220
             nrrdSpacingStatusScalarNoSpace,
221
             airEnumStr(nrrdSpacingStatus, nrrdSpacingStatusScalarNoSpace),
222
             status, airEnumStr(nrrdSpacingStatus, status));
223
    airMopError(mop); return 1;
224
  }
225
226
694
  if (nrrdSpacingStatusDirection == status) {
227
1388
    shape->fromOrientation = AIR_TRUE;
228
694
    if (3 != nin->spaceDim) {
229
      biffAddf(GAGE, "%s: orientation space dimension %d != 3",
230
               me, nin->spaceDim);
231
      airMopError(mop); return 1;
232
    }
233
  } else {
234
    shape->fromOrientation = AIR_FALSE;
235
  }
236
237
  /* ------ find centering (set shape->center) */
238
  /* NOTE: when the volume is being crammed in a bi-unit cube, the centering
239
     will actually affect the positions of the samples.  Otherwise,
240
     (having full orientation, or using orientationFromSpacing), the
241
     centering will only affect the probe-able bounds of the volume, but
242
     the sample positions in space don't depend on centering */
243
694
  cx = ax[0]->center;
244
694
  cy = ax[1]->center;
245
694
  cz = ax[2]->center;
246

1388
  if (!( cx == cy && cy == cz )) {
247
    biffAddf(GAGE,
248
             "%s: axes %d,%d,%d centerings (%s,%s,%s) not all equal",
249
             me, baseDim+0, baseDim+1, baseDim+2,
250
             airEnumStr(nrrdCenter, cx),
251
             airEnumStr(nrrdCenter, cy),
252
             airEnumStr(nrrdCenter, cz));
253
    airMopError(mop); return 1;
254
  }
255
  /* Hopefully, ctx->parm.defaultCenter == shape->defaultCenter; and this
256
     worry will be moot if ctx->parm.defaultCenter goes away */
257
1882
  shape->center = (nrrdCenterUnknown != cx
258
                   ? cx /* cx == cy == cz, by above */
259
400
                   : (ctx
260
100
                      ? ctx->parm.defaultCenter
261
100
                      : shape->defaultCenter));
262
263
  /* ------ find sizes (set shape->size[0,1,2]) */
264
694
  shape->size[0] = ax[0]->size;
265
694
  shape->size[1] = ax[1]->size;
266
694
  shape->size[2] = ax[2]->size;
267
694
  minsize = (nrrdCenterCell == shape->center ? 1 : 2);
268
  /* this can't be relaxed in the face of having full orientation info,
269
     because even then, you can't have a non-zero probe-able volume if
270
     there's only one sample along a node-centered axis */
271
1388
  if (!(shape->size[0] >= minsize
272
1388
        && shape->size[1] >= minsize
273
1388
        && shape->size[2] >= minsize )) {
274
    biffAddf(GAGE, "%s: sizes (%u,%u,%u) must all be >= %u "
275
             "(min number of %s-centered samples)", me,
276
             shape->size[0], shape->size[1], shape->size[2],
277
             minsize, airEnumStr(nrrdCenter, shape->center));
278
    airMopError(mop); return 1;
279
  }
280
281
  /* ------ find spacings[0,1,2] and ItoW matrix */
282
  /* Hopefully, ctx->parm.orientationFromSpacing and
283
     shape->orientationFromSpacing don't represent competing interests;
284
     this worry will be moot if ctx->parm.orientationFromSpacing goes away */
285
1041
  ofspc = ((ctx && ctx->parm.orientationFromSpacing)
286
1735
           || shape->orientationFromSpacing);
287
694
  if (shape->fromOrientation || ofspc) {
288
694
    if (ofspc) {
289
      /* need abs() in case an axis had negative spacing */
290
694
      ELL_3V_ABS(shape->spacing, spcCalc);
291
      ELL_3V_SET(vecCalc[0], airSgn(spcCalc[0]), 0.0, 0.0);
292
      ELL_3V_SET(vecCalc[1], 0.0, airSgn(spcCalc[1]), 0.0);
293
      ELL_3V_SET(vecCalc[2], 0.0, 0.0, airSgn(spcCalc[2]));
294
    } else {
295
694
      ELL_3V_COPY(shape->spacing, spcCalc);
296
      /* vecCalc set by nrrdSpacingCalculate */
297
    }
298
694
    if (shape->fromOrientation) {
299
      /* if the spaceOrigin isn't set, this will be all NaNs */
300
694
      nrrdSpaceOriginGet(nin, orig);
301
694
    } else {
302
      /* sorry, if you want to specify an image origin that over-rides the
303
         behavior of centering the volume at (0,0,0), then it has to be
304
         done through the full orientation info.  That is, we don't want
305
         to use nrrdOriginCalculate() because otherwise the logic gets
306
         too complicated */
307
      ELL_3V_SET(orig, AIR_NAN, AIR_NAN, AIR_NAN);
308
    }
309

2082
    if (!ELL_3V_EXISTS(orig)) {
310
      /* don't have origin, for whatever reason; center volume on (0,0,0) */
311
      ELL_3V_SET(orig, 0.0, 0.0, 0.0);
312
      ELL_3V_SCALE_INCR(orig, -(shape->size[0] - 1.0)*shape->spacing[0]/2.0,
313
                        vecCalc[0]);
314
      ELL_3V_SCALE_INCR(orig, -(shape->size[1] - 1.0)*shape->spacing[1]/2.0,
315
                        vecCalc[1]);
316
      ELL_3V_SCALE_INCR(orig, -(shape->size[2] - 1.0)*shape->spacing[2]/2.0,
317
                        vecCalc[2]);
318
    }
319
    vecD[3] = 0;
320
694
    ELL_3V_SCALE(vecD, spcCalc[0], vecCalc[0]);
321
694
    ELL_4MV_COL0_SET(shape->ItoW, vecD);
322
694
    ELL_3V_SCALE(vecD, spcCalc[1], vecCalc[1]);
323
694
    ELL_4MV_COL1_SET(shape->ItoW, vecD);
324
694
    ELL_3V_SCALE(vecD, spcCalc[2], vecCalc[2]);
325
694
    ELL_4MV_COL2_SET(shape->ItoW, vecD);
326
    vecD[3] = 1;
327
694
    ELL_3V_COPY(vecD, orig);
328
694
    ELL_4MV_COL3_SET(shape->ItoW, vecD);
329
    /*
330
    fprintf(stderr, "%s: %g (%g,%g,%g)\n", me,
331
            spcCalc[0], vecCalc[0][0], vecCalc[0][1], vecCalc[0][2]);
332
    fprintf(stderr, "%s: %g (%g,%g,%g)\n", me,
333
            spcCalc[1], vecCalc[1][0], vecCalc[1][1], vecCalc[1][2]);
334
    fprintf(stderr, "%s: %g (%g,%g,%g)\n", me,
335
            spcCalc[2], vecCalc[2][0], vecCalc[2][1], vecCalc[2][2]);
336
    */
337
    /*
338
    fprintf(stderr, "%s: ItoW = %g %g %g %g\n", me,
339
           shape->ItoW[ 0], shape->ItoW[ 1], shape->ItoW[ 2], shape->ItoW[ 3]);
340
    fprintf(stderr, "%s:        %g %g %g %g\n", me,
341
           shape->ItoW[ 4], shape->ItoW[ 5], shape->ItoW[ 6], shape->ItoW[ 7]);
342
    fprintf(stderr, "%s:        %g %g %g %g\n", me,
343
           shape->ItoW[ 8], shape->ItoW[ 9], shape->ItoW[10], shape->ItoW[11]);
344
    fprintf(stderr, "%s:        %g %g %g %g\n", me,
345
           shape->ItoW[12], shape->ItoW[13], shape->ItoW[14], shape->ItoW[15]);
346
    */
347
694
  } else { /* not (shape->fromOrientation || ofspc) */
348
    double maxLen, volHalfLen[3];
349
    size_t num[3];
350
    /* ------ learn lengths for bounding nrrd in bi-unit cube */
351
    ELL_3V_ABS(shape->spacing, spcCalc);
352
    maxLen = 0.0;
353
    for (ai=0; ai<=2; ai++) {
354
      num[ai] = (nrrdCenterNode == shape->center
355
                 ? shape->size[ai]-1
356
                 : shape->size[ai]);
357
      volHalfLen[ai] = num[ai]*shape->spacing[ai];
358
      maxLen = AIR_MAX(maxLen, volHalfLen[ai]);
359
    }
360
    /* Thu Dec 13 02:45:01 EST 2007
361
       fixed long-standing bug in handling vols without full orientation info:
362
       spacing[ai] was never scaled to account for being crammed into
363
       the bi-unit cube!! */
364
    for (ai=0; ai<=2; ai++) {
365
      volHalfLen[ai] /= maxLen;
366
      shape->spacing[ai] = 2*volHalfLen[ai]/num[ai];
367
    }
368
    ELL_3V_SET(vecC, 0, 0, 0);
369
    shapeUnitItoW(shape, vecA, vecC, volHalfLen);
370
    ELL_3V_SET(vecC, 1, 0, 0);
371
    shapeUnitItoW(shape, vecB, vecC, volHalfLen);
372
    ELL_3V_SUB(vecD, vecB, vecA);
373
    vecD[3] = 0;
374
    ELL_4MV_COL0_SET(shape->ItoW, vecD);
375
376
    ELL_3V_SET(vecC, 0, 1, 0);
377
    shapeUnitItoW(shape, vecB, vecC, volHalfLen);
378
    ELL_3V_SUB(vecD, vecB, vecA);
379
    vecD[3] = 0;
380
    ELL_4MV_COL1_SET(shape->ItoW, vecD);
381
382
    ELL_3V_SET(vecC, 0, 0, 1);
383
    shapeUnitItoW(shape, vecB, vecC, volHalfLen);
384
    ELL_3V_SUB(vecD, vecB, vecA);
385
    vecD[3] = 0;
386
    ELL_4MV_COL2_SET(shape->ItoW, vecD);
387
388
    vecA[3] = 1;
389
    ELL_4MV_COL3_SET(shape->ItoW, vecA);
390
  }
391
392
  /* ------ set the rest of the matrices */
393
694
  ell_4m_inv_d(shape->WtoI, shape->ItoW);
394
694
  ELL_34M_EXTRACT(matA, shape->ItoW);
395
694
  ell_3m_inv_d(shape->ItoWSubInv, matA);
396
694
  ELL_3M_TRANSPOSE(shape->ItoWSubInvTransp, shape->ItoWSubInv);
397
398
694
  airMopOkay(mop);
399
694
  return 0;
400
694
}
401
402
int
403
gageShapeSet(gageShape *shape, const Nrrd *nin, int baseDim) {
404
  static const char me[]="gageShapeSet";
405
406
  if (_gageShapeSet(NULL, shape, nin, baseDim)) {
407
    biffAddf(GAGE, "%s: trouble", me);
408
    return 1;
409
  }
410
  return 0;
411
}
412
413
/*
414
** this wasn't being used at all
415
void
416
gageShapeUnitWtoI(gageShape *shape, double indx[3], double world[3]) {
417
  int i;
418
419
  if (nrrdCenterNode == shape->center) {
420
    for (i=0; i<=2; i++) {
421
      indx[i] = NRRD_NODE_IDX(-shape->volHalfLen[i], shape->volHalfLen[i],
422
                               shape->size[i], world[i]);
423
    }
424
  } else {
425
    for (i=0; i<=2; i++) {
426
      indx[i] = NRRD_CELL_IDX(-shape->volHalfLen[i], shape->volHalfLen[i],
427
                               shape->size[i], world[i]);
428
    }
429
  }
430
}
431
*/
432
433
void
434
gageShapeWtoI(const gageShape *shape,
435
              double _indx[3], const double _world[3]) {
436
  /* static const char me[]="gageShapeWtoI"; */
437
  double indx[4], world[4];
438
439
  /*
440
  fprintf(stderr, "!%s: hello %p %p %p; %p\n", me,
441
          shape, _indx, _world, shape->WtoI);
442
  */
443
  ELL_3V_COPY(world, _world);
444
  world[3] = 1.0;
445
  ELL_4MV_MUL(indx, shape->WtoI, world);
446
  ELL_3V_SCALE(_indx, 1.0/indx[3], indx);
447
}
448
449
void
450
gageShapeItoW(const gageShape *shape,
451
              double _world[3], const double _indx[3]) {
452
  double world[4], indx[4];
453
454
  ELL_3V_COPY(indx, _indx);
455
  indx[3] = 1.0;
456
  ELL_4MV_MUL(world, shape->ItoW, indx);
457
  ELL_3V_SCALE(_world, 1.0/world[3], world);
458
}
459
460
/*
461
******** gageShapeEqual
462
**
463
** shapes not being equal is a biffable error,
464
** returning 0 signifies this "error"
465
** returning 1 means no error, they ARE equal
466
*/
467
int
468
gageShapeEqual(const gageShape *shape1, const char *_name1,
469
               const gageShape *shape2, const char *_name2) {
470
  static const char me[]="gageShapeEqual";
471
  const char *name1, *name2, what[] = "???";
472
473
468
  if (!( shape1 && shape2 )) {
474
    biffAddf(GAGE, "%s: can't judge equality w/ NULL pointer", me);
475
    return 0;
476
  }
477
234
  name1 = _name1 ? _name1 : what;
478
234
  name2 = _name2 ? _name2 : what;
479
234
  if (!( shape1->fromOrientation == shape2->fromOrientation )) {
480
    biffAddf(GAGE,
481
             "%s: fromOrientation of %s (%s) != %s's (%s)", me,
482
             name1, shape1->fromOrientation ? "true" : "false",
483
             name2, shape2->fromOrientation ? "true" : "false");
484
    return 0;
485
  }
486

468
  if (!( shape1->size[0] == shape2->size[0] &&
487
234
         shape1->size[1] == shape2->size[1] &&
488
234
         shape1->size[2] == shape2->size[2] )) {
489
    biffAddf(GAGE,
490
             "%s: dimensions of %s (%u,%u,%u) != %s's (%u,%u,%u)",
491
             me, name1,
492
             shape1->size[0], shape1->size[1], shape1->size[2],
493
             name2,
494
             shape2->size[0], shape2->size[1], shape2->size[2]);
495
    return 0;
496
  }
497
234
  if (shape1->fromOrientation) {
498








3744
    if (!( ELL_4M_EQUAL(shape1->ItoW, shape2->ItoW) )) {
499
      biffAddf(GAGE, "%s: ItoW matrices of %s and %s not the same", me,
500
               name1, name2);
501
      return 0;
502
    }
503
  } else {
504
    if (!( shape1->spacing[0] == shape2->spacing[0] &&
505
           shape1->spacing[1] == shape2->spacing[1] &&
506
           shape1->spacing[2] == shape2->spacing[2] )) {
507
      biffAddf(GAGE, "%s: spacings of %s (%g,%g,%g) != %s's (%g,%g,%g)",
508
               me, name1,
509
               shape1->spacing[0], shape1->spacing[1], shape1->spacing[2],
510
               name2,
511
               shape2->spacing[0], shape2->spacing[1], shape2->spacing[2]);
512
      return 0;
513
    }
514
    if (!( shape1->center == shape2->center )) {
515
      biffAddf(GAGE,
516
               "%s: centering of %s (%s) != %s's (%s)", me,
517
               name1, airEnumStr(nrrdCenter, shape1->center),
518
               name2, airEnumStr(nrrdCenter, shape2->center));
519
      return 0;
520
    }
521
  }
522
523
234
  return 1;
524
234
}
525
526
void
527
gageShapeBoundingBox(double min[3], double max[3],
528
                     const gageShape *shape) {
529
  /* static const char me[]="gageShapeBoundingBox"; */
530
  double minIdx[3], maxIdx[3], cornerIdx[8][3], tmp[3];
531
  unsigned int ii;
532
533
  if (!( min && max && shape )) {
534
    return;
535
  }
536
  if (nrrdCenterNode == shape->center) {
537
    ELL_3V_SET(minIdx, 0, 0, 0);
538
    ELL_3V_SET(maxIdx,
539
               shape->size[0]-1,
540
               shape->size[1]-1,
541
               shape->size[2]-1);
542
  } else {
543
    ELL_3V_SET(minIdx, -0.5, -0.5, -0.5);
544
    ELL_3V_SET(maxIdx,
545
               shape->size[0]-0.5,
546
               shape->size[1]-0.5,
547
               shape->size[2]-0.5);
548
  }
549
  ELL_3V_SET(cornerIdx[0], minIdx[0], minIdx[1], minIdx[2]);
550
  ELL_3V_SET(cornerIdx[1], maxIdx[0], minIdx[1], minIdx[2]);
551
  ELL_3V_SET(cornerIdx[2], minIdx[0], maxIdx[1], minIdx[2]);
552
  ELL_3V_SET(cornerIdx[3], maxIdx[0], maxIdx[1], minIdx[2]);
553
  ELL_3V_SET(cornerIdx[4], minIdx[0], minIdx[1], maxIdx[2]);
554
  ELL_3V_SET(cornerIdx[5], maxIdx[0], minIdx[1], maxIdx[2]);
555
  ELL_3V_SET(cornerIdx[6], minIdx[0], maxIdx[1], maxIdx[2]);
556
  ELL_3V_SET(cornerIdx[7], maxIdx[0], maxIdx[1], maxIdx[2]);
557
  gageShapeItoW(shape, tmp, cornerIdx[0]);
558
  ELL_3V_COPY(min, tmp);
559
  ELL_3V_COPY(max, tmp);
560
  for (ii=1; ii<8; ii++) {
561
    gageShapeItoW(shape, tmp, cornerIdx[ii]);
562
    ELL_3V_MIN(min, min, tmp);
563
    ELL_3V_MAX(max, max, tmp);
564
  }
565
  return;
566
}