예제 #1
0
파일: tg.c 프로젝트: BRAINSia/teem
void
washQtoM3(double m[9], double q[4]) {
  double p[4], w, x, y, z, len;

  ELL_4V_COPY(p, q);
  len = ELL_4V_LEN(p);
  ELL_4V_SCALE(p, 1.0/len, p);
  w = p[0];
  x = p[1];
  y = p[2];
  z = p[3];
  /* mathematica work implies that we should be
     setting ROW vectors here */
  ELL_3V_SET(m+0,
             1 - 2*(y*y + z*z),
             2*(x*y - w*z),
             2*(x*z + w*y));
  ELL_3V_SET(m+3,
             2*(x*y + w*z),
             1 - 2*(x*x + z*z),
             2*(y*z - w*x));
  ELL_3V_SET(m+6,
             2*(x*z - w*y),
             2*(y*z + w*x),
             1 - 2*(x*x + y*y));
}
예제 #2
0
파일: qglox.c 프로젝트: CIBC-Internal/teem
/* returns the index into unitq[] of the quaternion that led to the
   right alignment.  If it was already aligned, this will be 0,
   because unitq[0] is the identity quaternion */
int
_tenQGL_q_align(double qOut[4], const double qRef[4], const double qIn[4]) {
  unsigned int ii, maxDotIdx;
  double unitq[8][4] = {{+1, 0, 0, 0},
                        {-1, 0, 0, 0},
                        {0, +1, 0, 0},
                        {0, -1, 0, 0},
                        {0, 0, +1, 0},
                        {0, 0, -1, 0},
                        {0, 0, 0, +1},
                        {0, 0, 0, -1}};
  double dot[8], qInMul[8][4], maxDot;

  for (ii=0; ii<8; ii++) {
    ell_q_mul_d(qInMul[ii], qIn, unitq[ii]);
    dot[ii] = ELL_4V_DOT(qRef, qInMul[ii]);
  }
  maxDotIdx = 0;
  maxDot = dot[maxDotIdx];
  for (ii=1; ii<8; ii++) {
    if (dot[ii] > maxDot) {
      maxDotIdx = ii;
      maxDot = dot[maxDotIdx];
    }
  }
  ELL_4V_COPY(qOut, qInMul[maxDotIdx]);
  return maxDotIdx;
}
예제 #3
0
파일: quat.c 프로젝트: CIBC-Internal/teem
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;
}
예제 #4
0
파일: wheel.c 프로젝트: rblake/seg3d2
int
main(int argc, char *argv[]) {
  char *me, *outS;
  hestOpt *hopt;
  hestParm *hparm;
  airArray *mop;
  
  double tval[6], ABC[3], geom[3], rnth[3], RA, norm, mu2, tmpr=0, tmpa=0,
    xroot[3], yroot[3], bbox[4], htick, htth, psc;
  wheelPS wps;
  int correct, labels, drawRA;

  me = argv[0];
  mop = airMopNew();
  hparm = hestParmNew();
  hparm->elideMultipleNonExistFloatDefault = AIR_TRUE;
  hopt = NULL;
  airMopAdd(mop, hparm, (airMopper)hestParmFree, airMopAlways);
  hestOptAdd(&hopt, "t", "a b c d e f", airTypeDouble, 6, 6, tval,
             "nan nan nan nan nan nan nan",
             "six values of symmetric tensors");
  hestOptAdd(&hopt, "ABC", "A B C", airTypeDouble, 3, 3, ABC, "nan nan nan",
             "directly give coefficients of cubic polynomial "
             "(and override info from \"-t\")");
  hestOptAdd(&hopt, "g", "c rad th", airTypeDouble, 3, 3, geom, "nan nan nan",
             "directly give center, radius, and angle (in degrees) of wheel "
             "(and override info from \"-t\" and \"-ABC\"");
  hestOptAdd(&hopt, "p", "RA norm th", airTypeDouble, 3, 3, rnth, 
             "nan nan nan",
             "directly give RA, norm, and angle (in degrees) of tensor "
             "(and override info from \"-t\", \"-ABC\", and \"-geom\"");
  hestOptAdd(&hopt, "correct", NULL, airTypeInt, 0, 0, &correct, NULL,
             "when using \"-g\", be honest about what the estimated "
             "acos(sqrt(2)*skew)/3 is going to be");
  hestOptAdd(&hopt, "labels", NULL, airTypeInt, 0, 0, &labels, NULL,
             "put little labels on things; fix with psfrag in LaTeX");
  hestOptAdd(&hopt, "RA", NULL, airTypeInt, 0, 0, &drawRA, NULL,
             "draw extra geometry associated with RA");
  hestOptAdd(&hopt, "htick", "pos", airTypeDouble, 1, 1, &htick, "nan",
             "location of single tick mark on horizontal axis");
  hestOptAdd(&hopt, "htth", "thick", airTypeDouble, 1, 1, &htth, "3",
             "thickness of horizontal tick");
  hestOptAdd(&hopt, "bb", "bbox", airTypeDouble, 4, 4, bbox, 
             "nan nan nan nan", "bounding box, in world space around the "
             "region of the graph that should be drawn to EPS");
  hestOptAdd(&hopt, "ysc", "scale", airTypeDouble, 1, 1, &(wps.yscale), "0.5",
             "scaling on Y axis for drawing graph of characteristic "
             "polynomial, or \"0\" to turn this off.");
  hestOptAdd(&hopt, "psc", "scale", airTypeDouble, 1, 1, &psc, "100",
             "scaling from world space to PostScript points");
  hestOptAdd(&hopt, "o", "filename", airTypeString, 1, 1, &outS, "-",
             "file to write EPS output to");
  hestParseOrDie(hopt, argc-1, argv+1, hparm,
                 me, wheelInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE);
  airMopAdd(mop, hopt, (airMopper)hestOptFree, airMopAlways);
  airMopAdd(mop, hopt, (airMopper)hestParseFree, airMopAlways);

  if (!(wps.file = airFopen(outS, stdout, "wb"))) {
    fprintf(stderr, "%s: couldn't open output file\n", me);
    airMopError(mop); return 1;
  }
  airMopAdd(mop, wps.file, (airMopper)airFclose, airMopAlways);

  if (AIR_EXISTS(rnth[0])) {
    RA = rnth[0];
    norm = rnth[1];
    mu2 = (norm*norm/3)*(2*RA*RA/(1 + 2*RA*RA));
    geom[0] = sqrt(mu2)/(sqrt(2)*RA);
    geom[1] = sqrt(2*mu2);
    geom[2] = rnth[2];
    wheelGeomToRoot(xroot, yroot, geom);
    wheelGeomToABC(ABC, geom[0], geom[1], geom[2]);
  } else if (AIR_EXISTS(geom[0])) {
    wheelGeomToRoot(xroot, yroot, geom);
    if (correct) {
      tval[0] = xroot[0];
      tval[1] = tval[2] = 0;
      tval[3] = xroot[1];
      tval[4] = 0;
      tval[5] = xroot[2];
      wheelTenToGeom(geom,
                     tval[0], tval[1], tval[2], tval[3], tval[4], tval[5]);
      wheelGeomToRoot(xroot, yroot, geom);
    }
    wheelGeomToABC(ABC, geom[0], geom[1], geom[2]);
    wheelGeomToRNTH(rnth, geom[0], geom[1], geom[2]);
  } else if (AIR_EXISTS(ABC[0])) {
    wheelABCToGeom(geom, ABC[0], ABC[1], ABC[2]);
    wheelGeomToRNTH(rnth, geom[0], geom[1], geom[2]);
    wheelGeomToRoot(xroot, yroot, geom);
  } else {
    wheelTenToGeom(geom, tval[0], tval[1], tval[2], tval[3], tval[4], tval[5]);
    wheelGeomToRoot(xroot, yroot, geom);
    wheelGeomToRNTH(rnth, geom[0], geom[1], geom[2]);
    wheelGeomToABC(ABC, geom[0], geom[1], geom[2]);
  }
  fprintf(stderr, "%s: RNTH: %g %g %g\n", me, rnth[0], rnth[1], rnth[2]);
  fprintf(stderr, "%s: ABC: %g %g %g\n", me, ABC[0], ABC[1], ABC[2]);
  fprintf(stderr, "%s: xroot: %g %g %g\n",
          me, xroot[0], xroot[1], xroot[2]);
  fprintf(stderr, "%s: geom: %g %g %g\n", me, geom[0], geom[1], geom[2]);

  if (!AIR_EXISTS(bbox[0])) {
    bbox[0] = geom[0] - 1.2*geom[1];
    bbox[1] = - 1.2*geom[1];
    bbox[2] = geom[0] + 1.2*geom[1];
    bbox[3] = + 1.2*geom[1];
    fprintf(stderr, "%s: bbox %g %g %g %g\n", me,
            bbox[0], bbox[1], bbox[2], bbox[3]);
  }
  wps.psc = psc;
  ELL_4V_COPY(wps.bbox, bbox);
  wheelPreamble(&wps);

  /* graph */
  if (wps.yscale) {
    wheelWidth(&wps, 4);
    wheelGray(&wps, 0.5);
    wheelGraph(&wps, xroot[0], xroot[1], xroot[2]);
  }

  /* axis */
  wheelWidth(&wps, 2);
  wheelGray(&wps, 0.0);
  wheelLine(&wps, bbox[0], 0, bbox[2], 0);

  /* circle */
  wheelWidth(&wps, 3);
  wheelCircle(&wps, geom[0], 0, geom[1]);

  /* spokes */
  wheelWidth(&wps, 4);
  wheelLine(&wps, geom[0], 0, xroot[0], yroot[0]);
  wheelLine(&wps, geom[0], 0, xroot[1], yroot[1]);
  wheelLine(&wps, geom[0], 0, xroot[2], yroot[2]);
  
  /* dots at spoke ends */
  wheelDot(&wps, xroot[0], yroot[0], 0.025*geom[1]);
  wheelDot(&wps, xroot[1], yroot[1], 0.025*geom[1]);
  wheelDot(&wps, xroot[2], yroot[2], 0.025*geom[1]);

  /* lines from dots to roots */
  wheelWidth(&wps, 2);
  fprintf(wps.file, "gsave\n");
  fprintf(wps.file, "[2 4] 0 setdash\n");
  wheelLine(&wps, xroot[0], 0, xroot[0], yroot[0]);
  wheelLine(&wps, xroot[1], 0, xroot[1], yroot[1]);
  wheelLine(&wps, xroot[2], 0, xroot[2], yroot[2]);
  fprintf(wps.file, "grestore\n");

  /* tickmarks */
  wheelWidth(&wps, 6);
  wheelLine(&wps, xroot[0], -0.02*geom[1], xroot[0], 0.02*geom[1]);
  wheelLine(&wps, xroot[1], -0.02*geom[1], xroot[1], 0.02*geom[1]);
  wheelLine(&wps, xroot[2], -0.02*geom[1], xroot[2], 0.02*geom[1]);
  if (AIR_EXISTS(htick)) {
    wheelWidth(&wps, htth);
    wheelLine(&wps, htick, -0.04, htick, 0.04);
  }

  /* RA angle */
  if (drawRA) {
    wheelWidth(&wps, 3);
    wheelLine(&wps, 0.0, 0.0, geom[0], geom[1]);
    wheelWidth(&wps, 2);
    fprintf(wps.file, "gsave\n");
    fprintf(wps.file, "[2 4] 0 setdash\n");
    wheelLine(&wps, geom[0], geom[1], geom[0], 0);
    fprintf(wps.file, "grestore\n");
  }

  /* labels, if wanted */
  if (labels) {
    fprintf(wps.file, "/Helvetica findfont 20 scalefont setfont\n");
    wheelLabel(&wps, geom[0], 0, "center");
    wheelLine(&wps, geom[0], -0.02*geom[1], geom[0], 0.02*geom[1]);
    wheelLabel(&wps, (geom[0] + xroot[0])/1.8, yroot[0]/1.8, "radius");
    wheelWidth(&wps, 2);
    wheelArc(&wps, geom[0], 0, geom[1]/2, 0, geom[2]);
    wheelLabel(&wps, geom[0] + geom[1]*cos(AIR_PI*geom[2]/180/2)/2.5, 
               geom[1]*sin(AIR_PI*geom[2]/180/2)/2.5, "theta");
    if (drawRA) {
      tmpr = sqrt(geom[0]*geom[0] + geom[1]*geom[1]);
      tmpa = atan(2.0*rnth[0]);
      wheelWidth(&wps, 2);
      wheelArc(&wps, 0, 0, 0.2*tmpr, 0, 180*tmpa/AIR_PI);
      wheelLabel(&wps, 0.2*tmpr*cos(tmpa/2), 0.2*tmpr*sin(tmpa/2), "phi");
    }
    wheelLabel(&wps, xroot[0], yroot[0], "spoke0");
    wheelLabel(&wps, xroot[1], yroot[1], "spoke-");
    wheelLabel(&wps, xroot[2], yroot[2], "spoke+");
    wheelLabel(&wps, xroot[0], 0, "root0");
    wheelLabel(&wps, xroot[1], 0, "root-");
    wheelLabel(&wps, xroot[2], 0, "root+");
  }

  wheelEpilog(&wps);

  airMopOkay(mop);
  exit(0);
}
예제 #5
0
파일: cam.c 프로젝트: BRAINSia/teem
/*
******** limnCameraUpdate()
**
** sets in cam: W2V, V2W, U, V, N, vspNeer, vspFaar, vspDist
** and, if fov and aspect are set, this also sets uRange and vRange
**
** This does use biff to describe problems with camera settings
*/
int
limnCameraUpdate(limnCamera *cam) {
  static const char me[] = "limnCameraUpdate";
  double len, bb[4], uu[4], vv[4], nn[4], TT[16], RR[16];

  if (!cam) {
    biffAddf(LIMN, "%s: got NULL pointer", me);
    return 1;
  }

  ELL_4V_SET(uu, 0, 0, 0, 0);
  ELL_4V_SET(vv, 0, 0, 0, 0);
  ELL_4V_SET(nn, 0, 0, 0, 0);
  ELL_4V_SET(bb, 0, 0, 0, 1);
  ELL_3V_SUB(nn, cam->at, cam->from);
  len = ELL_3V_LEN(nn);
  if (!len) {
    biffAddf(LIMN, "%s: cam->at (%g,%g,%g) == cam->from", me,
             cam->at[0], cam->at[1], cam->at[2]);
    return 1;
  }
  if (cam->atRelative) {
    /* ctx->cam->{neer,dist} are "at" relative */
    cam->vspNeer = cam->neer + len;
    cam->vspFaar = cam->faar + len;
    cam->vspDist = cam->dist + len;
  }
  else {
    /* ctx->cam->{neer,dist} are eye relative */
    cam->vspNeer = cam->neer;
    cam->vspFaar = cam->faar;
    cam->vspDist = cam->dist;
  }
  if (!(cam->vspNeer > 0 && cam->vspDist > 0 && cam->vspFaar > 0)) {
    biffAddf(LIMN, "%s: eye-relative near (%g), dist (%g), or far (%g) <= 0",
             me, cam->vspNeer, cam->vspDist, cam->vspFaar);
    return 1;
  }
  if (!(cam->vspNeer <= cam->vspFaar)) {
    biffAddf(LIMN, "%s: eye-relative near (%g) further than far (%g)",
             me, cam->vspNeer, cam->vspFaar);
    return 1 ;
  }
  if (AIR_EXISTS(cam->fov)) {
    if (!( AIR_IN_OP(0.0, cam->fov, 180.0) )) {
      biffAddf(LIMN, "%s: cam->fov (%g) not in valid range between 0 and 180",
               me, cam->fov);
      return 1 ;
    }
    if (!AIR_EXISTS(cam->aspect)) {
      biffAddf(LIMN, "%s: cam->fov set, but cam->aspect isn't", me);
      return 1;
    }
    /* "fov" is half vertical angle */
    cam->vRange[0] = -tan(cam->fov*AIR_PI/360)*(cam->vspDist);
    cam->vRange[1] = -cam->vRange[0];
    cam->uRange[0] = cam->vRange[0]*(cam->aspect);
    cam->uRange[1] = -cam->uRange[0];
  }
  /* else cam->fov isn't set, but we're not going to complain if
     uRange and vRange aren't both set ... */

  ELL_3V_SCALE(nn, 1.0/len, nn);
  ELL_3V_CROSS(uu, nn, cam->up);
  len = ELL_3V_LEN(uu);
  if (!len) {
    biffAddf(LIMN, "%s: cam->up is co-linear with view direction", me);
    return 1 ;
  }
  ELL_3V_SCALE(uu, 1.0/len, uu);

  if (cam->rightHanded) {
    ELL_3V_CROSS(vv, nn, uu);
  }
  else {
    ELL_3V_CROSS(vv, uu, nn);
  }

  ELL_4V_COPY(cam->U, uu);
  ELL_4V_COPY(cam->V, vv);
  ELL_4V_COPY(cam->N, nn);
  ELL_4M_TRANSLATE_SET(TT, -cam->from[0], -cam->from[1], -cam->from[2]);
  ELL_4M_ROWS_SET(RR, uu, vv, nn, bb);
  ELL_4M_MUL(cam->W2V, RR, TT);
  ell_4m_inv_d(cam->V2W, cam->W2V);

  return 0;
}
예제 #6
0
int
_pullPointProcess(pullTask *task, pullBin *bin, pullPoint *point) {
  char me[]="pullPointProcess", err[BIFF_STRLEN];
  double energyOld, energyNew, force[4], distLimit, posOld[4],
    capvec[3], caplen, capscl;  /* related to capping distance traveled
                                   in a per-iteration way */
  int stepBad, giveUp;

  if (!point->stepEnergy) {
    sprintf(err, "%s: whoa, point %u step is zero!", me, point->idtag);
    biffAdd(PULL, err); return 1;
  }

  if (0 && 162 == point->idtag) {
    fprintf(stderr, "!%s: !!!!!!!!!!!! praying ON!\n", me);
    _pullPraying = AIR_TRUE;
    ELL_3V_COPY(_pullPrayCorner[0][0], point->pos);
    _pullPrayCorner[0][0][2] -= 1;
    ELL_3V_COPY(_pullPrayCorner[0][1], point->pos);
    _pullPrayCorner[0][1][2] += 1;
    fprintf(stderr, "!%s: corner[0][0] = %g %g %g\n", me, 
            _pullPrayCorner[0][0][0],
            _pullPrayCorner[0][0][1],
            _pullPrayCorner[0][0][2]);
    fprintf(stderr, "!%s: corner[0][1] = %g %g %g\n", me, 
            _pullPrayCorner[0][1][0],
            _pullPrayCorner[0][1][1],
            _pullPrayCorner[0][1][2]);
  } else {
    _pullPraying = AIR_FALSE;
  }

  if (_pullPraying) {
    fprintf(stderr, "%s: =============================== (%u) hi @ %g %g %g\n",
            me, point->idtag, point->pos[0], point->pos[1], point->pos[2]);
  }
  energyOld = _pullPointEnergyTotal(task, bin, point, force);
  if (_pullPraying) {
    fprintf(stderr, "!%s: =================== point %u has:\n "
            "     energy = %g ; ndist = %g, force %g %g %g %g\n", me,
            point->idtag, energyOld, point->neighDist,
            force[0], force[1], force[2], force[3]);
  }
  if (!( AIR_EXISTS(energyOld) && ELL_4V_EXISTS(force) )) {
    sprintf(err, "%s: point %u non-exist energy or force", me, point->idtag);
    biffAdd(PULL, err); return 1;
  }

  if (task->pctx->constraint) {
    /* we have a constraint, so do something to get the force more
       tangential to the constriant surface */
    double proj[9], pfrc[3];
    _pullConstraintTangent(task, point, proj);
    ELL_3MV_MUL(pfrc, proj, force);
    ELL_3V_COPY(force, pfrc);
  }

  point->status = 0; /* reset status bitflag */
  ELL_4V_COPY(posOld, point->pos);
  _pullPointHistInit(point);
  _pullPointHistAdd(point, pullCondOld);
  
  if (!ELL_4V_LEN(force)) {
    /* this particle has no reason to go anywhere; we're done with it */
    point->energy = energyOld;
    return 0;
  }
  distLimit = _pullDistLimit(task, point);

  /* find capscl */
  ELL_3V_SCALE(capvec, point->stepEnergy, force);
  caplen = ELL_3V_LEN(capvec);
  if (caplen > distLimit) {
    capscl = distLimit/caplen;
  } else {
    capscl = 1;
  }
  if (_pullPraying) {
    fprintf(stderr, "%s: ======= (%u) capscl = %g\n", me,
            point->idtag, capscl);
  }

  if (_pullPraying) {
    double nfrc[3], len, phold[4], ee;
    int cfail;
    ELL_4V_COPY(phold, point->pos);
    
    fprintf(stderr, "!%s(%u,%u): energy(%g,%g,%g) = %f (original)\n",
            me, task->pctx->iter, point->idtag,
            point->pos[0], point->pos[1], point->pos[2], energyOld);
    
    ELL_4V_SCALE_ADD2(point->pos, 1.0, posOld,
                      capscl*point->stepEnergy, force);
    ELL_3V_COPY(_pullPrayCorner[1][0], point->pos);
    _pullPrayCorner[1][0][2] -= 1;
    ELL_3V_COPY(_pullPrayCorner[1][1], point->pos);
    _pullPrayCorner[1][1][2] += 1;
    fprintf(stderr, "!%s: corner[1][0] = %g %g %g\n", me, 
            _pullPrayCorner[1][0][0],
            _pullPrayCorner[1][0][1],
            _pullPrayCorner[1][0][2]);
    fprintf(stderr, "!%s: corner[1][1] = %g %g %g\n", me, 
            _pullPrayCorner[1][1][0],
            _pullPrayCorner[1][1][1],
            _pullPrayCorner[1][1][2]);

#define PROBE(l)  if (_pullProbe(task, point)) {                   \
      sprintf(err, "%s: while praying", me);                       \
      biffAdd(PULL, err); return 1;                                \
    }                                                              \
    (l) = _pullPointScalar(task->pctx, point,                      \
                           pullInfoHeight, NULL, NULL);
    if (1) {
      double *enr, *lpl, uu, vv, vpos[2][3], ll, mid[3];
      unsigned int ui, vi;
      Nrrd *nenr, *nlpl;
      nenr = nrrdNew();
      nlpl = nrrdNew();
      nrrdMaybeAlloc_nva(nenr, nrrdTypeDouble, 2, _pullPrayRes);
      enr = AIR_CAST(double *, nenr->data);
      nrrdMaybeAlloc_nva(nlpl, nrrdTypeDouble, 2, _pullPrayRes);
      lpl = AIR_CAST(double *, nlpl->data);
      for (vi=0; vi<_pullPrayRes[1]; vi++) {
        vv = AIR_AFFINE(0, vi, _pullPrayRes[1]-1, 0, 1);
        ELL_3V_LERP(vpos[0], vv, _pullPrayCorner[0][0], _pullPrayCorner[0][1]);
        ELL_3V_LERP(vpos[1], vv, _pullPrayCorner[1][0], _pullPrayCorner[1][1]);
        for (ui=0; ui<_pullPrayRes[0]; ui++) {
          uu = AIR_AFFINE(0, ui, _pullPrayRes[0]-1, 0, 1);
          ELL_3V_LERP(point->pos, uu, vpos[0], vpos[1]);
          PROBE(ll);
          lpl[ui + _pullPrayRes[0]*vi] = ll;
          enr[ui + _pullPrayRes[0]*vi] = _pullPointEnergyTotal(task, bin,
                                                               point, NULL);
        }
      }
      nrrdSave("nenr.nrrd", nenr, NULL);
      nrrdSave("nlpl.nrrd", nlpl, NULL);
      nenr = nrrdNuke(nenr);
      nlpl = nrrdNuke(nlpl);
    }
#undef PROBE

    ELL_4V_COPY(point->pos, phold);
    _pullPointEnergyTotal(task, bin, point, NULL);
  }