SpatialGrid.h
上传用户:chinafayin
上传日期:2022-04-05
资源大小:153k
文件大小:11k
源码类别:

并行计算

开发平台:

Visual C++

  1. /*
  2.     FastGrid (formerly AutoGrid)
  3.     Copyright (C) 2009 The Scripps Research Institute. All rights reserved.
  4.     Copyright (C) 2009 Masaryk University. All rights reserved.
  5.     AutoGrid is a Trade Mark of The Scripps Research Institute.
  6.     This program is free software; you can redistribute it and/or
  7.     modify it under the terms of the GNU General Public License
  8.     as published by the Free Software Foundation; either version 2
  9.     of the License, or (at your option) any later version.
  10.     This program is distributed in the hope that it will be useful,
  11.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.     GNU General Public License for more details.
  14.     You should have received a copy of the GNU General Public License
  15.     along with this program; if not, write to the Free Software
  16.     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  17. */
  18. #pragma once
  19. #include <cstring>
  20. #include "autogrid.h"
  21. #include "Exceptions.h"
  22. typedef void *SpatialCell;
  23. class SpatialGrid
  24. {
  25. public:
  26.     typedef int32 T;
  27.     SpatialGrid(): grid(0) {}
  28. ~SpatialGrid()
  29. {
  30. if (grid)
  31. {
  32. for (int i = 0; i < numAllCells; i++)
  33. if (grid[i])
  34. delete [] grid[i];
  35. delete [] grid;
  36. }
  37. }
  38.     // Creates the grid with the given size of the grid and the maximal expected size of each cell
  39.     void create(const Vec3d &gridSize, double maxCellSize, const Vec3d &centerPos)
  40.     {
  41.         Vec3d minNumCells = gridSize / maxCellSize;
  42.         Vec3d numCells = Vec3d(Mathd::Ceil(minNumCells.x),
  43.                                Mathd::Ceil(minNumCells.y),
  44.                                Mathd::Ceil(minNumCells.z));
  45.         Vec3d cellSize = gridSize / numCells;
  46.         create(Vec3i(numCells + 0.5), cellSize, centerPos);
  47.     }
  48. // Returns the size of memory
  49. size_t getSizeOfMemory() const
  50. {
  51. if (!grid)
  52. return 0;
  53. size_t size = numAllCells * sizeof(T*);
  54. for (int i = 0; i < numAllCells; i++)
  55. size += (grid[i][0]+2) * sizeof(T);
  56. return size;
  57. }
  58.     // Creates the grid with the given number of cells and the size of each cell
  59.     void create(const Vec3i &numCells, const Vec3d &cellSize, const Vec3d &centerPos)
  60.     {
  61.         this->numCells = numCells;
  62.         for (int i = 0; i < 3; i++)
  63.             this->numCells[i] = Mathi::Max(1, this->numCells[i]);
  64.         this->numCellsMinus1 = this->numCells - 1;
  65.         this->cellSize = cellSize;
  66.         cellSizeHalf = cellSize * 0.5;
  67.         cellDiagonalHalf = cellSizeHalf.Magnitude();
  68.         cellSizeInv = cellSize;
  69.         cellSizeInv.Inverse();
  70.         // Allocate the grid
  71.         numAllCells = this->numCells.Cube();
  72.         grid = new T*[numAllCells];
  73. memset(grid, 0, sizeof(T*) * numAllCells);
  74.         // extent is a vector from the corner to the center
  75.         Vec3d extent = Vec3d(this->numCells) * cellSizeHalf;
  76.         // cornerPosMin is a position of the corner which has minimal coordinates
  77.         cornerPosMin = centerPos - extent;
  78.         // cornerCellPosMin is a position at the center of the corner cell
  79.         cornerCellPosMin = cornerPosMin + cellSizeHalf;
  80.     }
  81.     // Set indices to be in the valid range
  82.     void clampIndices(Vec3i &in) const
  83.     {
  84.         for (int i = 0; i < 3; i++)
  85.             in[i] = Mathi::Clamp(in[i], 0, numCellsMinus1[i]);
  86.     }
  87.     // Returns an internal cell ID, which is actually a pointer to the grid array
  88.     SpatialCell getCellFromIndices(const Vec3i &indices) const
  89.     {
  90.         Vec3i in = indices;
  91.         clampIndices(in);
  92.         return grid[getScalarIndexFromIndices(in)];
  93.     }
  94.     // Calculates a scalar index from 3D indices
  95.     int getScalarIndexFromIndices(const Vec3i &in) const
  96.     {
  97.         return numCells.x * (numCells.y * in.z + in.y) + in.x;
  98.     }
  99.     // Returns indices of the cell at the given position
  100.     void getIndicesFromPos(const Vec3d &pos, Vec3i &indices) const
  101.     {
  102.         indices = Vec3i((pos - cornerPosMin) * cellSizeInv);
  103.     }
  104.     // Returns the corner of the cell at the given indices
  105.     void getCellCornerPosFromIndices(const Vec3i &indices, Vec3d &pos) const
  106.     {
  107.         pos = Vec3d(indices) * cellSize + cornerPosMin;
  108.     }
  109.     // Returns the internal cell ID at the given position
  110.     SpatialCell getCellFromPos(const Vec3d &pos) const
  111.     {
  112.         Vec3i ipos;
  113.         getIndicesFromPos(pos, ipos);
  114.         return getCellFromIndices(ipos);
  115.     }
  116.     // Returns the number of elements in the cell
  117.     T getNumElements(const SpatialCell cell) const
  118.     {
  119.         return *((T*)cell + 1);
  120.     }
  121.     // Returns the index-th element in the given cell
  122.     T getElement(const SpatialCell cell, int index) const
  123.     {
  124.         return *((T*)cell + 2 + index);
  125.     }
  126.     // Inserts ID of your object into the cell
  127.     void insertIntoCell(SpatialCell cell, T id)
  128.     {
  129. T *c = (T*)cell;
  130. T maxNum = c[0]; // The maximum number of elements
  131.         T &num = c[1]; // The actual number of elements
  132. T *elements = c+2;
  133.         if (num < maxNum)
  134.         {
  135.             elements[num] = id;
  136.             ++num;
  137.         }
  138.         else
  139.         {
  140.             fprintf(stderr, "SpatialGrid::insertIntoCell: cell overflow!n");
  141.             throw ExitProgram(1);
  142.         }
  143.     }
  144.     // Inserts ID of your object to the cell at the given indices
  145.     void insertAtIndices(const Vec3i &indices, T id)
  146.     {
  147.         insertIntoCell(getCellFromIndices(indices), id);
  148.     }
  149.     // Inserts ID of your object to the cell at the given indices
  150.     void insertAtClampedIndices(const Vec3i &indices, T id)
  151.     {
  152.         insertIntoCell(grid[getScalarIndexFromIndices(indices)], id);
  153.     }
  154.     // Inserts ID to the cell at the given position
  155.     void insertPos(const Vec3d &pos, T id)
  156.     {
  157.         insertIntoCell(getCellFromPos(pos), id);
  158.     }
  159.     // Returns the numCells vector of the grid
  160.     const Vec3i &getNumCells() const
  161.     {
  162.         return numCells;
  163.     }
  164.     // For each sphere, for each cell: if the cell is in range of the sphere, insert its index into the cell.
  165.     template<typename Vec> // Restriction: Typecasting Vec -> Vec3d must be possible (e.g. allows using a Vec4d array)
  166.     void insertSpheres(int num, const Vec *pos, double radius)
  167.     {
  168.         // For each cell, precalculate a box representing the cell in Euclidean space
  169.         AxisAlignedBox3d *boxes = new AxisAlignedBox3d[numCells.Cube()];
  170. #if defined(AG_OPENMP)
  171.     #pragma omp parallel for schedule(dynamic, 4)
  172. #endif
  173.         for (int x = 0; x < numCells.x; x++)
  174.             for (int y = 0; y < numCells.y; y++)
  175.                 for (int z = 0; z < numCells.z; z++)
  176.                 {
  177.                     Vec3i indices(x, y, z);
  178.                     Vec3d cellPos;
  179.                     getCellCornerPosFromIndices(indices, cellPos);
  180.                     boxes[getScalarIndexFromIndices(indices)] = AxisAlignedBox3d(cellPos, cellPos + cellSize);
  181.                 }
  182.         // For each sphere, precalculate a range of possible indices of cells the sphere might intersect with
  183.         Vec3i *sphereIndicesRange = new Vec3i[num*2];
  184. #if defined(AG_OPENMP)
  185.     #pragma omp parallel for schedule(dynamic, 1024)
  186. #endif
  187.         for (int s = 0; s < num; s++)
  188.         {
  189.             int sindex = s*2;
  190.             getIndicesFromPos(Vec3d(pos[s]) - radius, sphereIndicesRange[sindex]);
  191.             getIndicesFromPos(Vec3d(pos[s]) + radius, sphereIndicesRange[sindex+1]);
  192.             clampIndices(sphereIndicesRange[sindex]);
  193.             clampIndices(sphereIndicesRange[sindex+1]);
  194.         }
  195.         // Divide the X axis up to 4*CPUs segments to balance fluctuating density of spheres
  196. #if defined(AG_OPENMP)
  197.         int segments = omp_get_max_threads() * 4;
  198. #else
  199.         int segments = 1;
  200. #endif
  201.         int numCellsXPerSegment = Mathi::Max(1, int(Mathd::Ceil(double(numCells.x) / segments)));
  202.         // The reason we do this is that iterating through all X elements concurrently and then through all the spheres
  203.         // would be waste of computational time since not all spheres intersect the particular X coordinate, so we divide
  204.         // the X axis to a reasonable number of segments and iterate only the ones which might really intersect the sphere.
  205. // FIRST DETERMINE THE SIZE OF EACH CELL
  206. assert(sizeof(size_t) == sizeof(T*));
  207. size_t *counts = (size_t*)grid;
  208. memset(counts, 0, sizeof(size_t) * numAllCells);
  209. #if defined(AG_OPENMP)
  210.     #pragma omp parallel for schedule(dynamic, 1)
  211. #endif
  212.         // For each segment and each sphere
  213.         for (int i = 0; i < segments; i++)
  214.             for (int s = 0; s < num; s++)
  215.             {
  216.                 // Get the range of indices for this sphere
  217.                 int sindex = s*2;
  218.                 Vec3i indicesMin = sphereIndicesRange[sindex];
  219.                 Vec3i indicesMax = sphereIndicesRange[sindex+1];
  220.                 // Adjust indices in the X axis for them to be in this segment
  221.                 indicesMin.x = Mathi::Max(i * numCellsXPerSegment, indicesMin.x);
  222.                 indicesMax.x = Mathi::Min((i+1) * numCellsXPerSegment - 1, indicesMax.x);
  223.                 // For each cell, insert the sphere if intersecting
  224.                 for (int x = indicesMin.x; x <= indicesMax.x; x++)
  225.                     for (int y = indicesMin.y; y <= indicesMax.y; y++)
  226.                         for (int z = indicesMin.z; z <= indicesMax.z; z++)
  227.                         {
  228.                             int index = getScalarIndexFromIndices(Vec3i(x, y, z));
  229.                             if (Intersect(boxes[index], Sphere3d(Vec3d(pos[s]), radius)))
  230.                                 ++counts[index];
  231.                         }
  232.             }
  233. // ALLOCATE CELLS
  234. for (int i = 0; i < numAllCells; i++)
  235. {
  236. T *cell = new T[2 + counts[i]];
  237. cell[0] = T(counts[i]);
  238. cell[1] = 0;
  239. grid[i] = cell;
  240. }
  241. // THEN, INSERT ATOMS INTO THE CELLS
  242. #if defined(AG_OPENMP)
  243.     #pragma omp parallel for schedule(dynamic, 1)
  244. #endif
  245.         // For each segment and each sphere
  246.         for (int i = 0; i < segments; i++)
  247.             for (int s = 0; s < num; s++)
  248.             {
  249.                 // Get the range of indices for this sphere
  250.                 int sindex = s*2;
  251.                 Vec3i indicesMin = sphereIndicesRange[sindex];
  252.                 Vec3i indicesMax = sphereIndicesRange[sindex+1];
  253.                 // Adjust indices in the X axis for them to be in this segment
  254.                 indicesMin.x = Mathi::Max(i * numCellsXPerSegment, indicesMin.x);
  255.                 indicesMax.x = Mathi::Min((i+1) * numCellsXPerSegment - 1, indicesMax.x);
  256.                 // For each cell, insert the sphere if intersecting
  257.                 for (int x = indicesMin.x; x <= indicesMax.x; x++)
  258.                     for (int y = indicesMin.y; y <= indicesMax.y; y++)
  259.                         for (int z = indicesMin.z; z <= indicesMax.z; z++)
  260.                         {
  261.                             int index = getScalarIndexFromIndices(Vec3i(x, y, z));
  262.                             if (Intersect(boxes[index], Sphere3d(Vec3d(pos[s]), radius)))
  263.                                 insertIntoCell(grid[index], T(s));
  264.                         }
  265.             }
  266.         delete [] sphereIndicesRange;
  267.         delete [] boxes;
  268.     }
  269. private:
  270.     T **grid;
  271.     Vec3i numCells, numCellsMinus1;
  272. int numAllCells;
  273.     Vec3d cellSize, cellSizeHalf, cellSizeInv;
  274.     Vec3d cornerPosMin, cornerCellPosMin;
  275.     double cellDiagonalHalf;
  276. };