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

3D图形编程

开发平台:

Visual C++

  1. //############################################################
  2. // 
  3. // CyraScan.cc
  4. //
  5. // Lucas Pereira
  6. // Thu Jul 16 15:20:47 1998
  7. //
  8. // Store range scan information from a Cyra Cyrax Beta 
  9. // time-of-flight laser range scanner.
  10. //
  11. //############################################################
  12. #include "CyraScan.h"
  13. #include <stdio.h>
  14. #include <iostream.h>
  15. #include <fstream.h>
  16. #include <stack.h>
  17. #include "defines.h"
  18. #include "TriMeshUtils.h"
  19. #include "KDindtree.h"
  20. #include "ColorUtils.h"
  21. #include "plvScene.h"
  22. #include "KDtritree.h"
  23. #include "Timer.h"
  24. #include "MeshTransport.h"
  25. //////////////////////////////////////////////////////////////////////
  26. // CyraScan (all resolution levels for a cyra scan)
  27. //////////////////////////////////////////////////////////////////////
  28. CyraScan::CyraScan(void)
  29.   : triKD(NULL), triKD_mesh(NULL)
  30. {
  31.   // Clear arrays
  32.   levels.clear();
  33. }
  34. CyraScan::~CyraScan ()
  35. {
  36.   delete triKD;
  37.   delete triKD_mesh;
  38.   /*
  39.     <Writing stuff deleted>
  40.     delete kdtree;
  41.   */
  42. }
  43. // perVertex: colors and normals for every vertex (not every 3)
  44. // stripped: triangle strips instead of triangles
  45. MeshTransport*
  46. CyraScan::mesh(bool perVertex, bool stripped,
  47.        ColorSource color, int colorSize)
  48. {
  49.   if (stripped && !perVertex) {
  50.     cerr << "No t-strips without per-vertex properties";
  51.     return NULL;
  52.   }
  53.   int resNum = current_resolution_index();
  54.   if (resNum >= levels.size())
  55.     return NULL;
  56.   return levels[resNum].mesh (perVertex, stripped, color, colorSize);
  57. }
  58. int 
  59. CyraScan::num_vertices()
  60. {
  61.   // Return num_vertices for current resolution
  62.   return(num_vertices(current_resolution_index()));
  63. }
  64. int 
  65. CyraScan::num_tris()
  66. {
  67.   // Return num_tris for current resolution
  68.   return(num_tris(current_resolution_index()));
  69. }
  70. int 
  71. CyraScan::num_vertices(int resNum)
  72. {
  73.   if (resNum < levels.size()) {
  74.     return(levels[resNum].num_vertices());
  75.   } else {
  76.     return 0;
  77.   }
  78. }
  79. int 
  80. CyraScan::num_tris(int resNum)
  81. {
  82.   if (resNum < levels.size()) {
  83.     return(levels[resNum].num_tris());
  84.   } else {
  85.     return 0;
  86.   }
  87. }
  88. void 
  89. CyraScan::computeBBox(void)
  90. {
  91.   bbox.clear();
  92.   int resNum = current_resolution_index();
  93.   // Add current level to the bbox
  94.   // Also set the center of rotation
  95.   if (resNum < levels.size()) {
  96.     levels[resNum].growBBox(bbox);
  97.     rot_ctr = bbox.center();
  98.   } else {
  99.     rot_ctr = Pnt3();
  100.   }
  101. }
  102. void
  103. CyraScan::flipNormals(void)
  104. {
  105.   // Flip all res levels
  106.   for (int i=0; i < levels.size(); i++) {
  107.     levels[i].flipNormals();
  108.   }
  109. }
  110. bool
  111. CyraScan::ReadPts(const crope &inname)
  112. {
  113.   // read it into a new level
  114.   CyraResLevel *level = new CyraResLevel();
  115.   if (level->ReadPts(inname)) {
  116.     levels.push_back(*level); 
  117.   } else {
  118.     return false;
  119.   }
  120.   // Tell scanalyze about the mesh...
  121.   insert_resolution(level->num_tris(), inname, true, true);
  122.   cerr << "Telling scanalyze about " << level->num_tris() << " tris." << endl;
  123.   // Now subsample 5 lower-res versions.
  124.   for (int i = 1; i < 6; i++) {
  125.     CyraResLevel *sublevel = new CyraResLevel();
  126.     levels.push_back(*sublevel);
  127.     
  128.     // Tell scanalyze about the mesh...
  129.     int estimate = levels[0].num_tris() / (1 << (2*i));
  130.     insert_resolution(estimate, inname, false, false);
  131.     cerr << "Telling scanalyze about approx. " << estimate
  132.  << " tris." << endl;
  133.   }
  134.   // Recompute the bounding box
  135.   computeBBox();
  136.   // default to displaying coarsest res
  137.   select_coarsest();
  138.   return true;
  139. }
  140. bool
  141. CyraScan::WritePts(const crope &inname)
  142. {
  143.   // write out the highest level to the .pts file
  144.   return(levels[0].WritePts(inname));
  145. }
  146. bool
  147. CyraScan::load_resolution (int iRes)
  148. {
  149.   if (resolutions[iRes].in_memory)
  150.     return true;
  151.   
  152.   int subSamp = 1;
  153.   for (int i = 0; i < iRes; i++)
  154.     subSamp *= SubSampleBase;
  155.   
  156.   CyraResLevel *sublevel = new CyraResLevel();
  157.   sublevel->Subsample(levels[0], subSamp, subSamp, cfMEAN50);
  158.   //delete levels[iRes];
  159.   levels[iRes] = *sublevel;
  160.   resolutions[iRes].in_memory = true;
  161.   resolutions[iRes].abs_resolution = sublevel->num_tris();
  162.   computeBBox();
  163.   return true;
  164. }
  165. bool 
  166. CyraScan::read(const crope &inname)
  167. {
  168.   set_name (inname);
  169.   assert(has_ending(".pts"));
  170.   // read it into the levels array
  171.   if (!ReadPts(inname))
  172.     return false;
  173.   // read registration xform?
  174.   TbObj::readXform (get_basename());
  175.   return true;
  176. }
  177. bool 
  178. CyraScan::write(const crope &fname)
  179. {
  180.   cerr << "CyraScan::write was called.n";
  181.   if (fname.empty()) {
  182.     // try to save to default name; quit if there isn't one
  183.     if (name.empty()) return false;
  184.   }
  185.   else {
  186.     if (name != fname) {
  187.       cout << "Saving to filename " << fname << endl;
  188.       set_name(fname);
  189.     }
  190.   }
  191.   // write .pts, or fail....
  192.   if (has_ending(".pts")) {
  193.     return(WritePts(name));
  194.   } else {
  195.     cerr << "Error: CyraScan doesn't know how to write " <<
  196.       "that kind of file." << endl;
  197.     return false;
  198.   }
  199. }
  200. bool
  201. CyraScan::filter_inplace(const VertexFilter& filter)
  202. {
  203.   // Filter each level 
  204.   for (int i=0; i < levels.size(); i++) {
  205.     CyraResLevel *level = &levels[i];
  206.     level->filter_inplace(filter);
  207.   }
  208.   // BUGBUG update_res_ctrl();
  209.   // Need to update the number of tris and stuff,
  210.   computeBBox();
  211.   theScene->invalidateDisplayCaches();
  212.   return true;
  213. }
  214. RigidScan* 
  215. CyraScan::filtered_copy(const VertexFilter& filter)
  216. {
  217.   CyraScan *newScan = new CyraScan;
  218.   assert (newScan != NULL);
  219.   // Filter each level into the newScan
  220.   for (int i=0; i < levels.size(); i++) {
  221.     CyraResLevel *newLevel = new CyraResLevel;
  222.     assert(newLevel != NULL);
  223.     
  224.     newLevel->filtered_copy(levels[i], filter);
  225.     newScan->levels.push_back(*newLevel);
  226.   }
  227.   // Compute new boundingbox
  228.   newScan->computeBBox();
  229.   // Set the name of newScan
  230.   crope clipName(get_basename());
  231.   char info[50];
  232.   sprintf(info, "clip.%d.cyra", newScan->num_vertices());
  233.   clipName += info;
  234.   // Tell scanalyze about the meshes...
  235.   for (i=0; i < newScan->levels.size(); i++) {
  236.     CyraResLevel *level = &newScan->levels[i];
  237.     newScan->insert_resolution(level->num_tris(), clipName, true, true);
  238.     cerr << "Telling scanalyze about " << level->num_tris() 
  239.  << " tris." << endl;
  240.   }
  241.   return newScan;
  242. }
  243. // for ICP...
  244. void 
  245. CyraScan::subsample_points(float rate, vector<Pnt3> &p, vector<Pnt3> &n)
  246. {
  247.   CyraResLevel& res = levels[curr_res];
  248.   res.subsample_points(rate, p, n);
  249. }
  250. bool 
  251. CyraScan::closest_point(const Pnt3 &p, const Pnt3 &n, 
  252. Pnt3 &cp, Pnt3 &cn,
  253. float thr, bool bdry_ok)
  254. {
  255.   CyraResLevel& res = levels[curr_res];
  256.   return res.closest_point(p, n, cp, cn, thr, bdry_ok);
  257. }
  258. ////////////////////////////////////////
  259. // for volumetric processing
  260. ////////////////////////////////////////
  261. float
  262. CyraScan::closest_point_on_mesh (const Pnt3 &p, Pnt3 &cl_pnt,
  263.  OccSt &status_p)
  264. {
  265.   static Pnt3  prev_point(1e33, 1e33, 1e33);
  266.   if (triKD == NULL) {
  267.     // create the triangle spheretree for finding the distance to the mesh
  268.     cout << "Obtaining geometry for KDtritree..." << flush;
  269.     triKD_mesh = levels[0].mesh (true, false, RigidScan::colorNone, 0);
  270.     cout << "building tree..." << flush;
  271.     triKD = create_KDtritree(triKD_mesh->vtx[0]->begin(),
  272.      triKD_mesh->tri_inds[0]->begin(),
  273.      triKD_mesh->tri_inds[0]->size());
  274.     cout << "done." << endl;
  275.   }
  276.   OccSt st;
  277.   closest_along_line_of_sight(p, cl_pnt, st);
  278.   float d   = dist2(p, cl_pnt);
  279.   float dp2 = dist2(prev_point, p);
  280.   if (d > dp2) {
  281.     d = dp2;
  282.     cl_pnt = prev_point;
  283.   }
  284.   d = sqrtf(d);
  285.   triKD->search(triKD_mesh->vtx[0]->begin(),
  286. triKD_mesh->tri_inds[0]->begin(),
  287. p, cl_pnt, d);
  288.   prev_point = cl_pnt;
  289.   // just a simple, hacky test for occlusion status
  290.   // assume that the camera center is at origin
  291.   //if (p.norm2() < cl_pnt.norm2()) status_p = OUTSIDE;
  292.   //else                            status_p = INSIDE;
  293.   status_p = st;
  294.   return 1.0;
  295. }
  296. //////////////////////////////////////////////////////////////////////
  297. // magi's hack implementation for vrip member functions follows...
  298. //////////////////////////////////////////////////////////////////////
  299. float 
  300. CyraScan::closest_along_line_of_sight(const Pnt3 &p, Pnt3 &cp, 
  301.       OccSt &status_p)
  302. {
  303.   build_vrip_accelerators();
  304.   CyraResLevel& res = levels[0];
  305.   status_p = INDETERMINATE;
  306.   // lookup given los direction in ray bins
  307.   Pnt3 dir = p; dir.normalize();
  308.   if (dir[0] < minx || dir[0] > maxx) {
  309.     //cout << "out of bounds x" << endl;
  310.     return 0.0;
  311.   }
  312.   if (dir[1] < miny || dir[1] > maxy) {
  313.     //cout << "out of bounds y" << endl;
  314.     return 0.0;
  315.   }
  316.   int binx = (dir[0] - minx) * binw;
  317.   int biny = (dir[1] - miny) * binh;
  318.   if (!bins[biny][binx].size()) {
  319.     if (binx > 1)
  320.       binx--;
  321.     else
  322.       if (biny > 1)
  323. biny--;
  324.       else
  325. return 0.0;
  326.     if (!bins[biny][binx].size())
  327.       return 0.0;
  328.   }
  329.   // have a valid bin
  330.   int ofs = bins[biny][binx].back();
  331.   if (!pntmag[ofs])
  332.     return 0.0;
  333.   int x = ofs / res.height;
  334.   int y = ofs % res.height;
  335.   Pnt3& pd = pntdir[ofs];
  336.   int l, r, t, b;
  337.   if (dir[0] < pd[0]) {
  338.     l = x - 1; // needs help
  339.     while (pntdir [l * res.height + y][0] > dir[0]) {
  340.       if (--l < 0)
  341. return 0.0;
  342.     }
  343.     r = l + 1;
  344.   } else {
  345.     r = x + 1; // needs help
  346.     while (pntdir [r * res.height + y][0] < dir[0]) {
  347.       if (++r >= res.width)
  348. return 0.0;
  349.     }
  350.     l = r - 1;
  351.   }
  352.   if (dir[1] < pd[1]) {
  353.     b = y - 1; // needs help
  354.     while (pntdir [l * res.height + b][1] > dir[1]) {
  355.       if (--b < 0)
  356. return 0.0;
  357.     }
  358.     t = b + 1;
  359.   } else {
  360.     t = y + 1; // needs help
  361.     while (pntdir [l * res.height + t][1] < dir[1]) {
  362.       if (++t >= res.height)
  363. return 0.0;
  364.     }
  365.     b = t - 1;
  366.   }
  367.   int ofss[4] = { t + l*res.height, t + r*res.height,
  368.   b + l*res.height, b + r*res.height };
  369.   for (int ii = 0; ii < 4; ii++) {
  370.     if (ofss[ii] < 0 || ofss[ii] >= pntdir.size() || !pntmag[ofss[ii]])
  371.       return 0.0;
  372.   }
  373.   //SHOW (x); SHOW (y);
  374.   //SHOW (l); SHOW (r); SHOW (t); SHOW (b);
  375.   // bilerp across (l, b), (r, b), (l, t), (r, t)
  376.   Pnt3& p00 = res.points[b + l*res.height].vtx;
  377.   Pnt3& p01 = res.points[t + l*res.height].vtx;
  378.   Pnt3& p10 = res.points[b + r*res.height].vtx;
  379.   Pnt3& p11 = res.points[t + r*res.height].vtx;
  380.   Pnt3& n00 = pntdir[b + l*res.height];
  381.   Pnt3& n01 = pntdir[t + l*res.height];
  382.   Pnt3& n10 = pntdir[b + r*res.height];
  383.   Pnt3& n11 = pntdir[t + r*res.height];
  384.   float ix = (n11[0] - n01[0]);
  385.   ix = (dir[0] - n01[0]) / ix;
  386.   float iyl = (n01[1] - n00[1]);
  387.   iyl = (dir[1] - n00[1]) / iyl;
  388.   float iyr = (n11[1] - n10[1]);
  389.   iyr = (dir[1] - n10[1]) / iyr;
  390.   Pnt3 L,R;
  391.   L.lerp(iyl, p01, p00);
  392.   R.lerp(iyr, p11, p10);
  393.   cp.lerp(ix, R, L);
  394.   //SHOW (ix); SHOW (iyl); SHOW (iyr);
  395.   //SHOW (L); SHOW (R); SHOW (cp);
  396.   float ind  = p.norm2();
  397.   float outd = cp.norm2();
  398.   if (ind < outd)
  399.     status_p = OUTSIDE;
  400.   else
  401.     status_p = INSIDE;
  402.   return 1.0;
  403. }
  404. static const float g_intersectTolerance = 1.e-5;
  405. bool 
  406. OriginRaySphereIntersect (const Pnt3& rayD, const Pnt3& center,
  407.   float sphereRad, float& t1, float& t2)
  408. {
  409.   //from Heckbert, "Writing a Ray Tracer", in Glassner p. 280
  410.   //int nroots;
  411.   float b, disc;
  412.   
  413.   b = dot (center, rayD);
  414.   disc = b*b - dot(center,center) + sphereRad*sphereRad;
  415.   if (disc < 0.) return false;                    // doesn't hit
  416. #ifdef WIN32
  417.   disc = sqrt(disc);
  418. #else
  419.   disc = sqrtf(disc);
  420. #endif
  421.   t2 = b + disc;                              // 2nd root...
  422.   if (t2 < g_intersectTolerance) return false;    // behind ray origin
  423.   t1 = b - disc;                              // 1st root...
  424.   return true;
  425. }
  426. #define HISTORY 1
  427. #if HISTORY
  428. struct carve_cache {
  429.   Pnt3  ctr;
  430.   float radius;
  431.   vector<int> pinds;
  432.   bool inside(const Pnt3 &p, float r)
  433.     {
  434.       return dist(p,ctr)+r <= radius;
  435.     }
  436. #if _MSC_VER == 1100
  437.   // this compiler (MSVC5) won't compile without being able to compare carve_caches!
  438.   bool operator< (const carve_cache& that) const
  439.     {
  440.       abort();
  441.       return 0;
  442.     }
  443.   bool operator== (const carve_cache& that) const
  444.     {
  445.       abort();
  446.       return 0;
  447.     }
  448. #endif
  449. };
  450. // carve history
  451. static stack<carve_cache> hist;
  452. #endif
  453. static vector<int> dummy;
  454. OccSt
  455. CyraScan::carve_cube  (const Pnt3 &ctr, float side)
  456. {
  457.   build_vrip_accelerators();
  458.   int numpoints = pntdir.size();
  459. #if HISTORY
  460.   while (!hist.empty() && !hist.top().inside(ctr, side)) {
  461.     hist.pop();
  462.   }
  463.   carve_cache cc;
  464.   cc.ctr = ctr;
  465.   //cc.radius = (side/2.0) * sqrt(3.0); // cube side to sphere radius
  466.   cc.radius = side * 0.866025404;
  467.   vector<int> &oldinds = dummy;
  468.   bool first = hist.empty();
  469.   if (!first) oldinds = hist.top().pinds;
  470.   hist.push(cc);
  471.   if (first)  hist.top().pinds.reserve(numpoints);
  472.   else        hist.top().pinds.reserve(oldinds.size());
  473. #endif
  474.   bool bBefore = false;
  475.   bool bAfter = false;
  476.   //cout << "CarveCube:" << endl;
  477.   //SHOW (ctr);
  478.   //SHOW (side);
  479.   // figure out which rays it's worth intersecting with
  480.   // take incoming bounding sphere, and figure out where it is on
  481.   // the [0..1] scale that we've normalized point-rays to
  482.   Pnt3 carvedir = ctr;
  483.   float carvemag = carvedir.norm();
  484.   carvedir /= carvemag;
  485.   float carverad = cc.radius / carvemag;
  486.   // now only keep rays that hit the square that bounds this circle in 2-d
  487.   float l = carvedir[0] - carverad, r = carvedir[0] + carverad;
  488.   float b = carvedir[1] - carverad, t = carvedir[1] + carverad;
  489.   l = max (l, minx); r = min (r, maxx);
  490.   b = max (b, miny); t = min (t, maxy);
  491.   if (l > r || b > t) return NOT_IN_FRUSTUM;
  492.   // look up the rays that fit in here
  493.   int binl = (l - minx) * binw;
  494.   int binr = (r - minx) * binw;
  495.   int binb = (b - miny) * binh;
  496.   int bint = (t - miny) * binh;
  497. #if HISTORY
  498.   if (first) {
  499.     cout << numpoints << ": ";
  500. #endif
  501.     for (int binx = binl; binx <= binr; binx++) {
  502.       for (int biny = binb; biny <= bint; biny++) {
  503. vector<int>& bin = bins[biny][binx];
  504. int binsize = bin.size();
  505. for (int inbin = 0; inbin < binsize; inbin++) {
  506.   int p = bin[inbin];
  507.   float mag = pntmag[p];
  508.   if (!mag) continue;
  509.   Pnt3& dir = pntdir[p];
  510.       
  511.   float t1, t2;
  512.   if (OriginRaySphereIntersect (dir, ctr, side, t1, t2)) {
  513. #if HISTORY
  514.     hist.top().pinds.push_back(p);
  515. #endif
  516.     // if mag is between t1 and t2, we hit:
  517.     if (mag < t1)
  518.       bBefore = true;
  519.     else if (mag > t2)
  520.       bAfter = true;
  521.     else {
  522.       //SHOW (p); SHOW (t1); SHOW (mag); SHOW (t2);
  523.       cout << "BOUND for point " << p << " of " << numpoints << endl;
  524.       // leave the rest of the points into cache
  525. #if HISTORY
  526.       for (; binx <= binr; binx++) {
  527. for (biny += 1; biny <= bint; biny++) {
  528.   vector<int>& bin = bins[biny][binx];
  529.   int binsize = bin.size();
  530.   for (int inbin = 0; inbin < binsize; inbin++) {
  531.     hist.top().pinds.push_back (bin[inbin]);
  532.   }
  533. }
  534.       }
  535. #endif
  536.       return BOUNDARY;
  537.     }
  538.   }
  539. }
  540.       }
  541.     }
  542. #if HISTORY
  543.   } else {
  544.     cout << oldinds.size() << ": ";
  545.     for (int* it = oldinds.begin(); it != oldinds.end(); it++) {
  546.       float mag = pntmag[*it];
  547.       if (!mag) continue;
  548.       Pnt3& dir = pntdir[*it];
  549.       
  550.       float t1, t2;
  551.       if (OriginRaySphereIntersect (dir, ctr, side, t1, t2)) {
  552. hist.top().pinds.push_back(*it);
  553. // if mag is between t1 and t2, we hit:
  554. if (mag < t1)
  555.   bBefore = true;
  556. else if (mag > t2)
  557.   bAfter = true;
  558. else {
  559.   //SHOW (p); SHOW (t1); SHOW (mag); SHOW (t2);
  560.   cout << "BOUND for point " << *it << " of " << numpoints << endl;
  561.   // leave the rest of the points into cache
  562.   hist.top().pinds.insert(hist.top().pinds.end(), ++it, oldinds.end());
  563.   return BOUNDARY;
  564. }
  565.       }
  566.     }
  567.   }
  568.   hist.push(cc);
  569. #endif
  570.   if (bAfter && bBefore) {
  571.     cout << "SILH" << endl;
  572.     return SILHOUETTE;
  573.   } else if (bAfter) {
  574.     cout << "IN" << endl;
  575.     return INSIDE;
  576.   } else if (bBefore) {
  577.     cout << "OUT" << endl; 
  578.    return OUTSIDE;
  579.   }
  580.   cout << "NOT_IN_FRUSTUM" << endl;
  581.   return NOT_IN_FRUSTUM;
  582. }
  583. /*
  584. // for debugging...
  585. void 
  586. CyraScan::TriOctreeMesh(vector<Pnt3> &p, vector<int> &ind)
  587. {
  588.   if (trioct == NULL) {
  589.     // force creation
  590.     Pnt3 p1, p2;
  591.     OccSt occ;
  592.     closest_point(p1, p2, occ);
  593.   }
  594.   p.clear();
  595.   ind.clear();
  596.   //trioct->visualize(p,ind);
  597. }
  598. */
  599. void
  600. CyraScan::build_vrip_accelerators (void)
  601. {
  602.   CyraResLevel& res = levels[0];
  603.   // if res.pntdir hasn't been initialized, do it now
  604.   if (pntdir.size() == 0) {
  605.     TIMER (Cyra_Vrip_Preprocess);
  606.     int np = res.points.size();
  607.     pntdir.reserve (np);
  608.     pntmag.reserve (np);
  609.     // separate point directions and magnitues, and find bounds
  610.     minx = 1e33; miny = 1e33; maxx = -1e33; maxy = -1e33;
  611.     Pnt3 p;
  612.     float mag;
  613.     for (int i = 0; i < np; i++) {
  614.       p = res.points[i].vtx;
  615.       mag = p.norm2();
  616.       if (mag) {
  617. mag = sqrtf (mag);
  618. p /= mag;
  619.       }
  620.       pntdir.push_back (p);
  621.       pntmag.push_back (mag);
  622.       minx = min (minx, p[0]); maxx = max (maxx, p[0]);
  623.       miny = min (miny, p[1]); maxy = max (maxy, p[1]);
  624.     }
  625.     // build bins
  626.     float w = maxx - minx;
  627.     float h = maxy - miny;
  628.     binw = (res.width - 1) / w;
  629.     binh = (res.height - 1) / h;
  630.     vector<int> _bin1;
  631.     // JED - changed the following to avoid compiler complaint
  632.     //vector< vector< int> > _bins1;
  633.     //_bins1.assign (res.width, _bin1);
  634.     //bins.assign (res.height, _bins1);
  635.     vector< vector< int> > _bins1(res.width, _bin1);
  636.     vector< vector< vector <int> > > tmp(res.height, _bins1);
  637.     bins = tmp;
  638.     for (int ip = 0; ip < np; ip++) {
  639.       if (pntmag[ip] == 0) continue;
  640.       const Pnt3& p = pntdir[ip];
  641.       int binx = (p[0] - minx) * binw;
  642.       int biny = (p[1] - miny) * binh;
  643.       assert (binx >= 0 && binx < res.width);
  644.       assert (biny >= 0 && biny < res.height);
  645.       bins[biny][binx].push_back (ip);
  646.     }
  647.   }
  648. }