void tend_helixDoit(Nrrd *nout, double bnd, double orig[3], double i2w[9], double mf[9], double r, double R, double S, double angle, int incrtwist, double ev[3], double bgEval) { int sx, sy, sz, xi, yi, zi; double th, t0, t1, t2, t3, v1, v2, wpos[3], vpos[3], mfT[9], W2H[9], H2W[9], H2C[9], C2H[9], fv[3], rv[3], uv[3], mA[9], mB[9], inside, tmp[3], len; float *out; sx = nout->axis[1].size; sy = nout->axis[2].size; sz = nout->axis[3].size; out = (float*)nout->data; ELL_3M_TRANSPOSE(mfT, mf); for (zi=0; zi<sz; zi++) { fprintf(stderr, "zi = %d/%d\n", zi, sz); for (yi=0; yi<sy; yi++) { for (xi=0; xi<sx; xi++) { ELL_3V_SET(tmp, xi, yi, zi); ELL_3MV_MUL(vpos, i2w, tmp); ELL_3V_INCR(vpos, orig); #define WPOS(pos, th) ELL_3V_SET((pos),R*cos(th), R*sin(th), S*(th)/(2*AIR_PI)) #define VAL(th) (WPOS(wpos, th), ELL_3V_DIST(wpos, vpos)) #define RR 0.61803399 #define CC (1.0-RR) #define SHIFT3(a,b,c,d) (a)=(b); (b)=(c); (c)=(d) #define SHIFT2(a,b,c) (a)=(b); (b)=(c) th = atan2(vpos[1], vpos[0]); th += 2*AIR_PI*floor(0.5 + vpos[2]/S - th/(2*AIR_PI)); if (S*th/(2*AIR_PI) > vpos[2]) { t0 = th - AIR_PI; t3 = th; } else { t0 = th; t3 = th + AIR_PI; } t1 = RR*t0 + CC*t3; t2 = CC*t0 + RR*t3; v1 = VAL(t1); v2 = VAL(t2); while ( t3-t0 > 0.000001*(AIR_ABS(t1)+AIR_ABS(t2)) ) { if (v1 < v2) { SHIFT3(t3, t2, t1, CC*t0 + RR*t2); SHIFT2(v2, v1, VAL(t1)); } else { SHIFT3(t0, t1, t2, RR*t1 + CC*t3); SHIFT2(v1, v2, VAL(t2)); } } /* t1 (and t2) are now the th for which the point on the helix (R*cos(th), R*sin(th), S*(th)/(2*AIR_PI)) is closest to vpos */ WPOS(wpos, t1); ELL_3V_SUB(wpos, vpos, wpos); ELL_3V_SET(fv, -R*sin(t1), R*cos(t1), S/AIR_PI); /* helix tangent */ ELL_3V_NORM(fv, fv, len); ELL_3V_COPY(rv, wpos); ELL_3V_NORM(rv, rv, len); len = ELL_3V_DOT(rv, fv); ELL_3V_SCALE(tmp, -len, fv); ELL_3V_ADD2(rv, rv, tmp); ELL_3V_NORM(rv, rv, len); /* rv now normal to helix, closest to pointing to vpos */ ELL_3V_CROSS(uv, rv, fv); ELL_3V_NORM(uv, uv, len); /* (rv,fv,uv) now right-handed frame */ ELL_3MV_ROW0_SET(W2H, uv); /* as is (uv,rv,fv) */ ELL_3MV_ROW1_SET(W2H, rv); ELL_3MV_ROW2_SET(W2H, fv); ELL_3M_TRANSPOSE(H2W, W2H); inside = 0.5 - 0.5*airErf((ELL_3V_LEN(wpos)-r)/(bnd + 0.0001)); if (incrtwist) { th = angle*ELL_3V_LEN(wpos)/r; } else { th = angle; } ELL_3M_ROTATE_Y_SET(H2C, th); ELL_3M_TRANSPOSE(C2H, H2C); ELL_3M_SCALE_SET(mA, AIR_LERP(inside, bgEval, ev[1]), AIR_LERP(inside, bgEval, ev[2]), AIR_LERP(inside, bgEval, ev[0])); ELL_3M_MUL(mB, mA, H2C); ELL_3M_MUL(mA, mB, W2H); ELL_3M_MUL(mB, mA, mf); ELL_3M_MUL(mA, C2H, mB); ELL_3M_MUL(mB, H2W, mA); ELL_3M_MUL(mA, mfT, mB); TEN_M2T_TT(out, float, mA); out[0] = 1.0; out += 7; } } } return; }
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; }
/* ** 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; }
void _miteRGBACalc(mite_t *R, mite_t *G, mite_t *B, mite_t *A, miteThread *mtt, miteRender *mrr, miteUser *muu) { static const char me[]="_miteRGBACalc"; mite_t tmp, ad[3], /* ambient+diffuse light contribution */ s[3] = {0,0,0}, /* specular light contribution */ col[3], E, ka, kd, ks, sp, /* txf-determined rendering variables */ LdotN=0, HdotN, H[3], N[3]; /* for lighting calculation */ col[0] = mtt->range[miteRangeRed]; col[1] = mtt->range[miteRangeGreen]; col[2] = mtt->range[miteRangeBlue]; E = mtt->range[miteRangeEmissivity]; ka = mtt->range[miteRangeKa]; kd = mtt->range[miteRangeKd]; ks = mtt->range[miteRangeKs]; ELL_3V_SCALE(ad, ka, muu->lit->amb); switch (mrr->shadeSpec->method) { case miteShadeMethodNone: /* nothing to do */ break; case miteShadeMethodPhong: if (kd || ks) { ELL_3V_NORM(N, mtt->shadeVec0, tmp); if (1 == muu->normalSide) { ELL_3V_SCALE(N, -1, N); } /* else -1==side --> N = -1*-1*N = N or 0==side --> N = N, so there's nothing to do */ if (kd) { LdotN = ELL_3V_DOT(muu->lit->dir[0], N); if (!muu->normalSide) { LdotN = AIR_ABS(LdotN); } if (LdotN > 0) { ELL_3V_SCALE_INCR(ad, LdotN*kd, muu->lit->col[0]); } } if (ks) { sp = mtt->range[miteRangeSP]; ELL_3V_ADD2(H, muu->lit->dir[0], mtt->V); ELL_3V_NORM(H, H, tmp); HdotN = ELL_3V_DOT(H, N); if (!muu->normalSide) { HdotN = AIR_ABS(HdotN); } if (HdotN > 0) { HdotN = pow(HdotN, sp); ELL_3V_SCALE(s, HdotN*ks, muu->lit->col[0]); } } } break; case miteShadeMethodLitTen: fprintf(stderr, "!%s: lit-tensor not yet implemented\n", me); break; default: fprintf(stderr, "!%s: PANIC, shadeMethod %d unimplemented\n", me, mrr->shadeSpec->method); exit(1); break; } *R = (E - 1 + ad[0])*col[0] + s[0]; *G = (E - 1 + ad[1])*col[1] + s[1]; *B = (E - 1 + ad[2])*col[2] + s[2]; *A = mtt->range[miteRangeAlpha]; *A = AIR_CLAMP(0.0, *A, 1.0); /* if (mtt->verbose) { fprintf(stderr, "%s: col[] = %g,%g,%g; A,E = %g,%g; Kads = %g,%g,%g\n", me, col[0], col[1], col[2], mtt->range[miteRangeAlpha], E, ka, kd, ks); fprintf(stderr, "%s: N = (%g,%g,%g), L = (%g,%g,%g) ---> LdotN = %g\n", me, N[0], N[1], N[2], muu->lit->dir[0][0], muu->lit->dir[0][1], muu->lit->dir[0][2], LdotN); fprintf(stderr, "%s: ad[] = %g,%g,%g\n", me, ad[0], ad[1], ad[2]); fprintf(stderr, "%s: --> R,G,B,A = %g,%g,%g,%g\n", me, *R, *G, *B, *A); } */ return; }
/* ******** tenFiberTraceSet ** ** slightly more flexible API for fiber tracking than tenFiberTrace ** ** EITHER: pass a non-NULL nfiber, and NULL, 0, NULL, NULL for ** the following arguments, and things are the same as with tenFiberTrace: ** data inside the nfiber is allocated, and the tract vertices are copied ** into it, having been stored in dynamically allocated airArrays ** ** OR: pass a NULL nfiber, and a buff allocated for 3*(2*halfBuffLen + 1) ** (note the "+ 1" !!!) doubles. The fiber tracking on each half will stop ** at halfBuffLen points. The given seedpoint will be stored in ** buff[0,1,2 + 3*halfBuffLen]. The indices for the end of the first ** tract half, and the end of the second tract half, will be set in ** *startIdxP and *endIdxP respectively. */ int tenFiberTraceSet(tenFiberContext *tfx, Nrrd *nfiber, double *buff, unsigned int halfBuffLen, unsigned int *startIdxP, unsigned int *endIdxP, double seed[3]) { char me[]="tenFiberTraceSet", err[BIFF_STRLEN]; airArray *fptsArr[2]; /* airArrays of backward (0) and forward (1) fiber points */ double *fpts[2]; /* arrays storing forward and backward fiber points */ double tmp[3], iPos[3], currPoint[3], forwDir[3], *fiber; /* array of both forward and backward points, when finished */ int ret, whyStop, buffIdx, fptsIdx, outIdx, oldStop; unsigned int i; airArray *mop; if (!(tfx)) { sprintf(err, "%s: got NULL pointer", me); biffAdd(TEN, err); return 1; } /* HEY: a hack to preserve the state inside tenFiberContext so that we have fewer side effects (tfx->maxNumSteps may still be set) */ oldStop = tfx->stop; if (!nfiber) { if (!( buff && halfBuffLen > 0 && startIdxP && startIdxP )) { sprintf(err, "%s: need either non-NULL nfiber or fpts buffer info", me); biffAdd(TEN, err); return 1; } if (tenFiberStopSet(tfx, tenFiberStopNumSteps, halfBuffLen)) { sprintf(err, "%s: error setting new fiber stop", me); biffAdd(TEN, err); return 1; } } /* initialize the quantities which describe the fiber halves */ tfx->halfLen[0] = tfx->halfLen[1] = 0.0; tfx->numSteps[0] = tfx->numSteps[1] = 0; tfx->whyStop[0] = tfx->whyStop[1] = tenFiberStopUnknown; /* try probing once */ if (tfx->useIndexSpace) { ret = gageProbe(tfx->gtx, AIR_CAST(gage_t, seed[0]), AIR_CAST(gage_t, seed[1]), AIR_CAST(gage_t, seed[2])); } else { gageShapeWtoI(tfx->gtx->shape, tmp, seed); ret = gageProbe(tfx->gtx, AIR_CAST(gage_t, tmp[0]), AIR_CAST(gage_t, tmp[1]), AIR_CAST(gage_t, tmp[2])); } if (ret) { sprintf(err, "%s: first gageProbe failed: %s (%d)", me, tfx->gtx->errStr, tfx->gtx->errNum); biffAdd(TEN, err); return 1; } /* see if we're doomed */ if ((whyStop = _tenFiberStopCheck(tfx))) { /* stopped immediately at seed point, but that's not an error */ tfx->whyNowhere = whyStop; if (nfiber) { nrrdEmpty(nfiber); } else { *startIdxP = *endIdxP = 0; } return 0; } else { /* did not immediately halt */ tfx->whyNowhere = tenFiberStopUnknown; } /* record the principal eigenvector at the seed point, which is needed to align the 4 intermediate steps of RK4 for the FIRST step of each half of the tract */ ELL_3V_COPY(tfx->firstEvec, tfx->evec + 3*0); /* airMop{Error,Okay}() can safely be called on NULL */ mop = nfiber ? airMopNew() : NULL; for (tfx->dir=0; tfx->dir<=1; tfx->dir++) { if (nfiber) { fptsArr[tfx->dir] = airArrayNew((void**)&(fpts[tfx->dir]), NULL, 3*sizeof(double), TEN_FIBER_INCR); airMopAdd(mop, fptsArr[tfx->dir], (airMopper)airArrayNuke, airMopAlways); buffIdx = -1; } else { fptsArr[tfx->dir] = NULL; fpts[tfx->dir] = NULL; buffIdx = halfBuffLen; fptsIdx = -1; } tfx->halfLen[tfx->dir] = 0; if (tfx->useIndexSpace) { ELL_3V_COPY(iPos, seed); gageShapeItoW(tfx->gtx->shape, tfx->wPos, iPos); } else { gageShapeWtoI(tfx->gtx->shape, iPos, seed); ELL_3V_COPY(tfx->wPos, seed); } ELL_3V_SET(tfx->lastDir, 0, 0, 0); tfx->lastDirSet = AIR_FALSE; for (tfx->numSteps[tfx->dir] = 0; AIR_TRUE; tfx->numSteps[tfx->dir]++) { if (_tenFiberProbe(tfx, tfx->wPos)) { /* even if gageProbe had an error OTHER than going out of bounds, we're not going to report it any differently here, alas */ tfx->whyStop[tfx->dir] = tenFiberStopBounds; break; } if ((whyStop = _tenFiberStopCheck(tfx))) { if (tenFiberStopNumSteps == whyStop) { /* we stopped along this direction because tfx->numSteps[tfx->dir] exceeded tfx->maxNumSteps. Okay. But tfx->numSteps[tfx->dir] is supposed to be a record of how steps were (successfully) taken. So we need to decrementing before moving on ... */ tfx->numSteps[tfx->dir]--; } tfx->whyStop[tfx->dir] = whyStop; break; } if (tfx->useIndexSpace) { gageShapeWtoI(tfx->gtx->shape, iPos, tfx->wPos); ELL_3V_COPY(currPoint, iPos); } else { ELL_3V_COPY(currPoint, tfx->wPos); } if (nfiber) { fptsIdx = airArrayLenIncr(fptsArr[tfx->dir], 1); ELL_3V_COPY(fpts[tfx->dir] + 3*fptsIdx, currPoint); } else { ELL_3V_COPY(buff + 3*buffIdx, currPoint); /* fprintf(stderr, "!%s: (dir %d) saving to %d pnt %g %g %g\n", me, tfx->dir, buffIdx, currPoint[0], currPoint[1], currPoint[2]); */ buffIdx += !tfx->dir ? -1 : 1; } /* forwDir is set by this to point to the next fiber point */ if (_tenFiberIntegrate[tfx->intg](tfx, forwDir)) { tfx->whyStop[tfx->dir] = tenFiberStopBounds; break; } ELL_3V_COPY(tfx->lastDir, forwDir); tfx->lastDirSet = AIR_TRUE; ELL_3V_ADD2(tfx->wPos, tfx->wPos, forwDir); tfx->halfLen[tfx->dir] += ELL_3V_LEN(forwDir); } } if (nfiber) { if (nrrdMaybeAlloc_va(nfiber, nrrdTypeDouble, 2, AIR_CAST(size_t, 3), AIR_CAST(size_t, (fptsArr[0]->len + fptsArr[1]->len - 1)))) { sprintf(err, "%s: couldn't allocate fiber nrrd", me); biffMove(TEN, err, NRRD); airMopError(mop); return 1; } fiber = (double*)(nfiber->data); outIdx = 0; for (i=fptsArr[0]->len-1; i>=1; i--) { ELL_3V_COPY(fiber + 3*outIdx, fpts[0] + 3*i); outIdx++; } for (i=0; i<=fptsArr[1]->len-1; i++) { ELL_3V_COPY(fiber + 3*outIdx, fpts[1] + 3*i); outIdx++; } } else { *startIdxP = halfBuffLen - tfx->numSteps[0]; *endIdxP = halfBuffLen + tfx->numSteps[1]; } tfx->stop = oldStop; airMopOkay(mop); return 0; }