/* ******** tenGradientDistribute ** ** Takes the given list of gradients, normalizes their lengths, ** optionally jitters their positions, does point repulsion, and then ** (optionally) selects a combination of directions with minimum vector sum. ** ** The complicated part of this is the point repulsion, which uses a ** gradient descent with variable set size. The progress of the system ** is measured by decrease in potential (when its measurement doesn't ** overflow to infinity) or an increase in the minimum angle. When a ** step results in negative progress, the step size is halved, and the ** iteration is attempted again. Based on the observation that at ** some points the step size must be made very small to get progress, ** the step size is cautiously increased ("nudged") at every ** iteration, to try to avoid using an overly small step. The amount ** by which the step is nudged is halved everytime the step is halved, ** to avoid endless cycling through step sizes. */ int tenGradientDistribute(Nrrd *nout, const Nrrd *nin, tenGradientParm *tgparm) { static const char me[]="tenGradientDistribute"; char filename[AIR_STRLEN_SMALL]; unsigned int ii, num, iter, oldIdx, newIdx, edgeShrink; airArray *mop; Nrrd *npos[2]; double *pos, len, meanVelocity, pot, potNew, potD, edge, edgeMin, angle, angleNew; int E; if (!nout || tenGradientCheck(nin, nrrdTypeUnknown, 2) || !tgparm) { biffAddf(TEN, "%s: got NULL pointer or invalid input", me); return 1; } num = AIR_UINT(nin->axis[1].size); mop = airMopNew(); npos[0] = nrrdNew(); npos[1] = nrrdNew(); airMopAdd(mop, npos[0], (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, npos[1], (airMopper)nrrdNuke, airMopAlways); if (nrrdConvert(npos[0], nin, nrrdTypeDouble) || nrrdConvert(npos[1], nin, nrrdTypeDouble)) { biffMovef(TEN, NRRD, "%s: trouble allocating temp buffers", me); airMopError(mop); return 1; } pos = (double*)(npos[0]->data); for (ii=0; ii<num; ii++) { ELL_3V_NORM(pos, pos, len); pos += 3; } if (tgparm->jitter) { if (tenGradientJitter(npos[0], npos[0], tgparm->jitter)) { biffAddf(TEN, "%s: problem jittering input", me); airMopError(mop); return 1; } } /* initialize things prior to first iteration; have to make sure that loop body tests pass 1st time around */ meanVelocity = 2*tgparm->minVelocity; potD = -2*tgparm->minPotentialChange; oldIdx = 0; newIdx = 1; tgparm->step = tgparm->initStep; tgparm->nudge = 0.1; tenGradientMeasure(&pot, &angle, NULL, npos[oldIdx], tgparm, AIR_TRUE); for (iter = 0; ((!!tgparm->minIteration && iter < tgparm->minIteration) || (iter < tgparm->maxIteration && (!tgparm->minPotentialChange || !AIR_EXISTS(potD) || -potD > tgparm->minPotentialChange) && (!tgparm->minVelocity || meanVelocity > tgparm->minVelocity) && tgparm->step > FLT_MIN)); iter++) { /* copy positions from old to new */ memcpy(npos[newIdx]->data, npos[oldIdx]->data, 3*num*sizeof(double)); edge = tenGradientIdealEdge(num, tgparm->single); edgeShrink = 0; /* try to do a position update, which will fail if repulsion values explode, from having an insufficiently small edge normalization, so retry with smaller edge next time */ do { E = _tenGradientUpdate(&meanVelocity, &edgeMin, npos[newIdx], edge, tgparm); if (E) { if (edgeShrink > tgparm->maxEdgeShrink) { biffAddf(TEN, "%s: %u > %u edge shrinks (%g), update still failed", me, edgeShrink, tgparm->maxEdgeShrink, edge); airMopError(mop); return 1; } edgeShrink++; /* re-initialize positions (HEY ugly code logic) */ memcpy(npos[newIdx]->data, npos[oldIdx]->data, 3*num*sizeof(double)); edge = edgeMin; } } while (E); tenGradientMeasure(&potNew, &angleNew, NULL, npos[newIdx], tgparm, AIR_TRUE); if ((AIR_EXISTS(pot) && AIR_EXISTS(potNew) && potNew <= pot) || angleNew >= angle) { /* there was progress of some kind, either through potential decrease, or angle increase */ potD = 2*(potNew - pot)/(potNew + pot); if (!(iter % tgparm->report) && tgparm->verbose) { fprintf(stderr, "%s(%d): . . . . . . step = %g, edgeShrink = %u\n" " velo = %g<>%g, phi = %g ~ %g<>%g, angle = %g ~ %g\n", me, iter, tgparm->step, edgeShrink, meanVelocity, tgparm->minVelocity, pot, potD, tgparm->minPotentialChange, angle, angleNew - angle); } if (tgparm->snap && !(iter % tgparm->snap)) { sprintf(filename, "%05d.nrrd", iter/tgparm->snap); if (tgparm->verbose) { fprintf(stderr, "%s(%d): . . . . . . saving %s\n", me, iter, filename); } if (nrrdSave(filename, npos[newIdx], NULL)) { char *serr; serr = biffGetDone(NRRD); if (tgparm->verbose) { /* perhaps shouldn't have this check */ fprintf(stderr, "%s: iter=%d, couldn't save snapshot:\n%s" "continuing ...\n", me, iter, serr); } free(serr); } } tgparm->step *= 1 + tgparm->nudge; tgparm->step = AIR_MIN(tgparm->initStep, tgparm->step); pot = potNew; angle = angleNew; /* swap buffers */ newIdx = 1 - newIdx; oldIdx = 1 - oldIdx; } else { /* oops, did not make progress; back off and try again */ if (tgparm->verbose) { fprintf(stderr, "%s(%d): ######## step %g --> %g\n" " phi = %g --> %g ~ %g, angle = %g --> %g\n", me, iter, tgparm->step, tgparm->step/2, pot, potNew, potD, angle, angleNew); } tgparm->step /= 2; tgparm->nudge /= 2; } } /* when the for-loop test fails, we stop before computing the next iteration (which starts with copying from npos[oldIdx] to npos[newIdx]) ==> the final results are in npos[oldIdx] */ if (tgparm->verbose) { fprintf(stderr, "%s: .......................... done distribution:\n" " (%d && %d) || (%d \n" " && (%d || %d || %d) \n" " && (%d || %d) \n" " && %d) is false\n", me, !!tgparm->minIteration, iter < tgparm->minIteration, iter < tgparm->maxIteration, !tgparm->minPotentialChange, !AIR_EXISTS(potD), AIR_ABS(potD) > tgparm->minPotentialChange, !tgparm->minVelocity, meanVelocity > tgparm->minVelocity, tgparm->step > FLT_MIN); fprintf(stderr, " iter=%d, velo = %g<>%g, phi = %g ~ %g<>%g;\n", iter, meanVelocity, tgparm->minVelocity, pot, potD, tgparm->minPotentialChange); fprintf(stderr, " minEdge = %g; idealEdge = %g\n", 2*sin(angle/2), tenGradientIdealEdge(num, tgparm->single)); } tenGradientMeasure(&pot, NULL, NULL, npos[oldIdx], tgparm, AIR_FALSE); tgparm->potential = pot; tenGradientMeasure(&pot, &angle, &edge, npos[oldIdx], tgparm, AIR_TRUE); tgparm->potentialNorm = pot; tgparm->angle = angle; tgparm->edge = edge; tgparm->itersUsed = iter; if ((tgparm->minMeanImprovement || tgparm->minMean) && !tgparm->single) { if (tgparm->verbose) { fprintf(stderr, "%s: optimizing balance:\n", me); } if (tenGradientBalance(nout, npos[oldIdx], tgparm)) { biffAddf(TEN, "%s: failed to minimize vector sum of gradients", me); airMopError(mop); return 1; } if (tgparm->verbose) { fprintf(stderr, "%s: .......................... done balancing.\n", me); } } else { if (tgparm->verbose) { fprintf(stderr, "%s: .......................... (no balancing)\n", me); } if (nrrdConvert(nout, npos[oldIdx], nrrdTypeDouble)) { biffMovef(TEN, NRRD, "%s: couldn't set output", me); airMopError(mop); return 1; } } airMopOkay(mop); return 0; }
int main(int argc, const char *argv[]) { const char *me; char *tmp; airArray *mop; AIR_UNUSED(argc); me = argv[0]; mop = airMopNew(); /* HEY: the creation and comparison of these strings is not very flexible, especially the hard-coded "good[]" strings */ biffAdd("axis", "the first error axis"); biffAdd("chard", "the first error chard"); biffAdd("chard", "the second error chard"); biffAdd("axis", "the second error axis"); biffAdd("chard", "the third error chard"); biffAdd("bingo", "zero-eth bingo message"); biffMove("bingo", NULL, "chard"); biffAdd("bingo", "the first error bingo"); biffAdd("bingo", "the second bll boo boo boo error bingo"); biffAdd("bingo", "the third error bingo"); biffAdd("axis", "the third error axis"); { char good[] = ("[bingo] the third error bingo\n" "[bingo] the second bll boo boo boo error bingo\n" "[bingo] the first error bingo\n" "[bingo] [chard] the third error chard\n" "[bingo] [chard] the second error chard\n" "[bingo] [chard] the first error chard\n" "[bingo] zero-eth bingo message\n"); tmp = biffGet("bingo"); airMopAdd(mop, tmp, airFree, airMopAlways); /* an ugly macro */ #define COMPARE(N) \ airMopAdd(mop, tmp, airFree, airMopAlways); \ if (strcmp(tmp, good)) { \ fprintf(stderr, "%s: %d: #%s# != #%s#\n", me, N, tmp, good); \ airMopError(mop); \ exit(1); \ } COMPARE(1); } { char good[] = ""; tmp = biffGet("chard"); COMPARE(2); } { char good[] = ("[axis] the third error axis\n" "[axis] the second error axis\n" "[axis] the first error axis\n"); tmp = biffGet("axis"); COMPARE(3); } biffAdd("harold", "the first error harold"); biffAdd("harold", "the second error harold"); biffAdd("harold", "the third error harold"); { char good[] = ("[harold] the third error harold\n" "[harold] the second error harold\n" "[harold] the first error harold\n"); tmp = biffGetDone("harold"); COMPARE(4); } biffDone("bingo"); biffDone("axis"); biffDone("chard"); biffAdd("axis", "the first error axis"); biffAdd("axis", "the second error axis"); biffAdd("axis", "the third error axis"); biffAdd("axis", "the fourth error axis"); biffAdd("axis", "the fifth error axis"); { char good[] = ("[axis] the fifth error axis\n" "[axis] the fourth error axis\n" "[axis] the third error axis\n" "[axis] the second error axis\n" "[axis] the first error axis\n"); tmp = biffGetDone("axis"); COMPARE(5); } biffAddf("test", "%s: this is a test of biffAddf %d %g", "me", 1, 4.2); { char good[] = "[test] me: this is a test of biffAddf 1 4.2\n"; tmp = biffGetDone("test"); COMPARE(6); } biffAddf("test2", "%s: this is a test of biffAddf %d %g", "me", 1, 4.2); biffMovef("test3", "test2", "%s: testing biffMove %d.", "me", 1729); { char good[] = ("[test3] me: testing biffMove 1729.\n" "[test3] [test2] me: this is a test of biffAddf 1 4.2\n"); tmp = biffGet("test3"); COMPARE(7); } airMopOkay(mop); exit(0); }
/* ******** limnCameraPathMake ** ** uses limnSplines to do camera paths based on key-frames ** ** output: cameras at all "numFrames" frames are set in the ** PRE-ALLOCATED array of output cameras, "cam". ** ** input: ** keycam: array of keyframe cameras ** time: times associated with the key frames ** ---> both of these arrays are length "numKeys" <--- ** trackWhat: takes values from the limnCameraPathTrack* enum ** quatType: spline to control camera orientations. This is needed for ** tracking at or from, but not needed for limnCameraPathTrackBoth. ** This is the only limnSplineTypeSpec* argument that can be NULL. ** posType: spline to control whichever of from, at, and up are needed for ** the given style of tracking. ** distType: spline to control neer, faar, dist: positions of near clipping, ** far clipping, and image plane, as well as the ** distance between from and at (which is used if not doing ** limnCameraPathTrackBoth) ** viewType: spline to control fov (and aspect, if you're crazy) ** ** NOTE: The "atRelative", "orthographic", and "rightHanded" fields ** are copied from keycam[0] into all output cam[i], but you still need ** to correctly set them for all keycam[i] for limnCameraUpdate to work ** as expected. Also, for the sake of simplicity, this function only works ** with fov and aspect, instead of {u,v}Range, and hence both "fov" and ** "aspect" need to set in *all* the keycams, even if neither of them ** ever changes! */ int limnCameraPathMake(limnCamera *cam, int numFrames, limnCamera *keycam, double *time, int numKeys, int trackWhat, limnSplineTypeSpec *quatType, limnSplineTypeSpec *posType, limnSplineTypeSpec *distType, limnSplineTypeSpec *viewType) { static const char me[]="limnCameraPathMake"; char which[AIR_STRLEN_MED]; airArray *mop; Nrrd *nquat, *nfrom, *natpt, *nupvc, *ndist, *nfova, *ntime, *nsample; double fratVec[3], *quat, *from, *atpt, *upvc, *dist, *fova, W2V[9], N[3], fratDist; limnSpline *timeSpline, *quatSpline, *fromSpline, *atptSpline, *upvcSpline, *distSpline, *fovaSpline; limnSplineTypeSpec *timeType; int ii, E; if (!( cam && keycam && time && posType && distType && viewType )) { biffAddf(LIMN, "%s: got NULL pointer", me); return 1; } if (!( AIR_IN_OP(limnCameraPathTrackUnknown, trackWhat, limnCameraPathTrackLast) )) { biffAddf(LIMN, "%s: trackWhat %d not in valid range [%d,%d]", me, trackWhat, limnCameraPathTrackUnknown+1, limnCameraPathTrackLast-1); return 1; } if (limnCameraPathTrackBoth != trackWhat && !quatType) { biffAddf(LIMN, "%s: need the quaternion limnSplineTypeSpec if not " "doing trackBoth", me); return 1; } /* create and allocate nrrds. For the time being, we're allocating more different nrrds, and filling their contents, than we need to-- nquat is not needed if we're doing limnCameraPathTrackBoth, for example. However, we do make an effort to only do the spline evaluation on the things we actually need to know. */ mop = airMopNew(); airMopAdd(mop, nquat = nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, nfrom = nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, natpt = nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, nupvc = nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, ndist = nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, nfova = nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, ntime = nrrdNew(), (airMopper)nrrdNix, airMopAlways); if (nrrdWrap_va(ntime, time, nrrdTypeDouble, 1, AIR_CAST(size_t, numKeys))) { biffMovef(LIMN, NRRD, "%s: trouble wrapping time values", me); airMopError(mop); return 1; } airMopAdd(mop, nsample = nrrdNew(), (airMopper)nrrdNuke, airMopAlways); timeType = limnSplineTypeSpecNew(limnSplineTypeTimeWarp); airMopAdd(mop, timeType, (airMopper)limnSplineTypeSpecNix, airMopAlways); if (nrrdMaybeAlloc_va(nquat, nrrdTypeDouble, 2, AIR_CAST(size_t, 4), AIR_CAST(size_t, numKeys)) || nrrdMaybeAlloc_va(nfrom, nrrdTypeDouble, 2, AIR_CAST(size_t, 3), AIR_CAST(size_t, numKeys)) || nrrdMaybeAlloc_va(natpt, nrrdTypeDouble, 2, AIR_CAST(size_t, 3), AIR_CAST(size_t, numKeys)) || nrrdMaybeAlloc_va(nupvc, nrrdTypeDouble, 2, AIR_CAST(size_t, 3), AIR_CAST(size_t, numKeys)) || nrrdMaybeAlloc_va(ndist, nrrdTypeDouble, 2, AIR_CAST(size_t, 4), AIR_CAST(size_t, numKeys)) || nrrdMaybeAlloc_va(nfova, nrrdTypeDouble, 2, AIR_CAST(size_t, 2), AIR_CAST(size_t, numKeys))) { biffMovef(LIMN, NRRD, "%s: couldn't allocate buffer nrrds", me); airMopError(mop); return 1; } quat = (double*)(nquat->data); from = (double*)(nfrom->data); atpt = (double*)(natpt->data); upvc = (double*)(nupvc->data); dist = (double*)(ndist->data); fova = (double*)(nfova->data); /* check cameras, and put camera information into nrrds */ for (ii=0; ii<numKeys; ii++) { if (limnCameraUpdate(keycam + ii)) { biffAddf(LIMN, "%s: trouble with camera at keyframe %d\n", me, ii); airMopError(mop); return 1; } if (!( AIR_EXISTS(keycam[ii].fov) && AIR_EXISTS(keycam[ii].aspect) )) { biffAddf(LIMN, "%s: fov, aspect not both defined on keyframe %d", me, ii); airMopError(mop); return 1; } ell_4m_to_q_d(quat + 4*ii, keycam[ii].W2V); if (ii) { if (0 > ELL_4V_DOT(quat + 4*ii, quat + 4*(ii-1))) { ELL_4V_SCALE(quat + 4*ii, -1, quat + 4*ii); } } ELL_3V_COPY(from + 3*ii, keycam[ii].from); ELL_3V_COPY(atpt + 3*ii, keycam[ii].at); ELL_3V_COPY(upvc + 3*ii, keycam[ii].up); ELL_3V_SUB(fratVec, keycam[ii].from, keycam[ii].at); fratDist = ELL_3V_LEN(fratVec); ELL_4V_SET(dist + 4*ii, fratDist, keycam[ii].neer, keycam[ii].dist, keycam[ii].faar); ELL_2V_SET(fova + 2*ii, keycam[ii].fov, keycam[ii].aspect); } /* create splines from nrrds */ if (!( (strcpy(which, "quaternion"), quatSpline = limnSplineCleverNew(nquat, limnSplineInfoQuaternion, quatType)) && (strcpy(which, "from point"), fromSpline = limnSplineCleverNew(nfrom, limnSplineInfo3Vector, posType)) && (strcpy(which, "at point"), atptSpline = limnSplineCleverNew(natpt, limnSplineInfo3Vector, posType)) && (strcpy(which, "up vector"), upvcSpline = limnSplineCleverNew(nupvc, limnSplineInfo3Vector, posType)) && (strcpy(which, "plane distances"), distSpline = limnSplineCleverNew(ndist, limnSplineInfo4Vector, distType)) && (strcpy(which, "field-of-view"), fovaSpline = limnSplineCleverNew(nfova, limnSplineInfo2Vector, viewType)) && (strcpy(which, "time warp"), timeSpline = limnSplineCleverNew(ntime, limnSplineInfoScalar, timeType)) )) { biffAddf(LIMN, "%s: trouble creating %s spline", me, which); airMopError(mop); return 1; } airMopAdd(mop, quatSpline, (airMopper)limnSplineNix, airMopAlways); airMopAdd(mop, fromSpline, (airMopper)limnSplineNix, airMopAlways); airMopAdd(mop, atptSpline, (airMopper)limnSplineNix, airMopAlways); airMopAdd(mop, upvcSpline, (airMopper)limnSplineNix, airMopAlways); airMopAdd(mop, distSpline, (airMopper)limnSplineNix, airMopAlways); airMopAdd(mop, fovaSpline, (airMopper)limnSplineNix, airMopAlways); airMopAdd(mop, timeSpline, (airMopper)limnSplineNix, airMopAlways); /* evaluate splines */ E = AIR_FALSE; if (!E) E |= limnSplineSample(nsample, timeSpline, limnSplineMinT(timeSpline), numFrames, limnSplineMaxT(timeSpline)); quat = NULL; from = NULL; atpt = NULL; upvc = NULL; switch(trackWhat) { case limnCameraPathTrackAt: if (!E) E |= limnSplineNrrdEvaluate(natpt, atptSpline, nsample); if (!E) atpt = (double*)(natpt->data); if (!E) E |= limnSplineNrrdEvaluate(nquat, quatSpline, nsample); if (!E) quat = (double*)(nquat->data); break; case limnCameraPathTrackFrom: if (!E) E |= limnSplineNrrdEvaluate(nfrom, fromSpline, nsample); if (!E) from = (double*)(nfrom->data); if (!E) E |= limnSplineNrrdEvaluate(nquat, quatSpline, nsample); if (!E) quat = (double*)(nquat->data); break; case limnCameraPathTrackBoth: if (!E) E |= limnSplineNrrdEvaluate(nfrom, fromSpline, nsample); if (!E) from = (double*)(nfrom->data); if (!E) E |= limnSplineNrrdEvaluate(natpt, atptSpline, nsample); if (!E) atpt = (double*)(natpt->data); if (!E) E |= limnSplineNrrdEvaluate(nupvc, upvcSpline, nsample); if (!E) upvc = (double*)(nupvc->data); break; } dist = NULL; if (!E) E |= limnSplineNrrdEvaluate(ndist, distSpline, nsample); if (!E) dist = (double*)(ndist->data); fova = NULL; if (!E) E |= limnSplineNrrdEvaluate(nfova, fovaSpline, nsample); if (!E) fova = (double*)(nfova->data); if (E) { biffAddf(LIMN, "%s: trouble evaluating splines", me); airMopError(mop); return 1; } /* copy information from nrrds back into cameras */ for (ii=0; ii<numFrames; ii++) { cam[ii].atRelative = keycam[0].atRelative; cam[ii].orthographic = keycam[0].orthographic; cam[ii].rightHanded = keycam[0].rightHanded; if (limnCameraPathTrackBoth == trackWhat) { ELL_3V_COPY(cam[ii].from, from + 3*ii); ELL_3V_COPY(cam[ii].at, atpt + 3*ii); ELL_3V_COPY(cam[ii].up, upvc + 3*ii); } else { fratDist = (dist + 4*ii)[0]; ell_q_to_3m_d(W2V, quat + 4*ii); ELL_3MV_ROW1_GET(cam[ii].up, W2V); if (cam[ii].rightHanded) { ELL_3V_SCALE(cam[ii].up, -1, cam[ii].up); } ELL_3MV_ROW2_GET(N, W2V); if (limnCameraPathTrackFrom == trackWhat) { ELL_3V_COPY(cam[ii].from, from + 3*ii); ELL_3V_SCALE_ADD2(cam[ii].at, 1.0, cam[ii].from, fratDist, N); } else { ELL_3V_COPY(cam[ii].at, atpt + 3*ii); ELL_3V_SCALE_ADD2(cam[ii].from, 1.0, cam[ii].at, -fratDist, N); } } cam[ii].neer = (dist + 4*ii)[1]; cam[ii].dist = (dist + 4*ii)[2]; cam[ii].faar = (dist + 4*ii)[3]; cam[ii].fov = (fova + 2*ii)[0]; cam[ii].aspect = (fova + 2*ii)[1]; if (limnCameraUpdate(cam + ii)) { biffAddf(LIMN, "%s: trouble with output camera %d\n", me, ii); airMopError(mop); return 1; } } airMopOkay(mop); return 0; }
/* ** parties until the gradients settle down */ int tenGradientBalance(Nrrd *nout, const Nrrd *nin, tenGradientParm *tgparm) { static const char me[]="tenGradientBalance"; double len, lastLen, improv; airRandMTState *rstate; Nrrd *ncopy; unsigned int iter, maxIter; int done; airArray *mop; if (!nout || tenGradientCheck(nin, nrrdTypeUnknown, 2) || !tgparm) { biffAddf(TEN, "%s: got NULL pointer (%p,%p) or invalid nin", me, AIR_VOIDP(nout), AIR_VOIDP(tgparm)); return 1; } if (nrrdConvert(nout, nin, nrrdTypeDouble)) { biffMovef(TEN, NRRD, "%s: can't initialize output with input", me); return 1; } mop = airMopNew(); ncopy = nrrdNew(); airMopAdd(mop, ncopy, (airMopper)nrrdNuke, airMopAlways); rstate = airRandMTStateNew(tgparm->seed); airMopAdd(mop, rstate, (airMopper)airRandMTStateNix, airMopAlways); /* HEY: factor of 100 is an approximate hack */ maxIter = 100*tgparm->maxIteration; lastLen = 1.0; done = AIR_FALSE; do { iter = 0; do { iter++; len = party(nout, rstate); } while (len > lastLen && iter < maxIter); if (iter >= maxIter) { if (tgparm->verbose) { fprintf(stderr, "%s: stopping at max iter %u\n", me, maxIter); } if (nrrdCopy(nout, ncopy)) { biffMovef(TEN, NRRD, "%s: trouble copying", me); airMopError(mop); return 1; } done = AIR_TRUE; } else { if (nrrdCopy(ncopy, nout)) { biffMovef(TEN, NRRD, "%s: trouble copying", me); airMopError(mop); return 1; } improv = lastLen - len; lastLen = len; if (tgparm->verbose) { fprintf(stderr, "%s: (iter %u) improvement: %g (mean length = %g)\n", me, iter, improv, len); } done = (improv <= tgparm->minMeanImprovement || len < tgparm->minMean); } } while (!done); airMopOkay(mop); return 0; }
int doit(Nrrd *nout, Nrrd *nin, int smart, float amount) { static const char me[]="doit"; Nrrd *nproj[3]; airArray *mop; int axis, srl, sap, ssi, E, margin, which; size_t min[3]; if (!(nout && nin)) { biffAddf(NINSPECT, "%s: got NULL pointer", me); return 1; } if (!(3 == nin->dim)) { biffAddf(NINSPECT, "%s: given nrrd has dimension %d, not 3\n", me, nin->dim); return 1; } mop = airMopNew(); airMopAdd(mop, nproj[0]=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, nproj[1]=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, nproj[2]=nrrdNew(), (airMopper)nrrdNuke, airMopAlways); /* how much space to put between and around the projections */ margin = 6; /* do projections for each axis, with some progress indication to sterr */ for (axis=0; axis<=2; axis++) { fprintf(stderr, "%s: doing axis %d projections ... ", me, axis); fflush(stderr); if (ninspect_proj(nproj[axis], nin, axis, smart, amount)) { fprintf(stderr, "ERROR\n"); biffAddf(NINSPECT, "%s: trouble doing projections for axis %d", me, axis); airMopError(mop); return 1; } fprintf(stderr, "done\n"); } if (nrrdSpaceRightAnteriorSuperior == nin->space) { if (fixproj(nproj, nin)) { fprintf(stderr, "ERROR\n"); biffAddf(NINSPECT, "%s: trouble orienting projections", me); airMopError(mop); return 1; } } srl = nproj[1]->axis[0+1].size; sap = nproj[0]->axis[0+1].size; ssi = nproj[1]->axis[1+1].size; /* allocate output as 8-bit color image. We know output type is nrrdTypeUChar because ninspect_proj finishes each projection with nrrdQuantize to 8-bits */ if (nrrdMaybeAlloc_va(nout, nrrdTypeUChar, 3, AIR_CAST(size_t, 3), AIR_CAST(size_t, srl + 3*margin + sap), AIR_CAST(size_t, ssi + 3*margin + sap))) { biffMovef(NINSPECT, NRRD, "%s: couldn't allocate output", me); airMopError(mop); return 1; } min[0] = 0; E = 0; which = 0; if (!E) { min[1] = margin; min[2] = margin; which = 1; } if (!E) E |= nrrdInset(nout, nout, nproj[1], min); if (!E) { min[1] = margin; min[2] = 2*margin + ssi; which = 2; } if (!E) E |= nrrdInset(nout, nout, nproj[2], min); if (!E) { min[1] = 2*margin + srl; min[2] = margin; which = 3; } if (!E) E |= nrrdInset(nout, nout, nproj[0], min); if (E) { biffAddf(NINSPECT, NRRD, "%s: couldn't composite output (which = %d)", me, which); airMopError(mop); return 1; } airMopOkay(mop); return 0; }
int fixproj(Nrrd *nproj[3], Nrrd *nvol) { static const char me[]="fixproj"; airArray *mop; Nrrd *ntmp[3], *nt; int sz[3], ii, map[3], h[3], E, mi; size_t rsz[3]; double vec[3][3], dot[3], sp[3], parm[NRRD_KERNEL_PARMS_NUM]; mop = airMopNew(); if (!( ELL_3V_EXISTS(nvol->axis[0].spaceDirection) && ELL_3V_EXISTS(nvol->axis[1].spaceDirection) && ELL_3V_EXISTS(nvol->axis[2].spaceDirection) )) { biffAddf(NINSPECT, "%s: space directions don't exist for all 3 axes", me); airMopError(mop); return 1; } airMopAdd(mop, nt = nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, ntmp[0] = nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, ntmp[1] = nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, ntmp[2] = nrrdNew(), (airMopper)nrrdNuke, airMopAlways); /* RL AP SI */ ELL_3V_SET(vec[0], 1, 0, 0); ELL_3V_SET(vec[1], 0, 1, 0); ELL_3V_SET(vec[2], 0, 0, 1); for (ii=0; ii<3; ii++) { dot[0] = ELL_3V_DOT(vec[ii], nvol->axis[0].spaceDirection); dot[1] = ELL_3V_DOT(vec[ii], nvol->axis[1].spaceDirection); dot[2] = ELL_3V_DOT(vec[ii], nvol->axis[2].spaceDirection); dot[0] = AIR_ABS(dot[0]); dot[1] = AIR_ABS(dot[1]); dot[2] = AIR_ABS(dot[2]); map[ii] = ELL_MAX3_IDX(dot[0], dot[1], dot[2]); } ELL_3V_SET(h, 1, 0, 0); E = 0; for (ii=0; ii<3; ii++) { if (h[map[ii]] != map[h[ii]]) { if (!E) E |= nrrdAxesSwap(ntmp[ii], nproj[map[ii]], 1, 2); } else { if (!E) E |= nrrdCopy(ntmp[ii], nproj[map[ii]]); } } if (E) { biffMovef(NINSPECT, NRRD, "%s: trouble with nrrd operations", me); airMopError(mop); return 1; } E = 0; if (nvol->axis[map[0]].spaceDirection[0] > 0) { if (!E) E |= nrrdFlip(nt, ntmp[1], 0+1); if (!E) E |= nrrdCopy(ntmp[1], nt); if (!E) E |= nrrdFlip(nt, ntmp[2], 0+1); if (!E) E |= nrrdCopy(ntmp[2], nt); } if (nvol->axis[map[1]].spaceDirection[1] > 0) { if (!E) E |= nrrdFlip(nt, ntmp[0], 0+1); if (!E) E |= nrrdCopy(ntmp[0], nt); if (!E) E |= nrrdFlip(nt, ntmp[2], 1+1); if (!E) E |= nrrdCopy(ntmp[2], nt); } if (nvol->axis[map[2]].spaceDirection[2] > 0) { if (!E) E |= nrrdFlip(nt, ntmp[0], 1+1); if (!E) E |= nrrdCopy(ntmp[0], nt); if (!E) E |= nrrdFlip(nt, ntmp[1], 1+1); if (!E) E |= nrrdCopy(ntmp[1], nt); } if (E) { biffMovef(NINSPECT, NRRD, "%s: trouble with nrrd operations", me); airMopError(mop); return 1; } for (ii=0; ii<3; ii++) { sz[ii] = nvol->axis[map[ii]].size; sp[ii] = ELL_3V_LEN(nvol->axis[map[ii]].spaceDirection); } mi = ELL_MIN3_IDX(sp[0], sp[1], sp[2]); sz[0] = (int)(sz[0]*sp[0]/sp[mi]); sz[1] = (int)(sz[1]*sp[1]/sp[mi]); sz[2] = (int)(sz[2]*sp[2]/sp[mi]); parm[0] = 1; ELL_3V_SET(rsz, 3, sz[1], sz[2]); nrrdSimpleResample(nproj[0], ntmp[0], nrrdKernelBox, parm, rsz, NULL); ELL_3V_SET(rsz, 3, sz[0], sz[2]); nrrdSimpleResample(nproj[1], ntmp[1], nrrdKernelBox, parm, rsz, NULL); ELL_3V_SET(rsz, 3, sz[0], sz[1]); nrrdSimpleResample(nproj[2], ntmp[2], nrrdKernelBox, parm, rsz, NULL); airMopOkay(mop); 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 miteNtxfCheck(const Nrrd *ntxf) { static const char me[]="miteNtxfCheck"; char *rangeStr, *domStr; gageItemSpec isp; unsigned int rii, axi; int ilog2; if (nrrdCheck(ntxf)) { biffMovef(MITE, NRRD, "%s: basic nrrd validity check failed", me); return 1; } if (!( nrrdTypeFloat == ntxf->type || nrrdTypeDouble == ntxf->type || nrrdTypeUChar == ntxf->type )) { biffAddf(MITE, "%s: need a type %s, %s or %s nrrd (not %s)", me, airEnumStr(nrrdType, nrrdTypeFloat), airEnumStr(nrrdType, nrrdTypeDouble), airEnumStr(nrrdType, nrrdTypeUChar), airEnumStr(nrrdType, ntxf->type)); return 1; } if (!( 2 <= ntxf->dim )) { biffAddf(MITE, "%s: nrrd dim (%d) isn't at least 2 (for a 1-D txf)", me, ntxf->dim); return 1; } rangeStr = ntxf->axis[0].label; if (0 == airStrlen(rangeStr)) { biffAddf(MITE, "%s: axis[0]'s label doesn't specify txf range", me); return 1; } if (airStrlen(rangeStr) != ntxf->axis[0].size) { char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL]; biffAddf(MITE, "%s: axis[0]'s size %s, but label specifies %s values", me, airSprintSize_t(stmp1, ntxf->axis[0].size), airSprintSize_t(stmp2, airStrlen(rangeStr))); return 1; } for (rii=0; rii<airStrlen(rangeStr); rii++) { if (!strchr(miteRangeChar, rangeStr[rii])) { biffAddf(MITE, "%s: char %d of axis[0]'s label (\"%c\") isn't a valid " "transfer function range specifier (not in \"%s\")", me, rii, rangeStr[rii], miteRangeChar); return 1; } } for (axi=1; axi<ntxf->dim; axi++) { if (1 == ntxf->axis[axi].size) { biffAddf(MITE, "%s: # samples on axis %d must be > 1", me, axi); return 1; } domStr = ntxf->axis[axi].label; if (0 == airStrlen(domStr)) { biffAddf(MITE, "%s: axis[%d] of txf didn't specify a domain variable", me, axi); return 1; } if (miteVariableParse(&isp, domStr)) { biffAddf(MITE, "%s: couldn't parse txf domain \"%s\" for axis %d\n", me, domStr, axi); return 1; } if (!( 1 == isp.kind->table[isp.item].answerLength || 3 == isp.kind->table[isp.item].answerLength )) { biffAddf(MITE, "%s: %s (item %d) not a scalar or vector " "(answerLength = %d): " "can't be a txf domain variable", me, domStr, isp.item, isp.kind->table[isp.item].answerLength); return 1; } if (3 == isp.kind->table[isp.item].answerLength) { /* has to be right length for one of the quantization schemes */ ilog2 = airLog2(ntxf->axis[axi].size); if (-1 == ilog2) { char stmp[AIR_STRLEN_SMALL]; biffAddf(MITE, "%s: txf axis size for %s must be power of 2 (not %s)", me, domStr, airSprintSize_t(stmp, ntxf->axis[axi].size)); return 1; } else { if (!( AIR_IN_CL(8, ilog2, 16) )) { biffAddf(MITE, "%s: log_2 of txf axis size for %s should be in " "range [8,16] (not %d)", me, domStr, ilog2); return 1; } } } else { if (!( AIR_EXISTS(ntxf->axis[axi].min) && AIR_EXISTS(ntxf->axis[axi].max) )) { biffAddf(MITE, "%s: min and max of axis %d aren't both set", me, axi); return 1; } if (!( ntxf->axis[axi].min < ntxf->axis[axi].max )) { biffAddf(MITE, "%s: min (%g) not less than max (%g) on axis %d", me, ntxf->axis[axi].min, ntxf->axis[axi].max, axi); return 1; } } } return 0; }
int tend_estimThresholdFind(double *threshP, Nrrd *nbmat, Nrrd *nin4d) { static const char me[]="tend_estimThresholdFind"; Nrrd **ndwi; airArray *mop; unsigned int slIdx, slNum, dwiAx, dwiNum, rangeAxisNum, rangeAxisIdx[NRRD_DIM_MAX]; double *bmat, bten[7], bnorm; int dwiIdx; mop = airMopNew(); if (!(threshP && nbmat && nin4d)) { biffAddf(TEN, "%s: got NULL pointer", me); airMopError(mop); return 1; } if (tenBMatrixCheck(nbmat, nrrdTypeDouble, 6)) { biffAddf(TEN, "%s: problem within given b-matrix", me); airMopError(mop); return 1; } /* HEY: copied from tenEpiRegister4D() */ rangeAxisNum = nrrdRangeAxesGet(nin4d, rangeAxisIdx); if (0 == rangeAxisNum) { /* we fall back on old behavior */ dwiAx = 0; } else if (1 == rangeAxisNum) { /* thankfully there's exactly one range axis */ dwiAx = rangeAxisIdx[0]; } else { biffAddf(TEN, "%s: have %u range axes instead of 1, don't know which " "is DWI axis", me, rangeAxisNum); airMopError(mop); return 1; } slNum = nin4d->axis[dwiAx].size; bmat = AIR_CAST(double *, nbmat->data); dwiNum = 0; for (slIdx=0; slIdx<slNum; slIdx++) { TEN_T_SET(bten, 1.0, bmat[0], bmat[1], bmat[2], bmat[3], bmat[4], bmat[5]); bnorm = TEN_T_NORM(bten); dwiNum += bnorm > 0.0; bmat += 6; } if (0 == dwiNum) { biffAddf(TEN, "%s: somehow got zero DWIs", me); airMopError(mop); return 1; } ndwi = AIR_CAST(Nrrd **, calloc(dwiNum, sizeof(Nrrd *))); airMopAdd(mop, ndwi, (airMopper)airFree, airMopAlways); bmat = AIR_CAST(double *, nbmat->data); dwiIdx = -1; for (slIdx=0; slIdx<slNum; slIdx++) { TEN_T_SET(bten, 1.0, bmat[0], bmat[1], bmat[2], bmat[3], bmat[4], bmat[5]); bnorm = TEN_T_NORM(bten); if (bnorm > 0.0) { dwiIdx++; ndwi[dwiIdx] = nrrdNew(); airMopAdd(mop, ndwi[dwiIdx], (airMopper)nrrdNuke, airMopAlways); if (nrrdSlice(ndwi[dwiIdx], nin4d, dwiAx, slIdx)) { biffMovef(TEN, NRRD, "%s: trouble slicing DWI at index %u", me, slIdx); airMopError(mop); return 1; } } bmat += 6; } if (_tenEpiRegThresholdFind(threshP, ndwi, dwiNum, AIR_FALSE, 1.5)) { biffAddf(TEN, "%s: trouble finding thresh", me); airMopError(mop); return 1; } airMopOkay(mop); return 0; }
int tenTripleCalc(Nrrd *nout, int ttype, const Nrrd *nten) { static const char me[]="tenTripleCalc"; size_t II, NN, size[NRRD_DIM_MAX]; double (*ins)(void *, size_t, double), (*lup)(const void *, size_t); if (!( nout && nten )) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } if (airEnumValCheck(tenTripleType, ttype)) { biffAddf(TEN, "%s: got invalid %s (%d)", me, tenTripleType->name, ttype); return 1; } if (tenTensorCheck(nten, nrrdTypeDefault, AIR_FALSE, AIR_TRUE)) { biffAddf(TEN, "%s: didn't get a valid DT array", me); return 1; } if (!( nrrdTypeFloat == nten->type || nrrdTypeDouble == nten->type )) { biffAddf(TEN, "%s: need input type %s or %s, not %s\n", me, airEnumStr(nrrdType, nrrdTypeFloat), airEnumStr(nrrdType, nrrdTypeFloat), airEnumStr(nrrdType, nten->type)); } nrrdAxisInfoGet_nva(nten, nrrdAxisInfoSize, size); size[0] = 3; if (nrrdMaybeAlloc_nva(nout, nten->type, nten->dim, size)) { biffMovef(TEN, NRRD, "%s: couldn't alloc output", me); return 1; } NN = nrrdElementNumber(nten)/7; lup = nrrdDLookup[nten->type]; ins = nrrdDInsert[nten->type]; for (II=0; II<NN; II++) { double ten[7], trip[3]; unsigned int vv; for (vv=0; vv<7; vv++) { ten[vv] = lup(nten->data, vv + 7*II); } tenTripleCalcSingle_d(trip, ttype, ten); for (vv=0; vv<3; vv++) { ins(nout->data, vv + 3*II, trip[vv]); } } if (nrrdAxisInfoCopy(nout, nten, NULL, (NRRD_AXIS_INFO_SIZE_BIT))) { biffMovef(TEN, NRRD, "%s: couldn't copy axis info", me); return 1; } nout->axis[0].kind = nrrdKindUnknown; if (nrrdBasicInfoCopy(nout, nten, NRRD_BASIC_INFO_ALL ^ NRRD_BASIC_INFO_SPACE)) { biffAddf(TEN, "%s:", me); return 1; } return 0; }
int pullInfoSpecAdd(pullContext *pctx, pullInfoSpec *ispec) { static const char me[]="pullInfoSpecAdd"; unsigned int ii, vi, haveLen, needLen; const gageKind *kind; if (!( pctx && ispec )) { biffAddf(PULL, "%s: got NULL pointer", me); return 1; } if (airEnumValCheck(pullInfo, ispec->info)) { biffAddf(PULL, "%s: %d not a valid %s value", me, ispec->info, pullInfo->name); return 1; } if (airEnumValCheck(pullSource, ispec->source)) { biffAddf(PULL, "%s: %d not a valid %s value", me, ispec->source, pullSource->name); return 1; } if (pctx->ispec[ispec->info]) { biffAddf(PULL, "%s: already set info %s (%d)", me, airEnumStr(pullInfo, ispec->info), ispec->info); return 1; } for (ii=0; ii<=PULL_INFO_MAX; ii++) { if (pctx->ispec[ii] == ispec) { biffAddf(PULL, "%s(%s): already got ispec %p as ispec[%u]", me, airEnumStr(pullInfo, ispec->info), AIR_VOIDP(ispec), ii); return 1; } } if (pctx->verbose) { printf("%s: ispec %s from vol %s\n", me, airEnumStr(pullInfo, ispec->info), ispec->volName); } needLen = pullInfoLen(ispec->info); if (pullSourceGage == ispec->source) { vi = _pullVolumeIndex(pctx, ispec->volName); if (UINT_MAX == vi) { biffAddf(PULL, "%s(%s): no volume has name \"%s\"", me, airEnumStr(pullInfo, ispec->info), ispec->volName); return 1; } kind = pctx->vol[vi]->kind; if (airEnumValCheck(kind->enm, ispec->item)) { biffAddf(PULL, "%s(%s): %d not a valid \"%s\" item", me, airEnumStr(pullInfo, ispec->info), ispec->item, kind->name); return 1; } haveLen = kind->table[ispec->item].answerLength; if (needLen != haveLen) { biffAddf(PULL, "%s(%s): need len %u, but \"%s\" item \"%s\" has len %u", me, airEnumStr(pullInfo, ispec->info), needLen, kind->name, airEnumStr(kind->enm, ispec->item), haveLen); return 1; } /* very tricky: seedOnly is initialized to true for everything */ if (pullInfoSeedThresh != ispec->info && pullInfoSeedPreThresh != ispec->info) { /* if the info is neither seedthresh nor seedprethresh, then the volume will have to be probed after the first iter, so turn *off* seedOnly */ pctx->vol[vi]->seedOnly = AIR_FALSE; } /* less tricky: turn on forSeedPreThresh as needed; its initialized to false */ if (pullInfoSeedPreThresh == ispec->info) { pctx->vol[vi]->forSeedPreThresh = AIR_TRUE; if (pctx->verbose) { printf("%s: volume %u %s used for %s\n", me, vi, pctx->vol[vi]->name, airEnumStr(pullInfo, pullInfoSeedPreThresh)); } } /* now set item in gage query */ if (gageQueryItemOn(pctx->vol[vi]->gctx, pctx->vol[vi]->gpvl, ispec->item)) { biffMovef(PULL, GAGE, "%s: trouble adding item %u to vol %u", me, ispec->item, vi); return 1; } ispec->volIdx = vi; } else if (pullSourceProp == ispec->source) { haveLen = pullPropLen(ispec->prop); if (needLen != haveLen) { biffAddf(PULL, "%s: need len %u, but \"%s\" \"%s\" has len %u", me, needLen, pullProp->name, airEnumStr(pullProp, ispec->prop), haveLen); return 1; } } else { biffAddf(PULL, "%s: sorry, source %s unsupported", me, airEnumStr(pullSource, ispec->source)); return 1; } if (haveLen > 9) { biffAddf(PULL, "%s: sorry, answer length (%u) > 9 unsupported", me, haveLen); return 1; } pctx->ispec[ispec->info] = ispec; return 0; }
int miteRenderBegin(miteRender **mrrP, miteUser *muu) { static const char me[]="miteRenderBegin"; gagePerVolume *pvl; int E, T, pvlIdx; gageQuery queryScl, queryVec, queryTen; gageItemSpec isp; unsigned int axi, thr; if (!(mrrP && muu)) { biffAddf(MITE, "%s: got NULL pointer", me); return 1; } if (_miteUserCheck(muu)) { biffAddf(MITE, "%s: problem with user-set parameters", me); return 1; } if (!( *mrrP = _miteRenderNew() )) { biffAddf(MITE, "%s: couldn't alloc miteRender", me); return 1; } if (_miteNtxfAlphaAdjust(*mrrP, muu)) { biffAddf(MITE, "%s: trouble copying and alpha-adjusting txfs", me); return 1; } GAGE_QUERY_RESET(queryScl); GAGE_QUERY_RESET(queryVec); GAGE_QUERY_RESET(queryTen); GAGE_QUERY_RESET((*mrrP)->queryMite); for (T=0; T<muu->ntxfNum; T++) { for (axi=1; axi<muu->ntxf[T]->dim; axi++) { miteVariableParse(&isp, muu->ntxf[T]->axis[axi].label); miteQueryAdd(queryScl, queryVec, queryTen, (*mrrP)->queryMite, &isp); } } miteVariableParse((*mrrP)->normalSpec, muu->normalStr); miteQueryAdd(queryScl, queryVec, queryTen, (*mrrP)->queryMite, (*mrrP)->normalSpec); miteShadeSpecParse((*mrrP)->shadeSpec, muu->shadeStr); miteShadeSpecQueryAdd(queryScl, queryVec, queryTen, (*mrrP)->queryMite, (*mrrP)->shadeSpec); (*mrrP)->queryMiteNonzero = GAGE_QUERY_NONZERO((*mrrP)->queryMite); E = 0; pvlIdx = 0; if (muu->nsin) { if (!E) E |= !(pvl = gagePerVolumeNew(muu->gctx0, muu->nsin, gageKindScl)); if (!E) E |= gageQuerySet(muu->gctx0, pvl, queryScl); if (!E) E |= gagePerVolumeAttach(muu->gctx0, pvl); if (!E) (*mrrP)->sclPvlIdx = pvlIdx++; } if (muu->nvin) { if (!E) E |= !(pvl = gagePerVolumeNew(muu->gctx0, muu->nvin, gageKindVec)); if (!E) E |= gageQuerySet(muu->gctx0, pvl, queryVec); if (!E) E |= gagePerVolumeAttach(muu->gctx0, pvl); if (!E) (*mrrP)->vecPvlIdx = pvlIdx++; } if (muu->ntin) { if (!E) E |= !(pvl = gagePerVolumeNew(muu->gctx0, muu->ntin, tenGageKind)); if (!E) E |= gageQuerySet(muu->gctx0, pvl, queryTen); if (!E) E |= gagePerVolumeAttach(muu->gctx0, pvl); if (!E) (*mrrP)->tenPvlIdx = pvlIdx++; } if (!E) E |= gageKernelSet(muu->gctx0, gageKernel00, muu->ksp[gageKernel00]->kernel, muu->ksp[gageKernel00]->parm); if (!E) E |= gageKernelSet(muu->gctx0, gageKernel11, muu->ksp[gageKernel11]->kernel, muu->ksp[gageKernel11]->parm); if (!E) E |= gageKernelSet(muu->gctx0, gageKernel22, muu->ksp[gageKernel22]->kernel, muu->ksp[gageKernel22]->parm); if (!E) E |= gageUpdate(muu->gctx0); if (E) { biffMovef(MITE, GAGE, "%s: gage trouble", me); return 1; } fprintf(stderr, "!%s: kernel support = %d^3 samples\n", me, 2*muu->gctx0->radius); if (nrrdMaybeAlloc_va(muu->nout, mite_nt, 3, AIR_CAST(size_t, 5) /* RGBAZ */ , AIR_CAST(size_t, muu->hctx->imgSize[0]), AIR_CAST(size_t, muu->hctx->imgSize[1]))) { biffMovef(MITE, NRRD, "%s: nrrd trouble", me); return 1; } muu->nout->axis[1].center = nrrdCenterCell; muu->nout->axis[1].min = muu->hctx->cam->uRange[0]; muu->nout->axis[1].max = muu->hctx->cam->uRange[1]; muu->nout->axis[2].center = nrrdCenterCell; muu->nout->axis[2].min = muu->hctx->cam->vRange[0]; muu->nout->axis[2].max = muu->hctx->cam->vRange[1]; for (thr=0; thr<muu->hctx->numThreads; thr++) { (*mrrP)->tt[thr] = miteThreadNew(); if (!((*mrrP)->tt[thr])) { biffAddf(MITE, "%s: couldn't allocate thread[%d]", me, thr); return 1; } airMopAdd((*mrrP)->rmop, (*mrrP)->tt[thr], (airMopper)miteThreadNix, airMopAlways); } (*mrrP)->time0 = airTime(); return 0; }
int coilContextAllSet(coilContext *cctx, const Nrrd *nin, const coilKind *kind, const coilMethod *method, unsigned int radius, unsigned int numThreads, int verbose, double parm[COIL_PARMS_NUM]) { static const char me[]="coilContextAllSet"; int someExist, allExist, baseDim, pi; size_t size[NRRD_DIM_MAX], sx, sy, sz; double xsp, ysp, zsp; airArray *mop; cctx->verbose = verbose; if (!( cctx && nin && kind && method )) { biffAddf(COIL, "%s: got NULL pointer", me); return 1; } if (coilVolumeCheck(nin, kind)) { biffAddf(COIL, "%s: input volume not usable as %s", me, kind->name); return 1; } if (!( radius >= 1 && numThreads >= 1 )) { biffAddf(COIL, "%s: radius (%d) not >= 1 or numThreads (%d) not >= 1", me, radius, numThreads); return 1; } if (!( AIR_IN_OP(coilMethodTypeUnknown, method->type, coilMethodTypeLast) )) { biffAddf(COIL, "%s: method->type %d not valid", me, method->type); return 1; } if (!kind->filter[method->type]) { biffAddf(COIL, "%s: sorry, %s filtering not available on %s kind", me, method->name, kind->name); return 1; } /* warn if we can't do the multiple threads user wants */ if (numThreads > 1 && !airThreadCapable && airThreadNoopWarning) { fprintf(stderr, "%s: WARNING: this Teem not thread capable: using 1 " "thread, not %d\n", me, numThreads); numThreads = 1; } mop = airMopNew(); /* set parms */ for (pi=0; pi<method->numParm; pi++) { if (!AIR_EXISTS(parm[pi])) { biffAddf(COIL, "%s: parm[%d] (need %d) doesn't exist", me, pi, method->numParm); airMopError(mop); return 1; } cctx->parm[pi] = parm[pi]; } /* set sizes and spacings */ baseDim = (1 == kind->valLen ? 0 : 1); sx = nin->axis[0 + baseDim].size; sy = nin->axis[1 + baseDim].size; sz = nin->axis[2 + baseDim].size; if (sz < numThreads) { char stmp[AIR_STRLEN_SMALL]; airSprintSize_t(stmp, sz); fprintf(stderr, "%s: wanted %d threads but volume only has %s slices, " "using %s threads instead\n", me, numThreads, stmp, stmp); numThreads = AIR_UINT(sz); } ELL_3V_SET(cctx->size, sx, sy, sz); xsp = nin->axis[0 + baseDim].spacing; ysp = nin->axis[1 + baseDim].spacing; zsp = nin->axis[2 + baseDim].spacing; someExist = AIR_EXISTS(xsp) || AIR_EXISTS(ysp) || AIR_EXISTS(zsp); allExist = AIR_EXISTS(xsp) && AIR_EXISTS(ysp) && AIR_EXISTS(zsp); if (!( someExist )) { fprintf(stderr, "%s: WARNING: assuming unit spacing for all axes\n", me); xsp = 1; ysp = 1; zsp = 1; } else { if ( !allExist ) { biffAddf(COIL, "%s: spacings (%g,%g,%g) not uniformly existent", me, xsp, ysp, zsp); airMopError(mop); return 1; } } ELL_3V_SET(cctx->spacing, xsp, ysp, zsp); if (cctx->verbose) { fprintf(stderr, "%s: spacings: %g %g %g\n", me, cctx->spacing[0], cctx->spacing[1], cctx->spacing[2]); } /* allocate nvol */ if (0 == baseDim) { ELL_4V_SET(size, 2, sx, sy, sz); } else { ELL_5V_SET(size, kind->valLen, 2, sx, sy, sz); } cctx->nvol = nrrdNew(); if (nrrdMaybeAlloc_nva(cctx->nvol, coil_nrrdType, 4 + baseDim, size)) { biffMovef(COIL, NRRD, "%s: couldn't allocate internal processing volume", me); airMopError(mop); return 1; } airMopAdd(mop, cctx->nvol, (airMopper)nrrdNuke, airMopOnError); cctx->nin = nin; cctx->kind = kind; cctx->method = method; cctx->radius = radius; cctx->numThreads = numThreads; airMopOkay(mop); return 0; }
static int theFunc(Nrrd *nout, const Nrrd *nin, int func, funcParm *parm) { static const char me[]="theFunc"; float *tin, *tout, eval[3], evec[9], weight[3], size, mean; size_t NN, II; unsigned int ri; if (!AIR_IN_OP(funcUnknown, func, funcLast)) { biffAddf(TEN, "%s: given func %d out of range [%d,%d]", me, func, funcUnknown+1, funcLast-1); return 1; } if (!(nout && nin && parm)) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } if (tenTensorCheck(nin, nrrdTypeFloat, AIR_FALSE, AIR_TRUE)) { biffAddf(TEN, "%s: didn't get a tensor nrrd", me); return 1; } if (nout != nin) { if (nrrdCopy(nout, nin)) { biffMovef(TEN, NRRD, "%s: couldn't allocate output", me); return 1; } } tin = (float*)(nin->data); tout = (float*)(nout->data); NN = nrrdElementNumber(nin)/7; switch(func) { case funcSizeNormalize: ELL_3V_COPY_TT(weight, float, parm->weight); size = weight[0] + weight[1] + weight[2]; if (!size) { biffAddf(TEN, "%s: some of eigenvalue weights is zero", me); return 1; } weight[0] /= size; weight[1] /= size; weight[2] /= size; for (II=0; II<=NN-1; II++) { tenEigensolve_f(eval, evec, tin); size = (weight[0]*AIR_ABS(eval[0]) + weight[1]*AIR_ABS(eval[1]) + weight[2]*AIR_ABS(eval[2])); ELL_3V_SET_TT(eval, float, AIR_AFFINE(0, parm->amount, 1, eval[0], parm->target*eval[0]/size), AIR_AFFINE(0, parm->amount, 1, eval[1], parm->target*eval[1]/size), AIR_AFFINE(0, parm->amount, 1, eval[2], parm->target*eval[2]/size)); tenMakeSingle_f(tout, tin[0], eval, evec); tin += 7; tout += 7; } break; case funcSizeScale: for (II=0; II<=NN-1; II++) { TEN_T_SET_TT(tout, float, tin[0], parm->amount*tin[1], parm->amount*tin[2], parm->amount*tin[3], parm->amount*tin[4], parm->amount*tin[5], parm->amount*tin[6]); tin += 7; tout += 7; } break; case funcAnisoScale: for (II=0; II<=NN-1; II++) { tenEigensolve_f(eval, evec, tin); if (parm->fixDet) { eval[0] = AIR_MAX(eval[0], 0.00001f); eval[1] = AIR_MAX(eval[1], 0.00001f); eval[2] = AIR_MAX(eval[2], 0.00001f); ELL_3V_SET_TT(eval, float, log(eval[0]), log(eval[1]), log(eval[2])); } mean = (eval[0] + eval[1] + eval[2])/3.0f; ELL_3V_SET_TT(eval, float, AIR_LERP(parm->scale, mean, eval[0]), AIR_LERP(parm->scale, mean, eval[1]), AIR_LERP(parm->scale, mean, eval[2])); if (parm->fixDet) { ELL_3V_SET_TT(eval, float, exp(eval[0]), exp(eval[1]), exp(eval[2])); } if (eval[2] < 0 && parm->makePositive) { eval[0] = AIR_MAX(eval[0], 0.0f); eval[1] = AIR_MAX(eval[1], 0.0f); eval[2] = AIR_MAX(eval[2], 0.0f); } tenMakeSingle_f(tout, tin[0], eval, evec); tin += 7; tout += 7; } break; case funcEigenvalueClamp: for (II=0; II<=NN-1; II++) { tenEigensolve_f(eval, evec, tin); if (AIR_EXISTS(parm->min)) { ELL_3V_SET_TT(eval, float, AIR_MAX(eval[0], parm->min), AIR_MAX(eval[1], parm->min), AIR_MAX(eval[2], parm->min)); } if (AIR_EXISTS(parm->max)) { ELL_3V_SET_TT(eval, float, AIR_MIN(eval[0], parm->max), AIR_MIN(eval[1], parm->max), AIR_MIN(eval[2], parm->max)); } tenMakeSingle_f(tout, tin[0], eval, evec); tin += 7; tout += 7; }
/* ******** miteThreadBegin() ** ** this has some of the body of what would be miteThreadInit */ int miteThreadBegin(miteThread **mttP, miteRender *mrr, miteUser *muu, int whichThread) { static const char me[]="miteThreadBegin"; /* all the miteThreads have already been allocated */ (*mttP) = mrr->tt[whichThread]; if (!whichThread) { /* this is the first thread- it just points to the parent gageContext */ (*mttP)->gctx = muu->gctx0; } else { /* we have to generate a new gageContext */ (*mttP)->gctx = gageContextCopy(muu->gctx0); if (!(*mttP)->gctx) { biffMovef(MITE, GAGE, "%s: couldn't set up thread %d", me, whichThread); return 1; } } if (-1 != mrr->sclPvlIdx) { (*mttP)->ansScl = (*mttP)->gctx->pvl[mrr->sclPvlIdx]->answer; (*mttP)->nPerp = ((*mttP)->ansScl + gageKindAnswerOffset(gageKindScl, gageSclNPerp)); (*mttP)->geomTens = ((*mttP)->ansScl + gageKindAnswerOffset(gageKindScl, gageSclGeomTens)); } else { (*mttP)->ansScl = NULL; (*mttP)->nPerp = NULL; (*mttP)->geomTens = NULL; } (*mttP)->ansVec = (-1 != mrr->vecPvlIdx ? (*mttP)->gctx->pvl[mrr->vecPvlIdx]->answer : NULL); (*mttP)->ansTen = (-1 != mrr->tenPvlIdx ? (*mttP)->gctx->pvl[mrr->tenPvlIdx]->answer : NULL); (*mttP)->thrid = whichThread; (*mttP)->raySample = 0; (*mttP)->samples = 0; (*mttP)->verbose = 0; (*mttP)->skip = 0; (*mttP)->_normal = _miteAnswerPointer(*mttP, mrr->normalSpec); /* set up shading answers */ switch(mrr->shadeSpec->method) { case miteShadeMethodNone: /* nothing to do */ break; case miteShadeMethodPhong: (*mttP)->shadeVec0 = _miteAnswerPointer(*mttP, mrr->shadeSpec->vec0); break; case miteShadeMethodLitTen: (*mttP)->shadeVec0 = _miteAnswerPointer(*mttP, mrr->shadeSpec->vec0); (*mttP)->shadeVec1 = _miteAnswerPointer(*mttP, mrr->shadeSpec->vec1); (*mttP)->shadeScl0 = _miteAnswerPointer(*mttP, mrr->shadeSpec->scl0); (*mttP)->shadeScl1 = _miteAnswerPointer(*mttP, mrr->shadeSpec->scl1); break; default: biffAddf(MITE, "%s: shade method %d not implemented!", me, mrr->shadeSpec->method); return 1; break; } if (_miteStageSet(*mttP, mrr)) { biffAddf(MITE, "%s: trouble setting up stage array", me); return 1; } return 0; }