/* ******** limnCameraUpdate() ** ** sets in cam: W2V, V2W, U, V, N, vspNeer, vspFaar, vspDist ** and, if fov and aspect are set, this also sets uRange and vRange ** ** This does use biff to describe problems with camera settings */ int limnCameraUpdate(limnCamera *cam) { static const char me[] = "limnCameraUpdate"; double len, bb[4], uu[4], vv[4], nn[4], TT[16], RR[16]; if (!cam) { biffAddf(LIMN, "%s: got NULL pointer", me); return 1; } ELL_4V_SET(uu, 0, 0, 0, 0); ELL_4V_SET(vv, 0, 0, 0, 0); ELL_4V_SET(nn, 0, 0, 0, 0); ELL_4V_SET(bb, 0, 0, 0, 1); ELL_3V_SUB(nn, cam->at, cam->from); len = ELL_3V_LEN(nn); if (!len) { biffAddf(LIMN, "%s: cam->at (%g,%g,%g) == cam->from", me, cam->at[0], cam->at[1], cam->at[2]); return 1; } if (cam->atRelative) { /* ctx->cam->{neer,dist} are "at" relative */ cam->vspNeer = cam->neer + len; cam->vspFaar = cam->faar + len; cam->vspDist = cam->dist + len; } else { /* ctx->cam->{neer,dist} are eye relative */ cam->vspNeer = cam->neer; cam->vspFaar = cam->faar; cam->vspDist = cam->dist; } if (!(cam->vspNeer > 0 && cam->vspDist > 0 && cam->vspFaar > 0)) { biffAddf(LIMN, "%s: eye-relative near (%g), dist (%g), or far (%g) <= 0", me, cam->vspNeer, cam->vspDist, cam->vspFaar); return 1; } if (!(cam->vspNeer <= cam->vspFaar)) { biffAddf(LIMN, "%s: eye-relative near (%g) further than far (%g)", me, cam->vspNeer, cam->vspFaar); return 1 ; } if (AIR_EXISTS(cam->fov)) { if (!( AIR_IN_OP(0.0, cam->fov, 180.0) )) { biffAddf(LIMN, "%s: cam->fov (%g) not in valid range between 0 and 180", me, cam->fov); return 1 ; } if (!AIR_EXISTS(cam->aspect)) { biffAddf(LIMN, "%s: cam->fov set, but cam->aspect isn't", me); return 1; } /* "fov" is half vertical angle */ cam->vRange[0] = -tan(cam->fov*AIR_PI/360)*(cam->vspDist); cam->vRange[1] = -cam->vRange[0]; cam->uRange[0] = cam->vRange[0]*(cam->aspect); cam->uRange[1] = -cam->uRange[0]; } /* else cam->fov isn't set, but we're not going to complain if uRange and vRange aren't both set ... */ ELL_3V_SCALE(nn, 1.0/len, nn); ELL_3V_CROSS(uu, nn, cam->up); len = ELL_3V_LEN(uu); if (!len) { biffAddf(LIMN, "%s: cam->up is co-linear with view direction", me); return 1 ; } ELL_3V_SCALE(uu, 1.0/len, uu); if (cam->rightHanded) { ELL_3V_CROSS(vv, nn, uu); } else { ELL_3V_CROSS(vv, uu, nn); } ELL_4V_COPY(cam->U, uu); ELL_4V_COPY(cam->V, vv); ELL_4V_COPY(cam->N, nn); ELL_4M_TRANSLATE_SET(TT, -cam->from[0], -cam->from[1], -cam->from[2]); ELL_4M_ROWS_SET(RR, uu, vv, nn, bb); ELL_4M_MUL(cam->W2V, RR, TT); ell_4m_inv_d(cam->V2W, cam->W2V); return 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; }
int main(int argc, char *argv[]) { char *me, *outS, *err; hestOpt *hopt; hestParm *hparm; airArray *mop; Nrrd *nin, *nmat, *ninv, *nidn; int (*func)(Nrrd *, Nrrd *); double m3[9], m4[16]; me = argv[0]; mop = airMopNew(); hparm = hestParmNew(); hopt = NULL; airMopAdd(mop, hparm, (airMopper)hestParmFree, airMopAlways); hestOptAdd(&hopt, NULL, "matrix", airTypeOther, 1, 1, &nin, NULL, "transform(s) to apply to image", NULL, NULL, nrrdHestNrrd); hestOptAdd(&hopt, "o", "filename", airTypeString, 1, 1, &outS, "-", "file to write output nrrd to"); hestParseOrDie(hopt, argc-1, argv+1, hparm, me, invInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); ninv = nrrdNew(); airMopAdd(mop, ninv, (airMopper)nrrdNuke, airMopAlways); nidn = nrrdNew(); airMopAdd(mop, nidn, (airMopper)nrrdNuke, airMopAlways); nmat = nrrdNew(); airMopAdd(mop, nmat, (airMopper)nrrdNuke, airMopAlways); nrrdConvert(nmat, nin, nrrdTypeDouble); if (3 == nmat->axis[0].size && 3 == nmat->axis[1].size) { ell_3m_inv_d(m3, (double *)nmat->data); fprintf(stderr, "%s: input:\n", me); ell_3m_print_d(stderr, (double *)nmat->data); fprintf(stderr, "%s: inverse:\n", me); ell_3m_print_d(stderr, m3); } if (4 == nmat->axis[0].size && 4 == nmat->axis[1].size) { ell_4m_inv_d(m4, (double *)nmat->data); fprintf(stderr, "%s: input:\n", me); ell_4m_print_d(stderr, (double *)nmat->data); fprintf(stderr, "%s: inverse:\n", me); ell_4m_print_d(stderr, m4); } func = (nmat->axis[0].size == nmat->axis[1].size ? ell_Nm_inv : ell_Nm_pseudo_inv); if (func(ninv, nmat)) { airMopAdd(mop, err = biffGetDone(ELL), airFree, airMopAlways); fprintf(stderr, "%s: problem inverting:\n%s\n", me, err); airMopError(mop); return 1; } if (nrrdSave(outS, ninv, NULL)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: problem saving output:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); exit(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; }