void _ell_align3_d(double v[9]) { double d0, d1, d2; int Mi, ai, bi; d0 = ELL_3V_DOT(v+0, v+0); d1 = ELL_3V_DOT(v+3, v+3); d2 = ELL_3V_DOT(v+6, v+6); Mi = ELL_MAX3_IDX(d0, d1, d2); ai = (Mi + 1) % 3; bi = (Mi + 2) % 3; /* lop A */ if (ELL_3V_DOT(v+3*Mi, v+3*ai) < 0) { ELL_3V_SCALE(v+3*ai, -1, v+3*ai); } if (ELL_3V_DOT(v+3*Mi, v+3*bi) < 0) { ELL_3V_SCALE(v+3*bi, -1, v+3*bi); } /* lob B */ /* we can't guarantee that dot(v+3*ai,v+3*bi) > 0 . . . */ }
int fixproj(Nrrd *nproj[3], Nrrd *nvol) { char me[]="fixproj", err[BIFF_STRLEN]; airArray *mop; Nrrd *ntmp[3], *nt; int sz[3], ii, map[3], h[3], E, mi; size_t rsz[3]; double vec[3][3], dot[3], sp[3], parm[NRRD_KERNEL_PARMS_NUM]; mop = airMopNew(); if (!( ELL_3V_EXISTS(nvol->axis[0].spaceDirection) && ELL_3V_EXISTS(nvol->axis[1].spaceDirection) && ELL_3V_EXISTS(nvol->axis[2].spaceDirection) )) { sprintf(err, "%s: space directions don't exist for all 3 axes", me); biffAdd(NINSPECT, err); airMopError(mop); return 1; } airMopAdd(mop, nt = nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, ntmp[0] = nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, ntmp[1] = nrrdNew(), (airMopper)nrrdNuke, airMopAlways); airMopAdd(mop, ntmp[2] = nrrdNew(), (airMopper)nrrdNuke, airMopAlways); /* RL AP SI */ ELL_3V_SET(vec[0], 1, 0, 0); ELL_3V_SET(vec[1], 0, 1, 0); ELL_3V_SET(vec[2], 0, 0, 1); for (ii=0; ii<3; ii++) { dot[0] = ELL_3V_DOT(vec[ii], nvol->axis[0].spaceDirection); dot[1] = ELL_3V_DOT(vec[ii], nvol->axis[1].spaceDirection); dot[2] = ELL_3V_DOT(vec[ii], nvol->axis[2].spaceDirection); dot[0] = AIR_ABS(dot[0]); dot[1] = AIR_ABS(dot[1]); dot[2] = AIR_ABS(dot[2]); map[ii] = ELL_MAX3_IDX(dot[0], dot[1], dot[2]); } ELL_3V_SET(h, 1, 0, 0); E = 0; for (ii=0; ii<3; ii++) { if (h[map[ii]] != map[h[ii]]) { if (!E) E |= nrrdAxesSwap(ntmp[ii], nproj[map[ii]], 1, 2); } else { if (!E) E |= nrrdCopy(ntmp[ii], nproj[map[ii]]); } } if (E) { sprintf(err, "%s: trouble with nrrd operations", me); biffMove(NINSPECT, err, NRRD); airMopError(mop); return 1; } E = 0; if (nvol->axis[map[0]].spaceDirection[0] > 0) { if (!E) E |= nrrdFlip(nt, ntmp[1], 0+1); if (!E) E |= nrrdCopy(ntmp[1], nt); if (!E) E |= nrrdFlip(nt, ntmp[2], 0+1); if (!E) E |= nrrdCopy(ntmp[2], nt); } if (nvol->axis[map[1]].spaceDirection[1] > 0) { if (!E) E |= nrrdFlip(nt, ntmp[0], 0+1); if (!E) E |= nrrdCopy(ntmp[0], nt); if (!E) E |= nrrdFlip(nt, ntmp[2], 1+1); if (!E) E |= nrrdCopy(ntmp[2], nt); } if (nvol->axis[map[2]].spaceDirection[2] > 0) { if (!E) E |= nrrdFlip(nt, ntmp[0], 1+1); if (!E) E |= nrrdCopy(ntmp[0], nt); if (!E) E |= nrrdFlip(nt, ntmp[1], 1+1); if (!E) E |= nrrdCopy(ntmp[1], nt); } if (E) { sprintf(err, "%s: trouble with nrrd operations", me); biffMove(NINSPECT, err, NRRD); airMopError(mop); return 1; } for (ii=0; ii<3; ii++) { sz[ii] = nvol->axis[map[ii]].size; sp[ii] = ELL_3V_LEN(nvol->axis[map[ii]].spaceDirection); } mi = ELL_MIN3_IDX(sp[0], sp[1], sp[2]); sz[0] = (int)(sz[0]*sp[0]/sp[mi]); sz[1] = (int)(sz[1]*sp[1]/sp[mi]); sz[2] = (int)(sz[2]*sp[2]/sp[mi]); parm[0] = 1; ELL_3V_SET(rsz, 3, sz[1], sz[2]); nrrdSimpleResample(nproj[0], ntmp[0], nrrdKernelBox, parm, rsz, NULL); ELL_3V_SET(rsz, 3, sz[0], sz[2]); nrrdSimpleResample(nproj[1], ntmp[1], nrrdKernelBox, parm, rsz, NULL); ELL_3V_SET(rsz, 3, sz[0], sz[1]); nrrdSimpleResample(nproj[2], ntmp[2], nrrdKernelBox, parm, rsz, NULL); airMopOkay(mop); return 0; }
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; }
/* Calculates 2 new centroids (and a new segmentation) from distances between Q-balls and centroids, returns true if segmentation changed */ int _tenCalccent2(const int gradcount, const double qpoints[], const double dists[], double centroid[6], unsigned int seg[]) { #if 0 /* HEY: Attempt to implement better line-adding by adding outerproducts of points and estimating major eigenvector afterwards */ int i,changed=AIR_FALSE; double sum0[9],sum1[9],mat[9], eval[3],evec[9]; ELL_3M_ZERO_SET( sum0 ); ELL_3M_ZERO_SET( sum1 ); for( i = 0; i < gradcount; i++ ) { if( dists[i] < dists[gradcount+i] ) { ELL_3MV_OUTER( mat, qpoints +3*i, qpoints +3*i ); ELL_3M_ADD2( sum0, sum0, mat ); changed = changed || (seg[i] != 0); seg[i] = 0; } else { ELL_3MV_OUTER( mat, qpoints +3*i +gradcount, qpoints +3*i +gradcount ); ELL_3M_ADD2( sum1, sum1, mat ); changed = changed || (seg[i] != 1); seg[i] = 1; } } ell_3m_eigensolve_d( eval, evec, sum0, 0 ); ELL_3V_COPY( centroid, evec + 3*ELL_MAX3_IDX( eval[0], eval[1], eval[2] ) ); /* ELL_3V_SCALE( centroid, ELL_3V_LEN( centroid ), centroid ); */ ell_3m_eigensolve_d( eval, evec, sum1, 0 ); ELL_3V_COPY( centroid +3, evec + 3*ELL_MAX3_IDX( eval[0], eval[1], eval[2] ) ); /* ELL_3V_SCALE( centroid +3, ELL_3V_LEN( centroid ), centroid +3); Normalize */ return changed; #endif int i, sign, seg0count=0, seg1count=0, changed=AIR_FALSE; double oldcentroid[6], diff[3], sum[3]; memcpy( oldcentroid, centroid, 6 * sizeof( double )); for( i = 0; i < gradcount; i++ ) { if( dists[ 0*gradcount +i] < dists[1*gradcount +i] ) { /* Try to resolve sign so that centroid do not end up as all 0 */ /* Choose signs so that the point lies "on the same side" as */ /* the previous centroid. */ diff[0] = oldcentroid[0] - qpoints[3*i +0]; diff[1] = oldcentroid[1] - qpoints[3*i +1]; diff[2] = oldcentroid[2] - qpoints[3*i +2]; sum[0] = oldcentroid[0] + qpoints[3*i +0]; sum[1] = oldcentroid[1] + qpoints[3*i +1]; sum[2] = oldcentroid[2] + qpoints[3*i +2]; sign = (diff[0]*diff[0] + diff[1]*diff[1] + diff[2]*diff[2]) < (sum[0]*sum[0] + sum[1]*sum[1] + sum[2]*sum[2]) ? -1 : +1; changed = changed || (seg[i] != 0); seg[i] = 0; centroid[0] += sign * qpoints[3*i +0]; centroid[1] += sign * qpoints[3*i +1]; centroid[2] += sign * qpoints[3*i +2]; seg0count++; } else { diff[0] = oldcentroid[3+0] - qpoints[3*i +0]; diff[1] = oldcentroid[3+1] - qpoints[3*i +1]; diff[2] = oldcentroid[3+2] - qpoints[3*i +2]; sum[0] = oldcentroid[3+0] + qpoints[3*i +0]; sum[1] = oldcentroid[3+1] + qpoints[3*i +1]; sum[2] = oldcentroid[3+2] + qpoints[3*i +2]; sign = (diff[0]*diff[0] + diff[1]*diff[1] + diff[2]*diff[2]) < (sum[0]*sum[0] + sum[1]*sum[1] + sum[2]*sum[2]) ? -1 : +1; changed = changed || (seg[i] != 1); seg[i] = 1; centroid[3+0] += sign * qpoints[3*i +0]; centroid[3+1] += sign * qpoints[3*i +1]; centroid[3+2] += sign * qpoints[3*i +2]; seg1count++; } } centroid[0] /= seg0count; centroid[1] /= seg0count; centroid[2] /= seg0count; centroid[3+0] /= seg1count; centroid[3+1] /= seg1count; centroid[3+2] /= seg1count; /* printf("cent = %f %f %f %f %f %f\n", centroid[0],centroid[1],centroid[2],centroid[3],centroid[4],centroid[5] ); */ /* Should give error if any segment contains less than 6 elements, i.e. if( seg0count < 6 || seg1count < 6 ), since that would imply that a tensor cannot be computed for that segment. */ return changed; }