示例#1
0
/*
** _gageShapeSet
**
** we are serving two masters here.  If ctx is non-NULL, we are being called
** from within gage, and we are to be lax or strict according to the settings
** of ctx->parm.requireAllSpacings and ctx->parm.requireEqualCenters.  If
** ctx is NULL, gageShapeSet was called, in which case we go with lax
** behavior (nothing "required")
**
** This function has subsumed the contents of the old gageVolumeCheck,
** and hence has become this weird beast- part error checker and part
** (gageShape) initializer.  Oh well...
*/
int
_gageShapeSet(const gageContext *ctx, gageShape *shape,
              const Nrrd *nin, unsigned int baseDim) {
  static const char me[]="_gageShapeSet";
  int ai, cx, cy, cz, statCalc[3], status, ofspc;
  unsigned int minsize;
  const NrrdAxisInfo *ax[3];
  double vecA[4], vecB[3], vecC[3], vecD[4], matA[9],
    spcCalc[3], vecCalc[3][NRRD_SPACE_DIM_MAX], orig[NRRD_SPACE_DIM_MAX];
  airArray *mop;

  /*
  fprintf(stderr, "!%s: ctx = %p (%s, %s)\n", me, ctx,
          (ctx
           ? (ctx->shape->fromOrientation
              ? "YES from orient"
              : "not from orient")
           : "???"),
          (ctx
           ? (ctx->parm.orientationFromSpacing
              ? "YES ofs"
              : "not ofs")
           : "???"));
  */
  /* ------ basic error checking */
  mop = airMopNew();
  airMopAdd(mop, shape, _mopShapeReset, airMopOnError);
  if (!( shape && nin )) {
    biffAddf(GAGE, "%s: got NULL pointer", me);
    airMopError(mop); return 1;
  }
  if (nrrdCheck(nin)) {
    biffMovef(GAGE, NRRD, "%s: basic nrrd validity check failed", me);
    airMopError(mop); return 1;
  }
  if (nrrdTypeBlock == nin->type) {
    biffAddf(GAGE, "%s: need a non-block type nrrd", me);
    airMopError(mop); return 1;
  }
  if (!(nin->dim == 3 + baseDim)) {
    biffAddf(GAGE, "%s: nrrd should be %u-D, not %u-D",
             me, 3 + baseDim, nin->dim);
    airMopError(mop); return 1;
  }
  ax[0] = &(nin->axis[baseDim+0]);
  ax[1] = &(nin->axis[baseDim+1]);
  ax[2] = &(nin->axis[baseDim+2]);

  statCalc[0] = nrrdSpacingCalculate(nin, baseDim + 0,
                                     spcCalc + 0, vecCalc[0]);
  statCalc[1] = nrrdSpacingCalculate(nin, baseDim + 1,
                                     spcCalc + 1, vecCalc[1]);
  statCalc[2] = nrrdSpacingCalculate(nin, baseDim + 2,
                                     spcCalc + 2, vecCalc[2]);
  /* see if nrrdSpacingCalculate ever *failed* */
  if (nrrdSpacingStatusUnknown == statCalc[0]
      || nrrdSpacingStatusUnknown == statCalc[1]
      || nrrdSpacingStatusUnknown == statCalc[2]) {
    biffAddf(GAGE, "%s: nrrdSpacingCalculate trouble on axis %d, %d, or %d",
             me, baseDim + 0, baseDim + 1, baseDim + 2);
    airMopError(mop); return 1;
  }
  if (!( statCalc[0] == statCalc[1] && statCalc[1] == statCalc[2] )) {
    biffAddf(GAGE, "%s: inconsistent spacing information on axes "
             "%u (%s), %u (%s), and %u (%s)", me,
             baseDim + 0, airEnumDesc(nrrdSpacingStatus, statCalc[0]),
             baseDim + 1, airEnumDesc(nrrdSpacingStatus, statCalc[1]),
             baseDim + 2, airEnumDesc(nrrdSpacingStatus, statCalc[2]));
    airMopError(mop); return 1;
  }
  /* this simplifies reasoning in the code that follows */
  status = statCalc[0];
  /* zero spacing would be problematic */
  if (0 == spcCalc[0] && 0 == spcCalc[1] && 0 == spcCalc[2]) {
    biffAddf(GAGE, "%s: spacings (%g,%g,%g) for axes %d,%d,%d not all "
             "non-zero", me, spcCalc[1], spcCalc[1], spcCalc[2],
             baseDim+0, baseDim+1, baseDim+2);
    airMopError(mop); return 1;
  }

  /* error checking based on status */
  if (nrrdSpacingStatusScalarWithSpace == status) {
    biffAddf(GAGE, "%s: sorry, can't handle per-axis spacing that isn't part "
             "of a surrounding world space (%s)",
             me, airEnumStr(nrrdSpacingStatus, status));
    airMopError(mop); return 1;
  }
  /* we no longer allow a nrrd to come in with no spacing info at all */
  if (nrrdSpacingStatusNone == status) {
    biffAddf(GAGE, "%s: sorry, need some spacing info for spatial axes "
             "%u, %u, %u", me,
             baseDim+0, baseDim+1, baseDim+2);
    airMopError(mop); return 1;
  }
  /* actually, there shouldn't be any other options for spacing status
     besides these too; this is just being careful */
  if (!( nrrdSpacingStatusDirection == status
         || nrrdSpacingStatusScalarNoSpace == status )) {
    biffAddf(GAGE, "%s: sorry, can only handle spacing status %d (%s) "
             "or %d (%s), not %d (%s)", me,
             nrrdSpacingStatusDirection,
             airEnumStr(nrrdSpacingStatus, nrrdSpacingStatusDirection),
             nrrdSpacingStatusScalarNoSpace,
             airEnumStr(nrrdSpacingStatus, nrrdSpacingStatusScalarNoSpace),
             status, airEnumStr(nrrdSpacingStatus, status));
    airMopError(mop); return 1;
  }

  if (nrrdSpacingStatusDirection == status) {
    shape->fromOrientation = AIR_TRUE;
    if (3 != nin->spaceDim) {
      biffAddf(GAGE, "%s: orientation space dimension %d != 3",
               me, nin->spaceDim);
      airMopError(mop); return 1;
    }
  } else {
    shape->fromOrientation = AIR_FALSE;
  }

  /* ------ find centering (set shape->center) */
  /* NOTE: when the volume is being crammed in a bi-unit cube, the centering
     will actually affect the positions of the samples.  Otherwise,
     (having full orientation, or using orientationFromSpacing), the
     centering will only affect the probe-able bounds of the volume, but
     the sample positions in space don't depend on centering */
  cx = ax[0]->center;
  cy = ax[1]->center;
  cz = ax[2]->center;
  if (!( cx == cy && cy == cz )) {
    biffAddf(GAGE,
             "%s: axes %d,%d,%d centerings (%s,%s,%s) not all equal",
             me, baseDim+0, baseDim+1, baseDim+2,
             airEnumStr(nrrdCenter, cx),
             airEnumStr(nrrdCenter, cy),
             airEnumStr(nrrdCenter, cz));
    airMopError(mop); return 1;
  }
  /* Hopefully, ctx->parm.defaultCenter == shape->defaultCenter; and this
     worry will be moot if ctx->parm.defaultCenter goes away */
  shape->center = (nrrdCenterUnknown != cx
                   ? cx /* cx == cy == cz, by above */
                   : (ctx
                      ? ctx->parm.defaultCenter
                      : shape->defaultCenter));

  /* ------ find sizes (set shape->size[0,1,2]) */
  shape->size[0] = ax[0]->size;
  shape->size[1] = ax[1]->size;
  shape->size[2] = ax[2]->size;
  minsize = (nrrdCenterCell == shape->center ? 1 : 2);
  /* this can't be relaxed in the face of having full orientation info,
     because even then, you can't have a non-zero probe-able volume if
     there's only one sample along a node-centered axis */
  if (!(shape->size[0] >= minsize
        && shape->size[1] >= minsize
        && shape->size[2] >= minsize )) {
    biffAddf(GAGE, "%s: sizes (%u,%u,%u) must all be >= %u "
             "(min number of %s-centered samples)", me,
             shape->size[0], shape->size[1], shape->size[2],
             minsize, airEnumStr(nrrdCenter, shape->center));
    airMopError(mop); return 1;
  }

  /* ------ find spacings[0,1,2] and ItoW matrix */
  /* Hopefully, ctx->parm.orientationFromSpacing and
     shape->orientationFromSpacing don't represent competing interests;
     this worry will be moot if ctx->parm.orientationFromSpacing goes away */
  ofspc = ((ctx && ctx->parm.orientationFromSpacing)
           || shape->orientationFromSpacing);
  if (shape->fromOrientation || ofspc) {
    if (ofspc) {
      /* need abs() in case an axis had negative spacing */
      ELL_3V_ABS(shape->spacing, spcCalc);
      ELL_3V_SET(vecCalc[0], airSgn(spcCalc[0]), 0.0, 0.0);
      ELL_3V_SET(vecCalc[1], 0.0, airSgn(spcCalc[1]), 0.0);
      ELL_3V_SET(vecCalc[2], 0.0, 0.0, airSgn(spcCalc[2]));
    } else {
      ELL_3V_COPY(shape->spacing, spcCalc);
      /* vecCalc set by nrrdSpacingCalculate */
    }
    if (shape->fromOrientation) {
      /* if the spaceOrigin isn't set, this will be all NaNs */
      nrrdSpaceOriginGet(nin, orig);
    } else {
      /* sorry, if you want to specify an image origin that over-rides the
         behavior of centering the volume at (0,0,0), then it has to be
         done through the full orientation info.  That is, we don't want
         to use nrrdOriginCalculate() because otherwise the logic gets
         too complicated */
      ELL_3V_SET(orig, AIR_NAN, AIR_NAN, AIR_NAN);
    }
    if (!ELL_3V_EXISTS(orig)) {
      /* don't have origin, for whatever reason; center volume on (0,0,0) */
      ELL_3V_SET(orig, 0.0, 0.0, 0.0);
      ELL_3V_SCALE_INCR(orig, -(shape->size[0] - 1.0)*shape->spacing[0]/2.0,
                        vecCalc[0]);
      ELL_3V_SCALE_INCR(orig, -(shape->size[1] - 1.0)*shape->spacing[1]/2.0,
                        vecCalc[1]);
      ELL_3V_SCALE_INCR(orig, -(shape->size[2] - 1.0)*shape->spacing[2]/2.0,
                        vecCalc[2]);
    }
    vecD[3] = 0;
    ELL_3V_SCALE(vecD, spcCalc[0], vecCalc[0]);
    ELL_4MV_COL0_SET(shape->ItoW, vecD);
    ELL_3V_SCALE(vecD, spcCalc[1], vecCalc[1]);
    ELL_4MV_COL1_SET(shape->ItoW, vecD);
    ELL_3V_SCALE(vecD, spcCalc[2], vecCalc[2]);
    ELL_4MV_COL2_SET(shape->ItoW, vecD);
    vecD[3] = 1;
    ELL_3V_COPY(vecD, orig);
    ELL_4MV_COL3_SET(shape->ItoW, vecD);
    /*
    fprintf(stderr, "%s: %g (%g,%g,%g)\n", me,
            spcCalc[0], vecCalc[0][0], vecCalc[0][1], vecCalc[0][2]);
    fprintf(stderr, "%s: %g (%g,%g,%g)\n", me,
            spcCalc[1], vecCalc[1][0], vecCalc[1][1], vecCalc[1][2]);
    fprintf(stderr, "%s: %g (%g,%g,%g)\n", me,
            spcCalc[2], vecCalc[2][0], vecCalc[2][1], vecCalc[2][2]);
    */
    /*
    fprintf(stderr, "%s: ItoW = %g %g %g %g\n", me,
           shape->ItoW[ 0], shape->ItoW[ 1], shape->ItoW[ 2], shape->ItoW[ 3]);
    fprintf(stderr, "%s:        %g %g %g %g\n", me,
           shape->ItoW[ 4], shape->ItoW[ 5], shape->ItoW[ 6], shape->ItoW[ 7]);
    fprintf(stderr, "%s:        %g %g %g %g\n", me,
           shape->ItoW[ 8], shape->ItoW[ 9], shape->ItoW[10], shape->ItoW[11]);
    fprintf(stderr, "%s:        %g %g %g %g\n", me,
           shape->ItoW[12], shape->ItoW[13], shape->ItoW[14], shape->ItoW[15]);
    */
  } else { /* not (shape->fromOrientation || ofspc) */
    double maxLen, volHalfLen[3];
    size_t num[3];
    /* ------ learn lengths for bounding nrrd in bi-unit cube */
    ELL_3V_ABS(shape->spacing, spcCalc);
    maxLen = 0.0;
    for (ai=0; ai<=2; ai++) {
      num[ai] = (nrrdCenterNode == shape->center
                 ? shape->size[ai]-1
                 : shape->size[ai]);
      volHalfLen[ai] = num[ai]*shape->spacing[ai];
      maxLen = AIR_MAX(maxLen, volHalfLen[ai]);
    }
    /* Thu Dec 13 02:45:01 EST 2007
       fixed long-standing bug in handling vols without full orientation info:
       spacing[ai] was never scaled to account for being crammed into
       the bi-unit cube!! */
    for (ai=0; ai<=2; ai++) {
      volHalfLen[ai] /= maxLen;
      shape->spacing[ai] = 2*volHalfLen[ai]/num[ai];
    }
    ELL_3V_SET(vecC, 0, 0, 0);
    shapeUnitItoW(shape, vecA, vecC, volHalfLen);
    ELL_3V_SET(vecC, 1, 0, 0);
    shapeUnitItoW(shape, vecB, vecC, volHalfLen);
    ELL_3V_SUB(vecD, vecB, vecA);
    vecD[3] = 0;
    ELL_4MV_COL0_SET(shape->ItoW, vecD);

    ELL_3V_SET(vecC, 0, 1, 0);
    shapeUnitItoW(shape, vecB, vecC, volHalfLen);
    ELL_3V_SUB(vecD, vecB, vecA);
    vecD[3] = 0;
    ELL_4MV_COL1_SET(shape->ItoW, vecD);

    ELL_3V_SET(vecC, 0, 0, 1);
    shapeUnitItoW(shape, vecB, vecC, volHalfLen);
    ELL_3V_SUB(vecD, vecB, vecA);
    vecD[3] = 0;
    ELL_4MV_COL2_SET(shape->ItoW, vecD);

    vecA[3] = 1;
    ELL_4MV_COL3_SET(shape->ItoW, vecA);
  }

  /* ------ set the rest of the matrices */
  ell_4m_inv_d(shape->WtoI, shape->ItoW);
  ELL_34M_EXTRACT(matA, shape->ItoW);
  ell_3m_inv_d(shape->ItoWSubInv, matA);
  ELL_3M_TRANSPOSE(shape->ItoWSubInvTransp, shape->ItoWSubInv);

  airMopOkay(mop);
  return 0;
}
示例#2
0
/*
** _gageShapeSet
**
** we are serving two masters here.  If ctx is non-NULL, we are being called
** from within gage, and we are to be lax or strict according to the settings
** of ctx->parm.requireAllSpacings and ctx->parm.requireEqualCenters.  If
** ctx is NULL, gageShapeSet was called, in which case we go with lax
** behavior (nothing "required")
**
** This function has subsumed the old gageVolumeCheck, and hence has 
** become this weird beast- part error checker and part (gageShape)
** initializer.  Oh well...
*/
int
_gageShapeSet(const gageContext *ctx, gageShape *shape,
              const Nrrd *nin, unsigned int baseDim) {
  char me[]="_gageShapeSet", err[BIFF_STRLEN];
  int ai, cx, cy, cz, defCenter, statCalc[3];
  unsigned int minsize, sx, sy, sz;
  const NrrdAxisInfo *ax[3];
  double maxLen, defSpacing,
    vecA[4], vecB[3], vecC[3], vecD[4], matA[9],
    spcCalc[3], vecCalc[3][NRRD_SPACE_DIM_MAX], orig[NRRD_SPACE_DIM_MAX];

  /* ------ basic error checking */
  if (!( shape && nin )) {
    sprintf(err, "%s: got NULL pointer", me);
    biffAdd(GAGE, err);  if (shape) { gageShapeReset(shape); }
    return 1;
  }
  if (nrrdCheck(nin)) {
    sprintf(err, "%s: basic nrrd validity check failed", me);
    biffMove(GAGE, err, NRRD); gageShapeReset(shape);
    return 1;
  }
  if (nrrdTypeBlock == nin->type) {
    sprintf(err, "%s: need a non-block type nrrd", me);
    biffAdd(GAGE, err); gageShapeReset(shape);
    return 1;
  }
  if (!(nin->dim == 3 + baseDim)) {
    sprintf(err, "%s: nrrd should be %u-D, not %u-D",
            me, 3 + baseDim, nin->dim);
    biffAdd(GAGE, err); gageShapeReset(shape);
    return 1;
  }
  ax[0] = &(nin->axis[baseDim+0]);
  ax[1] = &(nin->axis[baseDim+1]);
  ax[2] = &(nin->axis[baseDim+2]);
  /*
  if (1) {
    unsigned int ai;
    for (ai=0; ai<3; ai++) {
      fprintf(stderr, "!%s: ax[%u] spc = %g, sd = %g %g %g\n", me, ai,
              ax[ai]->spacing,
              ax[ai]->spaceDirection[0],
              ax[ai]->spaceDirection[1],
              ax[ai]->spaceDirection[2]);
    }
  }
  */
  statCalc[0] = nrrdSpacingCalculate(nin, baseDim + 0,
                                     spcCalc + 0, vecCalc[0]);
  statCalc[1] = nrrdSpacingCalculate(nin, baseDim + 1,
                                     spcCalc + 1, vecCalc[1]);
  statCalc[2] = nrrdSpacingCalculate(nin, baseDim + 2,
                                     spcCalc + 2, vecCalc[2]);
  /*
  fprintf(stderr, "!%s: axis stat %d %d %d\n", me,
          statCalc[0], statCalc[1], statCalc[2]);
  */
  /* see if nrrdSpacingCalculate ever *failed* */
  if (nrrdSpacingStatusUnknown == statCalc[0]
      || nrrdSpacingStatusUnknown == statCalc[1]
      || nrrdSpacingStatusUnknown == statCalc[2]) {
    sprintf(err, "%s: nrrdSpacingCalculate trouble on axis %d, %d, or %d",
            me, baseDim + 0, baseDim + 1, baseDim + 2);
    biffAdd(GAGE, err); gageShapeReset(shape);
    return 1;
  }
  /* see if nrrdSpacingCalculate encountered an axis with no space
     direction in a nrrd that nominally has a surrounding space */
  if (nrrdSpacingStatusScalarWithSpace == statCalc[0]
      || nrrdSpacingStatusScalarWithSpace == statCalc[1]
      || nrrdSpacingStatusScalarWithSpace == statCalc[2]) {
    sprintf(err, "%s: nrrdSpacingCalculate weirdness on axis %d, %d, or %d",
            me, baseDim + 0, baseDim + 1, baseDim + 2);
    biffAdd(GAGE, err); gageShapeReset(shape);
    return 1;
  }
  if (!( (   nrrdSpacingStatusDirection == statCalc[0]
          && nrrdSpacingStatusDirection == statCalc[1]
          && nrrdSpacingStatusDirection == statCalc[2])
         ||
         (   nrrdSpacingStatusDirection != statCalc[0]
          && nrrdSpacingStatusDirection != statCalc[1]
          && nrrdSpacingStatusDirection != statCalc[2])
         )) {
    sprintf(err, "%s: inconsistent space directions use "
            "in axis %d, %d, and %d",
            me, baseDim + 0, baseDim + 1, baseDim + 2);
    biffAdd(GAGE, err); gageShapeReset(shape);
    return 1;
  }
  if (   nrrdSpacingStatusDirection == statCalc[0]
      && nrrdSpacingStatusDirection == statCalc[1]
      && nrrdSpacingStatusDirection == statCalc[2]) {
    /* this will get reset to false in case of error */
    shape->fromOrientation = AIR_TRUE;
  } else {
    shape->fromOrientation = AIR_FALSE;
  }
  /* oh yea, we should make sure the space dimension is right! */
  if (shape->fromOrientation && 3 != nin->spaceDim) {
    sprintf(err, "%s: orientation space dimension %d != 3",
            me, nin->spaceDim);
    biffAdd(GAGE, err); gageShapeReset(shape);
    return 1;
  }

  /* ------ find centering (set shape->center) */
  /* HEY: when we have full orientation information (via spaceDirections
     and spaceOrigin) the centering information is moot for determining
     shape, but until all usage of gageShape stuff is properly overhauled
     to take orientation into account, we'll still set shape->center */
  cx = ax[0]->center;
  cy = ax[1]->center;
  cz = ax[2]->center;
  if (ctx && ctx->parm.requireEqualCenters) {
    if (!( cx == cy && cx == cz )) {
      sprintf(err, "%s: axes %d,%d,%d centerings (%s,%s,%s) not equal", me,
              baseDim+0, baseDim+1, baseDim+2,
              airEnumStr(nrrdCenter, cx),
              airEnumStr(nrrdCenter, cy),
              airEnumStr(nrrdCenter, cz));
      biffAdd(GAGE, err); gageShapeReset(shape);
      return 1;
    }
  } else {
    if (   (nrrdCenterUnknown != cx && nrrdCenterUnknown != cy && cx != cy) 
        || (nrrdCenterUnknown != cy && nrrdCenterUnknown != cz && cy != cz) 
        || (nrrdCenterUnknown != cx && nrrdCenterUnknown != cz && cx != cz) ) {
      sprintf(err, "%s: two known centerings (of %s,%s,%s) are unequal", me,
              airEnumStr(nrrdCenter, cx),
              airEnumStr(nrrdCenter, cy),
              airEnumStr(nrrdCenter, cz));
      biffAdd(GAGE, err); gageShapeReset(shape);
      return 1;
    }
  }
  defCenter = ctx ? ctx->parm.defaultCenter : shape->defaultCenter;
  shape->center = (nrrdCenterUnknown != cx ? cx
                   : (nrrdCenterUnknown != cy ? cy
                      : (nrrdCenterUnknown != cz ? cz
                         : defCenter)));

  /* ------ find sizes (set shape->size[0,1,2]) */
  sx = ax[0]->size;
  sy = ax[1]->size;
  sz = ax[2]->size;
  minsize = (nrrdCenterCell == shape->center ? 1 : 2);
  /* HEY: perhaps this should be relaxed if we have full orientation info */
  if (!(sx >= minsize && sy >= minsize && sz >= minsize )) {
    sprintf(err, "%s: sizes (%u,%u,%u) must all be >= %u "
            "(min number of %s-centered samples)", me, 
            sx, sy, sz, minsize, airEnumStr(nrrdCenter, shape->center));
    biffAdd(GAGE, err); gageShapeReset(shape);
    return 1;
  }
  shape->size[0] = sx;
  shape->size[1] = sy;
  shape->size[2] = sz;

  /* ------ find spacings (set shape->spacing[0,1,2]) */
  if (shape->fromOrientation) {
    shape->spacing[0] = AIR_ABS(spcCalc[0]);
    shape->spacing[1] = AIR_ABS(spcCalc[1]);
    shape->spacing[2] = AIR_ABS(spcCalc[2]);
    for (ai=0; ai<=2; ai++) {
      shape->volHalfLen[ai] = AIR_NAN;
      shape->voxLen[ai] = AIR_NAN;
    }
  } else {
    double xs, ys, zs;
    unsigned int num[3];
    xs = ax[0]->spacing;
    ys = ax[1]->spacing;
    zs = ax[2]->spacing;
    if (ctx && ctx->parm.requireAllSpacings) {
      if (!( AIR_EXISTS(xs) && AIR_EXISTS(ys) && AIR_EXISTS(zs) )) {
        sprintf(err, "%s: spacings for axes %d,%d,%d don't all exist",
                me, baseDim+0, baseDim+1, baseDim+2);
        biffAdd(GAGE, err); gageShapeReset(shape);
        return 1;
      }
    }
    /* there is no shape->defaultSpacing, we'll go out on a limb ... */
    defSpacing = ctx ? ctx->parm.defaultSpacing : nrrdDefaultSpacing;
    xs = AIR_EXISTS(xs) ? xs : defSpacing;
    ys = AIR_EXISTS(ys) ? ys : defSpacing;
    zs = AIR_EXISTS(zs) ? zs : defSpacing;
    if (!( xs != 0 && ys != 0 && zs != 0 )) {
      sprintf(err, "%s: spacings (%g,%g,%g) for axes %d,%d,%d not all "
              "non-zero", me, xs, ys, zs, baseDim+0, baseDim+1, baseDim+2);
      biffAdd(GAGE, err); gageShapeReset(shape);
      return 1;
    }
    /* ------ learn lengths for bounding nrrd in bi-unit cube
       (set shape->volHalfLen[0,1,2] and shape->voxLen[0,1,2]) */
    shape->spacing[0] = AIR_ABS(xs);
    shape->spacing[1] = AIR_ABS(ys);
    shape->spacing[2] = AIR_ABS(zs);
    maxLen = 0.0;
    for (ai=0; ai<=2; ai++) {
      num[ai] = (nrrdCenterNode == shape->center
                 ? shape->size[ai]-1
                 : shape->size[ai]);
      shape->volHalfLen[ai] = num[ai]*shape->spacing[ai];
      maxLen = AIR_MAX(maxLen, shape->volHalfLen[ai]);
    }
    /* Thu Dec 13 02:45:01 EST 2007
       fixed long-standing bug in handling vols without full orientation info:
       spacing[ai] was never scaled to account for being crammed into
       the bi-unit cube!! */
    for (ai=0; ai<=2; ai++) {
      shape->volHalfLen[ai] /= maxLen;
      shape->spacing[ai] = shape->voxLen[ai] = 2*shape->volHalfLen[ai]/num[ai];
    }
  }
  
  /* ------ set transform matrices */
  if (shape->fromOrientation) {
    /* find translation vector (we check above that spaceDim == 3) */
    nrrdSpaceOriginGet(nin, orig);
    if (!( AIR_EXISTS(orig[0]) &&
           AIR_EXISTS(orig[1]) && 
           AIR_EXISTS(orig[2]) )) {
      /* don't have origin, so set it to come from the middle of volume */
      ELL_3V_SET(orig, 0.0f, 0.0f, 0.0f);
      ELL_3V_SCALE_INCR(orig, -(shape->size[0] - 1.0f)*spcCalc[0]/2.0f,
                        vecCalc[0]);
      ELL_3V_SCALE_INCR(orig, -(shape->size[1] - 1.0f)*spcCalc[1]/2.0f,
                        vecCalc[1]);
      ELL_3V_SCALE_INCR(orig, -(shape->size[2] - 1.0f)*spcCalc[2]/2.0f,
                        vecCalc[2]);
    }
    vecD[3] = 0;
    ELL_3V_SCALE(vecD, spcCalc[0], vecCalc[0]);
    ELL_4MV_COL0_SET(shape->ItoW, vecD);
    ELL_3V_SCALE(vecD, spcCalc[1], vecCalc[1]);
    ELL_4MV_COL1_SET(shape->ItoW, vecD);
    ELL_3V_SCALE(vecD, spcCalc[2], vecCalc[2]);
    ELL_4MV_COL2_SET(shape->ItoW, vecD);
    vecD[3] = 1;
    ELL_3V_COPY(vecD, orig);
    ELL_4MV_COL3_SET(shape->ItoW, vecD);
    /*
    fprintf(stderr, "%s: %g (%g,%g,%g)\n", me,
            spcCalc[0], vecCalc[0][0], vecCalc[0][1], vecCalc[0][2]);
    fprintf(stderr, "%s: %g (%g,%g,%g)\n", me,
            spcCalc[1], vecCalc[1][0], vecCalc[1][1], vecCalc[1][2]);
    fprintf(stderr, "%s: %g (%g,%g,%g)\n", me,
            spcCalc[2], vecCalc[2][0], vecCalc[2][1], vecCalc[2][2]);
    */
  } else {
    ELL_3V_SET(vecC, 0, 0, 0);
    _gageShapeUnitItoW(shape, vecA, vecC);
    ELL_3V_SET(vecC, 1, 0, 0);
    _gageShapeUnitItoW(shape, vecB, vecC);
    ELL_3V_SUB(vecD, vecB, vecA);
    vecD[3] = 0;
    ELL_4MV_COL0_SET(shape->ItoW, vecD);

    ELL_3V_SET(vecC, 0, 1, 0);
    _gageShapeUnitItoW(shape, vecB, vecC);
    ELL_3V_SUB(vecD, vecB, vecA);
    vecD[3] = 0;
    ELL_4MV_COL1_SET(shape->ItoW, vecD);

    ELL_3V_SET(vecC, 0, 0, 1);
    _gageShapeUnitItoW(shape, vecB, vecC);
    ELL_3V_SUB(vecD, vecB, vecA);
    vecD[3] = 0;
    ELL_4MV_COL2_SET(shape->ItoW, vecD);

    vecA[3] = 1;
    ELL_4MV_COL3_SET(shape->ItoW, vecA);
  }
  ell_4m_inv_d(shape->WtoI, shape->ItoW);

  ELL_34M_EXTRACT(matA, shape->ItoW);
  ell_3m_inv_d(shape->ItoWSubInv, matA);
  ELL_3M_TRANSPOSE(shape->ItoWSubInvTransp, shape->ItoWSubInv);

  return 0;
}