/* ******** airArrayLenIncr() ** ** Like airArrayLenSet, but works with an increment instead of an ** absolute length. Return value is different: ** got NULL: return 0 ** allocation error: return 0, and a->data set to NULL ** no error, delta > 0: return index of 1st element in newly allocated ** segment (a->len before length was increased) ** no error, delta <= 0: return 0, and a->data unchanged ** ** HEY: it is apparently not clear how to do error checking (aside from ** looking at a->data) when there was NO data previously allocated, and the ** first index of the newly allocated data is zero. */ unsigned int airArrayLenIncr(airArray *a, int delta) { /* char me[]="airArrayLenIncr"; */ unsigned int oldlen, ret, negdel; if (!a) { return 0; } negdel = (delta < 0 ? AIR_UINT(-delta) : 0); if (delta < 0 && negdel > a->len) { /* error: asked for newlength to be negative */ airArrayLenSet(a, 0); return 0; } oldlen = a->len; airArrayLenSet(a, (delta >= 0 ? oldlen + AIR_UINT(delta) : oldlen - negdel)); if (!a->data) { /* allocation error */ ret = 0; } else { ret = (delta <= 0 ? 0 : oldlen); } return ret; }
/* ******** tenGradientJitter ** ** moves all gradients by amount dist on tangent plane, in a random ** direction, and then renormalizes. The distance is a fraction ** of the ideal edge length (via tenGradientIdealEdge) */ int tenGradientJitter(Nrrd *nout, const Nrrd *nin, double dist) { static const char me[]="tenGradientJitter"; double *grad, perp0[3], perp1[3], len, theta, cc, ss, edge; unsigned int gi, num; if (nrrdConvert(nout, nin, nrrdTypeDouble)) { biffMovef(TEN, NRRD, "%s: trouble converting input to double", me); return 1; } if (tenGradientCheck(nout, nrrdTypeDouble, 3)) { biffAddf(TEN, "%s: didn't get valid gradients", me); return 1; } grad = AIR_CAST(double*, nout->data); num = AIR_UINT(nout->axis[1].size); /* HEY: possible confusion between single and not */ edge = tenGradientIdealEdge(num, AIR_FALSE); for (gi=0; gi<num; gi++) { ELL_3V_NORM(grad, grad, len); ell_3v_perp_d(perp0, grad); ELL_3V_CROSS(perp1, perp0, grad); theta = AIR_AFFINE(0, airDrandMT(), 1, 0, 2*AIR_PI); cc = dist*edge*cos(theta); ss = dist*edge*sin(theta); ELL_3V_SCALE_ADD3(grad, 1.0, grad, cc, perp0, ss, perp1); ELL_3V_NORM(grad, grad, len); grad += 3; } return 0; }
int alanTensorSet(alanContext *actx, Nrrd *nten, int oversample) { static const char me[]="alanTensorSet"; if (!( actx && nten )) { biffAddf(ALAN, "%s: got NULL pointer", me); return 1; } DIM_SET; if (!( oversample > 0 )) { biffAddf(ALAN, "%s: oversample %d invalid", me, oversample); return 1; } if (2 == actx->dim) { if (!( 3 == nten->dim && 4 == nten->axis[0].size )) { biffAddf(ALAN, "%s: didn't get 3-D (4,X,Y) nrrd", me); return 1; } } else { if (!( 4 == nten->dim && 7 == nten->axis[0].size )) { biffAddf(ALAN, "%s: didn't get 4-D (7,X,Y,Z) nrrd", me); return 1; } } if (1 != oversample) { biffAddf(ALAN, "%s: sorry, can only handle oversample==1 now", me); return 1; } actx->nten = nrrdNuke(actx->nten); actx->nten = nrrdNew(); if (nrrdConvert(actx->nten, nten, alan_nt)) { biffMovef(ALAN, NRRD, "%s: trouble converting tensors to alan_t", me); return 1; } actx->size[0] = AIR_UINT(oversample*nten->axis[1].size); actx->size[1] = AIR_UINT(oversample*nten->axis[2].size); if (3 == actx->dim) { actx->size[2] = AIR_UINT(oversample*nten->axis[3].size); } else { actx->size[2] = 1; } return 0; }
/* ** max length of line formatted "[<key>] <err>\n" */ unsigned int biffMsgLineLenMax(const biffMsg *msg) { unsigned int ii, len, maxlen; if (biffMsgNoop == msg) { return 0; } maxlen = 0; for (ii=0; ii<msg->errNum; ii++) { len = AIR_UINT(strlen(msg->err[ii]) + strlen(msg->key) + strlen("[] \n")); maxlen = AIR_MAX(maxlen, len); } return maxlen; }
/* ** _airEnumIndex() ** ** given an enum "enm" and value "val", return the index into enm->str[] ** and enm->desc[] which correspond to that value. To be safe, when ** given an invalid enum value, we return zero. */ static unsigned int _airEnumIndex(const airEnum *enm, int val) { unsigned int ii, ret; ret = 0; if (enm->val) { for (ii=1; ii<=enm->M; ii++) { if (val == enm->val[ii]) { ret = ii; break; } } } else { unsigned int uval; uval = AIR_UINT(val); ret = (0 <= val && uval <= enm->M) ? uval : 0; } return ret; }
/* ******** biffMsgStrlen ** ** returns length of string (not including null termination, as usual) ** of the error message that will be generated by biffMsgStrSet */ unsigned int biffMsgStrlen(const biffMsg *msg) { static const char me[]="biffMsgStrlen"; unsigned int ii, len; if (biffMsgNoop == msg) { return 0; } if (!( msg )) { fprintf(stderr, "%s: PANIC got NULL msg %p\n", me, AIR_CVOIDP(msg)); return 0; /* exit(1); */ } len = 0; for (ii=0; ii<msg->errNum; ii++) { len += AIR_UINT(strlen(msg->key) + strlen(msg->err[ii]) + strlen("[] \n")); } return len+1; }
/* ** as of Sept 2013 this returns information: the index of the ** option just added. Returns UINT_MAX in case of error. */ unsigned int hestOptAdd(hestOpt **optP, const char *flag, const char *name, int type, int min, int max, void *valueP, const char *dflt, const char *info, ...) { hestOpt *ret = NULL; int num; va_list ap; unsigned int retIdx; if (!optP) return UINT_MAX; num = *optP ? _hestNumOpts(*optP) : 0; if (!( ret = AIR_CALLOC(num+2, hestOpt) )) { return UINT_MAX; } if (num) memcpy(ret, *optP, num*sizeof(hestOpt)); retIdx = AIR_UINT(num); ret[num].flag = airStrdup(flag); ret[num].name = airStrdup(name); ret[num].type = type; ret[num].min = min; ret[num].max = max; ret[num].valueP = valueP; ret[num].dflt = airStrdup(dflt); ret[num].info = airStrdup(info); /* initialize the things that may be set below */ ret[num].sawP = NULL; ret[num].enm = NULL; ret[num].CB = NULL; /* seems to be redundant with above _hestOptInit() */ ret[num].source = hestSourceUnknown; /* deal with var args */ if (5 == _hestKind(&(ret[num]))) { va_start(ap, info); ret[num].sawP = va_arg(ap, unsigned int*); va_end(ap); }
/* ** assign random signs to the vectors and measures the length of their ** mean, as quickly as possible */ static double party(Nrrd *npos, airRandMTState *rstate) { double *pos, mean[3]; unsigned int ii, num, rnd, rndBit; pos = (double *)(npos->data); num = AIR_UINT(npos->axis[1].size); rnd = airUIrandMT_r(rstate); rndBit = 0; ELL_3V_SET(mean, 0, 0, 0); for (ii=0; ii<num; ii++) { if (32 == rndBit) { rnd = airUIrandMT_r(rstate); rndBit = 0; } if (rnd & (1 << rndBit++)) { ELL_3V_SCALE(pos + 3*ii, -1, pos + 3*ii); } ELL_3V_INCR(mean, pos + 3*ii); } ELL_3V_SCALE(mean, 1.0/num, mean); return ELL_3V_LEN(mean); }
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; }
/* ******** 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; }
/* ** Do asynchronous update of positions in "npos', based on force ** calculations wherein the distances are normalized "edge". Using a ** small "edge" allows forces to either underflow to zero, or be ** finite, instead of exploding to infinity, for high exponents. ** ** The smallest seen edge length is recorded in "*edgeMin", which is ** initialized to the given "edge". This allows, for example, the ** caller to try again with a smaller edge normalization. ** ** The mean velocity of the points through the update is recorded in ** "*meanVel". ** ** Based on the observation that when using large exponents, numerical ** difficulties arise from the (force-based) update of the positions ** of the two (or few) closest particles, this function puts a speed ** limit (variable "limit") on the distance a particle may move during ** update, expressed as a fraction of the normalizing edge length. ** "limit" has been set heuristically, according to the exponent (we ** have to clamp speeds more aggresively with higher exponents), as ** well as (even more heuristically) according to the number of times ** the step size has been decreased. This latter factor has to be ** bounded, so that the update is not unnecessarily bounded when the ** step size gets very small at the last stages of computation. ** Without the step-size-based speed limit, the step size would ** sometimes (e.g. num=200, expo=300) have to reduced to a miniscule ** value, which slows subsequent convergence terribly. ** ** this function is not static, though it could be, so that mac's ** "Sampler" app can profile this */ int _tenGradientUpdate(double *meanVel, double *edgeMin, Nrrd *npos, double edge, tenGradientParm *tgparm) { /* static const char me[]="_tenGradientUpdate"; */ double *pos, newpos[3], grad[3], ngrad[3], dir[3], len, rep, step, diff[3], limit, expo; int num, ii, jj, E; E = 0; pos = AIR_CAST(double *, npos->data); num = AIR_UINT(npos->axis[1].size); *meanVel = 0; *edgeMin = edge; expo = tgparm->expo ? tgparm->expo : tgparm->expo_d; limit = expo*AIR_MIN(sqrt(expo), log(1 + tgparm->initStep/tgparm->step)); for (ii=0; ii<num; ii++) { ELL_3V_SET(grad, 0, 0, 0); for (jj=0; jj<num; jj++) { if (ii == jj) { continue; } ELL_3V_SUB(dir, pos + 3*ii, pos + 3*jj); ELL_3V_NORM(dir, dir, len); *edgeMin = AIR_MIN(*edgeMin, len); if (tgparm->expo) { rep = airIntPow(edge/len, tgparm->expo+1); } else { rep = pow(edge/len, tgparm->expo_d+1); } ELL_3V_SCALE_INCR(grad, rep/num, dir); if (!tgparm->single) { ELL_3V_ADD2(dir, pos + 3*ii, pos + 3*jj); ELL_3V_NORM(dir, dir, len); *edgeMin = AIR_MIN(*edgeMin, len); if (tgparm->expo) { rep = airIntPow(edge/len, tgparm->expo+1); } else { rep = pow(edge/len, tgparm->expo_d+1); } ELL_3V_SCALE_INCR(grad, rep/num, dir); } } ELL_3V_NORM(ngrad, grad, len); if (!( AIR_EXISTS(len) )) { /* things blew up, either in incremental force additions, or in the attempt at normalization */ E = 1; *meanVel = AIR_NAN; break; } if (0 == len) { /* if the length of grad[] underflowed to zero, we can legitimately zero out ngrad[] */ ELL_3V_SET(ngrad, 0, 0, 0); } step = AIR_MIN(len*tgparm->step, edge/limit); ELL_3V_SCALE_ADD2(newpos, 1.0, pos + 3*ii, step, ngrad); ELL_3V_NORM(newpos, newpos, len); ELL_3V_SUB(diff, pos + 3*ii, newpos); *meanVel += ELL_3V_LEN(diff); ELL_3V_COPY(pos + 3*ii, newpos); } *meanVel /= num; return E; }
void tenGradientMeasure(double *pot, double *minAngle, double *minEdge, const Nrrd *npos, tenGradientParm *tgparm, int edgeNormalize) { /* static const char me[]="tenGradientMeasure"; */ double diff[3], *pos, atmp=0, ptmp, edge, len; unsigned int ii, jj, num; /* allow minAngle NULL */ if (!(pot && npos && tgparm )) { return; } num = AIR_UINT(npos->axis[1].size); pos = AIR_CAST(double *, npos->data); edge = (edgeNormalize ? tenGradientIdealEdge(num, tgparm->single) : 1.0); *pot = 0; if (minAngle) { *minAngle = AIR_PI; } if (minEdge) { *minEdge = 2; } for (ii=0; ii<num; ii++) { for (jj=0; jj<ii; jj++) { ELL_3V_SUB(diff, pos + 3*ii, pos + 3*jj); len = ELL_3V_LEN(diff); if (minEdge) { *minEdge = AIR_MIN(*minEdge, len); } if (tgparm->expo) { ptmp = airIntPow(edge/len, tgparm->expo); } else { ptmp = pow(edge/len, tgparm->expo_d); } *pot += ptmp; if (minAngle) { atmp = ell_3v_angle_d(pos + 3*ii, pos + 3*jj); *minAngle = AIR_MIN(atmp, *minAngle); } if (!tgparm->single) { *pot += ptmp; ELL_3V_ADD2(diff, pos + 3*ii, pos + 3*jj); len = ELL_3V_LEN(diff); if (minEdge) { *minEdge = AIR_MIN(*minEdge, len); } if (tgparm->expo) { *pot += 2*airIntPow(edge/len, tgparm->expo); } else { *pot += 2*pow(edge/len, tgparm->expo_d); } if (minAngle) { *minAngle = AIR_MIN(AIR_PI-atmp, *minAngle); } } } } return; }
/* ******** unrrduCmdMain ** ** A "main" function for unu-like programs, which is very similar to ** teem/src/bin/unu.c:main(), and ** teem/src/bin/tend.c:main(), and ** teem/src/limn/test/lpu.c:main(). ** With more time (and a major Teem release), this function may change, ** and those programs may use this. ** ** A sneaky but basic issue is the const-correctness of how the hestParm ** is used; we'd like to take a const hestParm* to communicate parameters ** the caller has set, but the show-stopper is that unrrduCmd->main() ** takes a non-const hestParm, and it has to be that way, because some ** unu commands alter the given hparm (which probably shouldn't happen). ** Until that's fixed, we have a non-const hestParm* coming in here. */ int unrrduCmdMain(int argc, const char **argv, const char *cmd, const char *title, const unrrduCmd *const *cmdList, hestParm *_hparm, FILE *fusage) { int i, ret; const char *me; char *argv0 = NULL; hestParm *hparm; airArray *mop; me = argv[0]; /* parse environment variables first, in case they break nrrdDefault* or nrrdState* variables in a way that nrrdSanity() should see */ nrrdDefaultGetenv(); nrrdStateGetenv(); /* unu does some unu-specific environment-variable handling here */ nrrdSanityOrDie(me); mop = airMopNew(); if (_hparm) { hparm = _hparm; } else { hparm = hestParmNew(); airMopAdd(mop, hparm, (airMopper)hestParmFree, airMopAlways); hparm->elideSingleEnumType = AIR_TRUE; hparm->elideSingleOtherType = AIR_TRUE; hparm->elideSingleOtherDefault = AIR_FALSE; hparm->elideSingleNonExistFloatDefault = AIR_TRUE; hparm->elideMultipleNonExistFloatDefault = AIR_TRUE; hparm->elideSingleEmptyStringDefault = AIR_TRUE; hparm->elideMultipleEmptyStringDefault = AIR_TRUE; hparm->cleverPluralizeOtherY = AIR_TRUE; /* learning columns from current window; if ioctl is available if (1) { struct winsize ws; ioctl(1, TIOCGWINSZ, &ws); hparm->columns = ws.ws_col - 1; } */ hparm->columns = 78; } /* if there are no arguments, then we give general usage information */ if (1 >= argc) { /* this is like unrrduUsageUnu() */ unsigned int ii, maxlen = 0; char *buff, *fmt, tdash[] = "--- %s ---"; for (ii=0; cmdList[ii]; ii++) { if (cmdList[ii]->hidden) { continue; } maxlen = AIR_MAX(maxlen, AIR_UINT(strlen(cmdList[ii]->name))); } if (!maxlen) { fprintf(fusage, "%s: problem: maxlen = %u\n", me, maxlen); airMopError(mop); return 1; } buff = AIR_CALLOC(strlen(tdash) + strlen(title) + 1, char); airMopAdd(mop, buff, airFree, airMopAlways); sprintf(buff, tdash, title); fmt = AIR_CALLOC(hparm->columns + strlen(buff) + 1, char); /* generous */ airMopAdd(mop, buff, airFree, airMopAlways); sprintf(fmt, "%%%us\n", AIR_UINT((hparm->columns-strlen(buff))/2 + strlen(buff) - 1)); fprintf(fusage, fmt, buff); for (ii=0; cmdList[ii]; ii++) { unsigned int cc, len; if (cmdList[ii]->hidden) { continue; } len = AIR_UINT(strlen(cmdList[ii]->name)); strcpy(buff, ""); for (cc=len; cc<maxlen; cc++) strcat(buff, " "); strcat(buff, cmd); strcat(buff, " "); strcat(buff, cmdList[ii]->name); strcat(buff, " ... "); len = strlen(buff); fprintf(fusage, "%s", buff); _hestPrintStr(fusage, len, len, hparm->columns, cmdList[ii]->info, AIR_FALSE); } airMopError(mop); return 1; }
int unrrdu_diceMain(int argc, const char **argv, const char *me, hestParm *hparm) { hestOpt *opt = NULL; char *base, *err, fnout[AIR_STRLEN_MED], /* file name out */ fffname[AIR_STRLEN_MED], /* format for filename */ *ftmpl; /* format template */ Nrrd *nin, *nout; int pret, fit; unsigned int axis, start, pos, top, size, sanity; airArray *mop; OPT_ADD_AXIS(axis, "axis to slice along"); OPT_ADD_NIN(nin, "input nrrd"); hestOptAdd(&opt, "s,start", "start", airTypeUInt, 1, 1, &start, "0", "integer value to start numbering with"); hestOptAdd(&opt, "ff,format", "form", airTypeString, 1, 1, &ftmpl, "", "a printf-style format to use for generating all " "filenames. Use this to override the number of characters " "used to represent the slice position, or the file format " "of the output, e.g. \"-ff %03d.ppm\" for 000.ppm, " "001.ppm, etc. By default (not using this option), slices " "are saved in NRRD format (or PNM or PNG where possible) " "with shortest possible filenames."); /* the fact that we're using unsigned int instead of size_t is its own kind of sanity check */ hestOptAdd(&opt, "l,limit", "max#", airTypeUInt, 1, 1, &sanity, "9999", "a sanity check on how many slice files should be saved " "out, to prevent accidentally dicing the wrong axis " "or the wrong array. Can raise this value if needed."); hestOptAdd(&opt, "o,output", "prefix", airTypeString, 1, 1, &base, NULL, "output filename prefix (excluding info set via \"-ff\"), " "basically to set path of output files (so be sure to end " "with \"/\"."); mop = airMopNew(); airMopAdd(mop, opt, (airMopper)hestOptFree, airMopAlways); USAGE(_unrrdu_diceInfoL); PARSE(); airMopAdd(mop, opt, (airMopper)hestParseFree, airMopAlways); if (!( axis < nin->dim )) { fprintf(stderr, "%s: given axis (%u) outside range [0,%u]\n", me, axis, nin->dim-1); airMopError(mop); return 1; } if (nin->axis[axis].size > sanity) { char stmp[AIR_STRLEN_SMALL]; fprintf(stderr, "%s: axis %u size %s > sanity limit %u; " "increase via \"-l\"\n", me, axis, airSprintSize_t(stmp, nin->axis[axis].size), sanity); airMopError(mop); return 1; } size = AIR_UINT(nin->axis[axis].size); /* HEY: this should use nrrdSaveMulti(), and if there's additional smarts here, they should be moved into nrrdSaveMulti() */ if (airStrlen(ftmpl)) { if (!( _nrrdContainsPercentThisAndMore(ftmpl, 'd') || _nrrdContainsPercentThisAndMore(ftmpl, 'u') )) { fprintf(stderr, "%s: given filename format \"%s\" doesn't seem to " "have the converstion specification to print an integer\n", me, ftmpl); airMopError(mop); return 1; } sprintf(fffname, "%%s%s", ftmpl); } else { unsigned int dignum=0, tmps; tmps = top = start + size - 1; do { dignum++; tmps /= 10; } while (tmps); /* sprintf the number of digits into the string that will be used to sprintf the slice number into the filename */ sprintf(fffname, "%%s%%0%uu.nrrd", dignum); } nout = nrrdNew(); airMopAdd(mop, nout, (airMopper)nrrdNuke, airMopAlways); for (pos=0; pos<size; pos++) { if (nrrdSlice(nout, nin, axis, pos)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error slicing nrrd:%s\n", me, err); airMopError(mop); return 1; } if (0 == pos && !airStrlen(ftmpl)) { /* See if these slices would be better saved as PNG or PNM images. Altering the file name will tell nrrdSave() to use a different file format. We wait till now to check this so that we can work from the actual slice */ if (nrrdFormatPNG->fitsInto(nout, nrrdEncodingRaw, AIR_FALSE)) { strcpy(fffname + strlen(fffname) - 4, "png"); } else { fit = nrrdFormatPNM->fitsInto(nout, nrrdEncodingRaw, AIR_FALSE); if (2 == fit) { strcpy(fffname + strlen(fffname) - 4, "pgm"); } else if (3 == fit) { strcpy(fffname + strlen(fffname) - 4, "ppm"); } } } sprintf(fnout, fffname, base, pos+start); fprintf(stderr, "%s: %s ...\n", me, fnout); if (nrrdSave(fnout, nout, NULL)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: error writing nrrd to \"%s\":%s\n", me, fnout, err); airMopError(mop); return 1; } } airMopOkay(mop); 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; }