static void _rtp_r(double RThPh[3], const double r[3]) { RThPh[0] = R1; RThPh[1] = acos(AIR_CLAMP(-1, R3, 1))/3; RThPh[2] = asin(AIR_CLAMP(-1, (SQRT2/SQRT3)*R2, 1)); }
static void _wp_mu(double wp[3], const double mu[3]) { double stdv, mode; wp[0] = MU1; stdv = sqrt(MU2); wp[1] = SQRT2*stdv; mode = stdv ? SQRT2*MU3/(stdv*stdv*stdv) : 0; mode = AIR_CLAMP(-1, mode, 1); wp[2] = acos(AIR_CLAMP(-1, mode, 1))/3; }
static void _rtz_k(double rThZ[3], const double k[3]) { rThZ[0] = K2; rThZ[1] = acos(AIR_CLAMP(-1, K3, 1))/3; rThZ[2] = K1/SQRT3; }
static void _wp_k(double wp[3], const double k[3]) { wp[0] = K1/3; wp[1] = (SQRT2/SQRT3)*K2; wp[2] = acos(AIR_CLAMP(-1, K3, 1))/3; }
int _mossCenter(int center) { center = (nrrdCenterUnknown == center ? mossDefCenter : center); center = AIR_CLAMP(nrrdCenterUnknown+1, center, nrrdCenterLast-1); return center; }
/* ** _nrrdCenter() ** ** for nrrdCenterCell and nrrdCenterNode, return will be the same ** as input. Converts nrrdCenterUnknown into nrrdDefaultCenter, ** and then clamps to (nrrdCenterUnknown+1, nrrdCenterLast-1). ** ** Thus, this ALWAYS returns nrrdCenterNode or nrrdCenterCell ** (as long as those are the only two centering schemes). */ int _nrrdCenter(int center) { center = (nrrdCenterUnknown == center ? nrrdDefaultCenter : center); center = AIR_CLAMP(nrrdCenterUnknown+1, center, nrrdCenterLast-1); return center; }
int _nrrdCenter2(int center, int defCenter) { center = (nrrdCenterUnknown == center ? defCenter : center); center = AIR_CLAMP(nrrdCenterUnknown+1, center, nrrdCenterLast-1); return center; }
/* ** hist[0]: hue vs sat ** hist[1]: hue vs val */ void imageProc(Nrrd *nhproj[3], Nrrd *nhist[2], unsigned int sH, float *rgb, unsigned int size0, unsigned int sXY, unsigned int overSampleNum, float overSampleScale) { unsigned int xyi, hi, si, vi, oi; float rr, gg, bb, hh, ss, vv, *hist[2]; double rndA, rndB; nrrdZeroSet(nhist[0]); nrrdZeroSet(nhist[1]); hist[0] = AIR_CAST(float *, nhist[0]->data); hist[1] = AIR_CAST(float *, nhist[1]->data); for (xyi=0; xyi<sXY; xyi++) { rr = AIR_CLAMP(0, rgb[0], 255); gg = AIR_CLAMP(0, rgb[1], 255); bb = AIR_CLAMP(0, rgb[2], 255); rr = AIR_AFFINE(-1, rr, 256, 0, 1); gg = AIR_AFFINE(-1, gg, 256, 0, 1); bb = AIR_AFFINE(-1, bb, 256, 0, 1); dyeRGBtoHSV(&hh, &ss, &vv, rr, gg, bb); si = airIndexClamp(0, ss, 1, sH); vi = airIndexClamp(0, vv, 1, sH); #define UPDATE_HIST(rnd) \ hi = airIndexClamp(0, hh + overSampleScale*(1-ss)*(rnd), 1, sH); \ hist[0][hi + sH*si] += 1.0/overSampleNum; \ hist[1][hi + sH*vi] += 1.0/overSampleNum if (overSampleNum % 2 == 1) { airNormalRand(&rndA, NULL); UPDATE_HIST(rndA); overSampleNum -= 1; } for (oi=0; oi<overSampleNum; oi+=2) { airNormalRand(&rndA, &rndB); UPDATE_HIST(rndA); UPDATE_HIST(rndB); } rgb += size0; } nrrdProject(nhproj[0], nhist[0], 1, nrrdMeasureHistoMean, nrrdTypeFloat); nrrdProject(nhproj[1], nhist[1], 1, nrrdMeasureHistoMean, nrrdTypeFloat); nrrdProject(nhproj[2], nhist[1], 1, nrrdMeasureSum, nrrdTypeFloat); }
void _limnSplineIntervalFind_Warp(int *ii, double *ff, limnSpline *spline, double tt) { int N; N = spline->ncpt->axis[2].size; tt = AIR_CLAMP(spline->time[0], tt, spline->time[N-1]); *ii = AIR_CLAMP(0, *ii, N-2); /* the last value of ii may be the right one */ if (!AIR_IN_CL(spline->time[*ii], tt, spline->time[*ii+1])) { /* HEY: make this a binary search */ for (*ii=0; *ii<N-2; (*ii)++) { if (AIR_IN_CL(spline->time[*ii], tt, spline->time[*ii+1])) { break; } } } *ff = (tt - spline->time[*ii])/(spline->time[*ii+1] - spline->time[*ii]); return; }
void _limnSplineIntervalFind_NonWarp(int *ii, double *ff, limnSpline *spline, double tt) { int N; N = spline->ncpt->axis[2].size + (spline->loop ? 1 : 0); tt = AIR_CLAMP(0, tt, N-1); *ii = (int)tt; *ff = tt - *ii; return; }
int dyeColorGet(float *v0P, float *v1P, float *v2P, dyeColor *col) { int spc; spc = dyeSpaceUnknown; if (v0P && v1P && v2P && col) { col->ii = AIR_CLAMP(0, col->ii, 1); spc = col->spc[col->ii]; ELL_3V_GET(*v0P, *v1P, *v2P, col->val[col->ii]); } return spc; }
char * dyeColorSprintf(char *str, dyeColor *col) { if (str && col) { col->ii = AIR_CLAMP(0, col->ii, 1); sprintf(str, "%s:%g,%g,%g", dyeSpaceToStr[col->spc[col->ii]], col->val[col->ii][0], col->val[col->ii][1], col->val[col->ii][2]); } return str; }
int nrrdArithAffine(Nrrd *nout, double minIn, const Nrrd *nin, double maxIn, double minOut, double maxOut, int clamp) { static const char me[]="nrrdArithAffine"; size_t I, N; double (*ins)(void *v, size_t I, double d), (*lup)(const void *v, size_t I), mmin, mmax; if ( !nout || nrrdCheck(nin) ) { biffAddf(NRRD, "%s: got NULL pointer or invalid input", me); return 1; } if (nout != nin) { if (nrrdCopy(nout, nin)) { biffAddf(NRRD, "%s: couldn't initialize output", me); return 1; } } N = nrrdElementNumber(nin); ins = nrrdDInsert[nout->type]; lup = nrrdDLookup[nin->type]; mmin = AIR_MIN(minOut, maxOut); mmax = AIR_MAX(minOut, maxOut); for (I=0; I<N; I++) { double val; val = lup(nin->data, I); val = AIR_AFFINE(minIn, val, maxIn, minOut, maxOut); if (clamp) { val = AIR_CLAMP(mmin, val, mmax); } ins(nout->data, I, val); } /* HEY: it would be much better if the ordering here was the same as in AIR_AFFINE, but that's not easy with the way the content functions are now set up */ if (nrrdContentSet_va(nout, "affine", nin, "%g,%g,%g,%g", minIn, maxIn, minOut, maxOut)) { biffAddf(NRRD, "%s:", me); } return 0; }
int _baneClipAnswer_TopN(int *countP, Nrrd *hvol, double *clipParm) { static const char me[]="_baneClipAnwer_TopN"; Nrrd *copy; int *hits, tmp; size_t num; if (nrrdCopy(copy=nrrdNew(), hvol)) { biffMovef(BANE, NRRD, "%s: couldn't create copy of histovol", me); return 1; } hits = (int *)copy->data; num = nrrdElementNumber(copy); qsort(hits, num, sizeof(int), nrrdValCompare[nrrdTypeInt]); tmp = AIR_CLAMP(0, (int)clipParm[0], (int)num-1); *countP = hits[num-tmp-1]; nrrdNuke(copy); return 0; }
dyeColor * dyeColorSet(dyeColor *col, int space, float v0, float v1, float v2) { if (col && DYE_VALID_SPACE(space)) { col->ii = AIR_CLAMP(0, col->ii, 1); /* We switch to the other one if the current one seems to be used, but we don't switch if new and current colorspaces are the same. If the other one is being used too, oh well. */ if (dyeSpaceUnknown != col->spc[col->ii] && AIR_EXISTS(col->val[col->ii][0]) && col->spc[col->ii] != space) { col->ii = 1 - col->ii; } ELL_3V_SET(col->val[col->ii], v0, v1, v2); col->spc[col->ii] = space; } return col; }
void _limnSplineIndexFind(int *idx, limnSpline *spline, int ii) { int N, ti[4]; N = spline->ncpt->axis[2].size; if (limnSplineTypeHasImplicitTangents[spline->type]) { if (spline->loop) { ELL_4V_SET(ti, AIR_MOD(ii-1, N), AIR_MOD(ii+0, N), AIR_MOD(ii+1, N), AIR_MOD(ii+2, N)); } else { ELL_4V_SET(ti, AIR_CLAMP(0, ii-1, N-1), AIR_CLAMP(0, ii+0, N-1), AIR_CLAMP(0, ii+1, N-1), AIR_CLAMP(0, ii+2, N-1)); } ELL_4V_SET(idx, 1 + 3*ti[0], 1 + 3*ti[1], 1 + 3*ti[2], 1 + 3*ti[3]); } else { if (spline->loop) { ELL_4V_SET(ti, AIR_MOD(ii+0, N), AIR_MOD(ii+0, N), AIR_MOD(ii+1, N), AIR_MOD(ii+1, N)); } else { ELL_4V_SET(ti, AIR_CLAMP(0, ii+0, N-1), AIR_CLAMP(0, ii+0, N-1), AIR_CLAMP(0, ii+1, N-1), AIR_CLAMP(0, ii+1, N-1)); } ELL_4V_SET(idx, 1 + 3*ti[0], 2 + 3*ti[1], 0 + 3*ti[2], 1 + 3*ti[3]); } }
/* ** _nrrdApply1DLutOrRegMap() ** ** the guts of nrrdApply1DLut and nrrdApply1DRegMap ** ** yikes, does NOT use biff, since we're only supposed to be called ** after copious error checking. ** ** FOR INSTANCE, this allows nout == nin, which could be a big ** problem if mapAxis == 1. ** ** we don't need a typeOut arg because nout has already been allocated ** as some specific type; we'll look at that. ** ** NOTE: non-existant values get passed through regular maps and luts ** "unchanged". However, if the output type is integral, the results ** are probaby undefined. HEY: there is currently no warning message ** or error handling based on nrrdStateDisallowIntegerNonExist, but ** there really should be. */ int _nrrdApply1DLutOrRegMap(Nrrd *nout, const Nrrd *nin, const NrrdRange *range, const Nrrd *nmap, int ramps, int rescale, int multi) { /* char me[]="_nrrdApply1DLutOrRegMap"; */ char *inData, *outData, *mapData, *entData0, *entData1; size_t N, I; double (*inLoad)(const void *v), (*mapLup)(const void *v, size_t I), (*outInsert)(void *v, size_t I, double d), val, mapIdxFrac, domMin, domMax; unsigned int i, mapAxis, mapLen, mapIdx, entSize, entLen, inSize, outSize; if (!multi) { mapAxis = nmap->dim - 1; /* axis of nmap containing entries */ } else { mapAxis = nmap->dim - nin->dim - 1; } mapData = (char *)nmap->data; /* map data, as char* */ /* low end of map domain */ domMin = _nrrdApplyDomainMin(nmap, ramps, mapAxis); /* high end of map domain */ domMax = _nrrdApplyDomainMax(nmap, ramps, mapAxis); mapLen = nmap->axis[mapAxis].size; /* number of entries in map */ mapLup = nrrdDLookup[nmap->type]; /* how to get doubles out of map */ inData = (char *)nin->data; /* input data, as char* */ inLoad = nrrdDLoad[nin->type]; /* how to get doubles out of nin */ inSize = nrrdElementSize(nin); /* size of one input value */ outData = (char *)nout->data; /* output data, as char* */ outInsert = nrrdDInsert[nout->type]; /* putting doubles into output */ entLen = (mapAxis /* number of elements in one entry */ ? nmap->axis[0].size : 1); outSize = entLen*nrrdElementSize(nout); /* size of entry in output */ entSize = entLen*nrrdElementSize(nmap); /* size of entry in map */ N = nrrdElementNumber(nin); /* the number of values to be mapped */ if (ramps) { /* regular map */ for (I=0; I<N; I++) { /* ... if (!(I % 100)) fprintf(stderr, "I = %d\n", (int)I); ... */ val = inLoad(inData); /* ... fprintf(stderr, "##%s: val = \na% 31.15f --> ", me, val); ... */ if (rescale) { val = AIR_AFFINE(range->min, val, range->max, domMin, domMax); /* ... fprintf(stderr, "\nb% 31.15f (min,max = %g,%g)--> ", val, range->min, range->max); ... */ } /* ... fprintf(stderr, "\nc% 31.15f --> clamp(%g,%g), %d --> ", val, domMin, domMax, mapLen); ... */ if (AIR_EXISTS(val)) { val = AIR_CLAMP(domMin, val, domMax); mapIdxFrac = AIR_AFFINE(domMin, val, domMax, 0, mapLen-1); /* ... fprintf(stderr, "mapIdxFrac = \nd% 31.15f --> ", mapIdxFrac); ... */ mapIdx = (unsigned int)mapIdxFrac; mapIdx -= mapIdx == mapLen-1; mapIdxFrac -= mapIdx; /* ... fprintf(stderr, "%s: (%d,\ne% 31.15f) --> \n", me, mapIdx, mapIdxFrac); ... */ entData0 = mapData + mapIdx*entSize; entData1 = mapData + (mapIdx+1)*entSize; for (i=0; i<entLen; i++) { val = ((1-mapIdxFrac)*mapLup(entData0, i) + mapIdxFrac*mapLup(entData1, i)); outInsert(outData, i, val); /* ... fprintf(stderr, "f% 31.15f\n", val); ... */ } } else { /* copy non-existant values from input to output */ for (i=0; i<entLen; i++) { outInsert(outData, i, val); } } inData += inSize; outData += outSize; if (multi) { mapData += mapLen*entSize; } } } else { /* lookup table */ for (I=0; I<N; I++) { val = inLoad(inData); if (rescale) { val = AIR_AFFINE(range->min, val, range->max, domMin, domMax); } if (AIR_EXISTS(val)) { mapIdx = airIndexClamp(domMin, val, domMax, mapLen); entData0 = mapData + mapIdx*entSize; for (i=0; i<entLen; i++) { outInsert(outData, i, mapLup(entData0, i)); } } else { /* copy non-existant values from input to output */ for (i=0; i<entLen; i++) { outInsert(outData, i, val); } } inData += inSize; outData += outSize; if (multi) { mapData += mapLen*entSize; } } } return 0; }
static double _nrrdDClampFL(DB v) { return AIR_CLAMP(-FLT_MAX, v, FLT_MAX); }
static double _nrrdDClampUL(DB v) { return AIR_CLAMP(0, v, NRRD_ULLONG_MAX);}
static double _nrrdDClampLL(DB v) { return AIR_CLAMP(NRRD_LLONG_MIN, v, NRRD_LLONG_MAX);}
int tenGlyphGen(limnObject *glyphsLimn, echoScene *glyphsEcho, tenGlyphParm *parm, const Nrrd *nten, const Nrrd *npos, const Nrrd *nslc) { static const char me[]="tenGlyphGen"; gageShape *shape; airArray *mop; float *tdata, eval[3], evec[9], *cvec, rotEvec[9], mA_f[16], absEval[3], glyphScl[3]; double pI[3], pW[3], cl, cp, sRot[16], mA[16], mB[16], msFr[9], tmpvec[3], R, G, B, qA, qB, qC, glyphAniso, sliceGray; unsigned int duh; int slcCoord[3], idx, glyphIdx, axis, numGlyphs, svRGBAfl=AIR_FALSE; limnLook *look; int lookIdx; echoObject *eglyph, *inst, *list=NULL, *split, *esquare; echoPos_t eM[16], originOffset[3], edge0[3], edge1[3]; char stmp[AIR_STRLEN_SMALL]; /* int eret; double tmp1[3], tmp2[3]; */ if (!( (glyphsLimn || glyphsEcho) && nten && parm)) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } mop = airMopNew(); shape = gageShapeNew(); shape->defaultCenter = nrrdCenterCell; airMopAdd(mop, shape, (airMopper)gageShapeNix, airMopAlways); if (npos) { if (!( 2 == nten->dim && 7 == nten->axis[0].size )) { biffAddf(TEN, "%s: nten isn't 2-D 7-by-N array", me); airMopError(mop); return 1; } if (!( 2 == npos->dim && 3 == npos->axis[0].size && nten->axis[1].size == npos->axis[1].size )) { biffAddf(TEN, "%s: npos isn't 2-D 3-by-%s array", me, airSprintSize_t(stmp, nten->axis[1].size)); airMopError(mop); return 1; } if (!( nrrdTypeFloat == nten->type && nrrdTypeFloat == npos->type )) { biffAddf(TEN, "%s: nten and npos must be %s, not %s and %s", me, airEnumStr(nrrdType, nrrdTypeFloat), airEnumStr(nrrdType, nten->type), airEnumStr(nrrdType, npos->type)); airMopError(mop); return 1; } } else { if (tenTensorCheck(nten, nrrdTypeFloat, AIR_TRUE, AIR_TRUE)) { biffAddf(TEN, "%s: didn't get a valid DT volume", me); airMopError(mop); return 1; } } if (tenGlyphParmCheck(parm, nten, npos, nslc)) { biffAddf(TEN, "%s: trouble", me); airMopError(mop); return 1; } if (!npos) { if (gageShapeSet(shape, nten, tenGageKind->baseDim)) { biffMovef(TEN, GAGE, "%s: trouble", me); airMopError(mop); return 1; } } if (parm->doSlice) { ELL_3V_COPY(edge0, shape->spacing); ELL_3V_COPY(edge1, shape->spacing); edge0[parm->sliceAxis] = edge1[parm->sliceAxis] = 0.0; switch(parm->sliceAxis) { case 0: edge0[1] = edge1[2] = 0; ELL_4M_ROTATE_Y_SET(sRot, AIR_PI/2); break; case 1: edge0[0] = edge1[2] = 0; ELL_4M_ROTATE_X_SET(sRot, AIR_PI/2); break; case 2: default: edge0[0] = edge1[1] = 0; ELL_4M_IDENTITY_SET(sRot); break; } ELL_3V_COPY(originOffset, shape->spacing); ELL_3V_SCALE(originOffset, -0.5, originOffset); originOffset[parm->sliceAxis] *= -2*parm->sliceOffset; } if (glyphsLimn) { /* create limnLooks for diffuse and ambient-only shading */ /* ??? */ /* hack: save old value of setVertexRGBAFromLook, and set to true */ svRGBAfl = glyphsLimn->setVertexRGBAFromLook; glyphsLimn->setVertexRGBAFromLook = AIR_TRUE; } if (glyphsEcho) { list = echoObjectNew(glyphsEcho, echoTypeList); } if (npos) { numGlyphs = AIR_UINT(nten->axis[1].size); } else { numGlyphs = shape->size[0] * shape->size[1] * shape->size[2]; } /* find measurement frame transform */ if (3 == nten->spaceDim && AIR_EXISTS(nten->measurementFrame[0][0])) { /* msFr nten->measurementFrame ** 0 1 2 [0][0] [1][0] [2][0] ** 3 4 5 [0][1] [1][1] [2][1] ** 6 7 8 [0][2] [1][2] [2][2] */ msFr[0] = nten->measurementFrame[0][0]; msFr[3] = nten->measurementFrame[0][1]; msFr[6] = nten->measurementFrame[0][2]; msFr[1] = nten->measurementFrame[1][0]; msFr[4] = nten->measurementFrame[1][1]; msFr[7] = nten->measurementFrame[1][2]; msFr[2] = nten->measurementFrame[2][0]; msFr[5] = nten->measurementFrame[2][1]; msFr[8] = nten->measurementFrame[2][2]; } else { ELL_3M_IDENTITY_SET(msFr); } for (idx=0; idx<numGlyphs; idx++) { tdata = (float*)(nten->data) + 7*idx; if (parm->verbose >= 2) { fprintf(stderr, "%s: glyph %d/%d: hello %g %g %g %g %g %g %g\n", me, idx, numGlyphs, tdata[0], tdata[1], tdata[2], tdata[3], tdata[4], tdata[5], tdata[6]); } if (!( TEN_T_EXISTS(tdata) )) { /* there's nothing we can do here */ if (parm->verbose >= 2) { fprintf(stderr, "%s: glyph %d/%d: non-existent data\n", me, idx, numGlyphs); } continue; } if (npos) { ELL_3V_COPY(pW, (float*)(npos->data) + 3*idx); if (!( AIR_EXISTS(pW[0]) && AIR_EXISTS(pW[1]) && AIR_EXISTS(pW[2]) )) { /* position doesn't exist- perhaps because its from the push library, which might kill points by setting coords to nan */ continue; } } else { NRRD_COORD_GEN(pI, shape->size, 3, idx); /* this does take into account full orientation */ gageShapeItoW(shape, pW, pI); if (parm->nmask) { if (!( nrrdFLookup[parm->nmask->type](parm->nmask->data, idx) >= parm->maskThresh )) { if (parm->verbose >= 2) { fprintf(stderr, "%s: glyph %d/%d: doesn't meet mask thresh\n", me, idx, numGlyphs); } continue; } } } tenEigensolve_f(eval, evec, tdata); /* transform eigenvectors by measurement frame */ ELL_3MV_MUL(tmpvec, msFr, evec + 0); ELL_3V_COPY_TT(evec + 0, float, tmpvec); ELL_3MV_MUL(tmpvec, msFr, evec + 3); ELL_3V_COPY_TT(evec + 3, float, tmpvec); ELL_3MV_MUL(tmpvec, msFr, evec + 6); ELL_3V_COPY_TT(evec + 6, float, tmpvec); ELL_3V_CROSS(tmpvec, evec + 0, evec + 3); if (0 > ELL_3V_DOT(tmpvec, evec + 6)) { ELL_3V_SCALE(evec + 6, -1, evec + 6); } ELL_3M_TRANSPOSE(rotEvec, evec); if (parm->doSlice && pI[parm->sliceAxis] == parm->slicePos) { /* set sliceGray */ if (nslc) { /* we aren't masked by confidence, as anisotropy slice is */ for (duh=0; duh<parm->sliceAxis; duh++) { slcCoord[duh] = (int)(pI[duh]); } for (duh=duh<parm->sliceAxis; duh<2; duh++) { slcCoord[duh] = (int)(pI[duh+1]); } /* HEY: GLK has no idea what's going here */ slcCoord[0] = (int)(pI[0]); slcCoord[1] = (int)(pI[1]); slcCoord[2] = (int)(pI[2]); sliceGray = nrrdFLookup[nslc->type](nslc->data, slcCoord[0] + nslc->axis[0].size*slcCoord[1]); } else { if (!( tdata[0] >= parm->confThresh )) { if (parm->verbose >= 2) { fprintf(stderr, "%s: glyph %d/%d (slice): conf %g < thresh %g\n", me, idx, numGlyphs, tdata[0], parm->confThresh); } continue; } sliceGray = tenAnisoEval_f(eval, parm->sliceAnisoType); } if (parm->sliceGamma > 0) { sliceGray = AIR_AFFINE(0, sliceGray, 1, parm->sliceBias, 1); sliceGray = pow(sliceGray, 1.0/parm->sliceGamma); } else { sliceGray = AIR_AFFINE(0, sliceGray, 1, 0, 1-parm->sliceBias); sliceGray = 1.0 - pow(sliceGray, -1.0/parm->sliceGamma); } /* make slice contribution */ /* HEY: this is *NOT* aware of shape->fromOrientation */ if (glyphsLimn) { lookIdx = limnObjectLookAdd(glyphsLimn); look = glyphsLimn->look + lookIdx; ELL_4V_SET_TT(look->rgba, float, sliceGray, sliceGray, sliceGray, 1); ELL_3V_SET(look->kads, 1, 0, 0); look->spow = 0; glyphIdx = limnObjectSquareAdd(glyphsLimn, lookIdx); ELL_4M_IDENTITY_SET(mA); ell_4m_post_mul_d(mA, sRot); if (!npos) { ELL_4M_SCALE_SET(mB, shape->spacing[0], shape->spacing[1], shape->spacing[2]); } ell_4m_post_mul_d(mA, mB); ELL_4M_TRANSLATE_SET(mB, pW[0], pW[1], pW[2]); ell_4m_post_mul_d(mA, mB); ELL_4M_TRANSLATE_SET(mB, originOffset[0], originOffset[1], originOffset[2]); ell_4m_post_mul_d(mA, mB); ELL_4M_COPY_TT(mA_f, float, mA); limnObjectPartTransform(glyphsLimn, glyphIdx, mA_f); } if (glyphsEcho) { esquare = echoObjectNew(glyphsEcho,echoTypeRectangle); ELL_3V_ADD2(((echoRectangle*)esquare)->origin, pW, originOffset); ELL_3V_COPY(((echoRectangle*)esquare)->edge0, edge0); ELL_3V_COPY(((echoRectangle*)esquare)->edge1, edge1); echoColorSet(esquare, AIR_CAST(echoCol_t, sliceGray), AIR_CAST(echoCol_t, sliceGray), AIR_CAST(echoCol_t, sliceGray), 1); /* this is pretty arbitrary- but I want shadows to have some effect. Previously, the material was all ambient: (A,D,S) = (1,0,0), which avoided all shadow effects. */ echoMatterPhongSet(glyphsEcho, esquare, 0.4f, 0.6f, 0, 40); echoListAdd(list, esquare); } } if (parm->onlyPositive) { if (eval[2] < 0) { /* didn't have all positive eigenvalues, its outta here */ if (parm->verbose >= 2) { fprintf(stderr, "%s: glyph %d/%d: not all evals %g %g %g > 0\n", me, idx, numGlyphs, eval[0], eval[1], eval[2]); } continue; } } if (!( tdata[0] >= parm->confThresh )) { if (parm->verbose >= 2) { fprintf(stderr, "%s: glyph %d/%d: conf %g < thresh %g\n", me, idx, numGlyphs, tdata[0], parm->confThresh); } continue; } if (!( tenAnisoEval_f(eval, parm->anisoType) >= parm->anisoThresh )) { if (parm->verbose >= 2) { fprintf(stderr, "%s: glyph %d/%d: aniso[%d] %g < thresh %g\n", me, idx, numGlyphs, parm->anisoType, tenAnisoEval_f(eval, parm->anisoType), parm->anisoThresh); } continue; } glyphAniso = tenAnisoEval_f(eval, parm->colAnisoType); /* fprintf(stderr, "%s: eret = %d; evals = %g %g %g\n", me, eret, eval[0], eval[1], eval[2]); ELL_3V_CROSS(tmp1, evec+0, evec+3); tmp2[0] = ELL_3V_LEN(tmp1); ELL_3V_CROSS(tmp1, evec+0, evec+6); tmp2[1] = ELL_3V_LEN(tmp1); ELL_3V_CROSS(tmp1, evec+3, evec+6); tmp2[2] = ELL_3V_LEN(tmp1); fprintf(stderr, "%s: crosses = %g %g %g\n", me, tmp2[0], tmp2[1], tmp2[2]); */ /* set transform (in mA) */ ELL_3V_ABS(absEval, eval); ELL_4M_IDENTITY_SET(mA); /* reset */ ELL_3V_SCALE(glyphScl, parm->glyphScale, absEval); /* scale by evals */ ELL_4M_SCALE_SET(mB, glyphScl[0], glyphScl[1], glyphScl[2]); ell_4m_post_mul_d(mA, mB); ELL_43M_INSET(mB, rotEvec); /* rotate by evecs */ ell_4m_post_mul_d(mA, mB); ELL_4M_TRANSLATE_SET(mB, pW[0], pW[1], pW[2]); /* translate */ ell_4m_post_mul_d(mA, mB); /* set color (in R,G,B) */ cvec = evec + 3*(AIR_CLAMP(0, parm->colEvec, 2)); R = AIR_ABS(cvec[0]); /* standard mapping */ G = AIR_ABS(cvec[1]); B = AIR_ABS(cvec[2]); /* desaturate by colMaxSat */ R = AIR_AFFINE(0.0, parm->colMaxSat, 1.0, parm->colIsoGray, R); G = AIR_AFFINE(0.0, parm->colMaxSat, 1.0, parm->colIsoGray, G); B = AIR_AFFINE(0.0, parm->colMaxSat, 1.0, parm->colIsoGray, B); /* desaturate some by anisotropy */ R = AIR_AFFINE(0.0, parm->colAnisoModulate, 1.0, R, AIR_AFFINE(0.0, glyphAniso, 1.0, parm->colIsoGray, R)); G = AIR_AFFINE(0.0, parm->colAnisoModulate, 1.0, G, AIR_AFFINE(0.0, glyphAniso, 1.0, parm->colIsoGray, G)); B = AIR_AFFINE(0.0, parm->colAnisoModulate, 1.0, B, AIR_AFFINE(0.0, glyphAniso, 1.0, parm->colIsoGray, B)); /* clamp and do gamma */ R = AIR_CLAMP(0.0, R, 1.0); G = AIR_CLAMP(0.0, G, 1.0); B = AIR_CLAMP(0.0, B, 1.0); R = pow(R, parm->colGamma); G = pow(G, parm->colGamma); B = pow(B, parm->colGamma); /* find axis, and superquad exponents qA and qB */ if (eval[2] > 0) { /* all evals positive */ cl = AIR_MIN(0.99, tenAnisoEval_f(eval, tenAniso_Cl1)); cp = AIR_MIN(0.99, tenAnisoEval_f(eval, tenAniso_Cp1)); if (cl > cp) { axis = 0; qA = pow(1-cp, parm->sqdSharp); qB = pow(1-cl, parm->sqdSharp); } else { axis = 2; qA = pow(1-cl, parm->sqdSharp); qB = pow(1-cp, parm->sqdSharp); } qC = qB; } else if (eval[0] < 0) { /* all evals negative */ float aef[3]; aef[0] = absEval[2]; aef[1] = absEval[1]; aef[2] = absEval[0]; cl = AIR_MIN(0.99, tenAnisoEval_f(aef, tenAniso_Cl1)); cp = AIR_MIN(0.99, tenAnisoEval_f(aef, tenAniso_Cp1)); if (cl > cp) { axis = 2; qA = pow(1-cp, parm->sqdSharp); qB = pow(1-cl, parm->sqdSharp); } else { axis = 0; qA = pow(1-cl, parm->sqdSharp); qB = pow(1-cp, parm->sqdSharp); } qC = qB; } else { #define OOSQRT2 0.70710678118654752440 #define OOSQRT3 0.57735026918962576451 /* double poleA[3]={OOSQRT3, OOSQRT3, OOSQRT3}; */ double poleB[3]={1, 0, 0}; double poleC[3]={OOSQRT2, OOSQRT2, 0}; double poleD[3]={OOSQRT3, -OOSQRT3, -OOSQRT3}; double poleE[3]={OOSQRT2, 0, -OOSQRT2}; double poleF[3]={OOSQRT3, OOSQRT3, -OOSQRT3}; double poleG[3]={0, -OOSQRT2, -OOSQRT2}; double poleH[3]={0, 0, -1}; /* double poleI[3]={-OOSQRT3, -OOSQRT3, -OOSQRT3}; */ double funk[3]={0,4,2}, thrn[3]={1,4,4}; double octa[3]={0,2,2}, cone[3]={1,2,2}; double evalN[3], tmp, bary[3]; double qq[3]; ELL_3V_NORM(evalN, eval, tmp); if (eval[1] >= -eval[2]) { /* inside B-F-C */ ell_3v_barycentric_spherical_d(bary, poleB, poleF, poleC, evalN); ELL_3V_SCALE_ADD3(qq, bary[0], octa, bary[1], thrn, bary[2], cone); axis = 2; } else if (eval[0] >= -eval[2]) { /* inside B-D-F */ if (eval[1] >= 0) { /* inside B-E-F */ ell_3v_barycentric_spherical_d(bary, poleB, poleE, poleF, evalN); ELL_3V_SCALE_ADD3(qq, bary[0], octa, bary[1], funk, bary[2], thrn); axis = 2; } else { /* inside B-D-E */ ell_3v_barycentric_spherical_d(bary, poleB, poleD, poleE, evalN); ELL_3V_SCALE_ADD3(qq, bary[0], cone, bary[1], thrn, bary[2], funk); axis = 0; } } else if (eval[0] < -eval[1]) { /* inside D-G-H */ ell_3v_barycentric_spherical_d(bary, poleD, poleG, poleH, evalN); ELL_3V_SCALE_ADD3(qq, bary[0], thrn, bary[1], cone, bary[2], octa); axis = 0; } else if (eval[1] < 0) { /* inside E-D-H */ ell_3v_barycentric_spherical_d(bary, poleE, poleD, poleH, evalN); ELL_3V_SCALE_ADD3(qq, bary[0], funk, bary[1], thrn, bary[2], octa); axis = 0; } else { /* inside F-E-H */ ell_3v_barycentric_spherical_d(bary, poleF, poleE, poleH, evalN); ELL_3V_SCALE_ADD3(qq, bary[0], thrn, bary[1], funk, bary[2], cone); axis = 2; } qA = qq[0]; qB = qq[1]; qC = qq[2]; #undef OOSQRT2 #undef OOSQRT3 } /* add the glyph */ if (parm->verbose >= 2) { fprintf(stderr, "%s: glyph %d/%d: the glyph stays!\n", me, idx, numGlyphs); } if (glyphsLimn) { lookIdx = limnObjectLookAdd(glyphsLimn); look = glyphsLimn->look + lookIdx; ELL_4V_SET_TT(look->rgba, float, R, G, B, 1); ELL_3V_SET(look->kads, parm->ADSP[0], parm->ADSP[1], parm->ADSP[2]); look->spow = 0; switch(parm->glyphType) { case tenGlyphTypeBox: glyphIdx = limnObjectCubeAdd(glyphsLimn, lookIdx); break; case tenGlyphTypeSphere: glyphIdx = limnObjectPolarSphereAdd(glyphsLimn, lookIdx, axis, 2*parm->facetRes, parm->facetRes); break; case tenGlyphTypeCylinder: glyphIdx = limnObjectCylinderAdd(glyphsLimn, lookIdx, axis, parm->facetRes); break; case tenGlyphTypeSuperquad: default: glyphIdx = limnObjectPolarSuperquadFancyAdd(glyphsLimn, lookIdx, axis, AIR_CAST(float, qA), AIR_CAST(float, qB), AIR_CAST(float, qC), 0, 2*parm->facetRes, parm->facetRes); break; } ELL_4M_COPY_TT(mA_f, float, mA); limnObjectPartTransform(glyphsLimn, glyphIdx, mA_f); } if (glyphsEcho) { switch(parm->glyphType) { case tenGlyphTypeBox: eglyph = echoObjectNew(glyphsEcho, echoTypeCube); /* nothing else to set */ break; case tenGlyphTypeSphere: eglyph = echoObjectNew(glyphsEcho, echoTypeSphere); echoSphereSet(eglyph, 0, 0, 0, 1); break; case tenGlyphTypeCylinder: eglyph = echoObjectNew(glyphsEcho, echoTypeCylinder); echoCylinderSet(eglyph, axis); break; case tenGlyphTypeSuperquad: default: eglyph = echoObjectNew(glyphsEcho, echoTypeSuperquad); echoSuperquadSet(eglyph, axis, qA, qB); break; } echoColorSet(eglyph, AIR_CAST(echoCol_t, R), AIR_CAST(echoCol_t, G), AIR_CAST(echoCol_t, B), 1); echoMatterPhongSet(glyphsEcho, eglyph, parm->ADSP[0], parm->ADSP[1], parm->ADSP[2], parm->ADSP[3]); inst = echoObjectNew(glyphsEcho, echoTypeInstance); ELL_4M_COPY(eM, mA); echoInstanceSet(inst, eM, eglyph); echoListAdd(list, inst); } } if (glyphsLimn) { glyphsLimn->setVertexRGBAFromLook = svRGBAfl; } if (glyphsEcho) { split = echoListSplit3(glyphsEcho, list, 10); echoObjectAdd(glyphsEcho, split); } airMopOkay(mop); return 0; }
static double _nrrdDClampSH(DB v) { return AIR_CLAMP(SHRT_MIN, v, SHRT_MAX);}
/* ******** nrrdDClamp ** ** same as nrrdDClamp, but for doubles. One change: in the case of ** floats, doubles are clamped to the range -FLT_MAX to FLT_MAX. */ static double _nrrdDClampCH(DB v) { return AIR_CLAMP(SCHAR_MIN, v, SCHAR_MAX);}
/* ******** nrrdApply1DIrregMap() ** ** Linear interpolation between irregularly spaced control points. ** Obviously, the location of the control point has to be given ** explicitly. The map nrrd must have dimension 2, and each ** control point is represented by a scanline along axis 0. The ** first value is the position of the control point, and the remaining ** value(s) are linearly weighted according to the position of the ** input value among the control point locations. ** ** To allow "coloring" of non-existant values -inf, NaN, and +inf, if ** the very first value of the map (the location of the first control ** point) is non-existant, then the first three control point locations ** must be -inf, NaN, and +inf, in that order, and the information ** about these points will be used for corresponding input values. ** Doing this makes everything slower, however, because airFPClass_f() ** is called on every single value. ** ** This assumes that nrrd1DIrregMapCheck has been called on "nmap", ** and that nrrd1DIrregAclCheck has been called on "nacl" (if it is ** non-NULL). */ int nrrdApply1DIrregMap(Nrrd *nout, const Nrrd *nin, const NrrdRange *_range, const Nrrd *nmap, const Nrrd *nacl, int typeOut, int rescale) { char me[]="nrrdApply1DIrregMap", err[BIFF_STRLEN]; size_t N, I; int i, *acl, entLen, posLen, aclLen, mapIdx, aclIdx, entSize, colSize, inSize, lo, hi, baseI; double val, *pos, domMin, domMax, mapIdxFrac, (*mapLup)(const void *v, size_t I), (*inLoad)(const void *v), (*outInsert)(void *v, size_t I, double d); char *inData, *outData, *entData0, *entData1; NrrdRange *range; airArray *mop; if (!(nout && nmap && nin)) { sprintf(err, "%s: got NULL pointer", me); biffAdd(NRRD, err); return 1; } mop = airMopNew(); if (_range) { range = nrrdRangeCopy(_range); nrrdRangeSafeSet(range, nin, nrrdBlind8BitRangeState); } else { range = nrrdRangeNewSet(nin, nrrdBlind8BitRangeState); } airMopAdd(mop, range, (airMopper)nrrdRangeNix, airMopAlways); if (_nrrdApply1DSetUp(nout, nin, range, nmap, kindImap, typeOut, rescale, AIR_FALSE)) { sprintf(err, "%s:", me); biffAdd(NRRD, err); airMopError(mop); return 1; } if (nacl && nrrd1DIrregAclCheck(nacl)) { sprintf(err, "%s: given acl isn't valid", me); biffAdd(NRRD, err); airMopError(mop); return 1; } if (nacl) { acl = (int *)nacl->data; aclLen = nacl->axis[1].size; } else { acl = NULL; aclLen = 0; } pos = _nrrd1DIrregMapDomain(&posLen, &baseI, nmap); if (!pos) { sprintf(err, "%s: couldn't determine domain", me); biffAdd(NRRD, err); airMopError(mop); return 1; } airMopAdd(mop, pos, airFree, airMopAlways); mapLup = nrrdDLookup[nmap->type]; inData = (char *)nin->data; inLoad = nrrdDLoad[nin->type]; inSize = nrrdElementSize(nin); mapLup = nrrdDLookup[nmap->type]; entLen = nmap->axis[0].size; /* entLen is really 1 + entry length */ entSize = entLen*nrrdElementSize(nmap); colSize = (entLen-1)*nrrdTypeSize[typeOut]; outData = (char *)nout->data; outInsert = nrrdDInsert[nout->type]; domMin = pos[0]; domMax = pos[posLen-1]; N = nrrdElementNumber(nin); for (I=0; I<N; I++, inData += inSize, outData += colSize) { val = inLoad(inData); /* _VV = ( (AIR_EXISTS(val) && (21 == (int)(-val))) || 22400 < I ); */ /* if (_VV) fprintf(stderr, "##%s: (%d) val = % 31.15f\n", me, (int)I, val); */ if (!AIR_EXISTS(val)) { /* got a non-existant value */ if (baseI) { /* and we know how to deal with them */ switch (airFPClass_d(val)) { case airFP_NEG_INF: mapIdx = 0; break; case airFP_SNAN: case airFP_QNAN: mapIdx = 1; break; case airFP_POS_INF: mapIdx = 2; break; default: mapIdx = 0; fprintf(stderr, "%s: PANIC: non-existant value/class %g/%d " "not handled\n", me, val, airFPClass_d(val)); exit(1); } entData0 = (char*)(nmap->data) + mapIdx*entSize; for (i=1; i<entLen; i++) { outInsert(outData, i-1, mapLup(entData0, i)); } continue; /* we're done! (with this value) */ } else { /* we don't know how to properly deal with this non-existant value: we use the first entry, and then fall through to code below */ mapIdx = 0; mapIdxFrac = 0.0; } } else { /* we have an existant value */ if (rescale) { val = AIR_AFFINE(range->min, val, range->max, domMin, domMax); /* if (_VV) fprintf(stderr, " rescaled --> % 31.15f\n", val); */ } val = AIR_CLAMP(domMin, val, domMax); if (acl) { aclIdx = airIndex(domMin, val, domMax, aclLen); lo = acl[0 + 2*aclIdx]; hi = acl[1 + 2*aclIdx]; } else { lo = 0; hi = posLen-2; } if (lo < hi) { mapIdx = _nrrd1DIrregFindInterval(pos, val, lo, hi); } else { /* acl did its job ==> lo == hi */ mapIdx = lo; } } mapIdxFrac = AIR_AFFINE(pos[mapIdx], val, pos[mapIdx+1], 0.0, 1.0); /* if (_VV) fprintf(stderr, "##%s: val=\n% 31.15f --> " "mapIdx,frac = %d,\n% 31.15f\n", me, val, mapIdx, mapIdxFrac); */ entData0 = (char*)(nmap->data) + (baseI+mapIdx)*entSize; entData1 = (char*)(nmap->data) + (baseI+mapIdx+1)*entSize; /* if (_VV) fprintf(stderr, "##%s: 2; %d/\n% 31.15f --> entLen=%d " "baseI=%d -->\n", me, mapIdx, mapIdxFrac, entLen, baseI); */ for (i=1; i<entLen; i++) { val = ((1-mapIdxFrac)*mapLup(entData0, i) + mapIdxFrac*mapLup(entData1, i)); /* if (_VV) fprintf(stderr, "% 31.15f\n", val); */ outInsert(outData, i-1, val); } /* if (_VV) fprintf(stderr, "##%s: 3\n", me); */ } airMopOkay(mop); return 0; }
int baneGkms_txfMain(int argc, char **argv, char *me, hestParm *hparm) { hestOpt *opt = NULL; char *out, *perr, err[BIFF_STRLEN]; Nrrd *nout; airArray *mop; int pret, E, res[2], vi, gi, step; float min[2], max[2], top[2], v0, g0, *data, v, g, gwidth, width, mwidth, tvl, tvr, vl, vr, tmp, maxa; hestOptAdd(&opt, "r", "Vres Gres", airTypeInt, 2, 2, res, "256 256", "resolution of the transfer function in value and gradient " "magnitude"); hestOptAdd(&opt, "min", "Vmin Gmin", airTypeFloat, 2, 2, min, "0.0 0.0", "minimum value and grad mag in txf"); hestOptAdd(&opt, "max", "Vmax Gmax", airTypeFloat, 2, 2, max, NULL, "maximum value and grad mag in txf"); hestOptAdd(&opt, "v", "base value", airTypeFloat, 1, 1, &v0, NULL, "data value at which to position bottom of triangle"); hestOptAdd(&opt, "g", "gthresh", airTypeFloat, 1, 1, &g0, "0.0", "lowest grad mag to receive opacity"); hestOptAdd(&opt, "gw", "gwidth", airTypeFloat, 1, 1, &gwidth, "0.0", "range of grad mag values over which to apply threshold " "at low gradient magnitudes"); hestOptAdd(&opt, "top", "Vtop Gtop", airTypeFloat, 2, 2, top, NULL, "data value and grad mag at center of top of triangle"); hestOptAdd(&opt, "w", "value width", airTypeFloat, 1, 1, &width, NULL, "range of values to be spanned at top of triangle"); hestOptAdd(&opt, "mw", "value width", airTypeFloat, 1, 1, &mwidth, "0", "range of values to be spanned at BOTTOM of triangle"); hestOptAdd(&opt, "step", NULL, airTypeInt, 0, 0, &step, NULL, "instead of assigning opacity inside a triangular region, " "make it more like a step function, in which opacity never " "decreases in increasing data value"); hestOptAdd(&opt, "a", "max opac", airTypeFloat, 1, 1, &maxa, "1.0", "highest opacity to assign"); hestOptAdd(&opt, "o", "opacOut", airTypeString, 1, 1, &out, NULL, "output opacity function filename"); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_baneGkms_txfInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); E = 0; if (!E) E |= nrrdMaybeAlloc_va(nout, nrrdTypeFloat, 3, AIR_CAST(size_t, 1), AIR_CAST(size_t, res[0]), AIR_CAST(size_t, res[1])); if (!E) E |= !(nout->axis[0].label = airStrdup("A")); if (!E) E |= !(nout->axis[1].label = airStrdup("gage(scalar:v)")); if (!E) nrrdAxisInfoSet_va(nout, nrrdAxisInfoMin, AIR_NAN, (double)min[0], (double)min[1]); if (!E) nrrdAxisInfoSet_va(nout, nrrdAxisInfoMax, AIR_NAN, (double)max[0], (double)max[1]); if (!E) E |= !(nout->axis[2].label = airStrdup("gage(scalar:gm)")); if (E) { sprintf(err, "%s: trouble creating opacity function nrrd", me); biffMove(BANE, err, NRRD); airMopError(mop); return 1; } data = (float *)nout->data; tvl = top[0] - width/2; tvr = top[0] + width/2; mwidth /= 2; for (gi=0; gi<res[1]; gi++) { g = AIR_CAST(float, NRRD_CELL_POS(min[1], max[1], res[1], gi)); for (vi=0; vi<res[0]; vi++) { v = AIR_CAST(float, NRRD_CELL_POS(min[0], max[0], res[0], vi)); vl = AIR_CAST(float, AIR_AFFINE(0, g, top[1], v0-mwidth, tvl)); vr = AIR_CAST(float, AIR_AFFINE(0, g, top[1], v0+mwidth, tvr)); if (g > top[1]) { data[vi + res[0]*gi] = 0; continue; } tmp = AIR_CAST(float, (v - vl)/(0.00001 + vr - vl)); tmp = 1 - AIR_ABS(2*tmp - 1); if (step && v > (vr + vl)/2) { tmp = 1; } tmp = AIR_MAX(0, tmp); data[vi + res[0]*gi] = tmp*maxa; tmp = AIR_CAST(float, AIR_AFFINE(g0 - gwidth/2, g, g0 + gwidth/2, 0.0, 1.0)); tmp = AIR_CLAMP(0, tmp, 1); data[vi + res[0]*gi] *= tmp; } } if (nrrdSave(out, nout, NULL)) { sprintf(err, "%s: trouble saving opacity function", me); biffMove(BANE, err, NRRD); airMopError(mop); return 1; } airMopOkay(mop); return 0; }
static double _nrrdDClampUS(DB v) { return AIR_CLAMP(0, v, USHRT_MAX);}
/* ** _nrrdResampleMakeWeightIndex() ** ** _allocate_ and fill the arrays of indices and weights that are ** needed to process all the scanlines along a given axis; also ** be so kind as to set the sampling ratio (<1: downsampling, ** new sample spacing larger, >1: upsampling, new sample spacing smaller) ** ** returns "dotLen", the number of input samples which are required ** for resampling this axis, or 0 if there was an error. Uses biff. */ int _nrrdResampleMakeWeightIndex(nrrdResample_t **weightP, int **indexP, double *ratioP, const Nrrd *nin, const NrrdResampleInfo *info, unsigned int ai) { char me[]="_nrrdResampleMakeWeightIndex", err[BIFF_STRLEN]; int sizeIn, sizeOut, center, dotLen, halfLen, *index, base, idx; nrrdResample_t minIn, maxIn, minOut, maxOut, spcIn, spcOut, ratio, support, integral, pos, idxD, wght; nrrdResample_t *weight; double parm[NRRD_KERNEL_PARMS_NUM]; int e, i; if (!(info->kernel[ai])) { sprintf(err, "%s: don't see a kernel for dimension %d", me, ai); biffAdd(NRRD, err); *weightP = NULL; *indexP = NULL; return 0; } center = _nrrdCenter(nin->axis[ai].center); sizeIn = nin->axis[ai].size; sizeOut = info->samples[ai]; minIn = AIR_CAST(nrrdResample_t, nin->axis[ai].min); maxIn = AIR_CAST(nrrdResample_t, nin->axis[ai].max); minOut = AIR_CAST(nrrdResample_t, info->min[ai]); maxOut = AIR_CAST(nrrdResample_t, info->max[ai]); spcIn = NRRD_SPACING(center, minIn, maxIn, sizeIn); spcOut = NRRD_SPACING(center, minOut, maxOut, sizeOut); *ratioP = ratio = spcIn/spcOut; support = AIR_CAST(nrrdResample_t, info->kernel[ai]->support(info->parm[ai])); integral = AIR_CAST(nrrdResample_t, info->kernel[ai]->integral(info->parm[ai])); /* fprintf(stderr, "!%s(%d): size{In,Out} = %d, %d, support = %f; ratio = %f\n", me, d, sizeIn, sizeOut, support, ratio); */ if (ratio > 1) { /* if upsampling, we need only as many samples as needed for interpolation with the given kernel */ dotLen = (int)(2*ceil(support)); } else { /* if downsampling, we need to use all the samples covered by the stretched out version of the kernel */ if (info->cheap) { dotLen = (int)(2*ceil(support)); } else { dotLen = (int)(2*ceil(support/ratio)); } } /* fprintf(stderr, "!%s(%d): dotLen = %d\n", me, d, dotLen); */ weight = (nrrdResample_t*)calloc(sizeOut*dotLen, sizeof(nrrdResample_t)); index = (int*)calloc(sizeOut*dotLen, sizeof(int)); if (!(weight && index)) { sprintf(err, "%s: can't allocate weight and index arrays", me); biffAdd(NRRD, err); *weightP = NULL; *indexP = NULL; return 0; } /* calculate sample locations and do first pass on indices */ halfLen = dotLen/2; for (i=0; i<sizeOut; i++) { pos = AIR_CAST(nrrdResample_t, NRRD_POS(center, minOut, maxOut, sizeOut, i)); idxD = AIR_CAST(nrrdResample_t, NRRD_IDX(center, minIn, maxIn, sizeIn, pos)); base = (int)floor(idxD) - halfLen + 1; for (e=0; e<dotLen; e++) { index[e + dotLen*i] = base + e; weight[e + dotLen*i] = idxD - index[e + dotLen*i]; } /* ******** if (!i) { fprintf(stderr, "%s: sample locations:\n", me); } fprintf(stderr, "%s: %d (sample locations)\n ", me, i); for (e=0; e<dotLen; e++) { fprintf(stderr, "%d/%g ", index[e + dotLen*i], weight[e + dotLen*i]); } fprintf(stderr, "\n"); ******** */ } /* nrrdBoundaryPad, 1: fill with some user-specified value nrrdBoundaryBleed, 2: copy the last/first value out as needed nrrdBoundaryWrap, 3: wrap-around nrrdBoundaryWeight, 4: normalize the weighting on the existing samples; ONLY sensible for a strictly positive kernel which integrates to unity (as in blurring) */ /* figure out what to do with the out-of-range indices */ for (i=0; i<dotLen*sizeOut; i++) { idx = index[i]; if (!AIR_IN_CL(0, idx, sizeIn-1)) { switch(info->boundary) { case nrrdBoundaryPad: case nrrdBoundaryWeight: /* this will be further handled later */ idx = sizeIn; break; case nrrdBoundaryBleed: idx = AIR_CLAMP(0, idx, sizeIn-1); break; case nrrdBoundaryWrap: idx = AIR_MOD(idx, sizeIn); break; default: sprintf(err, "%s: boundary behavior %d unknown/unimplemented", me, info->boundary); biffAdd(NRRD, err); *weightP = NULL; *indexP = NULL; return 0; } index[i] = idx; } } /* run the sample locations through the chosen kernel. We play a sneaky trick on the kernel parameter 0 in case of downsampling to create the blurring of the old index space, but only if !cheap */ memcpy(parm, info->parm[ai], NRRD_KERNEL_PARMS_NUM*sizeof(double)); if (ratio < 1 && !(info->cheap)) { parm[0] /= ratio; } info->kernel[ai]->EVALN(weight, weight, dotLen*sizeOut, parm); /* ******** for (i=0; i<sizeOut; i++) { fprintf(stderr, "%s: %d (sample weights)\n ", me, i); for (e=0; e<dotLen; e++) { fprintf(stderr, "%d/%g ", index[e + dotLen*i], weight[e + dotLen*i]); } fprintf(stderr, "\n"); } ******** */ if (nrrdBoundaryWeight == info->boundary) { if (integral) { /* above, we set to sizeIn all the indices that were out of range. We now use that to determine the sum of the weights for the indices that were in-range */ for (i=0; i<sizeOut; i++) { wght = 0; for (e=0; e<dotLen; e++) { if (sizeIn != index[e + dotLen*i]) { wght += weight[e + dotLen*i]; } } for (e=0; e<dotLen; e++) { idx = index[e + dotLen*i]; if (sizeIn != idx) { weight[e + dotLen*i] *= integral/wght; } else { weight[e + dotLen*i] = 0; } } } } } else { /* try to remove ripple/grating on downsampling */ /* if (ratio < 1 && info->renormalize && integral) { */ if (info->renormalize && integral) { for (i=0; i<sizeOut; i++) { wght = 0; for (e=0; e<dotLen; e++) { wght += weight[e + dotLen*i]; } if (wght) { for (e=0; e<dotLen; e++) { /* this used to normalize the weights so that they summed to integral ("*= integral/wght"), which meant that if you use a very truncated Gaussian, then your over-all image brightness goes down. This seems very contrary to the whole point of renormalization. */ weight[e + dotLen*i] *= AIR_CAST(nrrdResample_t, 1.0/wght); } } } } } /* ******** fprintf(stderr, "%s: sample weights:\n", me); for (i=0; i<sizeOut; i++) { fprintf(stderr, "%s: %d\n ", me, i); wght = 0; for (e=0; e<dotLen; e++) { fprintf(stderr, "%d/%g ", index[e + dotLen*i], weight[e + dotLen*i]); wght += weight[e + dotLen*i]; } fprintf(stderr, " (sum = %g)\n", wght); } ******** */ *weightP = weight; *indexP = index; /* fprintf(stderr, "!%s: dotLen = %d\n", me, dotLen); */ return dotLen; }
static double _nrrdDClampJN(DB v) { return AIR_CLAMP(INT_MIN, v, INT_MAX);}
static double _nrrdDClampUC(DB v) { return AIR_CLAMP(0, v, UCHAR_MAX);}
static double _nrrdDClampUI(DB v) { return AIR_CLAMP(0, v, UINT_MAX);}