/* ******** ell_3m_svd_d ** ** singular value decomposition: ** mat = uu * diag(sval) * vv ** ** singular values are square roots of eigenvalues of mat * mat^T ** columns of uu are eigenvectors of mat * mat^T ** rows of vv are eigenvectors of mat^T * mat ** ** returns info about singular values according to ellCubeRoot enum ** ** HEY: I think this does the wrong thing when given a symmetric ** matrix with negative eigenvalues . . . */ int ell_3m_svd_d(double uu[9], double sval[3], double vv[9], const double mat[9], const int newton) { double trn[9], msqr[9], eval[3], evec[9]; int roots; ELL_3M_TRANSPOSE(trn, mat); ELL_3M_MUL(msqr, mat, trn); roots = ell_3m_eigensolve_d(eval, evec, msqr, newton); sval[0] = sqrt(eval[0]); sval[1] = sqrt(eval[1]); sval[2] = sqrt(eval[2]); ELL_3M_TRANSPOSE(uu, evec); ELL_3M_MUL(msqr, trn, mat); _ell_3m_evecs_d(vv, eval, roots, msqr); return roots; }
void tenQGLInterpTwoEvec(double oevec[9], const double evecA[9], const double evecB[9], double tt) { double rotA[9], rotB[9], orot[9], oq[4], qA[4], qB[4], _qB[4], qdiv[4], angle, axis[3], qq[4]; ELL_3M_TRANSPOSE(rotA, evecA); ELL_3M_TRANSPOSE(rotB, evecB); ell_3m_to_q_d(qA, rotA); ell_3m_to_q_d(_qB, rotB); _tenQGL_q_align(qB, qA, _qB); /* there's probably a faster way to do this slerp qA --> qB */ ell_q_div_d(qdiv, qA, qB); /* div = A^-1 * B */ angle = ell_q_to_aa_d(axis, qdiv); ell_aa_to_q_d(qq, angle*tt, axis); ell_q_mul_d(oq, qA, qq); ell_q_to_3m_d(orot, oq); ELL_3M_TRANSPOSE(oevec, orot); }
/* ******** ell_3m_2d_nullspace_d() ** ** the given matrix is assumed to have a nullspace of dimension two. ** ** The given nullspace matrix is NOT modified ** ** This does NOT use biff */ void ell_3m_2d_nullspace_d(double ans0[3], double ans1[3], const double _n[9]) { double n[9], tmp[3], norm; ELL_3M_TRANSPOSE(n, _n); _ell_align3_d(n); ELL_3V_ADD3(tmp, n+0, n+3, n+6); ELL_3V_NORM(tmp, tmp, norm); /* any two vectors which are perpendicular to the (supposedly 1D) span of the column vectors span the nullspace */ ell_3v_perp_d(ans0, tmp); ELL_3V_NORM(ans0, ans0, norm); ELL_3V_CROSS(ans1, tmp, ans0); return; }
/* ******** ell_3m_1d_nullspace_d() ** ** the given matrix is assumed to have a nullspace of dimension one. ** A normalized vector which spans the nullspace is put into ans. ** ** The given nullspace matrix is NOT modified. ** ** This does NOT use biff */ void ell_3m_1d_nullspace_d(double ans[3], const double _n[9]) { double t[9], n[9], norm; ELL_3M_TRANSPOSE(n, _n); /* find the three cross-products of pairs of column vectors of n */ ELL_3V_CROSS(t+0, n+0, n+3); ELL_3V_CROSS(t+3, n+0, n+6); ELL_3V_CROSS(t+6, n+3, n+6); /* lop A */ _ell_align3_d(t); /* lop B */ /* add them up (longer, hence more accurate, should dominate) */ ELL_3V_ADD3(ans, t+0, t+3, t+6); /* normalize */ ELL_3V_NORM(ans, ans, norm); return; }
/* ******** limnPolyDataTransform_f, limnPolyDataTransform_d ** ** transforms a surface (both positions, and normals (if set)) ** by given homogenous transform */ void limnPolyDataTransform_f(limnPolyData *pld, const float homat[16]) { double hovec[4], mat[9], inv[9], norm[3], nmat[9]; unsigned int vertIdx; if (pld && homat) { if (pld->norm) { ELL_34M_EXTRACT(mat, homat); ell_3m_inv_d(inv, mat); ELL_3M_TRANSPOSE(nmat, inv); } else { ELL_3M_IDENTITY_SET(nmat); /* hush unused value compiler warnings */ } for (vertIdx=0; vertIdx<pld->xyzwNum; vertIdx++) { ELL_4MV_MUL(hovec, homat, pld->xyzw + 4*vertIdx); ELL_4V_COPY_TT(pld->xyzw + 4*vertIdx, float, hovec); if (pld->norm) { ELL_3MV_MUL(norm, nmat, pld->norm + 3*vertIdx); ELL_3V_COPY_TT(pld->norm + 3*vertIdx, float, norm); } } }
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; }
/* ** This does (non-optionally) use biff, to report convergence failures ** ** we do in fact require non-NULL tip, because it holds the buffers we need */ int _tenQGLInterpNEvec(double evecOut[9], const double *evecIn, /* size 9 -by- NN */ const double *wght, /* size NN */ unsigned int NN, tenInterpParm *tip) { static const char me[]="_tenQGLInterpNEvec"; double qOut[4], maxWght, len, /* odsum, */ dsum, rot[9]; unsigned int ii, centerIdx=0, fix, qiter; if (!( evecOut && evecIn && tip )) { biffAddf(TEN, "%s: got NULL pointer", me); return 1; } /* convert to quaternions */ for (ii=0; ii<NN; ii++) { ELL_3M_TRANSPOSE(rot, evecIn + 9*ii); ell_3m_to_q_d(tip->qIn + 4*ii, rot); } /* HEY: what should this be used for? variable odsum set but not used */ /* odsum = _tenQGL_q_interdot(¢erIdx, tip->qIn, tip->qInter, NN); */ /* find quaternion with maximal weight, use it as is (decree that its the right representative), and then align rest with that. This is actually principled; symmetry allows it */ centerIdx = 0; if (wght) { maxWght = wght[centerIdx]; for (ii=1; ii<NN; ii++) { if (wght[ii] > maxWght) { centerIdx = ii; maxWght = wght[centerIdx]; } } } for (ii=0; ii<NN; ii++) { if (ii == centerIdx) { continue; } _tenQGL_q_align(tip->qIn + 4*ii, tip->qIn + 4*centerIdx, tip->qIn + 4*ii); } dsum = _tenQGL_q_interdot(¢erIdx, tip->qIn, tip->qInter, NN); /* try to settle on tightest set of representatives */ qiter = 0; do { fix = 0; for (ii=0; ii<NN; ii++) { unsigned int ff; if (ii == centerIdx) { continue; } ff = _tenQGL_q_align(tip->qIn + 4*ii, tip->qIn + 4*centerIdx, tip->qIn + 4*ii); fix = AIR_MAX(fix, ff); } dsum = _tenQGL_q_interdot(¢erIdx, tip->qIn, tip->qInter, NN); if (tip->maxIter && qiter > tip->maxIter) { biffAddf(TEN, "%s: q tightening unconverged after %u iters; " "interdot = %g -> maxfix = %u; center = %u\n", me, tip->maxIter, dsum, fix, centerIdx); return 1; } qiter++; } while (fix); /* fprintf(stderr, "!%s: dsum %g --%u--> %g\n", me, odsum, qiter, dsum); */ /* make sure they're normalized */ for (ii=0; ii<NN; ii++) { ELL_4V_NORM(tip->qIn + 4*ii, tip->qIn + 4*ii, len); } /* compute iterated weighted mean, stored in qOut */ if (ell_q_avgN_d(qOut, &qiter, tip->qIn, tip->qBuff, wght, NN, tip->convEps, tip->maxIter)) { biffMovef(TEN, ELL, "%s: problem doing quaternion mean", me); return 1; } /* fprintf(stderr, "!%s: q avg converged in %u\n", me, qiter); */ /* finish, convert back to evec */ ell_q_to_3m_d(rot, qOut); ELL_3M_TRANSPOSE(evecOut, rot); return 0; }
int main(int argc, char *argv[]) { float angleA_f, axisA_f[3], angleB_f, axisB_f[3], qA_f[4], qB_f[4], qC_f[4], mat3A_f[9], mat4A_f[16], mat3B_f[9], mat4B_f[16], mat3C_f[9], mat4C_f[16], pntA_f[4], pntB_f[4], pntC_f[4]; double angleA_d, axisA_d[3], angleB_d, axisB_d[3], qA_d[4], qB_d[4], qC_d[4], mat3A_d[9], mat4A_d[16], mat3B_d[9], mat4B_d[16], mat3C_d[9], mat4C_d[16], pntA_d[4], pntB_d[4], pntC_d[4]; int I, N; double tmp, det, frob; me = argv[0]; N = 100000; AIR_UNUSED(pntA_d); AIR_UNUSED(pntB_d); AIR_UNUSED(pntC_d); AIR_UNUSED(mat4C_d); AIR_UNUSED(mat3C_d); AIR_UNUSED(mat4B_d); AIR_UNUSED(mat3B_d); AIR_UNUSED(mat4A_d); AIR_UNUSED(mat3A_d); AIR_UNUSED(qC_d); AIR_UNUSED(qB_d); AIR_UNUSED(qA_d); AIR_UNUSED(axisB_d); AIR_UNUSED(angleB_d); AIR_UNUSED(axisA_d); AIR_UNUSED(angleA_d); AIR_UNUSED(argc); for (I=0; I<N; I++) { /* make a rotation (as a quaternion) */ ELL_3V_SET(axisA_f, 2*airDrandMT()-1, 2*airDrandMT()-1, 2*airDrandMT()-1); ELL_3V_NORM(axisA_f, axisA_f, tmp); /* yea, not uniform, so what */ angleA_f = AIR_PI*(2*airDrandMT()-1); ell_aa_to_q_f(qA_f, angleA_f, axisA_f); /* convert to AA and back, and back */ angleB_f = ell_q_to_aa_f(axisB_f, qA_f); if (ELL_3V_DOT(axisB_f, axisA_f) < 0) { ELL_3V_SCALE(axisB_f, -1, axisB_f); angleB_f *= -1; } ELL_3V_SUB(axisA_f, axisA_f, axisB_f); printf(" aa -> q -> aa error: %g, %g\n", CA + AIR_ABS(angleA_f - angleB_f), CA + ELL_3V_LEN(axisA_f)); /* convert to 3m and back, and back */ ell_q_to_3m_f(mat3A_f, qA_f); ell_3m_to_q_f(qB_f, mat3A_f); if (ELL_4V_DOT(qA_f, qB_f) < 0) { ELL_4V_SCALE(qB_f, -1, qB_f); } ELL_4V_SUB(qC_f, qA_f, qB_f); ELL_Q_TO_3M(mat3B_f, qA_f); ELL_3M_SUB(mat3C_f, mat3B_f, mat3A_f); printf(" q -> 3m -> q error: %g, %g\n", CA + ELL_4V_LEN(qC_f), CA + ELL_3M_FROB(mat3C_f)); /* convert to 4m and back, and back */ ell_q_to_4m_f(mat4A_f, qA_f); ell_4m_to_q_f(qB_f, mat4A_f); if (ELL_4V_DOT(qA_f, qB_f) < 0) { ELL_4V_SCALE(qB_f, -1, qB_f); } ELL_4V_SUB(qC_f, qA_f, qB_f); ELL_Q_TO_4M(mat4B_f, qA_f); ELL_4M_SUB(mat4C_f, mat4B_f, mat4A_f); printf(" q -> 4m -> q error: %g, %g\n", CA + ELL_4V_LEN(qC_f), CA + ELL_4M_FROB(mat4C_f)); /* make a point that we'll rotate */ ELL_3V_SET(pntA_f, 2*airDrandMT()-1, 2*airDrandMT()-1, 2*airDrandMT()-1); /* effect rotation in two different ways, and compare results */ ELL_3MV_MUL(pntB_f, mat3A_f, pntA_f); ell_q_3v_rotate_f(pntC_f, qA_f, pntA_f); ELL_3V_SUB(pntA_f, pntB_f, pntC_f); printf(" rotation error = %g\n", CA + ELL_3V_LEN(pntA_f)); /* mix up inversion with conversion */ ell_3m_inv_f(mat3C_f, mat3A_f); ell_3m_to_q_f(qB_f, mat3C_f); ell_q_mul_f(qC_f, qA_f, qB_f); if (ELL_4V_DOT(qA_f, qC_f) < 0) { ELL_4V_SCALE(qC_f, -1, qC_f); } printf(" inv mul = %g %g %g %g\n", qC_f[0], CA + qC_f[1], CA + qC_f[2], CA + qC_f[3]); ell_q_inv_f(qC_f, qB_f); ELL_4V_SUB(qC_f, qB_f, qB_f); printf(" inv diff = %g %g %g %g\n", CA + qC_f[0], CA + qC_f[1], CA + qC_f[2], CA + qC_f[3]); /* exp and log */ ell_q_log_f(qC_f, qA_f); ell_q_log_f(qB_f, qC_f); ell_q_exp_f(qC_f, qB_f); ell_q_exp_f(qB_f, qC_f); ELL_4V_SUB(qC_f, qB_f, qA_f); printf(" exp/log diff = %g %g %g %g\n", CA + qC_f[0], CA + qC_f[1], CA + qC_f[2], CA + qC_f[3]); /* pow, not very exhaustive */ ell_q_to_3m_f(mat3A_f, qA_f); ell_3m_post_mul_f(mat3A_f, mat3A_f); ell_3m_post_mul_f(mat3A_f, mat3A_f); ell_q_pow_f(qB_f, qA_f, 4); ell_q_to_3m_f(mat3B_f, qB_f); ELL_3M_SUB(mat3B_f, mat3B_f, mat3A_f); printf(" pow diff = %g\n", CA + ELL_3M_FROB(mat3B_f)); if (ELL_3M_FROB(mat3B_f) > 2) { printf(" start q = %g %g %g %g\n", qA_f[0], qA_f[1], qA_f[2], qA_f[3]); angleA_f = ell_q_to_aa_f(axisA_f, qA_f); printf(" --> aa = %g (%g %g %g)\n", angleA_f, axisA_f[0], axisA_f[1], axisA_f[2]); printf(" q^3 = %g %g %g %g\n", qB_f[0], qB_f[1], qB_f[2], qB_f[3]); angleA_f = ell_q_to_aa_f(axisA_f, qB_f); printf(" --> aa = %g (%g %g %g)\n", angleA_f, axisA_f[0], axisA_f[1], axisA_f[2]); exit(1); } /* make sure it looks like a rotation matrix */ ell_q_to_3m_f(mat3A_f, qA_f); det = ELL_3M_DET(mat3A_f); frob = ELL_3M_FROB(mat3A_f); ELL_3M_TRANSPOSE(mat3B_f, mat3A_f); ell_3m_inv_f(mat3C_f, mat3A_f); ELL_3M_SUB(mat3C_f, mat3B_f, mat3C_f); printf(" det = %g; size = %g; err = %g\n", det, frob*frob/3, CA + ELL_3M_FROB(mat3C_f)); } exit(0); }
void _gageVecAnswer(gageContext *ctx, gagePerVolume *pvl) { char me[]="_gageVecAnswer"; double cmag, tmpMat[9], mgevec[9], mgeval[3]; double symm[9], asym[9], tran[9], eval[3], tmpVec[3], norm; gage_t *vecAns, *normAns, *jacAns, *curlAns, *hesAns, *curlGradAns, *helGradAns, *dirHelDirAns, *curlnormgradAns; /* int asw; */ vecAns = pvl->directAnswer[gageVecVector]; normAns = pvl->directAnswer[gageVecNormalized]; jacAns = pvl->directAnswer[gageVecJacobian]; curlAns = pvl->directAnswer[gageVecCurl]; hesAns = pvl->directAnswer[gageVecHessian]; curlGradAns = pvl->directAnswer[gageVecCurlGradient]; curlnormgradAns = pvl->directAnswer[gageVecCurlNormGrad]; helGradAns = pvl->directAnswer[gageVecHelGradient]; dirHelDirAns = pvl->directAnswer[gageVecDirHelDeriv]; if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecVector)) { /* done if doV */ if (ctx->verbose) { fprintf(stderr, "vec = "); ell_3v_PRINT(stderr, vecAns); } } /* done if doV if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecVector{0,1,2})) { } */ if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecLength)) { pvl->directAnswer[gageVecLength][0] = AIR_CAST(gage_t, ELL_3V_LEN(vecAns)); } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecNormalized)) { if (pvl->directAnswer[gageVecLength][0]) { ELL_3V_SCALE_TT(normAns, gage_t, 1.0/pvl->directAnswer[gageVecLength][0], vecAns); } else { ELL_3V_COPY(normAns, gageZeroNormal); } } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecJacobian)) { /* done if doD1 */ /* 0:dv_x/dx 1:dv_x/dy 2:dv_x/dz 3:dv_y/dx 4:dv_y/dy 5:dv_y/dz 6:dv_z/dx 7:dv_z/dy 8:dv_z/dz */ if (ctx->verbose) { fprintf(stderr, "%s: jac = \n", me); ell_3m_PRINT(stderr, jacAns); } } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecDivergence)) { pvl->directAnswer[gageVecDivergence][0] = jacAns[0] + jacAns[4] + jacAns[8]; if (ctx->verbose) { fprintf(stderr, "%s: div = %g + %g + %g = %g\n", me, jacAns[0], jacAns[4], jacAns[8], pvl->directAnswer[gageVecDivergence][0]); } } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecCurl)) { ELL_3V_SET(curlAns, jacAns[7] - jacAns[5], jacAns[2] - jacAns[6], jacAns[3] - jacAns[1]); } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecCurlNorm)) { pvl->directAnswer[gageVecCurlNorm][0] = AIR_CAST(gage_t, ELL_3V_LEN(curlAns)); } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecHelicity)) { pvl->directAnswer[gageVecHelicity][0] = ELL_3V_DOT(vecAns, curlAns); } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecNormHelicity)) { cmag = ELL_3V_LEN(curlAns); pvl->directAnswer[gageVecNormHelicity][0] = AIR_CAST(gage_t, cmag ? ELL_3V_DOT(normAns, curlAns)/cmag : 0); } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecLambda2)) { ELL_3M_TRANSPOSE(tran, jacAns); /* symmetric part */ ELL_3M_SCALE_ADD2(symm, 0.5, jacAns, 0.5, tran); /* antisymmetric part */ ELL_3M_SCALE_ADD2(asym, 0.5, jacAns, -0.5, tran); /* square symmetric part */ ELL_3M_MUL(tmpMat, symm, symm); ELL_3M_COPY(symm, tmpMat); /* square antisymmetric part */ ELL_3M_MUL(tmpMat, asym, asym); /* sum of both */ ELL_3M_ADD2(symm, symm, tmpMat); /* get eigenvalues in sorted order */ /* asw = */ ell_3m_eigenvalues_d(eval, symm, AIR_TRUE); pvl->directAnswer[gageVecLambda2][0] = AIR_CAST(gage_t, eval[1]); } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecImaginaryPart)) { pvl->directAnswer[gageVecImaginaryPart][0] = AIR_CAST(gage_t, gage_imaginary_part_eigenvalues(jacAns)); } /* 2nd order vector derivative continued */ if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecHessian)) { /* done if doD2 */ /* the ordering is induced by the scalar hessian computation : 0:d2v_x/dxdx 1:d2v_x/dxdy 2:d2v_x/dxdz 3:d2v_x/dydx 4:d2v_x/dydy 5:d2v_x/dydz 6:d2v_x/dzdx 7:d2v_x/dzdy 8:d2v_x/dzdz 9:d2v_y/dxdx [...] [...] 24:dv2_z/dzdx 25:d2v_z/dzdy 26:d2v_z/dzdz */ if (ctx->verbose) { fprintf(stderr, "%s: hes = \n", me); ell_3m_PRINT(stderr, hesAns); /* ?? */ } } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecDivGradient)) { pvl->directAnswer[gageVecDivGradient][0] = hesAns[0] + hesAns[12] + hesAns[24]; pvl->directAnswer[gageVecDivGradient][1] = hesAns[1] + hesAns[13] + hesAns[25]; pvl->directAnswer[gageVecDivGradient][2] = hesAns[2] + hesAns[14] + hesAns[26]; } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecCurlGradient)) { pvl->directAnswer[gageVecCurlGradient][0] = hesAns[21]-hesAns[15]; pvl->directAnswer[gageVecCurlGradient][1] = hesAns[22]-hesAns[16]; pvl->directAnswer[gageVecCurlGradient][2] = hesAns[23]-hesAns[17]; pvl->directAnswer[gageVecCurlGradient][3] = hesAns[ 6]-hesAns[18]; pvl->directAnswer[gageVecCurlGradient][4] = hesAns[ 7]-hesAns[19]; pvl->directAnswer[gageVecCurlGradient][5] = hesAns[ 8]-hesAns[20]; pvl->directAnswer[gageVecCurlGradient][6] = hesAns[ 9]-hesAns[ 1]; pvl->directAnswer[gageVecCurlGradient][7] = hesAns[10]-hesAns[ 2]; pvl->directAnswer[gageVecCurlGradient][8] = hesAns[11]-hesAns[ 3]; } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecCurlNormGrad)) { norm = 1./ELL_3V_LEN(curlAns); tmpVec[0] = hesAns[21] - hesAns[15]; tmpVec[1] = hesAns[ 6] - hesAns[18]; tmpVec[2] = hesAns[ 9] - hesAns[ 3]; pvl->directAnswer[gageVecCurlNormGrad][0]= AIR_CAST(gage_t, norm*ELL_3V_DOT(tmpVec, curlAns)); tmpVec[0] = hesAns[22] - hesAns[16]; tmpVec[1] = hesAns[ 7] - hesAns[19]; tmpVec[2] = hesAns[10] - hesAns[ 4]; pvl->directAnswer[gageVecCurlNormGrad][1]= AIR_CAST(gage_t, norm*ELL_3V_DOT(tmpVec, curlAns)); tmpVec[0] = hesAns[23] - hesAns[17]; tmpVec[1] = hesAns[ 8] - hesAns[20]; tmpVec[2] = hesAns[11] - hesAns[ 5]; pvl->directAnswer[gageVecCurlNormGrad][2]= AIR_CAST(gage_t, norm*ELL_3V_DOT(tmpVec, curlAns)); } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecNCurlNormGrad)) { norm = 1./ELL_3V_LEN(curlnormgradAns); ELL_3V_SCALE_TT(pvl->directAnswer[gageVecNCurlNormGrad], gage_t, norm, pvl->directAnswer[gageVecCurlNormGrad]); } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecHelGradient)) { pvl->directAnswer[gageVecHelGradient][0] = jacAns[0]*curlAns[0]+ jacAns[3]*curlAns[1]+ jacAns[6]*curlAns[2]+ curlGradAns[0]*vecAns[0]+ curlGradAns[3]*vecAns[1]+ curlGradAns[6]*vecAns[2]; pvl->directAnswer[gageVecHelGradient][1] = jacAns[1]*curlAns[0]+ jacAns[4]*curlAns[1]+ jacAns[7]*curlAns[2]+ curlGradAns[1]*vecAns[0]+ curlGradAns[4]*vecAns[1]+ curlGradAns[7]*vecAns[2]; pvl->directAnswer[gageVecHelGradient][0] = jacAns[2]*curlAns[0]+ jacAns[5]*curlAns[1]+ jacAns[8]*curlAns[2]+ curlGradAns[2]*vecAns[0]+ curlGradAns[5]*vecAns[1]+ curlGradAns[8]*vecAns[2]; } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecDirHelDeriv)) { pvl->directAnswer[gageVecDirHelDeriv][0] = ELL_3V_DOT(normAns, helGradAns); } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecProjHelGradient)) { pvl->directAnswer[gageVecDirHelDeriv][0] = helGradAns[0]-dirHelDirAns[0]*normAns[0]; pvl->directAnswer[gageVecDirHelDeriv][1] = helGradAns[1]-dirHelDirAns[0]*normAns[1]; pvl->directAnswer[gageVecDirHelDeriv][2] = helGradAns[2]-dirHelDirAns[0]*normAns[2]; } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecGradient0)) { ELL_3V_SET(pvl->directAnswer[gageVecGradient0], jacAns[0], jacAns[1], jacAns[2]); } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecGradient1)) { ELL_3V_SET(pvl->directAnswer[gageVecGradient1], jacAns[3], jacAns[4], jacAns[5]); } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecGradient2)) { ELL_3V_SET(pvl->directAnswer[gageVecGradient2], jacAns[6], jacAns[7], jacAns[8]); } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecMultiGrad)) { ELL_3M_IDENTITY_SET(pvl->directAnswer[gageVecMultiGrad]); ELL_3MV_OUTER_ADD(pvl->directAnswer[gageVecMultiGrad], pvl->directAnswer[gageVecGradient0], pvl->directAnswer[gageVecGradient0]); ELL_3MV_OUTER_ADD(pvl->directAnswer[gageVecMultiGrad], pvl->directAnswer[gageVecGradient1], pvl->directAnswer[gageVecGradient1]); ELL_3MV_OUTER_ADD(pvl->directAnswer[gageVecMultiGrad], pvl->directAnswer[gageVecGradient2], pvl->directAnswer[gageVecGradient2]); } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecMGFrob)) { pvl->directAnswer[gageVecMGFrob][0] = AIR_CAST(gage_t, ELL_3M_FROB(pvl->directAnswer[gageVecMultiGrad])); } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecMGEval)) { ELL_3M_COPY(tmpMat, pvl->directAnswer[gageVecMultiGrad]); /* HEY: look at the return value for root multiplicity? */ ell_3m_eigensolve_d(mgeval, mgevec, tmpMat, AIR_TRUE); ELL_3V_COPY_TT(pvl->directAnswer[gageVecMGEval], gage_t, mgeval); } if (GAGE_QUERY_ITEM_TEST(pvl->query, gageVecMGEvec)) { ELL_3M_COPY_TT(pvl->directAnswer[gageVecMGEvec], gage_t, mgevec); } return; }
int main(int argc, const char *argv[]) { const char *me; char *err, *outS; double eval[3], matA[9], matB[9], sval[3], uu[9], vv[9], escl[5], view[3]; float matAf[9], matBf[16]; float pp[3], qq[4], mR[9], len, gamma; float os, vs, rad, AB[2], ten[7]; hestOpt *hopt=NULL; airArray *mop; limnObject *obj; limnLook *look; int lookRod, lookSoid; float kadsRod[3], kadsSoid[3]; int gtype, partIdx=-1; /* sssh */ int res; FILE *file; me = argv[0]; hestOptAdd(&hopt, "sc", "evals", airTypeDouble, 3, 3, eval, "1 1 1", "original eigenvalues of tensor to be visualized"); hestOptAdd(&hopt, "AB", "A, B exponents", airTypeFloat, 2, 2, AB, "nan nan", "Directly set the A, B parameters to the superquadric surface, " "over-riding the default behavior of determining them from the " "scalings \"-sc\" as superquadric tensor glyphs"); hestOptAdd(&hopt, "os", "over-all scaling", airTypeFloat, 1, 1, &os, "1", "over-all scaling (multiplied by scalings)"); hestOptAdd(&hopt, "vs", "view-dir scaling", airTypeFloat, 1, 1, &vs, "1", "scaling along view-direction (to show off bas-relief " "ambibuity of ellipsoids versus superquads)"); hestOptAdd(&hopt, "es", "extra scaling", airTypeDouble, 5, 5, escl, "2 1 0 0 1", "extra scaling specified with five values " "0:tensor|1:geometry|2:none vx vy vz scaling"); hestOptAdd(&hopt, "fr", "from (eye) point", airTypeDouble, 3, 3, &view, "4 4 4", "eye point, needed for non-unity \"-vs\""); hestOptAdd(&hopt, "gamma", "superquad sharpness", airTypeFloat, 1, 1, &gamma, "0", "how much to sharpen edges as a " "function of differences between eigenvalues"); hestOptAdd(&hopt, "g", "glyph shape", airTypeEnum, 1, 1, >ype, "sqd", "glyph to use; not all are implemented here", NULL, tenGlyphType); hestOptAdd(&hopt, "pp", "x y z", airTypeFloat, 3, 3, pp, "0 0 0", "transform: rotation identified by" "location in quaternion quotient space"); hestOptAdd(&hopt, "r", "radius", airTypeFloat, 1, 1, &rad, "0.015", "black axis cylinder radius (or 0.0 to not drawn these)"); hestOptAdd(&hopt, "res", "resolution", airTypeInt, 1, 1, &res, "25", "tesselation resolution for both glyph and axis cylinders"); hestOptAdd(&hopt, "pg", "ka kd ks", airTypeFloat, 3, 3, kadsSoid, "0.2 0.8 0.0", "phong coefficients for glyph"); hestOptAdd(&hopt, "pr", "ka kd ks", airTypeFloat, 3, 3, kadsRod, "1 0 0", "phong coefficients for black rods (if being drawn)"); hestOptAdd(&hopt, "o", "output OFF", airTypeString, 1, 1, &outS, "out.off", "output file to save OFF into"); hestParseOrDie(hopt, argc-1, argv+1, NULL, me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE); mop = airMopNew(); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); obj = limnObjectNew(1000, AIR_TRUE); airMopAdd(mop, obj, (airMopper)limnObjectNix, airMopAlways); if (!( 0 == escl[0] || 1 == escl[0] || 2 == escl[0] )) { fprintf(stderr, "%s: escl[0] %g not 0, 1 or 2\n", me, escl[0]); airMopError(mop); return 1; } if (!(tenGlyphTypeBox == gtype || tenGlyphTypeSphere == gtype || tenGlyphTypeSuperquad == gtype)) { fprintf(stderr, "%s: got %s %s, but here only do %s, %s, or %s\n", me, tenGlyphType->name, airEnumStr(tenGlyphType, gtype), airEnumStr(tenGlyphType, tenGlyphTypeBox), airEnumStr(tenGlyphType, tenGlyphTypeSphere), airEnumStr(tenGlyphType, tenGlyphTypeSuperquad)); airMopError(mop); return 1; } /* create limnLooks for glyph and for rods */ lookSoid = limnObjectLookAdd(obj); look = obj->look + lookSoid; ELL_4V_SET(look->rgba, 1, 1, 1, 1); ELL_3V_COPY(look->kads, kadsSoid); look->spow = 0; lookRod = limnObjectLookAdd(obj); look = obj->look + lookRod; ELL_4V_SET(look->rgba, 0, 0, 0, 1); ELL_3V_COPY(look->kads, kadsRod); look->spow = 0; ELL_3M_IDENTITY_SET(matA); /* A = I */ ELL_3V_SCALE(eval, os, eval); ELL_3M_SCALE_SET(matB, eval[0], eval[1], eval[2]); /* B = diag(eval) */ ell_3m_post_mul_d(matA, matB); /* A = B*A = diag(eval) */ if (0 == escl[0]) { scalingMatrix(matB, escl + 1, escl[4]); ell_3m_post_mul_d(matA, matB); } if (1 != vs) { if (!ELL_3V_LEN(view)) { fprintf(stderr, "%s: need non-zero view for vs %g != 1\n", me, vs); airMopError(mop); return 1; } scalingMatrix(matB, view, vs); /* the scaling along the view direction is a symmetric matrix, but applying that scaling to the symmetric input tensor is not necessarily symmetric */ ell_3m_post_mul_d(matA, matB); /* A = B*A */ } /* so we do an SVD to get rotation U and the scalings sval[] */ /* U * diag(sval) * V */ ell_3m_svd_d(uu, sval, vv, matA, AIR_TRUE); /* fprintf(stderr, "%s: ____________________________________\n", me); fprintf(stderr, "%s: mat = \n", me); ell_3m_print_d(stderr, matA); fprintf(stderr, "%s: uu = \n", me); ell_3m_print_d(stderr, uu); ELL_3M_TRANSPOSE(matC, uu); ELL_3M_MUL(matB, uu, matC); fprintf(stderr, "%s: uu * uu^T = \n", me); ell_3m_print_d(stderr, matB); fprintf(stderr, "%s: sval = %g %g %g\n", me, sval[0], sval[1], sval[2]); fprintf(stderr, "%s: vv = \n", me); ell_3m_print_d(stderr, vv); ELL_3M_MUL(matB, vv, vv); fprintf(stderr, "%s: vv * vv^T = \n", me); ELL_3M_TRANSPOSE(matC, vv); ELL_3M_MUL(matB, vv, matC); ell_3m_print_d(stderr, matB); ELL_3M_IDENTITY_SET(matA); ell_3m_pre_mul_d(matA, uu); ELL_3M_SCALE_SET(matB, sval[0], sval[1], sval[2]); ell_3m_pre_mul_d(matA, matB); ell_3m_pre_mul_d(matA, vv); fprintf(stderr, "%s: uu * diag(sval) * vv = \n", me); ell_3m_print_d(stderr, matA); fprintf(stderr, "%s: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", me); */ /* now create symmetric matrix out of U and sval */ /* A = I */ ELL_3M_IDENTITY_SET(matA); ell_3m_pre_mul_d(matA, uu); /* A = A*U = I*U = U */ ELL_3M_SCALE_SET(matB, sval[0], sval[1], sval[2]); /* B = diag(sval) */ ell_3m_pre_mul_d(matA, matB); /* A = U*diag(sval) */ ELL_3M_TRANSPOSE(matB, uu); ell_3m_pre_mul_d(matA, matB); /* A = U*diag(sval)*U^T */ TEN_M2T(ten, matA); partIdx = soidDoit(obj, lookSoid, gtype, gamma, res, (AIR_EXISTS(AB[0]) && AIR_EXISTS(AB[1])) ? AB : NULL, ten); if (1 == escl[0]) { scalingMatrix(matB, escl + 1, escl[4]); ELL_43M_INSET(matBf, matB); limnObjectPartTransform(obj, partIdx, matBf); } /* this is a rotate on the geomtry; nothing to do with the tensor */ ELL_4V_SET(qq, 1, pp[0], pp[1], pp[2]); ELL_4V_NORM(qq, qq, len); ell_q_to_3m_f(mR, qq); ELL_43M_INSET(matBf, mR); limnObjectPartTransform(obj, partIdx, matBf); if (rad) { partIdx = limnObjectCylinderAdd(obj, lookRod, 0, res); ELL_4M_IDENTITY_SET(matAf); ELL_4M_SCALE_SET(matBf, (1-eval[0])/2, rad, rad); ell_4m_post_mul_f(matAf, matBf); ELL_4M_TRANSLATE_SET(matBf, (1+eval[0])/2, 0.0, 0.0); ell_4m_post_mul_f(matAf, matBf); limnObjectPartTransform(obj, partIdx, matAf); partIdx = limnObjectCylinderAdd(obj, lookRod, 0, res); ELL_4M_IDENTITY_SET(matAf); ELL_4M_SCALE_SET(matBf, (1-eval[0])/2, rad, rad); ell_4m_post_mul_f(matAf, matBf); ELL_4M_TRANSLATE_SET(matBf, -(1+eval[0])/2, 0.0, 0.0); ell_4m_post_mul_f(matAf, matBf); limnObjectPartTransform(obj, partIdx, matAf); partIdx = limnObjectCylinderAdd(obj, lookRod, 1, res); ELL_4M_IDENTITY_SET(matAf); ELL_4M_SCALE_SET(matBf, rad, (1-eval[1])/2, rad); ell_4m_post_mul_f(matAf, matBf); ELL_4M_TRANSLATE_SET(matBf, 0.0, (1+eval[1])/2, 0.0); ell_4m_post_mul_f(matAf, matBf); limnObjectPartTransform(obj, partIdx, matAf); partIdx = limnObjectCylinderAdd(obj, lookRod, 1, res); ELL_4M_IDENTITY_SET(matAf); ELL_4M_SCALE_SET(matBf, rad, (1-eval[1])/2, rad); ell_4m_post_mul_f(matAf, matBf); ELL_4M_TRANSLATE_SET(matBf, 0.0, -(1+eval[1])/2, 0.0); ell_4m_post_mul_f(matAf, matBf); limnObjectPartTransform(obj, partIdx, matAf); partIdx = limnObjectCylinderAdd(obj, lookRod, 2, res); ELL_4M_IDENTITY_SET(matAf); ELL_4M_SCALE_SET(matBf, rad, rad, (1-eval[2])/2); ell_4m_post_mul_f(matAf, matBf); ELL_4M_TRANSLATE_SET(matBf, 0.0, 0.0, (1+eval[2])/2); ell_4m_post_mul_f(matAf, matBf); limnObjectPartTransform(obj, partIdx, matAf); partIdx = limnObjectCylinderAdd(obj, lookRod, 2, res); ELL_4M_IDENTITY_SET(matAf); ELL_4M_SCALE_SET(matBf, rad, rad, (1-eval[2])/2); ell_4m_post_mul_f(matAf, matBf); ELL_4M_TRANSLATE_SET(matBf, 0.0, 0.0, -(1+eval[2])/2); ell_4m_post_mul_f(matAf, matBf); limnObjectPartTransform(obj, partIdx, matAf); } file = airFopen(outS, stdout, "w"); airMopAdd(mop, file, (airMopper)airFclose, airMopAlways); if (limnObjectWriteOFF(file, obj)) { airMopAdd(mop, err = biffGetDone(LIMN), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; }
static tenFiberContext * _tenFiberContextCommonNew(const Nrrd *vol, int useDwi, double thresh, double soft, double valueMin, int ten1method, int ten2method) { char me[]="_tenFiberContextCommonNew", err[BIFF_STRLEN]; tenFiberContext *tfx; gageKind *kind; if (!( tfx = (tenFiberContext *)calloc(1, sizeof(tenFiberContext)) )) { sprintf(err, "%s: couldn't allocate new context", me); biffAdd(TEN, err); return NULL; } if (useDwi) { Nrrd *ngrad=NULL, *nbmat=NULL; double bval=0; unsigned int *skip, skipNum; tfx->useDwi = AIR_TRUE; /* default fiber type */ tfx->fiberType = tenDwiFiberTypeUnknown; if (tenDWMRIKeyValueParse(&ngrad, &nbmat, &bval, &skip, &skipNum, vol)) { sprintf(err, "%s: trouble parsing DWI info", me ); biffAdd(TEN, err); return NULL; } if (skipNum) { sprintf(err, "%s: sorry, can't do DWI skipping here", me); biffAdd(TEN, err); return NULL; } kind = tenDwiGageKindNew(); if (tenDwiGageKindSet(kind, thresh, soft, bval, valueMin, ngrad, NULL, ten1method, ten2method, 42)) { sprintf(err, "%s: trouble setting DWI kind", me); biffAdd(TEN, err); return NULL; } } else { /* it should be a tensor volume */ tfx->useDwi = AIR_FALSE; /* default fiber type */ tfx->fiberType = tenFiberTypeUnknown; if (tenTensorCheck(vol, nrrdTypeUnknown, AIR_TRUE, AIR_TRUE)) { sprintf(err, "%s: didn't get a tensor volume", me); biffAdd(TEN, err); return NULL; } kind = tenGageKind; } if ( !(tfx->gtx = gageContextNew()) || !(tfx->pvl = gagePerVolumeNew(tfx->gtx, vol, kind)) || (gagePerVolumeAttach(tfx->gtx, tfx->pvl)) ) { sprintf(err, "%s: gage trouble", me); biffMove(TEN, err, GAGE); free(tfx); return NULL; } tfx->nin = vol; tfx->ksp = nrrdKernelSpecNew(); if (nrrdKernelSpecParse(tfx->ksp, tenDefFiberKernel)) { sprintf(err, "%s: couldn't parse tenDefFiberKernel \"%s\"", me, tenDefFiberKernel); biffMove(TEN, err, NRRD); return NULL; } if (tenFiberKernelSet(tfx, tfx->ksp->kernel, tfx->ksp->parm)) { sprintf(err, "%s: couldn't set default kernel", me); biffAdd(TEN, err); return NULL; } /* looks to GK like GK says that we must set some stop criterion */ tfx->intg = tenDefFiberIntg; tfx->anisoStopType = tenDefFiberAnisoStopType; tfx->anisoSpeedType = tenAnisoUnknown; tfx->stop = 0; tfx->anisoThresh = tenDefFiberAnisoThresh; /* so I'm not using the normal default mechanism, shoot me */ tfx->anisoSpeedFunc[0] = 0; tfx->anisoSpeedFunc[1] = 0; tfx->anisoSpeedFunc[2] = 0; tfx->maxNumSteps = tenDefFiberMaxNumSteps; tfx->minNumSteps = 0; tfx->useIndexSpace = tenDefFiberUseIndexSpace; tfx->verbose = 0; tfx->stepSize = tenDefFiberStepSize; tfx->maxHalfLen = tenDefFiberMaxHalfLen; tfx->minWholeLen = 0.0; tfx->confThresh = 0.5; /* why do I even bother setting these- they'll only get read if the right tenFiberStopSet has been called, in which case they'll be set... */ tfx->minRadius = 1; /* above lament applies here as well */ tfx->minFraction = 0.5; /* and here */ tfx->wPunct = tenDefFiberWPunct; GAGE_QUERY_RESET(tfx->query); tfx->mframe[0] = vol->measurementFrame[0][0]; tfx->mframe[1] = vol->measurementFrame[1][0]; tfx->mframe[2] = vol->measurementFrame[2][0]; tfx->mframe[3] = vol->measurementFrame[0][1]; tfx->mframe[4] = vol->measurementFrame[1][1]; tfx->mframe[5] = vol->measurementFrame[2][1]; tfx->mframe[6] = vol->measurementFrame[0][2]; tfx->mframe[7] = vol->measurementFrame[1][2]; tfx->mframe[8] = vol->measurementFrame[2][2]; if (ELL_3M_EXISTS(tfx->mframe)) { tfx->mframeUse = AIR_TRUE; ELL_3M_TRANSPOSE(tfx->mframeT, tfx->mframe); } else { tfx->mframeUse = AIR_FALSE; } tfx->gageAnisoStop = NULL; tfx->gageAnisoSpeed = NULL; tfx->ten2AnisoStop = AIR_NAN; /* ... don't really see the point of initializing the ten2 stuff here; its properly done in tenFiberTraceSet() ... */ tfx->radius = AIR_NAN; return tfx; }
void _tenDwiGageAnswer(gageContext *ctx, gagePerVolume *pvl) { char me[]="_tenDwiGageAnswer"; unsigned int dwiIdx; tenDwiGageKindData *kindData; tenDwiGagePvlData *pvlData; double *dwiAll, dwiMean=0, tentmp[7]; kindData = AIR_CAST(tenDwiGageKindData *, pvl->kind->data); pvlData = AIR_CAST(tenDwiGagePvlData *, pvl->data); dwiAll = pvl->directAnswer[tenDwiGageAll]; if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageAll)) { /* done if doV */ if (ctx->verbose) { for (dwiIdx=0; dwiIdx<pvl->kind->valLen; dwiIdx++) { fprintf(stderr, "%s(%d+%g,%d+%g,%d+%g): dwi[%u] = %g\n", me, ctx->point.xi, ctx->point.xf, ctx->point.yi, ctx->point.yf, ctx->point.zi, ctx->point.zf, dwiIdx, dwiAll[dwiIdx]); } fprintf(stderr, "%s: type(ngrad) = %d = %s\n", me, kindData->ngrad->type, airEnumStr(nrrdType, kindData->ngrad->type)); } } /* if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageB0)) { if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageJustDWI)) { done if doV } */ /* HEY this isn't valid for multiple b-values */ if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageADC)) { double logdwi, logb0; logb0 = log(AIR_MAX(kindData->valueMin, pvl->directAnswer[tenDwiGageB0][0])); for (dwiIdx=1; dwiIdx<pvl->kind->valLen; dwiIdx++) { logdwi = log(AIR_MAX(kindData->valueMin, pvl->directAnswer[tenDwiGageJustDWI][dwiIdx-1])); pvl->directAnswer[tenDwiGageADC][dwiIdx-1] = (logb0 - logdwi)/kindData->bval; } } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageMeanDWIValue)) { dwiMean = 0; for (dwiIdx=1; dwiIdx<pvl->kind->valLen; dwiIdx++) { dwiMean += dwiAll[dwiIdx]; } dwiMean /= pvl->kind->valLen; pvl->directAnswer[tenDwiGageMeanDWIValue][0] = dwiMean; } /* note: the gage interface to tenEstimate functionality allows you exactly one kind of tensor estimation (per kind), so the function call to do the estimation is actually repeated over and over again; the copy into the answer buffer is what changes... */ if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorLLS)) { tenEstimate1TensorSingle_d(pvlData->tec1, tentmp, dwiAll); TEN_T_COPY(pvl->directAnswer[tenDwiGageTensorLLS], tentmp); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorLLSError)) { pvl->directAnswer[tenDwiGageTensorLLSError][0] = pvlData->tec1->errorDwi; } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorLLSErrorLog)) { pvl->directAnswer[tenDwiGageTensorLLSErrorLog][0] = pvlData->tec1->errorLogDwi; } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorWLS)) { tenEstimate1TensorSingle_d(pvlData->tec1, tentmp, dwiAll); TEN_T_COPY(pvl->directAnswer[tenDwiGageTensorWLS], tentmp); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorNLS)) { tenEstimate1TensorSingle_d(pvlData->tec1, tentmp, dwiAll); TEN_T_COPY(pvl->directAnswer[tenDwiGageTensorNLS], tentmp); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorMLE)) { tenEstimate1TensorSingle_d(pvlData->tec1, tentmp, dwiAll); TEN_T_COPY(pvl->directAnswer[tenDwiGageTensorMLE], tentmp); } /* HEY: have to implement all the different kinds of errors */ /* BEGIN sneakiness ........ */ if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensor)) { gageItemEntry *item; item = pvl->kind->table + tenDwiGageTensor; TEN_T_COPY(pvl->directAnswer[tenDwiGageTensor], pvl->directAnswer[item->prereq[0]]); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorError)) { gageItemEntry *item; item = pvl->kind->table + tenDwiGageTensorError; pvl->directAnswer[tenDwiGageTensorError][0] = pvl->directAnswer[item->prereq[0]][0]; } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorErrorLog)) { gageItemEntry *item; item = pvl->kind->table + tenDwiGageTensorErrorLog; pvl->directAnswer[tenDwiGageTensorErrorLog][0] = pvl->directAnswer[item->prereq[0]][0]; } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorLikelihood)) { gageItemEntry *item; item = pvl->kind->table + tenDwiGageTensorLikelihood; pvl->directAnswer[tenDwiGageTensorLikelihood][0] = pvl->directAnswer[item->prereq[0]][0]; } /* END sneakiness ........ */ if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageFA)) { pvl->directAnswer[tenDwiGageFA][0] = pvl->directAnswer[tenDwiGageTensor][0] * tenAnisoTen_d(pvl->directAnswer[tenDwiGageTensor], tenAniso_FA); } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGageTensorAllDWIError)) { const double *grads; int gradcount; double *ten, d; int i; /* HEY: should switch to tenEstimate-based DWI simulation */ ten = pvl->directAnswer[tenDwiGageTensor]; gradcount = pvl->kind->valLen -1; /* Dont count b0 */ grads = ((const double*) kindData->ngrad->data) +3; /* Ignore b0 grad */ for( i=0; i < gradcount; i++ ) { d = dwiAll[0]*exp(- pvlData->tec1->bValue * TEN_T3V_CONTR(ten, grads + 3*i)); pvl->directAnswer[tenDwiGageTensorAllDWIError][i] = dwiAll[i+1] - d; } } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGage2TensorQSeg)) { const double *grads; int gradcount; double *twoten; unsigned int valIdx, E; twoten = pvl->directAnswer[tenDwiGage2TensorQSeg]; gradcount = pvl->kind->valLen -1; /* Dont count b0 */ grads = ((const double*) kindData->ngrad->data) +3; /* Ignore b0 grad */ if (dwiAll[0] != 0) { /* S0 = 0 */ _tenQball(pvlData->tec2->bValue, gradcount, dwiAll, grads, pvlData->qvals); _tenQvals2points(gradcount, pvlData->qvals, grads, pvlData->qpoints); _tenSegsamp2(gradcount, pvlData->qvals, grads, pvlData->qpoints, pvlData->wght + 1, pvlData->dists ); } else { /* stupid; should really return right here since data is garbage */ for (valIdx=1; valIdx < AIR_CAST(unsigned int, gradcount+1); valIdx++) { pvlData->wght[valIdx] = valIdx % 2; } } E = 0; for (valIdx=1; valIdx<pvl->kind->valLen; valIdx++) { if (!E) E |= tenEstimateSkipSet(pvlData->tec2, valIdx, pvlData->wght[valIdx]); } if (!E) E |= tenEstimateUpdate(pvlData->tec2); if (!E) E |= tenEstimate1TensorSingle_d(pvlData->tec2, twoten + 0, dwiAll); for (valIdx=1; valIdx<pvl->kind->valLen; valIdx++) { if (!E) E |= tenEstimateSkipSet(pvlData->tec2, valIdx, 1 - pvlData->wght[valIdx]); } if (!E) E |= tenEstimateUpdate(pvlData->tec2); if (!E) E |= tenEstimate1TensorSingle_d(pvlData->tec2, twoten + 7, dwiAll); if (E) { fprintf(stderr, "!%s: (trouble) %s\n", me, biffGetDone(TEN)); } /* hack: confidence for two-tensor fit */ twoten[0] = (twoten[0] + twoten[7])/2; twoten[7] = 0.5; /* fraction that is the first tensor (initial value) */ /* twoten[1 .. 6] = first tensor */ /* twoten[8 .. 13] = second tensor */ /* Compute fraction between tensors if not garbage in this voxel */ if (twoten[0] > 0.5) { double exp0,exp1,d,e=0,g=0, a=0,b=0; int i; for( i=0; i < gradcount; i++ ) { exp0 = exp(-pvlData->tec2->bValue * TEN_T3V_CONTR(twoten + 0, grads + 3*i)); exp1 = exp(-pvlData->tec2->bValue * TEN_T3V_CONTR(twoten + 7, grads + 3*i)); d = dwiAll[i+1] / dwiAll[0]; e = exp0 - exp1; g = d - exp1; a += .5*e*e; b += e*g; } twoten[7] = AIR_CLAMP(0, 0.5*(b/a), 1); } } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGage2TensorQSegError)) { const double *grads; int gradcount; double *twoten, d; int i; /* HEY: should switch to tenEstimate-based DWI simulation */ if (dwiAll[0] != 0) { /* S0 = 0 */ twoten = pvl->directAnswer[tenDwiGage2TensorQSeg]; gradcount = pvl->kind->valLen -1; /* Dont count b0 */ grads = ((const double*) kindData->ngrad->data) +3; /* Ignore b0 grad */ pvl->directAnswer[tenDwiGage2TensorQSegError][0] = 0; for( i=0; i < gradcount; i++ ) { d = twoten[7]*exp(-pvlData->tec2->bValue * TEN_T3V_CONTR(twoten + 0, grads + 3*i)); d += (1 - twoten[7])*exp(-pvlData->tec2->bValue *TEN_T3V_CONTR(twoten + 7, grads + 3*i)); d = dwiAll[i+1]/dwiAll[0] - d; pvl->directAnswer[tenDwiGage2TensorQSegError][0] += d*d; } pvl->directAnswer[tenDwiGage2TensorQSegError][0] = sqrt( pvl->directAnswer[tenDwiGage2TensorQSegError][0] ); } else { /* HEY: COMPLETELY WRONG!! An error is not defined! */ pvl->directAnswer[tenDwiGage2TensorQSegError][0] = 0; } /* printf("%f\n",pvl->directAnswer[tenDwiGage2TensorQSegError][0]); */ } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGage2TensorQSegAndError)) { double *twoten, *err, *twotenerr; twoten = pvl->directAnswer[tenDwiGage2TensorQSeg]; err = pvl->directAnswer[tenDwiGage2TensorQSegError]; twotenerr = pvl->directAnswer[tenDwiGage2TensorQSegAndError]; TEN_T_COPY(twotenerr + 0, twoten + 0); TEN_T_COPY(twotenerr + 7, twoten + 7); twotenerr[14] = err[0]; } if (GAGE_QUERY_ITEM_TEST(pvl->query, tenDwiGage2TensorPeled)) { #if TEEM_LEVMAR #define PARAMS 4 double *twoTen, Cp /* , residual, AICSingFit, AICTwoFit */; /* Vars for the NLLS */ double guess[PARAMS], loBnd[PARAMS], upBnd[PARAMS], opts[LM_OPTS_SZ], *grad, *egrad, tenA[7], tenB[7], matA[9], matB[9], matTmp[9], rott[9]; unsigned int gi; int lmret; /* Pointer to the location where the two tensor will be written */ twoTen = pvl->directAnswer[tenDwiGage2TensorPeled]; /* Estimate the DWI error, error is given as standard deviation */ pvlData->tec2->recordErrorDwi = AIR_FALSE; /* Estimate the single tensor */ tenEstimate1TensorSingle_d(pvlData->tec2, pvlData->ten1, dwiAll); /* Get the eigenValues and eigen vectors for this tensor */ tenEigensolve_d(pvlData->ten1Eval, pvlData->ten1Evec, pvlData->ten1); /* Get westins Cp */ Cp = tenAnisoEval_d(pvlData->ten1Eval, tenAniso_Cp1); /* Calculate the residual, need the variance to sqr it */ /* residual = pvlData->tec2->errorDwi*pvlData->tec2->errorDwi; */ /* Calculate the AIC for single tensor fit */ /* AICSingFit = _tenComputeAIC(residual, pvlData->tec2->dwiNum, 6); */ /* the CP-based test is gone; caller's responsibility */ /* rotate DW gradients by inverse of eigenvector column matrix and place into pvlData->nten1EigenGrads (which has been allocated by _tenDwiGagePvlDataNew()) */ grad = AIR_CAST(double *, kindData->ngrad->data); egrad = AIR_CAST(double *, pvlData->nten1EigenGrads->data); for (gi=0; gi<kindData->ngrad->axis[1].size; gi++) { /* yes, this is also transforming some zero-length (B0) gradients; that's harmless */ ELL_3MV_MUL(egrad, pvlData->ten1Evec, grad); grad += 3; egrad += 3; } /* Lower and upper bounds for the NLLS routine */ loBnd[0] = 0.0; loBnd[1] = 0.0; loBnd[2] = -AIR_PI/2; loBnd[3] = -AIR_PI/2; upBnd[0] = pvlData->ten1Eval[0]*5; upBnd[1] = 1.0; upBnd[2] = AIR_PI/2; upBnd[3] = AIR_PI/2; /* Starting point for the NLLS */ guess[0] = pvlData->ten1Eval[0]; guess[1] = 0.5; guess[2] = AIR_PI/4; guess[3] = -AIR_PI/4; /* guess[2] = AIR_AFFINE(0, airDrandMT_r(pvlData->randState), 1, AIR_PI/6, AIR_PI/3); guess[3] = AIR_AFFINE(0, airDrandMT_r(pvlData->randState), 1, -AIR_PI/6, -AIR_PI/3); */ /* Fill in the constraints for the LM optimization, the threshold of error difference */ opts[0] = pvlData->levmarTau; opts[1] = pvlData->levmarEps1; opts[2] = pvlData->levmarEps2; opts[3] = pvlData->levmarEps3; /* Very imp to set this opt, note that only forward differences are used to approx Jacobian */ opts[4] = pvlData->levmarDelta; /* run NLLS, results are stored back into guess[] */ pvlData->levmarUseFastExp = AIR_FALSE; lmret = dlevmar_bc_dif(_tenLevmarPeledCB, guess, pvlData->tec2->dwi, PARAMS, pvlData->tec2->dwiNum, loBnd, upBnd, pvlData->levmarMaxIter, opts, pvlData->levmarInfo, NULL, NULL, pvlData); if (-1 == lmret) { ctx->errNum = 1; sprintf(ctx->errStr, "%s: dlevmar_bc_dif() failed!", me); } else { /* Get the AIC for the two tensor fit, use the levmarinfo to get the residual */ /* residual = pvlData->levmarInfo[1]/pvlData->tec2->dwiNum; AICTwoFit = _tenComputeAIC(residual, pvlData->tec2->dwiNum, 12); */ /* Form the tensors using the estimated pp, returned in guess */ _tenPeledRotate2D(tenA, guess[0], pvlData->ten1Eval[2], guess[2]); _tenPeledRotate2D(tenB, guess[0], pvlData->ten1Eval[2], guess[3]); TEN_T2M(matA, tenA); TEN_T2M(matB, tenB); ELL_3M_TRANSPOSE(rott, pvlData->ten1Evec); ELL_3M_MUL(matTmp, matA, pvlData->ten1Evec); ELL_3M_MUL(matA, rott, matTmp); ELL_3M_MUL(matTmp, matB, pvlData->ten1Evec); ELL_3M_MUL(matB, rott, matTmp); /* Copy two two tensors */ /* guess[1] is population fraction of first tensor */ if (guess[1] > 0.5) { twoTen[7] = guess[1]; TEN_M2T(twoTen + 0, matA); TEN_M2T(twoTen + 7, matB); } else { twoTen[7] = 1 - guess[1]; TEN_M2T(twoTen + 0, matB); TEN_M2T(twoTen + 7, matA); } twoTen[0] = 1; } #undef PARAMS #else double *twoTen; twoTen = pvl->directAnswer[tenDwiGage2TensorPeled]; TEN_T_SET(twoTen + 0, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN); TEN_T_SET(twoTen + 7, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN); fprintf(stderr, "%s: sorry, not compiled with TEEM_LEVMAR\n", me); #endif }
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 main(int argc, const char *argv[]) { const char *me; char *err, *outS; double scale[3], matA[9], matB[9], matC[9], sval[3], uu[9], vv[9]; float matAf[9], matBf[16]; float p[3], q[4], mR[9], len, gamma; float os, vs, rad, AB[2], ten[7], view[3]; hestOpt *hopt=NULL; airArray *mop; limnObject *obj; limnLook *look; int lookRod, lookSoid; int partIdx=-1; /* sssh */ int res, sphere; FILE *file; me = argv[0]; hestOptAdd(&hopt, "sc", "scalings", airTypeDouble, 3, 3, scale, "1 1 1", "axis-aligned scaling to do on ellipsoid"); hestOptAdd(&hopt, "AB", "A, B exponents", airTypeFloat, 2, 2, AB, "nan nan", "Directly set the A, B parameters to the superquadric surface, " "over-riding the default behavior of determining them from the " "scalings \"-sc\" as superquadric tensor glyphs"); hestOptAdd(&hopt, "os", "over-all scaling", airTypeFloat, 1, 1, &os, "1", "over-all scaling (multiplied by scalings)"); hestOptAdd(&hopt, "vs", "over-all scaling", airTypeFloat, 1, 1, &vs, "1", "scaling along view-direction (to show off bas-relief " "ambibuity of ellipsoids versus superquads)"); hestOptAdd(&hopt, "fr", "from (eye) point", airTypeFloat, 3, 3, &view, "4 4 4", "eye point, needed for non-unity \"-vs\""); hestOptAdd(&hopt, "gamma", "superquad sharpness", airTypeFloat, 1, 1, &gamma, "0", "how much to sharpen edges as a " "function of differences between eigenvalues"); hestOptAdd(&hopt, "sphere", NULL, airTypeInt, 0, 0, &sphere, NULL, "use a sphere instead of a superquadric"); hestOptAdd(&hopt, "p", "x y z", airTypeFloat, 3, 3, p, "0 0 0", "location in quaternion quotient space"); hestOptAdd(&hopt, "r", "radius", airTypeFloat, 1, 1, &rad, "0.015", "black axis cylinder radius (or 0.0 to not drawn these)"); hestOptAdd(&hopt, "res", "resolution", airTypeInt, 1, 1, &res, "25", "tesselation resolution for both glyph and axis cylinders"); hestOptAdd(&hopt, "o", "output OFF", airTypeString, 1, 1, &outS, "out.off", "output file to save OFF into"); hestParseOrDie(hopt, argc-1, argv+1, NULL, me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE); mop = airMopNew(); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); obj = limnObjectNew(1000, AIR_TRUE); airMopAdd(mop, obj, (airMopper)limnObjectNix, airMopAlways); /* create limnLooks for ellipsoid and for rods */ lookSoid = limnObjectLookAdd(obj); look = obj->look + lookSoid; ELL_4V_SET(look->rgba, 1, 1, 1, 1); ELL_3V_SET(look->kads, 0.2, 0.8, 0); look->spow = 0; lookRod = limnObjectLookAdd(obj); look = obj->look + lookRod; ELL_4V_SET(look->rgba, 0, 0, 0, 1); ELL_3V_SET(look->kads, 1, 0, 0); look->spow = 0; ELL_3M_IDENTITY_SET(matA); ELL_3V_SCALE(scale, os, scale); ELL_3M_SCALE_SET(matB, scale[0], scale[1], scale[2]); ell_3m_post_mul_d(matA, matB); if (1 != vs) { ELL_3V_NORM(view, view, len); if (!len) { /* HEY: perhaps do more diplomatic error message here */ fprintf(stderr, "%s: stupido!\n", me); exit(1); } ELL_3MV_OUTER(matB, view, view); ELL_3M_SCALE(matB, vs-1, matB); ELL_3M_IDENTITY_SET(matC); ELL_3M_ADD2(matB, matC, matB); ell_3m_post_mul_d(matA, matB); } ell_3m_svd_d(uu, sval, vv, matA, AIR_TRUE); /* fprintf(stderr, "%s: ____________________________________\n", me); fprintf(stderr, "%s: mat = \n", me); ell_3m_print_d(stderr, matA); fprintf(stderr, "%s: uu = \n", me); ell_3m_print_d(stderr, uu); ELL_3M_TRANSPOSE(matC, uu); ELL_3M_MUL(matB, uu, matC); fprintf(stderr, "%s: uu * uu^T = \n", me); ell_3m_print_d(stderr, matB); fprintf(stderr, "%s: sval = %g %g %g\n", me, sval[0], sval[1], sval[2]); fprintf(stderr, "%s: vv = \n", me); ell_3m_print_d(stderr, vv); ELL_3M_MUL(matB, vv, vv); fprintf(stderr, "%s: vv * vv^T = \n", me); ELL_3M_TRANSPOSE(matC, vv); ELL_3M_MUL(matB, vv, matC); ell_3m_print_d(stderr, matB); ELL_3M_IDENTITY_SET(matA); ell_3m_pre_mul_d(matA, uu); ELL_3M_SCALE_SET(matB, sval[0], sval[1], sval[2]); ell_3m_pre_mul_d(matA, matB); ell_3m_pre_mul_d(matA, vv); fprintf(stderr, "%s: uu * diag(sval) * vv = \n", me); ell_3m_print_d(stderr, matA); fprintf(stderr, "%s: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", me); */ ELL_3M_IDENTITY_SET(matA); ell_3m_pre_mul_d(matA, uu); ELL_3M_SCALE_SET(matB, sval[0], sval[1], sval[2]); ell_3m_pre_mul_d(matA, matB); ELL_3M_TRANSPOSE(matB, uu); ell_3m_pre_mul_d(matA, matB); TEN_M2T(ten, matA); partIdx = soidDoit(obj, lookSoid, sphere, gamma, res, (AIR_EXISTS(AB[0]) && AIR_EXISTS(AB[1])) ? AB : NULL, ten); ELL_4V_SET(q, 1, p[0], p[1], p[2]); ELL_4V_NORM(q, q, len); ell_q_to_3m_f(mR, q); ELL_43M_INSET(matBf, mR); limnObjectPartTransform(obj, partIdx, matBf); if (rad) { partIdx = limnObjectCylinderAdd(obj, lookRod, 0, res); ELL_4M_IDENTITY_SET(matAf); ELL_4M_SCALE_SET(matBf, (1-scale[0])/2, rad, rad); ell_4m_post_mul_f(matAf, matBf); ELL_4M_TRANSLATE_SET(matBf, (1+scale[0])/2, 0.0, 0.0); ell_4m_post_mul_f(matAf, matBf); limnObjectPartTransform(obj, partIdx, matAf); partIdx = limnObjectCylinderAdd(obj, lookRod, 0, res); ELL_4M_IDENTITY_SET(matAf); ELL_4M_SCALE_SET(matBf, (1-scale[0])/2, rad, rad); ell_4m_post_mul_f(matAf, matBf); ELL_4M_TRANSLATE_SET(matBf, -(1+scale[0])/2, 0.0, 0.0); ell_4m_post_mul_f(matAf, matBf); limnObjectPartTransform(obj, partIdx, matAf); partIdx = limnObjectCylinderAdd(obj, lookRod, 1, res); ELL_4M_IDENTITY_SET(matAf); ELL_4M_SCALE_SET(matBf, rad, (1-scale[1])/2, rad); ell_4m_post_mul_f(matAf, matBf); ELL_4M_TRANSLATE_SET(matBf, 0.0, (1+scale[1])/2, 0.0); ell_4m_post_mul_f(matAf, matBf); limnObjectPartTransform(obj, partIdx, matAf); partIdx = limnObjectCylinderAdd(obj, lookRod, 1, res); ELL_4M_IDENTITY_SET(matAf); ELL_4M_SCALE_SET(matBf, rad, (1-scale[1])/2, rad); ell_4m_post_mul_f(matAf, matBf); ELL_4M_TRANSLATE_SET(matBf, 0.0, -(1+scale[1])/2, 0.0); ell_4m_post_mul_f(matAf, matBf); limnObjectPartTransform(obj, partIdx, matAf); partIdx = limnObjectCylinderAdd(obj, lookRod, 2, res); ELL_4M_IDENTITY_SET(matAf); ELL_4M_SCALE_SET(matBf, rad, rad, (1-scale[2])/2); ell_4m_post_mul_f(matAf, matBf); ELL_4M_TRANSLATE_SET(matBf, 0.0, 0.0, (1+scale[2])/2); ell_4m_post_mul_f(matAf, matBf); limnObjectPartTransform(obj, partIdx, matAf); partIdx = limnObjectCylinderAdd(obj, lookRod, 2, res); ELL_4M_IDENTITY_SET(matAf); ELL_4M_SCALE_SET(matBf, rad, rad, (1-scale[2])/2); ell_4m_post_mul_f(matAf, matBf); ELL_4M_TRANSLATE_SET(matBf, 0.0, 0.0, -(1+scale[2])/2); ell_4m_post_mul_f(matAf, matBf); limnObjectPartTransform(obj, partIdx, matAf); } file = airFopen(outS, stdout, "w"); airMopAdd(mop, file, (airMopper)airFclose, airMopAlways); if (limnObjectWriteOFF(file, obj)) { airMopAdd(mop, err = biffGetDone(LIMN), airFree, airMopAlways); fprintf(stderr, "%s: trouble:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; }
/* ** _gageShapeSet ** ** we are serving two masters here. If ctx is non-NULL, we are being called ** from within gage, and we are to be lax or strict according to the settings ** of ctx->parm.requireAllSpacings and ctx->parm.requireEqualCenters. If ** ctx is NULL, gageShapeSet was called, in which case we go with lax ** behavior (nothing "required") ** ** This function has subsumed the contents of the old gageVolumeCheck, ** and hence has become this weird beast- part error checker and part ** (gageShape) initializer. Oh well... */ int _gageShapeSet(const gageContext *ctx, gageShape *shape, const Nrrd *nin, unsigned int baseDim) { static const char me[]="_gageShapeSet"; int ai, cx, cy, cz, statCalc[3], status, ofspc; unsigned int minsize; const NrrdAxisInfo *ax[3]; double vecA[4], vecB[3], vecC[3], vecD[4], matA[9], spcCalc[3], vecCalc[3][NRRD_SPACE_DIM_MAX], orig[NRRD_SPACE_DIM_MAX]; airArray *mop; /* fprintf(stderr, "!%s: ctx = %p (%s, %s)\n", me, ctx, (ctx ? (ctx->shape->fromOrientation ? "YES from orient" : "not from orient") : "???"), (ctx ? (ctx->parm.orientationFromSpacing ? "YES ofs" : "not ofs") : "???")); */ /* ------ basic error checking */ mop = airMopNew(); airMopAdd(mop, shape, _mopShapeReset, airMopOnError); if (!( shape && nin )) { biffAddf(GAGE, "%s: got NULL pointer", me); airMopError(mop); return 1; } if (nrrdCheck(nin)) { biffMovef(GAGE, NRRD, "%s: basic nrrd validity check failed", me); airMopError(mop); return 1; } if (nrrdTypeBlock == nin->type) { biffAddf(GAGE, "%s: need a non-block type nrrd", me); airMopError(mop); return 1; } if (!(nin->dim == 3 + baseDim)) { biffAddf(GAGE, "%s: nrrd should be %u-D, not %u-D", me, 3 + baseDim, nin->dim); airMopError(mop); return 1; } ax[0] = &(nin->axis[baseDim+0]); ax[1] = &(nin->axis[baseDim+1]); ax[2] = &(nin->axis[baseDim+2]); statCalc[0] = nrrdSpacingCalculate(nin, baseDim + 0, spcCalc + 0, vecCalc[0]); statCalc[1] = nrrdSpacingCalculate(nin, baseDim + 1, spcCalc + 1, vecCalc[1]); statCalc[2] = nrrdSpacingCalculate(nin, baseDim + 2, spcCalc + 2, vecCalc[2]); /* see if nrrdSpacingCalculate ever *failed* */ if (nrrdSpacingStatusUnknown == statCalc[0] || nrrdSpacingStatusUnknown == statCalc[1] || nrrdSpacingStatusUnknown == statCalc[2]) { biffAddf(GAGE, "%s: nrrdSpacingCalculate trouble on axis %d, %d, or %d", me, baseDim + 0, baseDim + 1, baseDim + 2); airMopError(mop); return 1; } if (!( statCalc[0] == statCalc[1] && statCalc[1] == statCalc[2] )) { biffAddf(GAGE, "%s: inconsistent spacing information on axes " "%u (%s), %u (%s), and %u (%s)", me, baseDim + 0, airEnumDesc(nrrdSpacingStatus, statCalc[0]), baseDim + 1, airEnumDesc(nrrdSpacingStatus, statCalc[1]), baseDim + 2, airEnumDesc(nrrdSpacingStatus, statCalc[2])); airMopError(mop); return 1; } /* this simplifies reasoning in the code that follows */ status = statCalc[0]; /* zero spacing would be problematic */ if (0 == spcCalc[0] && 0 == spcCalc[1] && 0 == spcCalc[2]) { biffAddf(GAGE, "%s: spacings (%g,%g,%g) for axes %d,%d,%d not all " "non-zero", me, spcCalc[1], spcCalc[1], spcCalc[2], baseDim+0, baseDim+1, baseDim+2); airMopError(mop); return 1; } /* error checking based on status */ if (nrrdSpacingStatusScalarWithSpace == status) { biffAddf(GAGE, "%s: sorry, can't handle per-axis spacing that isn't part " "of a surrounding world space (%s)", me, airEnumStr(nrrdSpacingStatus, status)); airMopError(mop); return 1; } /* we no longer allow a nrrd to come in with no spacing info at all */ if (nrrdSpacingStatusNone == status) { biffAddf(GAGE, "%s: sorry, need some spacing info for spatial axes " "%u, %u, %u", me, baseDim+0, baseDim+1, baseDim+2); airMopError(mop); return 1; } /* actually, there shouldn't be any other options for spacing status besides these too; this is just being careful */ if (!( nrrdSpacingStatusDirection == status || nrrdSpacingStatusScalarNoSpace == status )) { biffAddf(GAGE, "%s: sorry, can only handle spacing status %d (%s) " "or %d (%s), not %d (%s)", me, nrrdSpacingStatusDirection, airEnumStr(nrrdSpacingStatus, nrrdSpacingStatusDirection), nrrdSpacingStatusScalarNoSpace, airEnumStr(nrrdSpacingStatus, nrrdSpacingStatusScalarNoSpace), status, airEnumStr(nrrdSpacingStatus, status)); airMopError(mop); return 1; } if (nrrdSpacingStatusDirection == status) { shape->fromOrientation = AIR_TRUE; if (3 != nin->spaceDim) { biffAddf(GAGE, "%s: orientation space dimension %d != 3", me, nin->spaceDim); airMopError(mop); return 1; } } else { shape->fromOrientation = AIR_FALSE; } /* ------ find centering (set shape->center) */ /* NOTE: when the volume is being crammed in a bi-unit cube, the centering will actually affect the positions of the samples. Otherwise, (having full orientation, or using orientationFromSpacing), the centering will only affect the probe-able bounds of the volume, but the sample positions in space don't depend on centering */ cx = ax[0]->center; cy = ax[1]->center; cz = ax[2]->center; if (!( cx == cy && cy == cz )) { biffAddf(GAGE, "%s: axes %d,%d,%d centerings (%s,%s,%s) not all equal", me, baseDim+0, baseDim+1, baseDim+2, airEnumStr(nrrdCenter, cx), airEnumStr(nrrdCenter, cy), airEnumStr(nrrdCenter, cz)); airMopError(mop); return 1; } /* Hopefully, ctx->parm.defaultCenter == shape->defaultCenter; and this worry will be moot if ctx->parm.defaultCenter goes away */ shape->center = (nrrdCenterUnknown != cx ? cx /* cx == cy == cz, by above */ : (ctx ? ctx->parm.defaultCenter : shape->defaultCenter)); /* ------ find sizes (set shape->size[0,1,2]) */ shape->size[0] = ax[0]->size; shape->size[1] = ax[1]->size; shape->size[2] = ax[2]->size; minsize = (nrrdCenterCell == shape->center ? 1 : 2); /* this can't be relaxed in the face of having full orientation info, because even then, you can't have a non-zero probe-able volume if there's only one sample along a node-centered axis */ if (!(shape->size[0] >= minsize && shape->size[1] >= minsize && shape->size[2] >= minsize )) { biffAddf(GAGE, "%s: sizes (%u,%u,%u) must all be >= %u " "(min number of %s-centered samples)", me, shape->size[0], shape->size[1], shape->size[2], minsize, airEnumStr(nrrdCenter, shape->center)); airMopError(mop); return 1; } /* ------ find spacings[0,1,2] and ItoW matrix */ /* Hopefully, ctx->parm.orientationFromSpacing and shape->orientationFromSpacing don't represent competing interests; this worry will be moot if ctx->parm.orientationFromSpacing goes away */ ofspc = ((ctx && ctx->parm.orientationFromSpacing) || shape->orientationFromSpacing); if (shape->fromOrientation || ofspc) { if (ofspc) { /* need abs() in case an axis had negative spacing */ ELL_3V_ABS(shape->spacing, spcCalc); ELL_3V_SET(vecCalc[0], airSgn(spcCalc[0]), 0.0, 0.0); ELL_3V_SET(vecCalc[1], 0.0, airSgn(spcCalc[1]), 0.0); ELL_3V_SET(vecCalc[2], 0.0, 0.0, airSgn(spcCalc[2])); } else { ELL_3V_COPY(shape->spacing, spcCalc); /* vecCalc set by nrrdSpacingCalculate */ } if (shape->fromOrientation) { /* if the spaceOrigin isn't set, this will be all NaNs */ nrrdSpaceOriginGet(nin, orig); } else { /* sorry, if you want to specify an image origin that over-rides the behavior of centering the volume at (0,0,0), then it has to be done through the full orientation info. That is, we don't want to use nrrdOriginCalculate() because otherwise the logic gets too complicated */ ELL_3V_SET(orig, AIR_NAN, AIR_NAN, AIR_NAN); } if (!ELL_3V_EXISTS(orig)) { /* don't have origin, for whatever reason; center volume on (0,0,0) */ ELL_3V_SET(orig, 0.0, 0.0, 0.0); ELL_3V_SCALE_INCR(orig, -(shape->size[0] - 1.0)*shape->spacing[0]/2.0, vecCalc[0]); ELL_3V_SCALE_INCR(orig, -(shape->size[1] - 1.0)*shape->spacing[1]/2.0, vecCalc[1]); ELL_3V_SCALE_INCR(orig, -(shape->size[2] - 1.0)*shape->spacing[2]/2.0, vecCalc[2]); } vecD[3] = 0; ELL_3V_SCALE(vecD, spcCalc[0], vecCalc[0]); ELL_4MV_COL0_SET(shape->ItoW, vecD); ELL_3V_SCALE(vecD, spcCalc[1], vecCalc[1]); ELL_4MV_COL1_SET(shape->ItoW, vecD); ELL_3V_SCALE(vecD, spcCalc[2], vecCalc[2]); ELL_4MV_COL2_SET(shape->ItoW, vecD); vecD[3] = 1; ELL_3V_COPY(vecD, orig); ELL_4MV_COL3_SET(shape->ItoW, vecD); /* fprintf(stderr, "%s: %g (%g,%g,%g)\n", me, spcCalc[0], vecCalc[0][0], vecCalc[0][1], vecCalc[0][2]); fprintf(stderr, "%s: %g (%g,%g,%g)\n", me, spcCalc[1], vecCalc[1][0], vecCalc[1][1], vecCalc[1][2]); fprintf(stderr, "%s: %g (%g,%g,%g)\n", me, spcCalc[2], vecCalc[2][0], vecCalc[2][1], vecCalc[2][2]); */ /* fprintf(stderr, "%s: ItoW = %g %g %g %g\n", me, shape->ItoW[ 0], shape->ItoW[ 1], shape->ItoW[ 2], shape->ItoW[ 3]); fprintf(stderr, "%s: %g %g %g %g\n", me, shape->ItoW[ 4], shape->ItoW[ 5], shape->ItoW[ 6], shape->ItoW[ 7]); fprintf(stderr, "%s: %g %g %g %g\n", me, shape->ItoW[ 8], shape->ItoW[ 9], shape->ItoW[10], shape->ItoW[11]); fprintf(stderr, "%s: %g %g %g %g\n", me, shape->ItoW[12], shape->ItoW[13], shape->ItoW[14], shape->ItoW[15]); */ } else { /* not (shape->fromOrientation || ofspc) */ double maxLen, volHalfLen[3]; size_t num[3]; /* ------ learn lengths for bounding nrrd in bi-unit cube */ ELL_3V_ABS(shape->spacing, spcCalc); maxLen = 0.0; for (ai=0; ai<=2; ai++) { num[ai] = (nrrdCenterNode == shape->center ? shape->size[ai]-1 : shape->size[ai]); volHalfLen[ai] = num[ai]*shape->spacing[ai]; maxLen = AIR_MAX(maxLen, volHalfLen[ai]); } /* Thu Dec 13 02:45:01 EST 2007 fixed long-standing bug in handling vols without full orientation info: spacing[ai] was never scaled to account for being crammed into the bi-unit cube!! */ for (ai=0; ai<=2; ai++) { volHalfLen[ai] /= maxLen; shape->spacing[ai] = 2*volHalfLen[ai]/num[ai]; } ELL_3V_SET(vecC, 0, 0, 0); shapeUnitItoW(shape, vecA, vecC, volHalfLen); ELL_3V_SET(vecC, 1, 0, 0); shapeUnitItoW(shape, vecB, vecC, volHalfLen); ELL_3V_SUB(vecD, vecB, vecA); vecD[3] = 0; ELL_4MV_COL0_SET(shape->ItoW, vecD); ELL_3V_SET(vecC, 0, 1, 0); shapeUnitItoW(shape, vecB, vecC, volHalfLen); ELL_3V_SUB(vecD, vecB, vecA); vecD[3] = 0; ELL_4MV_COL1_SET(shape->ItoW, vecD); ELL_3V_SET(vecC, 0, 0, 1); shapeUnitItoW(shape, vecB, vecC, volHalfLen); ELL_3V_SUB(vecD, vecB, vecA); vecD[3] = 0; ELL_4MV_COL2_SET(shape->ItoW, vecD); vecA[3] = 1; ELL_4MV_COL3_SET(shape->ItoW, vecA); } /* ------ set the rest of the matrices */ ell_4m_inv_d(shape->WtoI, shape->ItoW); ELL_34M_EXTRACT(matA, shape->ItoW); ell_3m_inv_d(shape->ItoWSubInv, matA); ELL_3M_TRANSPOSE(shape->ItoWSubInvTransp, shape->ItoWSubInv); airMopOkay(mop); return 0; }
/* ** _gageShapeSet ** ** we are serving two masters here. If ctx is non-NULL, we are being called ** from within gage, and we are to be lax or strict according to the settings ** of ctx->parm.requireAllSpacings and ctx->parm.requireEqualCenters. If ** ctx is NULL, gageShapeSet was called, in which case we go with lax ** behavior (nothing "required") ** ** This function has subsumed the old gageVolumeCheck, and hence has ** become this weird beast- part error checker and part (gageShape) ** initializer. Oh well... */ int _gageShapeSet(const gageContext *ctx, gageShape *shape, const Nrrd *nin, unsigned int baseDim) { char me[]="_gageShapeSet", err[BIFF_STRLEN]; int ai, cx, cy, cz, defCenter, statCalc[3]; unsigned int minsize, sx, sy, sz; const NrrdAxisInfo *ax[3]; double maxLen, defSpacing, vecA[4], vecB[3], vecC[3], vecD[4], matA[9], spcCalc[3], vecCalc[3][NRRD_SPACE_DIM_MAX], orig[NRRD_SPACE_DIM_MAX]; /* ------ basic error checking */ if (!( shape && nin )) { sprintf(err, "%s: got NULL pointer", me); biffAdd(GAGE, err); if (shape) { gageShapeReset(shape); } return 1; } if (nrrdCheck(nin)) { sprintf(err, "%s: basic nrrd validity check failed", me); biffMove(GAGE, err, NRRD); gageShapeReset(shape); return 1; } if (nrrdTypeBlock == nin->type) { sprintf(err, "%s: need a non-block type nrrd", me); biffAdd(GAGE, err); gageShapeReset(shape); return 1; } if (!(nin->dim == 3 + baseDim)) { sprintf(err, "%s: nrrd should be %u-D, not %u-D", me, 3 + baseDim, nin->dim); biffAdd(GAGE, err); gageShapeReset(shape); return 1; } ax[0] = &(nin->axis[baseDim+0]); ax[1] = &(nin->axis[baseDim+1]); ax[2] = &(nin->axis[baseDim+2]); /* if (1) { unsigned int ai; for (ai=0; ai<3; ai++) { fprintf(stderr, "!%s: ax[%u] spc = %g, sd = %g %g %g\n", me, ai, ax[ai]->spacing, ax[ai]->spaceDirection[0], ax[ai]->spaceDirection[1], ax[ai]->spaceDirection[2]); } } */ statCalc[0] = nrrdSpacingCalculate(nin, baseDim + 0, spcCalc + 0, vecCalc[0]); statCalc[1] = nrrdSpacingCalculate(nin, baseDim + 1, spcCalc + 1, vecCalc[1]); statCalc[2] = nrrdSpacingCalculate(nin, baseDim + 2, spcCalc + 2, vecCalc[2]); /* fprintf(stderr, "!%s: axis stat %d %d %d\n", me, statCalc[0], statCalc[1], statCalc[2]); */ /* see if nrrdSpacingCalculate ever *failed* */ if (nrrdSpacingStatusUnknown == statCalc[0] || nrrdSpacingStatusUnknown == statCalc[1] || nrrdSpacingStatusUnknown == statCalc[2]) { sprintf(err, "%s: nrrdSpacingCalculate trouble on axis %d, %d, or %d", me, baseDim + 0, baseDim + 1, baseDim + 2); biffAdd(GAGE, err); gageShapeReset(shape); return 1; } /* see if nrrdSpacingCalculate encountered an axis with no space direction in a nrrd that nominally has a surrounding space */ if (nrrdSpacingStatusScalarWithSpace == statCalc[0] || nrrdSpacingStatusScalarWithSpace == statCalc[1] || nrrdSpacingStatusScalarWithSpace == statCalc[2]) { sprintf(err, "%s: nrrdSpacingCalculate weirdness on axis %d, %d, or %d", me, baseDim + 0, baseDim + 1, baseDim + 2); biffAdd(GAGE, err); gageShapeReset(shape); return 1; } if (!( ( nrrdSpacingStatusDirection == statCalc[0] && nrrdSpacingStatusDirection == statCalc[1] && nrrdSpacingStatusDirection == statCalc[2]) || ( nrrdSpacingStatusDirection != statCalc[0] && nrrdSpacingStatusDirection != statCalc[1] && nrrdSpacingStatusDirection != statCalc[2]) )) { sprintf(err, "%s: inconsistent space directions use " "in axis %d, %d, and %d", me, baseDim + 0, baseDim + 1, baseDim + 2); biffAdd(GAGE, err); gageShapeReset(shape); return 1; } if ( nrrdSpacingStatusDirection == statCalc[0] && nrrdSpacingStatusDirection == statCalc[1] && nrrdSpacingStatusDirection == statCalc[2]) { /* this will get reset to false in case of error */ shape->fromOrientation = AIR_TRUE; } else { shape->fromOrientation = AIR_FALSE; } /* oh yea, we should make sure the space dimension is right! */ if (shape->fromOrientation && 3 != nin->spaceDim) { sprintf(err, "%s: orientation space dimension %d != 3", me, nin->spaceDim); biffAdd(GAGE, err); gageShapeReset(shape); return 1; } /* ------ find centering (set shape->center) */ /* HEY: when we have full orientation information (via spaceDirections and spaceOrigin) the centering information is moot for determining shape, but until all usage of gageShape stuff is properly overhauled to take orientation into account, we'll still set shape->center */ cx = ax[0]->center; cy = ax[1]->center; cz = ax[2]->center; if (ctx && ctx->parm.requireEqualCenters) { if (!( cx == cy && cx == cz )) { sprintf(err, "%s: axes %d,%d,%d centerings (%s,%s,%s) not equal", me, baseDim+0, baseDim+1, baseDim+2, airEnumStr(nrrdCenter, cx), airEnumStr(nrrdCenter, cy), airEnumStr(nrrdCenter, cz)); biffAdd(GAGE, err); gageShapeReset(shape); return 1; } } else { if ( (nrrdCenterUnknown != cx && nrrdCenterUnknown != cy && cx != cy) || (nrrdCenterUnknown != cy && nrrdCenterUnknown != cz && cy != cz) || (nrrdCenterUnknown != cx && nrrdCenterUnknown != cz && cx != cz) ) { sprintf(err, "%s: two known centerings (of %s,%s,%s) are unequal", me, airEnumStr(nrrdCenter, cx), airEnumStr(nrrdCenter, cy), airEnumStr(nrrdCenter, cz)); biffAdd(GAGE, err); gageShapeReset(shape); return 1; } } defCenter = ctx ? ctx->parm.defaultCenter : shape->defaultCenter; shape->center = (nrrdCenterUnknown != cx ? cx : (nrrdCenterUnknown != cy ? cy : (nrrdCenterUnknown != cz ? cz : defCenter))); /* ------ find sizes (set shape->size[0,1,2]) */ sx = ax[0]->size; sy = ax[1]->size; sz = ax[2]->size; minsize = (nrrdCenterCell == shape->center ? 1 : 2); /* HEY: perhaps this should be relaxed if we have full orientation info */ if (!(sx >= minsize && sy >= minsize && sz >= minsize )) { sprintf(err, "%s: sizes (%u,%u,%u) must all be >= %u " "(min number of %s-centered samples)", me, sx, sy, sz, minsize, airEnumStr(nrrdCenter, shape->center)); biffAdd(GAGE, err); gageShapeReset(shape); return 1; } shape->size[0] = sx; shape->size[1] = sy; shape->size[2] = sz; /* ------ find spacings (set shape->spacing[0,1,2]) */ if (shape->fromOrientation) { shape->spacing[0] = AIR_ABS(spcCalc[0]); shape->spacing[1] = AIR_ABS(spcCalc[1]); shape->spacing[2] = AIR_ABS(spcCalc[2]); for (ai=0; ai<=2; ai++) { shape->volHalfLen[ai] = AIR_NAN; shape->voxLen[ai] = AIR_NAN; } } else { double xs, ys, zs; unsigned int num[3]; xs = ax[0]->spacing; ys = ax[1]->spacing; zs = ax[2]->spacing; if (ctx && ctx->parm.requireAllSpacings) { if (!( AIR_EXISTS(xs) && AIR_EXISTS(ys) && AIR_EXISTS(zs) )) { sprintf(err, "%s: spacings for axes %d,%d,%d don't all exist", me, baseDim+0, baseDim+1, baseDim+2); biffAdd(GAGE, err); gageShapeReset(shape); return 1; } } /* there is no shape->defaultSpacing, we'll go out on a limb ... */ defSpacing = ctx ? ctx->parm.defaultSpacing : nrrdDefaultSpacing; xs = AIR_EXISTS(xs) ? xs : defSpacing; ys = AIR_EXISTS(ys) ? ys : defSpacing; zs = AIR_EXISTS(zs) ? zs : defSpacing; if (!( xs != 0 && ys != 0 && zs != 0 )) { sprintf(err, "%s: spacings (%g,%g,%g) for axes %d,%d,%d not all " "non-zero", me, xs, ys, zs, baseDim+0, baseDim+1, baseDim+2); biffAdd(GAGE, err); gageShapeReset(shape); return 1; } /* ------ learn lengths for bounding nrrd in bi-unit cube (set shape->volHalfLen[0,1,2] and shape->voxLen[0,1,2]) */ shape->spacing[0] = AIR_ABS(xs); shape->spacing[1] = AIR_ABS(ys); shape->spacing[2] = AIR_ABS(zs); maxLen = 0.0; for (ai=0; ai<=2; ai++) { num[ai] = (nrrdCenterNode == shape->center ? shape->size[ai]-1 : shape->size[ai]); shape->volHalfLen[ai] = num[ai]*shape->spacing[ai]; maxLen = AIR_MAX(maxLen, shape->volHalfLen[ai]); } /* Thu Dec 13 02:45:01 EST 2007 fixed long-standing bug in handling vols without full orientation info: spacing[ai] was never scaled to account for being crammed into the bi-unit cube!! */ for (ai=0; ai<=2; ai++) { shape->volHalfLen[ai] /= maxLen; shape->spacing[ai] = shape->voxLen[ai] = 2*shape->volHalfLen[ai]/num[ai]; } } /* ------ set transform matrices */ if (shape->fromOrientation) { /* find translation vector (we check above that spaceDim == 3) */ nrrdSpaceOriginGet(nin, orig); if (!( AIR_EXISTS(orig[0]) && AIR_EXISTS(orig[1]) && AIR_EXISTS(orig[2]) )) { /* don't have origin, so set it to come from the middle of volume */ ELL_3V_SET(orig, 0.0f, 0.0f, 0.0f); ELL_3V_SCALE_INCR(orig, -(shape->size[0] - 1.0f)*spcCalc[0]/2.0f, vecCalc[0]); ELL_3V_SCALE_INCR(orig, -(shape->size[1] - 1.0f)*spcCalc[1]/2.0f, vecCalc[1]); ELL_3V_SCALE_INCR(orig, -(shape->size[2] - 1.0f)*spcCalc[2]/2.0f, vecCalc[2]); } vecD[3] = 0; ELL_3V_SCALE(vecD, spcCalc[0], vecCalc[0]); ELL_4MV_COL0_SET(shape->ItoW, vecD); ELL_3V_SCALE(vecD, spcCalc[1], vecCalc[1]); ELL_4MV_COL1_SET(shape->ItoW, vecD); ELL_3V_SCALE(vecD, spcCalc[2], vecCalc[2]); ELL_4MV_COL2_SET(shape->ItoW, vecD); vecD[3] = 1; ELL_3V_COPY(vecD, orig); ELL_4MV_COL3_SET(shape->ItoW, vecD); /* fprintf(stderr, "%s: %g (%g,%g,%g)\n", me, spcCalc[0], vecCalc[0][0], vecCalc[0][1], vecCalc[0][2]); fprintf(stderr, "%s: %g (%g,%g,%g)\n", me, spcCalc[1], vecCalc[1][0], vecCalc[1][1], vecCalc[1][2]); fprintf(stderr, "%s: %g (%g,%g,%g)\n", me, spcCalc[2], vecCalc[2][0], vecCalc[2][1], vecCalc[2][2]); */ } else { ELL_3V_SET(vecC, 0, 0, 0); _gageShapeUnitItoW(shape, vecA, vecC); ELL_3V_SET(vecC, 1, 0, 0); _gageShapeUnitItoW(shape, vecB, vecC); ELL_3V_SUB(vecD, vecB, vecA); vecD[3] = 0; ELL_4MV_COL0_SET(shape->ItoW, vecD); ELL_3V_SET(vecC, 0, 1, 0); _gageShapeUnitItoW(shape, vecB, vecC); ELL_3V_SUB(vecD, vecB, vecA); vecD[3] = 0; ELL_4MV_COL1_SET(shape->ItoW, vecD); ELL_3V_SET(vecC, 0, 0, 1); _gageShapeUnitItoW(shape, vecB, vecC); ELL_3V_SUB(vecD, vecB, vecA); vecD[3] = 0; ELL_4MV_COL2_SET(shape->ItoW, vecD); vecA[3] = 1; ELL_4MV_COL3_SET(shape->ItoW, vecA); } ell_4m_inv_d(shape->WtoI, shape->ItoW); ELL_34M_EXTRACT(matA, shape->ItoW); ell_3m_inv_d(shape->ItoWSubInv, matA); ELL_3M_TRANSPOSE(shape->ItoWSubInvTransp, shape->ItoWSubInv); return 0; }
int main(int argc, char *argv[]) { char *me, *err, *outS; hestOpt *hopt=NULL; airArray *mop; int sx, sy, xi, yi, samp, version, whole, right; float *tdata; double p[3], xyz[3], q[4], len, hackcp=0, maxca; double ca, cp, mD[9], mRF[9], mRI[9], mT[9], hack; Nrrd *nten; mop = airMopNew(); me = argv[0]; hestOptAdd(&hopt, "n", "# samples", airTypeInt, 1, 1, &samp, "4", "number of glyphs along each edge of triangle"); hestOptAdd(&hopt, "p", "x y z", airTypeDouble, 3, 3, p, NULL, "location in quaternion quotient space"); hestOptAdd(&hopt, "ca", "max ca", airTypeDouble, 1, 1, &maxca, "0.8", "maximum ca to use at bottom edge of triangle"); hestOptAdd(&hopt, "r", NULL, airTypeInt, 0, 0, &right, NULL, "sample a right-triangle-shaped region, instead of " "a roughly equilateral triangle. "); hestOptAdd(&hopt, "w", NULL, airTypeInt, 0, 0, &whole, NULL, "sample the whole triangle of constant trace, " "instead of just the " "sixth of it in which the eigenvalues have the " "traditional sorted order. "); hestOptAdd(&hopt, "hack", "hack", airTypeDouble, 1, 1, &hack, "0.04", "this is a hack"); hestOptAdd(&hopt, "v", "version", airTypeInt, 1, 1, &version, "1", "which version of the Westin metrics to use to parameterize " "triangle; \"1\" for ISMRM 97, \"2\" for MICCAI 99"); hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, "-", "output file to save tensors into"); hestParseOrDie(hopt, argc-1, argv+1, NULL, me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); nten = nrrdNew(); airMopAdd(mop, nten, (airMopper)nrrdNuke, airMopAlways); if (!( 1 == version || 2 == version )) { fprintf(stderr, "%s: version must be 1 or 2 (not %d)\n", me, version); airMopError(mop); return 1; } if (right) { sx = samp; sy = (int)(1.0*samp/sqrt(3.0)); } else { sx = 2*samp-1; sy = samp; } if (nrrdMaybeAlloc_va(nten, nrrdTypeFloat, 4, AIR_CAST(size_t, 7), AIR_CAST(size_t, sx), AIR_CAST(size_t, sy), AIR_CAST(size_t, 3))) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: couldn't allocate output:\n%s\n", me, err); airMopError(mop); return 1; } q[0] = 1.0; q[1] = p[0]; q[2] = p[1]; q[3] = p[2]; len = ELL_4V_LEN(q); ELL_4V_SCALE(q, 1.0/len, q); washQtoM3(mRF, q); ELL_3M_TRANSPOSE(mRI, mRF); if (right) { _ra2t(nten, 0.00, 0.0, mRI, mRF, hack); _ra2t(nten, 0.10, 0.0, mRI, mRF, hack); _ra2t(nten, 0.10, 60.0, mRI, mRF, hack); _ra2t(nten, 0.20, 0.0, mRI, mRF, hack); _ra2t(nten, 0.20, 30.0, mRI, mRF, hack); _ra2t(nten, 0.20, 60.0, mRI, mRF, hack); _ra2t(nten, 0.30, 0.0, mRI, mRF, hack); _ra2t(nten, 0.30, 20.0, mRI, mRF, hack); _ra2t(nten, 0.30, 40.0, mRI, mRF, hack); _ra2t(nten, 0.30, 60.0, mRI, mRF, hack); _ra2t(nten, 0.40, 0.0, mRI, mRF, hack); _ra2t(nten, 0.40, 15.0, mRI, mRF, hack); _ra2t(nten, 0.40, 30.0, mRI, mRF, hack); _ra2t(nten, 0.40, 45.0, mRI, mRF, hack); _ra2t(nten, 0.40, 60.0, mRI, mRF, hack); _ra2t(nten, 0.50, 0.0, mRI, mRF, hack); _ra2t(nten, 0.50, 12.0, mRI, mRF, hack); _ra2t(nten, 0.50, 24.0, mRI, mRF, hack); _ra2t(nten, 0.50, 36.0, mRI, mRF, hack); _ra2t(nten, 0.50, 48.0, mRI, mRF, hack); _ra2t(nten, 0.50, 60.0, mRI, mRF, hack); /* _ra2t(nten, 0.60, 30.0, mRI, mRF, hack); */ _ra2t(nten, 0.60, 40.0, mRI, mRF, hack); _ra2t(nten, 0.60, 50.0, mRI, mRF, hack); _ra2t(nten, 0.60, 60.0, mRI, mRF, hack); /* _ra2t(nten, 0.70, 34.3, mRI, mRF, hack); */ /* _ra2t(nten, 0.70, 42.8, mRI, mRF, hack); */ _ra2t(nten, 0.70, 51.4, mRI, mRF, hack); _ra2t(nten, 0.70, 60.0, mRI, mRF, hack); /* _ra2t(nten, 0.80, 45.0, mRI, mRF, hack); */ _ra2t(nten, 0.80, 52.5, mRI, mRF, hack); _ra2t(nten, 0.80, 60.0, mRI, mRF, hack); _ra2t(nten, 0.90, 60.0, mRI, mRF, hack); _ra2t(nten, 1.00, 60.0, mRI, mRF, hack); /* _ra2t(nten, 0.000, 0.0, mRI, mRF, hack); _ra2t(nten, 0.125, 0.0, mRI, mRF, hack); _ra2t(nten, 0.125, 60.0, mRI, mRF, hack); _ra2t(nten, 0.250, 0.0, mRI, mRF, hack); _ra2t(nten, 0.250, 30.0, mRI, mRF, hack); _ra2t(nten, 0.250, 60.0, mRI, mRF, hack); _ra2t(nten, 0.375, 0.0, mRI, mRF, hack); _ra2t(nten, 0.375, 20.0, mRI, mRF, hack); _ra2t(nten, 0.375, 40.0, mRI, mRF, hack); _ra2t(nten, 0.375, 60.0, mRI, mRF, hack); _ra2t(nten, 0.500, 0.0, mRI, mRF, hack); _ra2t(nten, 0.500, 15.0, mRI, mRF, hack); _ra2t(nten, 0.500, 30.0, mRI, mRF, hack); _ra2t(nten, 0.500, 45.0, mRI, mRF, hack); _ra2t(nten, 0.500, 60.0, mRI, mRF, hack); _ra2t(nten, 0.625, 37.0, mRI, mRF, hack); _ra2t(nten, 0.625, 47.5, mRI, mRF, hack); _ra2t(nten, 0.625, 60.0, mRI, mRF, hack); _ra2t(nten, 0.750, 49.2, mRI, mRF, hack); _ra2t(nten, 0.750, 60.0, mRI, mRF, hack); _ra2t(nten, 0.875, 60.0, mRI, mRF, hack); _ra2t(nten, 1.000, 60.0, mRI, mRF, hack); */ nten->axis[1].spacing = 1; nten->axis[2].spacing = (sx-1)/(sqrt(3.0)*(sy-1)); nten->axis[3].spacing = 1; } else { for (yi=0; yi<samp; yi++) { if (whole) { ca = AIR_AFFINE(0, yi, samp-1, 0.0, 1.0); } else { ca = AIR_AFFINE(0, yi, samp-1, hack, maxca); hackcp = AIR_AFFINE(0, yi, samp-1, hack, 0); } for (xi=0; xi<=yi; xi++) { if (whole) { cp = AIR_AFFINE(0, xi, samp-1, 0.0, 1.0); } else { cp = AIR_AFFINE(0, xi, samp-1, hackcp, maxca-hack/2.0); } _cap2xyz(xyz, ca, cp, version, whole); /* fprintf(stderr, "%s: (%d,%d) -> (%g,%g) -> %g %g %g\n", me, yi, xi, ca, cp, xyz[0], xyz[1], xyz[2]); */ ELL_3M_IDENTITY_SET(mD); ELL_3M_DIAG_SET(mD, xyz[0], xyz[1], xyz[2]); ELL_3M_IDENTITY_SET(mT); ell_3m_post_mul_d(mT, mRI); ell_3m_post_mul_d(mT, mD); ell_3m_post_mul_d(mT, mRF); tdata = (float*)nten->data + 7*(2*(samp-1-xi) - (samp-1-yi) + (2*samp-1)*((samp-1-yi) + samp)); tdata[0] = 1.0; TEN_M2T(tdata, mT); } } nten->axis[1].spacing = 1; nten->axis[2].spacing = 1.5; nten->axis[3].spacing = 1; } if (nrrdSave(outS, nten, NULL)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: couldn't save output:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; }
int main(int argc, const char *argv[]) { const char *me; char *err, *outS; hestOpt *hopt=NULL; airArray *mop; int xi, yi, zi, samp; float *tdata; double clp[2], xyz[3], q[4], len; double mD[9], mRF[9], mRI[9], mT[9]; Nrrd *nten; mop = airMopNew(); me = argv[0]; hestOptAdd(&hopt, "n", "# samples", airTypeInt, 1, 1, &samp, "4", "number of samples along each edge of cube"); hestOptAdd(&hopt, "c", "cl cp", airTypeDouble, 2, 2, clp, NULL, "shape of tensor to use; \"cl\" and \"cp\" are cl1 " "and cp1 values, both in [0.0,1.0]"); hestOptAdd(&hopt, "o", "nout", airTypeString, 1, 1, &outS, "-", "output file to save tensors into"); hestParseOrDie(hopt, argc-1, argv+1, NULL, me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE); airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways); airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways); nten = nrrdNew(); airMopAdd(mop, nten, (airMopper)nrrdNuke, airMopAlways); _clp2xyz(xyz, clp); fprintf(stderr, "%s: want evals = %g %g %g\n", me, xyz[0], xyz[1], xyz[2]); if (nrrdMaybeAlloc_va(nten, nrrdTypeFloat, 4, AIR_CAST(size_t, 7), AIR_CAST(size_t, samp), AIR_CAST(size_t, samp), AIR_CAST(size_t, samp))) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: couldn't allocate output:\n%s\n", me, err); airMopError(mop); return 1; } ELL_3M_IDENTITY_SET(mD); ELL_3M_DIAG_SET(mD, xyz[0], xyz[1], xyz[2]); tdata = (float*)nten->data; for (zi=0; zi<samp; zi++) { for (yi=0; yi<samp; yi++) { for (xi=0; xi<samp; xi++) { q[0] = 1.0; q[1] = AIR_AFFINE(-0.5, (float)xi, samp-0.5, -1, 1); q[2] = AIR_AFFINE(-0.5, (float)yi, samp-0.5, -1, 1); q[3] = AIR_AFFINE(-0.5, (float)zi, samp-0.5, -1, 1); len = ELL_4V_LEN(q); ELL_4V_SCALE(q, 1.0/len, q); washQtoM3(mRF, q); ELL_3M_TRANSPOSE(mRI, mRF); ELL_3M_IDENTITY_SET(mT); ell_3m_post_mul_d(mT, mRI); ell_3m_post_mul_d(mT, mD); ell_3m_post_mul_d(mT, mRF); tdata[0] = 1.0; TEN_M2T(tdata, mT); tdata += 7; } } } if (nrrdSave(outS, nten, NULL)) { airMopAdd(mop, err = biffGetDone(NRRD), airFree, airMopAlways); fprintf(stderr, "%s: couldn't save output:\n%s\n", me, err); airMopError(mop); return 1; } airMopOkay(mop); return 0; }
static int csimDo(double tm[7], double tcov[21], double rm[3], double rv[3], Nrrd *ntbuff, tenEstimateContext *tec, double *dwibuff, double sigma, double bvalue, double B0, unsigned int NN, int randrot, double _tenOrig[7]) { char me[]="csimDo", err[BIFF_STRLEN]; double *tbuff; unsigned int II, taa, tbb, cc; if (!(ntbuff && ntbuff->data && 2 == ntbuff->dim && 7 == ntbuff->axis[0].size && NN == ntbuff->axis[1].size)) { sprintf(err, "%s: ntbuff not allocated for 2-by-%u array of %s", me, NN, airEnumStr(nrrdType, nrrdTypeDouble)); biffAdd(TEN, err); return 1; } /* find all tensors from simulated DWIs */ tbuff = AIR_CAST(double *, ntbuff->data); for (II=0; II<NN; II++) { double tenOrig[7], rotf[9], rotb[9], matA[9], matB[9], qq[4], tmp; ELL_3M_IDENTITY_SET(rotf); /* sssh warnings */ ELL_3M_IDENTITY_SET(rotb); /* sssh warnings */ if (randrot) { if (1) { double eval[3], evec[9], eps, ma[9], mb[9], rf[9], rb[9]; tenEigensolve_d(eval, evec, _tenOrig); airNormalRand(&eps, NULL); ell_aa_to_3m_d(rf, 0*eps/20, evec + 0); TEN_T_SCALE_INCR(_tenOrig, 0*eps/30, _tenOrig); TEN_T2M(ma, _tenOrig); ELL_3M_TRANSPOSE(rb, rf); ELL_3M_MUL(mb, ma, rf); ELL_3M_MUL(ma, rb, mb); TEN_M2T(_tenOrig, ma); } TEN_T2M(matA, _tenOrig); airNormalRand(qq+0, qq+1); airNormalRand(qq+2, qq+3); ELL_4V_NORM(qq, qq, tmp); ell_q_to_3m_d(rotf, qq); ELL_3M_TRANSPOSE(rotb, rotf); ELL_3M_MUL(matB, matA, rotf); ELL_3M_MUL(matA, rotb, matB); TEN_M2T(tenOrig, matA); } else { TEN_T_COPY(tenOrig, _tenOrig); } if (tenEstimate1TensorSimulateSingle_d(tec, dwibuff, sigma, bvalue, B0, tenOrig) || tenEstimate1TensorSingle_d(tec, tbuff, dwibuff)) { sprintf(err, "%s: trouble on exp %u/%u", me, II, NN); biffAdd(TEN, err); return 1; } if (randrot) { TEN_T2M(matA, tbuff); ELL_3M_MUL(matB, matA, rotb); ELL_3M_MUL(matA, rotf, matB); TEN_M2T(tbuff, matA); } /* else we leave tbuff as it is */ /* if (_tenOrig[0] > 0.5) { double tdiff[7]; TEN_T_SUB(tdiff, _tenOrig, tbuff); fprintf(stderr, "!%s: %g\n" " (%g) %g,%g,%g %g,%g %g\n" " (%g) %g,%g,%g %g,%g %g\n", me, TEN_T_NORM(tdiff), _tenOrig[0], _tenOrig[1], _tenOrig[2], _tenOrig[3], _tenOrig[4], _tenOrig[5], _tenOrig[6], tbuff[0], tbuff[1], tbuff[2], tbuff[3], tbuff[4], tbuff[5], tbuff[6]); } */ tbuff += 7; } /* find mean tensor, and mean R_i */ tbuff = AIR_CAST(double *, ntbuff->data); TEN_T_SET(tm, 0, 0, 0, 0, 0, 0, 0); ELL_3V_SET(rm, 0, 0, 0); for (II=0; II<NN; II++) { TEN_T_INCR(tm, tbuff); rm[0] += sqrt(_tenAnisoTen_d[tenAniso_S](tbuff)); rm[1] += _tenAnisoTen_d[tenAniso_FA](tbuff); rm[2] += _tenAnisoTen_d[tenAniso_Mode](tbuff); tbuff += 7; } rm[0] /= NN; rm[1] /= NN; rm[2] /= NN; TEN_T_SCALE(tm, 1.0/NN, tm); /* accumulate covariance tensor, and R_i variances */ for (cc=0; cc<21; cc++) { tcov[cc] = 0; } ELL_3V_SET(rv, 0, 0, 0); tbuff = AIR_CAST(double *, ntbuff->data); for (II=0; II<NN; II++) { double r[3]; r[0] = sqrt(_tenAnisoTen_d[tenAniso_S](tbuff)); r[1] = _tenAnisoTen_d[tenAniso_FA](tbuff); r[2] = _tenAnisoTen_d[tenAniso_Mode](tbuff); cc = 0; rv[0] += (r[0] - rm[0])*(r[0] - rm[0])/(NN-1); rv[1] += (r[1] - rm[1])*(r[1] - rm[1])/(NN-1); rv[2] += (r[2] - rm[2])*(r[2] - rm[2])/(NN-1); for (taa=0; taa<6; taa++) { for (tbb=taa; tbb<6; tbb++) { tcov[cc] += (10000*(tbuff[taa+1]-tm[taa+1]) *10000*(tbuff[tbb+1]-tm[tbb+1])/(NN-1)); cc++; } } tbuff += 7; } return 0; }