/* ** unlike the thing above, this one assumes that the quaternions in qq ** have already been normalized, and, that the sum of wght[] is 1.0 */ int ell_q_avgN_d(double mm[4], unsigned int *iterP, const double *qq, double *qlog, const double *wght, const unsigned int NN, const double eps, const unsigned int maxIter) { static const char me[]="ell_q_avgN_d"; double tmp, qdiv[4], elen; unsigned int ii, iter; /* iterP optional */ /* wght optional, to signify equal 1/NN weighting for all */ if (!( mm && qq )) { biffAddf(ELL, "%s: got NULL pointer", me); return 1; } if (!( eps >= 0 )) { biffAddf(ELL, "%s: need eps >= 0 (not %g)", me, eps); return 1; } /* initialize with euclidean mean */ ELL_4V_SET(mm, 0, 0, 0, 0); for (ii=0; ii<NN; ii++) { double ww; ww = wght ? wght[ii] : 1.0/NN; ELL_4V_SCALE_INCR(mm, ww, qq + 4*ii); } ELL_4V_NORM(mm, mm, tmp); iter = 0; do { double logavg[4], qexp[4]; /* take log of everyone */ for (ii=0; ii<NN; ii++) { ell_q_div_d(qdiv, mm, qq + 4*ii); /* div = mm^1 * qq[ii] */ ell_q_log_d(qlog + 4*ii, qdiv); } /* average, and find length */ ELL_4V_SET(logavg, 0, 0, 0, 0); for (ii=0; ii<NN; ii++) { double ww; ww = wght ? wght[ii] : 1.0/NN; ELL_4V_SCALE_INCR(logavg, ww, qlog + 4*ii); } elen = ELL_4V_LEN(logavg); /* use exp to put it back on S^3 */ ell_q_exp_d(qexp, logavg); ell_q_mul_d(mm, mm, qexp); iter++; } while ((!maxIter || iter < maxIter) && elen > eps); if (elen > eps) { biffAddf(ELL, "%s: still have error %g (> eps %g) after max %d iters", me, elen, eps, maxIter); return 1; } if (iterP) { *iterP = iter; } return 0; }
void ell_3m_to_q_d(double q[4], const double m[9]) { _ELL_M_TO_Q(double, 0, 1, 2, 3, 4, 5, 6, 7, 8); ELL_4V_NORM(q, q, len); }
void ell_q_to_4m_d(double m[16], const double q[4]) { double u[4], w=0.0, x=0.0, y=0.0, z=0.0; ELL_4V_NORM(u, q, w); _ELL_Q_TO_4M(double); }
int ell_q_avg4_d(double m[4], unsigned int *iterP, const double _q1[4], const double _q2[4], const double _q3[4], const double _q4[4], const double _wght[4], const double eps, const unsigned int maxIter) { static const char me[]="ell_q_avg4_d"; double N, elen, a[4], b[4], c[4], d[4], tmp[4], la[4], lb[4], lc[4], ld[4], u[4], wght[4]; unsigned int iter; /* *iterP optional */ if (!( m && _q1 && _q2 && _q3 && _q4 && _wght )) { biffAddf(ELL, "%s: got NULL pointer", me); return 1; } if (!( eps >= 0 )) { biffAddf(ELL, "%s: need eps >= 0 (not %g)", me, eps); return 1; } /* normalize (wrt L2) all given quaternions */ ELL_4V_NORM(a, _q1, N); ELL_4V_NORM(b, _q2, N); ELL_4V_NORM(c, _q3, N); ELL_4V_NORM(d, _q4, N); /* normalize (wrt L1) the given weights */ ELL_4V_COPY(wght, _wght); N = wght[0] + wght[1] + wght[2] + wght[3]; ELL_4V_SCALE(wght, 1.0/N, wght); /* initialize mean to normalized euclidean mean */ ELL_4V_SCALE_ADD4(m, wght[0], a, wght[1], b, wght[2], c, wght[3], d); ELL_4V_NORM(m, m, N); iter = 0; do { /* take log of everyone */ ell_q_div_d(tmp, m, a); ell_q_log_d(la, tmp); ell_q_div_d(tmp, m, b); ell_q_log_d(lb, tmp); ell_q_div_d(tmp, m, c); ell_q_log_d(lc, tmp); ell_q_div_d(tmp, m, d); ell_q_log_d(ld, tmp); /* average, and find length */ ELL_4V_SCALE_ADD4(u, wght[0], la, wght[1], lb, wght[2], lc, wght[3], ld); elen = ELL_4V_LEN(u); /* use exp to put it back on S^3 */ ell_q_exp_d(tmp, u); ell_q_mul_d(m, m, tmp); iter++; } while ((!maxIter || iter < maxIter) && elen > eps); if (elen > eps) { biffAddf(ELL, "%s: still have error %g after max %d iters", me, elen, maxIter); return 1; } if (iterP) { *iterP = iter; } 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; }
void ell_4m_to_q_d(double q[4], const double m[16]) { _ELL_M_TO_Q(double, 0, 1, 2, 4, 5, 6, 8, 9, 10); ELL_4V_NORM(q, q, len); }
int main(int argc, const char *argv[]) { const char *me; char *err, *outS; float p[3], q[4], mR[9], eval[3]={0,0,0}, scale[3], len, sh, cl, cp, qA, qB; float matA[16], matB[16], os, rad, AB[2]; hestOpt *hopt=NULL; airArray *mop; limnObject *obj; limnLook *look; int lookRod, lookSoid; int partIdx=-1; /* sssh */ int res, axis, sphere; FILE *file; me = argv[0]; hestOptAdd(&hopt, "sc", "scalings", airTypeFloat, 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, "sh", "superquad sharpness", airTypeFloat, 1, 1, &sh, "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(100, AIR_FALSE); 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_4V_SET(q, 1, p[0], p[1], p[2]); ELL_4V_NORM(q, q, len); ell_q_to_3m_f(mR, q); if (AIR_EXISTS(AB[0]) && AIR_EXISTS(AB[1])) { qA = AB[0]; qB = AB[1]; axis = 2; } else { ELL_3V_SCALE(scale, os, scale); ELL_3V_COPY(eval, scale); ELL_SORT3(eval[0], eval[1], eval[2], cl); cl = (eval[0] - eval[1])/(eval[0] + eval[1] + eval[2]); cp = 2*(eval[1] - eval[2])/(eval[0] + eval[1] + eval[2]); if (cl > cp) { axis = ELL_MAX3_IDX(scale[0], scale[1], scale[2]); qA = pow(1-cp, sh); qB = pow(1-cl, sh); } else { axis = ELL_MIN3_IDX(scale[0], scale[1], scale[2]); qA = pow(1-cl, sh); qB = pow(1-cp, sh); } /* fprintf(stderr, "eval = %g %g %g -> cl=%g %s cp=%g -> axis = %d\n", eval[0], eval[1], eval[2], cl, cl > cp ? ">" : "<", cp, axis); */ } if (sphere) { partIdx = limnObjectPolarSphereAdd(obj, lookSoid, 0, 2*res, res); } else { partIdx = limnObjectPolarSuperquadAdd(obj, lookSoid, axis, qA, qB, 2*res, res); } ELL_4M_IDENTITY_SET(matA); ELL_4M_SCALE_SET(matB, scale[0], scale[1], scale[2]); ell_4m_post_mul_f(matA, matB); ELL_43M_INSET(matB, mR); ell_4m_post_mul_f(matA, matB); limnObjectPartTransform(obj, partIdx, matA); if (rad) { partIdx = limnObjectCylinderAdd(obj, lookRod, 0, res); ELL_4M_IDENTITY_SET(matA); ELL_4M_SCALE_SET(matB, (1-eval[0])/2, rad, rad); ell_4m_post_mul_f(matA, matB); ELL_4M_TRANSLATE_SET(matB, (1+eval[0])/2, 0.0, 0.0); ell_4m_post_mul_f(matA, matB); limnObjectPartTransform(obj, partIdx, matA); partIdx = limnObjectCylinderAdd(obj, lookRod, 0, res); ELL_4M_IDENTITY_SET(matA); ELL_4M_SCALE_SET(matB, (1-eval[0])/2, rad, rad); ell_4m_post_mul_f(matA, matB); ELL_4M_TRANSLATE_SET(matB, -(1+eval[0])/2, 0.0, 0.0); ell_4m_post_mul_f(matA, matB); limnObjectPartTransform(obj, partIdx, matA); partIdx = limnObjectCylinderAdd(obj, lookRod, 1, res); ELL_4M_IDENTITY_SET(matA); ELL_4M_SCALE_SET(matB, rad, (1-eval[1])/2, rad); ell_4m_post_mul_f(matA, matB); ELL_4M_TRANSLATE_SET(matB, 0.0, (1+eval[1])/2, 0.0); ell_4m_post_mul_f(matA, matB); limnObjectPartTransform(obj, partIdx, matA); partIdx = limnObjectCylinderAdd(obj, lookRod, 1, res); ELL_4M_IDENTITY_SET(matA); ELL_4M_SCALE_SET(matB, rad, (1-eval[1])/2, rad); ell_4m_post_mul_f(matA, matB); ELL_4M_TRANSLATE_SET(matB, 0.0, -(1+eval[1])/2, 0.0); ell_4m_post_mul_f(matA, matB); limnObjectPartTransform(obj, partIdx, matA); partIdx = limnObjectCylinderAdd(obj, lookRod, 2, res); ELL_4M_IDENTITY_SET(matA); ELL_4M_SCALE_SET(matB, rad, rad, (1-eval[2])/2); ell_4m_post_mul_f(matA, matB); ELL_4M_TRANSLATE_SET(matB, 0.0, 0.0, (1+eval[2])/2); ell_4m_post_mul_f(matA, matB); limnObjectPartTransform(obj, partIdx, matA); partIdx = limnObjectCylinderAdd(obj, lookRod, 2, res); ELL_4M_IDENTITY_SET(matA); ELL_4M_SCALE_SET(matB, rad, rad, (1-eval[2])/2); ell_4m_post_mul_f(matA, matB); ELL_4M_TRANSLATE_SET(matB, 0.0, 0.0, -(1+eval[2])/2); ell_4m_post_mul_f(matA, matB); limnObjectPartTransform(obj, partIdx, matA); } 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; }
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; }
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; }
int main(int argc, const char *argv[]) { const char *me; hestOpt *hopt=NULL; airArray *mop; double _tt[6], tt[7], ss, pp[3], qq[4], rot[9], mat1[9], mat2[9], tmp, evalA[3], evecA[9], evalB[3], evecB[9]; int roots; mop = airMopNew(); me = argv[0]; hestOptAdd(&hopt, NULL, "m00 m01 m02 m11 m12 m22", airTypeDouble, 6, 6, _tt, NULL, "symmtric matrix coeffs"); hestOptAdd(&hopt, "p", "vec", airTypeDouble, 3, 3, pp, "0 0 0", "rotation as P vector"); hestOptAdd(&hopt, "s", "scl", airTypeDouble, 1, 1, &ss, "1.0", "scaling"); 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); ELL_6V_COPY(tt + 1, _tt); tt[0] = 1.0; TEN_T_SCALE(tt, ss, tt); ELL_4V_SET(qq, 1, pp[0], pp[1], pp[2]); ELL_4V_NORM(qq, qq, tmp); ell_q_to_3m_d(rot, qq); printf("%s: rot\n", me); printf(" %g %g %g\n", rot[0], rot[1], rot[2]); printf(" %g %g %g\n", rot[3], rot[4], rot[5]); printf(" %g %g %g\n", rot[6], rot[7], rot[8]); TEN_T2M(mat1, tt); ell_3m_mul_d(mat2, rot, mat1); ELL_3M_TRANSPOSE_IP(rot, tmp); ell_3m_mul_d(mat1, mat2, rot); TEN_M2T(tt, mat1); printf("input matrix = \n %g %g %g\n %g %g\n %g\n", tt[1], tt[2], tt[3], tt[4], tt[5], tt[6]); printf("================== tenEigensolve_d ==================\n"); roots = tenEigensolve_d(evalA, evecA, tt); printf("%s roots\n", airEnumStr(ell_cubic_root, roots)); testeigen(tt, evalA, evecA); printf("================== new eigensolve ==================\n"); roots = evals(evalB, tt[1], tt[2], tt[3], tt[4], tt[5], tt[6]); printf("%s roots: %g %g %g\n", airEnumStr(ell_cubic_root, roots), evalB[0], evalB[1], evalB[2]); roots = evals_evecs(evalB, evecB, tt[1], tt[2], tt[3], tt[4], tt[5], tt[6]); printf("%s roots\n", airEnumStr(ell_cubic_root, roots)); testeigen(tt, evalB, evecB); 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; }