CyberXform.cc
上传用户:kellyonhid
上传日期:2013-10-12
资源大小:932k
文件大小:18k
源码类别:

3D图形编程

开发平台:

Visual C++

  1. //############################################################
  2. // 
  3. // CyberXform.cc
  4. //
  5. // Kari Pulli
  6. // Fri Dec 11 11:40:42 CET 1998
  7. //
  8. // Class for transforming raw Cyberware data (the Digital 
  9. // Michelangelo scanner) into 3D, as well as projecting
  10. // 3D points into scans
  11. //
  12. //############################################################
  13. #include "CyberXform.h"
  14. #include "CyberCalib.h"
  15. #include <stdio.h>
  16. #include <iostream.h>
  17. #include <fstream.h>
  18. #ifdef WIN32
  19. # include "defines.h"
  20. #endif
  21. #define SHOW(X) cout << #X " = " << X << endl
  22. static CyberCalib cc;           // calibration data
  23.                                 // and some other calibration
  24.                                 // data that hasn't been integrated
  25.                                 // into CyberCalib
  26. // these numbers come from the CyTemplate-WAG8007.iv file
  27. // and describe the range of the raw data for y and z
  28. static int CorrRowOffset = 4;  
  29. static int CorrNumRows   = 236;
  30. static int CorrZOffset   = 60;
  31. static int CorrZSize     = 2880;
  32. static int otableSize = 9;
  33. // The calibration values and derived calibration constants used
  34. // by these macros are defined in CyberCalib.h
  35. // a <- dtssp 
  36. // b <- dtsep 
  37. // c <- offt 
  38. // minscrew <- -6
  39. // function ang = screw_to_angle(screw, a, b, c, minscrew)
  40. //   ang = angle(a, b, screw-minscrew+c) - angle(a, b, c);
  41. //  
  42. // function res = angle(a,b,c)
  43. //   % get the angle opposite to c in a triangle a,b,c
  44. //   res = acos((a*a+b*b-c*c)/(2*a*b));
  45. #define TURN_SCREW_TO_ANGLE(scr) 
  46. (acos((cc.t_h2-((scr)+cc.t_h1)*((scr)+cc.t_h1))*cc.t_h3) - cc.t_h4)
  47. #define NOD_SCREW_TO_ANGLE(scr) 
  48. (acos((cc.n_h2-((scr)+cc.n_h1)*((scr)+cc.n_h1))*cc.n_h3) - cc.n_h4)
  49. #define SCREW_TO_ANGLE(scr) 
  50. (acos((_h2-((scr)+_h1)*((scr)+_h1))*_h3) - _h4)
  51. #define ANGLE_TO_SCREW(ang) 
  52. (sqrtf(_h2 - cos(ang+_h4)/_h3) - _h1)
  53. static float corners[4][2] = {
  54.   { CorrRowOffset*2, CorrZOffset },
  55.   { (CorrRowOffset+CorrNumRows)*2, CorrZOffset },
  56.   { CorrRowOffset*2, CorrZOffset+CorrZSize },
  57.   { (CorrRowOffset+CorrNumRows)*2, CorrZOffset+CorrZSize }
  58. };
  59. // the answer is laser_ctr_in_laser_coords() = [0 18.9049 -1215.23]
  60. Pnt3
  61. CyberXform::laser_ctr_in_laser_coords(void)
  62. {
  63.   Pnt3 a(0, corners[0][0], corners[0][1]);
  64.   Pnt3 b(0, corners[1][0], corners[1][1]);
  65.   Pnt3 c(0, corners[2][0], corners[2][1]);
  66.   Pnt3 d(0, corners[3][0], corners[3][1]);
  67.   raw_to_laser(a);
  68.   raw_to_laser(b);
  69.   raw_to_laser(c);
  70.   raw_to_laser(d);
  71.   Pnt3 ans;
  72.   bool ok = lines_intersect(a,c,b,d, ans);
  73.   assert(ok);
  74.   return ans;
  75. }
  76. void
  77. CyberXform::bounds_for_circle_on_laser_in_raw(float ly, float lz, float r,
  78.       short ywin[2], short zwin[2])
  79.   Xform<double> laser_to_raw = raw_to_laser;
  80.   laser_to_raw.invert();
  81.   //SHOW("...");
  82.   // need to convert from laser coordinates back to raw
  83.   Pnt3 p(0, ly+r, lz);
  84.   //SHOW(p);
  85.   laser_to_raw(p);
  86.   //SHOW(p);
  87.   ywin[0] = ywin[1] = p[1];
  88.   zwin[0] = zwin[1] = p[2];
  89.   p.set(0, ly-r, lz);
  90.   //SHOW(p);
  91.   laser_to_raw(p);
  92.   //SHOW(p);
  93.   if      (ywin[0] > p[1]) ywin[0] = p[1];
  94.   else if (ywin[1] < p[1]) ywin[1] = p[1];
  95.   if      (zwin[0] > p[2]) zwin[0] = p[2];
  96.   else if (zwin[1] < p[2]) zwin[1] = p[2];
  97.   p.set(0, ly, lz+r);
  98.   //SHOW(p);
  99.   laser_to_raw(p);
  100.   //SHOW(p);
  101.   if      (ywin[0] > p[1]) ywin[0] = p[1];
  102.   else if (ywin[1] < p[1]) ywin[1] = p[1];
  103.   if      (zwin[0] > p[2]) zwin[0] = p[2];
  104.   else if (zwin[1] < p[2]) zwin[1] = p[2];
  105.   p.set(0, ly, lz-r);
  106.   //SHOW(p);
  107.   laser_to_raw(p);
  108.   //SHOW(p);
  109.   if      (ywin[0] > p[1]) ywin[0] = p[1];
  110.   else if (ywin[1] < p[1]) ywin[1] = p[1];
  111.   if      (zwin[0] > p[2]) zwin[0] = p[2];
  112.   else if (zwin[1] < p[2]) zwin[1] = p[2];
  113.   //SHOW(ywin[0]);
  114.   //SHOW(ywin[1]);
  115.   //SHOW(zwin[0]);
  116.   //SHOW(zwin[1]);
  117. }
  118. // with the current optical transform, the raw z grows toward
  119. // scanner, the laser z grows away from it
  120. // raw and laser y behave more nicely
  121. void
  122. CyberXform::setup(bool vscan, bool tup, int tconf, bool tright,
  123.   float hor_trans, float screw, float vert_trans)
  124. {
  125.   vertical_scan = vscan;
  126.   // the transformation goes as follows:
  127.   // first, apply the optical correction to the data
  128.   // (the raw data comes in two shorts, 
  129.   // y [8,479] and z [60,2939])
  130.   // then, apply the optical frame transform
  131.   // then, apply the nod,
  132.   // then, apply the turn,
  133.   // finally, translate
  134.   // only the nod is variable, all the rest can be
  135.   // collapsed (optical correction and opt. frame xform,
  136.   // turn and translate)
  137.   // get the optical transformation first
  138.   
  139.   // map the raw data to range [1,otableSize]
  140.   double scaley = float(otableSize-1) / (2*CorrNumRows-1);
  141.   double scalez = float(otableSize-1) / (CorrZSize-1);
  142.   raw_to_laser.identity();
  143.   // remove offset
  144.   raw_to_laser.translate(0.0,
  145.  - (2*CorrRowOffset),
  146.  - CorrZOffset);
  147.   // scale to [0..scale]
  148.   raw_to_laser.scale(1.0, scaley, scalez);
  149.   // move up by one
  150.   raw_to_laser.translate(0., 1., 1.);
  151.   // do the optical table correction
  152.   raw_to_laser *= Xform<double>(cc.A);
  153.   // do the laser-to-nod frame transformation
  154.   laser_to_scanaxis = Xform<double>(cc.opt_frm_v);
  155.   scanaxis_to_horz.identity();
  156.   if (vertical_scan) {
  157.     _h1 = cc.n_h1; _h2 = cc.n_h2; _h3 = cc.n_h3; _h4 = cc.n_h4;
  158.     axdir[0] = cc.axnod[0]; axdir[1] = cc.axnod[1]; axdir[2] = cc.axnod[2];
  159.     ax0[0]  = cc.axnod0[0]; ax0[1]  = cc.axnod0[1]; ax0[2]  = cc.axnod0[2];
  160.     scanaxis_to_horz.rot(TURN_SCREW_TO_ANGLE(screw),
  161.  cc.axturn[0], cc.axturn[1], cc.axturn[2],
  162.  cc.axturn0[0], cc.axturn0[1], cc.axturn0[2]);
  163.   } else {
  164.     _h1 = cc.t_h1; _h2 = cc.t_h2; _h3 = cc.t_h3; _h4 = cc.t_h4;
  165.     axdir[0] = cc.axturn[0];axdir[1] = cc.axturn[1];axdir[2] = cc.axturn[2];
  166.     ax0[0]  = cc.axturn0[0]; ax0[1] = cc.axturn0[1]; ax0[2] = cc.axturn0[2];
  167.     laser_to_scanaxis *= Xform<double>(cc.opt_frm_h);
  168.     laser_to_scanaxis.rot(NOD_SCREW_TO_ANGLE(screw),
  169.   cc.axnod[0], cc.axnod[1], cc.axnod[2],
  170.   cc.axnod0[0], cc.axnod0[1], cc.axnod0[2]);
  171.   }
  172.   // Add translation
  173.   if (!tright) tup = !tup;
  174.   if (tup)  {
  175.     if (tconf == 1)  {
  176.       scanaxis_to_horz.translate(hor_trans * cc.arm_up1[0],
  177.  hor_trans * cc.arm_up1[1],
  178.  hor_trans * cc.arm_up1[2] );
  179.     } else if (tconf == 2)  {
  180.       scanaxis_to_horz.translate(hor_trans * cc.arm_up2[0],
  181.  hor_trans * cc.arm_up2[1],
  182.  hor_trans * cc.arm_up2[2] );
  183.     } else if (tconf == 3)  {
  184.       scanaxis_to_horz.translate(hor_trans * cc.arm_up3[0],
  185.  hor_trans * cc.arm_up3[1],
  186.  hor_trans * cc.arm_up3[2] );
  187.     } else {
  188.       scanaxis_to_horz.translate(hor_trans * cc.arm_up4[0],
  189.  hor_trans * cc.arm_up4[1],
  190.  hor_trans * cc.arm_up4[2] );
  191.     }
  192.   } else {
  193.     if (tconf == 1)  {
  194.       scanaxis_to_horz.translate(hor_trans * cc.arm_down1[0],
  195.  hor_trans * cc.arm_down1[1],
  196.  hor_trans * cc.arm_down1[2] );
  197.     } else if (tconf == 2)  {
  198.       scanaxis_to_horz.translate(hor_trans * cc.arm_down2[0],
  199.  hor_trans * cc.arm_down2[1],
  200.  hor_trans * cc.arm_down2[2] );
  201.     } else if (tconf == 3)  {
  202.       scanaxis_to_horz.translate(hor_trans * cc.arm_down3[0],
  203.  hor_trans * cc.arm_down3[1],
  204.  hor_trans * cc.arm_down3[2] );
  205.     } else {
  206.       scanaxis_to_horz.translate(hor_trans * cc.arm_down4[0],
  207.  hor_trans * cc.arm_down4[1],
  208.  hor_trans * cc.arm_down4[2] );
  209.     }
  210.   }
  211.   scanaxis_to_horz.translate(0,0,vert_trans);
  212.   // precalculations for backprojection
  213.   laser_n = &((double *) laser_to_scanaxis)[0];
  214.   laser_t = &((double *) laser_to_scanaxis)[12];
  215.   n_dot_t = laser_n[0]*laser_t[0]+laser_n[1]*laser_t[1]
  216.     +laser_n[2]*laser_t[2];
  217.   // vector s gives the direction of the line that we
  218.   // get when we drop a perpendicular from a 3D point
  219.   // to the nod axis, and rotate it so it cuts the laser
  220.   // plane
  221.   // s = cross(n,nodaxis)
  222.   s[0] = laser_n[1]*axdir[2] - laser_n[2]*axdir[1];
  223.   s[1] = laser_n[2]*axdir[0] - laser_n[0]*axdir[2];
  224.   s[2] = laser_n[0]*axdir[1] - laser_n[1]*axdir[0];
  225.   double sinvlen = 1.0 / sqrt(s[0]*s[0]+s[1]*s[1]+s[2]*s[2]);
  226.   s[0] *= sinvlen; s[1] *= sinvlen; s[2] *= sinvlen; 
  227.   q[0] = s[1]*axdir[2] - s[2]*axdir[1];
  228.   q[1] = s[2]*axdir[0] - s[0]*axdir[2];
  229.   q[2] = s[0]*axdir[1] - s[1]*axdir[0];
  230.   n_dot_q = laser_n[0]*q[0]+laser_n[1]*q[1]+laser_n[2]*q[2];
  231.   s_dot_q = s[0]*q[0] + s[1]*q[1] + s[2]*q[2];
  232.   raw_to_scanaxis     = laser_to_scanaxis * raw_to_laser;
  233.   raw_to_scanaxis_inv = raw_to_scanaxis;
  234.   raw_to_scanaxis_inv.invert();
  235.   toYZ = (const double *) raw_to_scanaxis_inv;
  236.   // figure out some limits for testing whether a point
  237.   // can project to within the working volume
  238.   axislimit_min = 1.e33; axislimit_max = -axislimit_min;
  239.   axisdist_near = 1.e33; axisdist_far = -1.0;
  240.   for (int i=0; i<4; i++) {
  241.     Pnt3 p(0, corners[i][0], corners[i][1]);
  242.     raw_to_scanaxis(p);
  243.     // change to vector from a0 to p
  244.     p[0] -= ax0[0]; p[1] -= ax0[1]; p[2] -= ax0[2];
  245.     float tmp = p[0]*axdir[0] + p[1]*axdir[1] + p[2]*axdir[2];
  246.     if (tmp < axislimit_min) axislimit_min = tmp;
  247.     if (tmp > axislimit_max) axislimit_max = tmp;
  248.     // calculate also the squared distance to the axis
  249.     tmp = dot(p, p) - tmp*tmp; // pythagorean rule
  250.     assert(tmp > 0.0);
  251.     if (tmp < axisdist_near) axisdist_near = tmp;
  252.     if (tmp > axisdist_far ) axisdist_far  = tmp;
  253.   }
  254.   axisdist_near = sqrtf(axisdist_near);
  255.   axisdist_far  = sqrtf(axisdist_far);
  256.   /*
  257.   SHOW(vertical_scan);
  258.   Pnt3 lzr = laser_ctr_in_laser_coords();
  259.   laser_to_scanaxis(lzr);
  260.   Pnt3 t1 = lzr - Pnt3(ax0);
  261.   float t = dot(t1, Pnt3(axdir));
  262.   SHOW(sqrtf(t1.norm2() - t*t));
  263.   assert(0);
  264.   */
  265. }
  266. void 
  267. CyberXform::set_screw(float even_scr, float odd_scr)
  268. {
  269.   odd_xform = even_xform = raw_to_scanaxis;
  270.   even_xform.rot(SCREW_TO_ANGLE(even_scr),
  271.  axdir[0], axdir[1], axdir[2],
  272.  ax0[0], ax0[1], ax0[2]);
  273.   odd_xform.rot (SCREW_TO_ANGLE(odd_scr),
  274.  axdir[0], axdir[1], axdir[2],
  275.  ax0[0], ax0[1], ax0[2]);
  276.   even_xform *= scanaxis_to_horz;
  277.   odd_xform  *= scanaxis_to_horz;
  278.   even_xf = (const double *) even_xform;
  279.   odd_xf  = (const double *) odd_xform;
  280.   
  281.   Xform<double> blah = laser_to_scanaxis;
  282.   blah.rot(SCREW_TO_ANGLE(even_scr),
  283.    axdir[0], axdir[1], axdir[2],
  284.    ax0[0], ax0[1], ax0[2]);
  285.   blah *= scanaxis_to_horz;
  286.   Pnt3 lzr(0, 18.9, -1215.2);
  287.   blah(lzr);
  288.   laser_ctr = lzr;
  289. }
  290. void 
  291. CyberXform::set_screw(float even_scr)
  292. {
  293.   even_xform = raw_to_scanaxis;
  294.   even_xform.rot(SCREW_TO_ANGLE(even_scr),
  295.  axdir[0], axdir[1], axdir[2],
  296.  ax0[0], ax0[1], ax0[2]);
  297.   even_xform *= scanaxis_to_horz;
  298.   even_xf = (const double *) even_xform;
  299. }
  300. // use this only if you want to see how the scanhead
  301. // moves, e.g., for color camera stuff
  302. Xform<double>
  303. CyberXform::set_and_get_geometric_xform(float screw)
  304. {
  305.   Xform<double> xf = laser_to_scanaxis;
  306.   xf.rot(SCREW_TO_ANGLE(screw),
  307.  axdir[0], axdir[1], axdir[2],
  308.  ax0[0], ax0[1], ax0[2]);
  309.   xf *= scanaxis_to_horz;
  310.   return xf;
  311. }
  312. Pnt3
  313. CyberXform::apply_xform(short y, short z)
  314. {
  315.   if (y%2) {
  316.     double invw = 
  317.       1.0 / (odd_xf[7]*y+odd_xf[11]*z+odd_xf[15]);
  318.     return Pnt3((odd_xf[4]*y+odd_xf[8]*z+odd_xf[12])*invw,
  319. (odd_xf[5]*y+odd_xf[9]*z+odd_xf[13])*invw,
  320. (odd_xf[6]*y+odd_xf[10]*z+odd_xf[14])*invw);
  321.   } else {
  322.     double invw = 
  323.       1.0 / (even_xf[7]*y+even_xf[11]*z+even_xf[15]);
  324.     return Pnt3((even_xf[4]*y+even_xf[8]*z+even_xf[12])*invw,
  325. (even_xf[5]*y+even_xf[9]*z+even_xf[13])*invw,
  326. (even_xf[6]*y+even_xf[10]*z+even_xf[14])*invw);
  327.   }
  328. }
  329. void
  330. CyberXform::apply_xform(short y, short z, Pnt3 &p)
  331. {
  332.   if (y%2) {
  333.     double invw = 
  334.       1.0 / (odd_xf[7]*y+odd_xf[11]*z+odd_xf[15]);
  335.     p.set((odd_xf[4]*y+odd_xf[8]*z+odd_xf[12])*invw,
  336.   (odd_xf[5]*y+odd_xf[9]*z+odd_xf[13])*invw,
  337.   (odd_xf[6]*y+odd_xf[10]*z+odd_xf[14])*invw);
  338.   } else {
  339.     double invw = 
  340.       1.0 / (even_xf[7]*y+even_xf[11]*z+even_xf[15]);
  341.     p.set((even_xf[4]*y+even_xf[8]*z+even_xf[12])*invw,
  342.   (even_xf[5]*y+even_xf[9]*z+even_xf[13])*invw,
  343.   (even_xf[6]*y+even_xf[10]*z+even_xf[14])*invw);
  344.   }
  345. }
  346. // used by CyberTurn...
  347. Pnt3
  348. CyberXform::apply_raw_to_laser(short y, short z)
  349. {
  350.   Pnt3 p(0,y,z);
  351.   raw_to_laser(p);
  352.   return p;
  353. }
  354. // move a raw data point to the same coordinates as
  355. // the scan axis, project it to it, get a float
  356. // that expresses the point's position on axis
  357. float
  358. CyberXform::axis_project(short y, short z)
  359. {
  360.   Pnt3 p(0,y,z);
  361.   raw_to_scanaxis(p);
  362.   return ((p[0]-ax0[0])*axdir[0] +
  363.   (p[1]-ax0[1])*axdir[1] +
  364.   (p[2]-ax0[2])*axdir[2]);
  365. }
  366. static double axis_proj_pos;
  367. static double scan_angle;
  368. // return the scr,y,z raw coordinates that would have
  369. // exactly scanned p_in
  370. // return false if the point is not within the viewing frustum
  371. // (can be in front or back of the actual working volume,
  372. //  but not on the side)
  373. bool
  374. CyberXform::back_project(const Pnt3 &p_in, Pnt3 &p_out,
  375.  bool check_frustum)
  376. {
  377.   // the sequence of events:
  378.   // * remove the transformation from horizontal translation
  379.   //   until the scan axis
  380.   // * project the point p onto the scan axis (call that pa)
  381.   // * can already check whether in working volume
  382.   //   (too far left or right, too far, too close,
  383.   //    although close one's can be used for carving)
  384.   // * find the intersection of the laser plane with p
  385.   //   think about rotating not the plane but p
  386.   //   find where they intersect, from that get the 
  387.   //   y,z coordinates, and also the rotation angle,
  388.   //   from which get the screw length
  389.   // * calculate the rotation intersection by first
  390.   //   going from pa to laser plane,
  391.   //   then continue along the plane so far that
  392.   //   the distance from pa becomes the same as |p-pa|.
  393.   float *p = &p_out[0];
  394.   // * remove the horizontal translation, maybe also turn
  395.   scanaxis_to_horz.apply_inv(p_in, p);
  396.   // * project the point p onto the scan axis (call that pa)
  397.   axis_proj_pos = ((p[0]-ax0[0])*axdir[0] +
  398.    (p[1]-ax0[1])*axdir[1] +
  399.    (p[2]-ax0[2])*axdir[2]);
  400.   // within frustum?
  401.   if (check_frustum && 
  402.       (axis_proj_pos < axislimit_min ||
  403.        axis_proj_pos > axislimit_max)) 
  404.     return false;
  405.   double pa[3],r[3];
  406.   pa[0] = axis_proj_pos*axdir[0]+ax0[0];
  407.   pa[1] = axis_proj_pos*axdir[1]+ax0[1];
  408.   pa[2] = axis_proj_pos*axdir[2]+ax0[2];
  409.   
  410.   // r is pa to p
  411.   r[0] = p[0]-pa[0];
  412.   r[1] = p[1]-pa[1];
  413.   r[2] = p[2]-pa[2];
  414.   double r2 = r[0]*r[0]+r[1]*r[1]+r[2]*r[2];
  415.   // find the intersection with laser plane
  416.   // alpha = n.t - n.pa (is also distance of pa from laser plane)
  417.   double alpha = (n_dot_t - (laser_n[0]*pa[0]+
  418.      laser_n[1]*pa[1]+
  419.      laser_n[2]*pa[2])) / n_dot_q;
  420.   double alpha2 = alpha*alpha;
  421.   if (r2 < alpha2) {
  422.     cerr << "Really weird input to backproject!" << endl;
  423.     cerr << "(too close to scan axis)" << endl;
  424.     return false;
  425.   }
  426.   double beta;
  427.   double tmp = sqrtf(alpha2*s_dot_q*s_dot_q - (alpha2-r2));
  428.   if (vertical_scan) beta = -alpha*s_dot_q - tmp;
  429.   else               beta = -alpha*s_dot_q + tmp;
  430.   // get the "rotated" point on the laser plane
  431.   double rp[3];
  432.   rp[0] = pa[0] + alpha*q[0] + beta*s[0];
  433.   rp[1] = pa[1] + alpha*q[1] + beta*s[1];
  434.   rp[2] = pa[2] + alpha*q[2] + beta*s[2];
  435.   
  436.   // get the y,z coords
  437.   // don't need to calculate x because it should become 0
  438.   tmp = 
  439.     1.0/(toYZ[3]*rp[0]+toYZ[7]*rp[1]+toYZ[11]*rp[2]+toYZ[15]);
  440.   p[1] =(toYZ[1]*rp[0]+toYZ[5]*rp[1]+toYZ[9]*rp[2]+toYZ[13])*tmp;
  441.   p[2] =(toYZ[2]*rp[0]+toYZ[6]*rp[1]+toYZ[10]*rp[2]+toYZ[14])*tmp;
  442.   
  443.   // get the screw
  444.   scan_angle = acos((alpha * (r[0]*q[0]+r[1]*q[1]+r[2]*q[2]) +
  445.      beta *  (r[0]*s[0]+r[1]*s[1]+r[2]*s[2]))/r2);
  446.   // try to deal with slightly negative angles
  447.   if (scan_angle > .75 * M_PI) scan_angle -= M_PI;
  448.   p[0] = ANGLE_TO_SCREW(scan_angle);
  449.   return true;
  450. }
  451. // return values
  452. // 0 not in frustum
  453. // 1 not fully in frustum
  454. // 2 possibly fully in frustum
  455. int
  456. CyberXform::sphere_status(const Pnt3 &ctr, float radius,
  457.   float &screw,
  458.   float ax_min, float ax_max,
  459.   short ywin[2], short zwin[2], 
  460.   float swin[2])
  461. {
  462.   // first, back project the center
  463.   Pnt3 p;
  464.   scanaxis_to_horz.apply_inv(ctr, p);
  465.   axis_proj_pos = ((p[0]-ax0[0])*axdir[0] +
  466.    (p[1]-ax0[1])*axdir[1] +
  467.    (p[2]-ax0[2])*axdir[2]);
  468.   // check whether in viewing frustum
  469.   if (axis_proj_pos < ax_min - radius ||
  470.       axis_proj_pos > ax_max + radius) {
  471.     //cout << "wide " << radius << endl;
  472.     return 0; // NOT_IN_FRUSTUM
  473.   }
  474.   double pa[3],r[3];
  475.   pa[0] = axis_proj_pos*axdir[0]+ax0[0];
  476.   pa[1] = axis_proj_pos*axdir[1]+ax0[1];
  477.   pa[2] = axis_proj_pos*axdir[2]+ax0[2];
  478.   
  479.   // r is pa to p
  480.   r[0] = p[0]-pa[0];
  481.   r[1] = p[1]-pa[1];
  482.   r[2] = p[2]-pa[2];
  483.   double r2 = r[0]*r[0]+r[1]*r[1]+r[2]*r[2];
  484.   double dist_axis = sqrt(r2);
  485.   if (dist_axis > axisdist_far + radius) {
  486.     //cout << "far " << radius << endl;
  487.     return 0; // NOT_IN_FRUSTUM
  488.   }
  489.   // check whether can be fully in viewing frustum
  490.   if (axis_proj_pos > ax_min + radius &&
  491.       axis_proj_pos < ax_max - radius) {
  492.     // can't be fully in frustum
  493.     return 1;
  494.   }
  495.   if (dist_axis > axisdist_far - radius) {
  496.     // can't be fully in frustum
  497.     return 1;
  498.   }
  499.   Pnt3 bp;
  500.   back_project(ctr, bp, false);
  501.   // how much can we rotate laser plane and still hit sphere?
  502.   // BUGBUG: not accurate at all...
  503.   float alpha = asin(radius / dist_axis);
  504.   swin[0] = scan_angle - alpha;
  505.   swin[1] = scan_angle + alpha;
  506.   if (swin[0] < 0 || swin[1] > M_PI * .75) return 1;
  507.   // the corners of the working volume in laser coordinates
  508.   // are
  509.   // (-2.5, 142.4)
  510.   // (143.2, 140.9
  511.   // (-.3, 1)
  512.   // (130.3, .4)
  513.   // Bbox: [(-2.5, .4); (143.2, 142.4)]
  514.   // convert the y,z to laser coordinates, in mm
  515.   Pnt3 lzr(0,bp[1],bp[2]);
  516.   raw_to_laser(lzr);
  517.   // could the sphere be fully within the working volume?
  518.   bounds_for_circle_on_laser_in_raw(lzr[1], lzr[2], radius,
  519.     ywin, zwin);
  520.   if (ywin[0] <  CorrRowOffset*2 ||
  521.       ywin[1] >= (CorrRowOffset+CorrNumRows)*2) {
  522.     // can't be fully in frustum
  523.     return 1;
  524.   }
  525.   if (zwin[0] <  CorrZOffset) {
  526.     //zwin[1] >= (CorrZOffset+CorrZSize)) {
  527.     // can't be fully in frustum
  528.     return 1;
  529.   }
  530.   swin[0] = ANGLE_TO_SCREW(swin[0]);
  531.   swin[1] = ANGLE_TO_SCREW(swin[1]);
  532.   // could be fully within frustum (for space carving purposes)
  533.   screw = bp[0];
  534.   return 2;
  535. }
  536. #if 0
  537. void
  538. main(void)
  539. {
  540.   CyberXform xf;
  541.   xf.setup(1, 1, 1, 1, 10, 10);
  542.   xf.set_nod(10,20);
  543.   SHOW(xf.even_xform);
  544.   SHOW(xf.odd_xform);
  545. }
  546. #endif