]> OCCT Git - occt.git/commitdiff
Modeling - Bnd_BoundSortBox::Compare fails in some cases #518
authorDmitrii Kulikov <164657232+AtheneNoctuaPt@users.noreply.github.com>
Fri, 16 May 2025 15:04:48 +0000 (16:04 +0100)
committerGitHub <noreply@github.com>
Fri, 16 May 2025 15:04:48 +0000 (16:04 +0100)
Class Bnd_BoundSortBox is refactored.
Some cases when Bnd_BoundSortBox could potentially fail to check
  intersection between boxes are fixed.
Google tests are added for Bnd_BoundSortBox class.

src/FoundationClasses/TKMath/Bnd/Bnd_BoundSortBox.cxx
src/FoundationClasses/TKMath/Bnd/Bnd_BoundSortBox.hxx
src/FoundationClasses/TKMath/GTests/Bnd_BoundSortBox_Test.cxx [new file with mode: 0644]
src/FoundationClasses/TKMath/GTests/FILES.cmake

index 28fedb1d6c95da7c241174378577d2d90916e5af..5f0501fb78f6e07afed74d34d2e6f8f24da43e89 100644 (file)
 // Alternatively, this file may be used under the terms of Open CASCADE
 // commercial license or contractual agreement.
 
-#include <Bnd_Array1OfBox.hxx>
 #include <Bnd_BoundSortBox.hxx>
-#include <Bnd_Box.hxx>
+
 #include <gp_Pln.hxx>
 #include <Standard_MultiplyDefined.hxx>
 #include <Standard_NullValue.hxx>
-#include <TColStd_DataMapIteratorOfDataMapOfIntegerInteger.hxx>
-#include <TColStd_ListIteratorOfListOfInteger.hxx>
 
-#include <stdio.h>
-//-- ================================================================================
+#include <unordered_map>
+
+//==================================================================================================
 //--  lbr le 27 fev 97
 //--
 //--
 //--
 //--       *) Preserve a table representation of bit of voxels of space BE
 //--          that contains at least one box Bi.
-//--       *) While box bt is texted, it is checked if this box includes in
+//--       *) While box bt is tested, it is checked if this box includes in
 //--          the table of bit at least one occupied voxel.
 //--          If the occupied voxel is touched : no rejection
 //--          Otherwise return
 //--
 //--      **) Another rejection was adopted. It consists in trying to locate in
 //--          the above structures (tables X,Y,Z and  table of Bits) a box Bi which is greater than
-// the
-//--          bounding box BE.
+//--          the bounding box BE.
 //--
-//--          The indices of these boxes are located in table ToTest, and these
+//--          The indices of these boxes are located in table myLargeBoxes, and these
 //            boxes are compared systematically with bt.
 //--
 //--   Note : tables C replace here HArray1OfListOfInteger and other
 //--          structures that are sufficient for data adding but slow for reading.
 //--
 //--          Here the data is added at start (Initialize and SortBoxes) and later,
-//--          it takes much time to parse the tables. The slowly written, byut fastly read
-// structures
-//--          are thus better.
+//--          it takes much time to parse the tables. The slowly written, but quickly read
+//--          structures are thus better.
 //--
-//=======================================================================
-#define VERIFICATION 0
-#define DEBUG 0
-#define DIMAXIS 20
-
-#if DEBUG
-static unsigned int APPELREJECTION  = 0L;
-static unsigned int REJECTNIV0      = 0L;
-static unsigned int REJECTNIV1      = 0L;
-static unsigned int NBCOMPARE       = 0L;
-static unsigned int NBBOITES        = 0L;
-static unsigned int NBBOITESATESTER = 0L;
-#endif
-//=======================================================================
-static Standard_Integer ComputeSize(const Standard_Integer n)
-{
-  if (n > 40000)
-    return (128);
-  if (n > 10000)
-    return (64);
-  if (n > 1000)
-    return (32);
-  if (n > 100)
-    return (16);
-  return (8);
-}
+//==================================================================================================
 
-//=======================================================================
-static unsigned int _P2[32] = {
-  1,        2,        4,        8,         16,        32,        64,         128,
-  256,      512,      1024,     2048,      4096,      8192,      16384,      32768,
-  65536,    131072,   262144,   524288,    1048576,   2097152,   4194304,    8388608,
-  16777216, 33554432, 67108864, 134217728, 268435456, 536870912, 1073741824, 2147483648U};
+//==================================================================================================
 
-//-- size is power of 2 > 4
-class BSB_T3Bits
+// This function determines the resolution of the voxel grid based on the number of boxes.
+// The resolution is a power of 2 as in the original code. Current implementation supports
+// resolutions any value of resolution.
+// @param theBoxesCount The number of boxes to be sorted.
+// @return The resolution of the voxel grid.
+static Standard_Integer getBnd_VoxelGridResolution(const Standard_Integer theBoxesCount)
 {
-public:
-  Standard_Integer _DECAL;
-  Standard_Integer _DECAL2;
-  Standard_Integer _BASE;
-  Standard_Integer _BASEM1;
-
-  unsigned int     ind;
-  unsigned int     Isize;
-  Standard_Integer ssize;
-  Standard_Real    Xmin, Xmax, Ymin, Ymax, Zmin, Zmax;
-
-  unsigned int*      p;
-  Standard_Integer** axisX;
-  Standard_Integer** axisY;
-  Standard_Integer** axisZ;
-
-  Standard_Integer* ToTest;
-
-public:
-  BSB_T3Bits(int size);
-  ~BSB_T3Bits();
-
-  //-- Part HArray1OfListOfInteger
-
-  void AppendAxisX(const Standard_Integer i, const Standard_Integer v);
-  void AppendAxisY(const Standard_Integer i, const Standard_Integer v);
-  void AppendAxisZ(const Standard_Integer i, const Standard_Integer v);
-
-  void Add(unsigned int t)
+  if (theBoxesCount > 40000)
   {
-    int o = t & 31;
-    int k = t >> 5;
-    p[k] |= _P2[o];
+    return 128;
   }
-
-  int Val(unsigned int t)
+  if (theBoxesCount > 10000)
   {
-    int o = t & 31;
-    int k = t >> 5;
-    return (p[k] & _P2[o]);
+    return 64;
   }
-
-  void Raz(unsigned int t)
+  if (theBoxesCount > 1000)
   {
-    int o = t & 31;
-    int k = t >> 5;
-    p[k] &= ~(_P2[o]);
+    return 32;
   }
+  if (theBoxesCount > 100)
+  {
+    return 16;
+  }
+  return 8;
+}
 
-  Standard_Integer NbAxisX(const Standard_Integer i) { return (axisX[0][i]); }
+//==================================================================================================
 
-  Standard_Integer NbAxisY(const Standard_Integer i) { return (axisY[0][i]); }
+// This class represents a 3D voxel grid used for spatial partitioning of boxes.
+// It provides methods to add boxes, check occupancy, and retrieve slices of the grid.
+// It can be populated with boxes by using the AddBox method, which takes a box index and its
+// voxel coordinates.
+class Bnd_VoxelGrid : public Standard_Transient
+{
+public:
+  DEFINE_STANDARD_RTTIEXT(Bnd_VoxelGrid, Standard_Transient)
 
-  Standard_Integer NbAxisZ(const Standard_Integer i) { return (axisZ[0][i]); }
+public:
+  using VectorInt = std::vector<Standard_Integer, NCollection_Allocator<Standard_Integer>>;
 
-  inline Standard_Integer GrilleInteger(Standard_Integer ix,
-                                        Standard_Integer iy,
-                                        Standard_Integer iz)
-  {
-    Standard_Integer tz = iz << _DECAL2;
-    Standard_Integer ty = iy << _DECAL;
-    Standard_Integer t  = ix;
-    t |= ty;
-    t |= tz;
-    return (t);
-  }
+private:
+  using VectorBool = std::vector<bool, NCollection_Allocator<bool>>;
+  using VoxelMap =
+    std::unordered_map<Standard_Integer,
+                       VectorInt,
+                       std::hash<Standard_Integer>,
+                       std::equal_to<Standard_Integer>,
+                       NCollection_Allocator<std::pair<const Standard_Integer, VectorInt>>>;
 
-  inline void IntegerGrille(Standard_Integer  t,
-                            Standard_Integer& ix,
-                            Standard_Integer& iy,
-                            Standard_Integer& iz)
-  {
-    ix = t & _BASEM1;
-    t >>= _DECAL;
-    iy = t & _BASEM1;
-    t >>= _DECAL;
-    iz = t;
+public:
+  // Constructor that initializes the Bnd_VoxelGrid object with a given size.
+  // @param theSize The size of the grid in each dimension.
+  Bnd_VoxelGrid(const Standard_Integer theResolution);
+
+  // Adds a box to the voxel grid.
+  // The box is defined by its minimum and maximum voxel coordinates.
+  // @param theBoxIndex The index of the box to be added.
+  // @param theVoxelBox An array of 6 integers representing the minimum and maximum voxel
+  //                    coordinates of the box in the order: [minX, minY, minZ, maxX, maxY, maxZ].
+  void AddBox(const Standard_Integer                 theBoxIndex,
+              const std::array<Standard_Integer, 6>& theVoxelBox);
+
+  // Returns the list of box indices that occupy the specified voxel index in the X direction.
+  // In other words, the vector will contain the indices of boxes that occupy the voxels with
+  // indices in the range
+  // [[theVoxelIndex, 0, 0], [theVoxelIndex, 0, 1], ..., [theVoxelIndex, resolution, resolution]].
+  // @param theVoxelIndex The index of the voxel in the X direction.
+  // @return A pointer to a vector of integers representing the box indices.
+  //         The vector can be null if no boxes occupy the voxel.
+  const VectorInt* GetSliceX(const Standard_Integer theVoxelIndex) const;
+
+  // Returns the list of box indices that occupy the specified voxel index in the Y direction.
+  // In other words, the vector will contain the indices of boxes that occupy the voxels with
+  // indices in the range
+  // [[0, theVoxelIndex, 0], [1, theVoxelIndex, 0], ..., [resolution, theVoxelIndex, resolution]].
+  // @param theVoxelIndex The index of the voxel in the Y direction.
+  // @return A pointer to a vector of integers representing the box indices.
+  //         The vector can be null if no boxes occupy the voxel.
+  const VectorInt* GetSliceY(const Standard_Integer theVoxelIndex) const;
+
+  // Returns the list of box indices that occupy the specified voxel index in the Z direction.
+  // In other words, the vector will contain the indices of boxes that occupy the voxels with
+  // indices in the range
+  // [[0, 0, theVoxelIndex], [1, 0, theVoxelIndex], ..., [resolution, resolution, theVoxelIndex]].
+  // @param theVoxelIndex The index of the voxel in the Z direction.
+  // @return A pointer to a vector of integers representing the box indices.
+  //         The vector can be null if no boxes occupy the voxel.
+  const VectorInt* GetSliceZ(const Standard_Integer theVoxelIndex) const;
+
+  // Returns true if the specified voxel box is occupied by any box previously added to the grid
+  // via the AddBox method.
+  // @param theVoxelBox An array of 6 integers representing the minimum and maximum voxel
+  //                    coordinates of the box in the order: [minX, minY, minZ, maxX, maxY, maxZ].
+  // @return True if the voxel box is occupied, false otherwise.
+  bool IsOccupied(const std::array<Standard_Integer, 6>& theVoxelBox) const;
+
+private:
+  // Appends a slice of the voxel grid in the X direction.
+  // This method is used to store the indices of boxes that occupy a specific voxel index in the X
+  // direction.
+  // @param theVoxelIndexMin The minimum voxel index in the X direction.
+  // @param theVoxelIndexMax The maximum voxel index in the X direction.
+  // @param theBoxIndex The index of the box to be added.
+  void AppendSliceX(const Standard_Integer theVoxelIndexMin,
+                    const Standard_Integer theVoxelIndexMax,
+                    const Standard_Integer theBoxIndex);
+
+  // Appends a slice of the voxel grid in the Y direction.
+  // This method is used to store the indices of boxes that occupy a specific voxel index in the Y
+  // direction.
+  // @param theVoxelIndexMin The minimum voxel index in the Y direction.
+  // @param theVoxelIndexMax The maximum voxel index in the Y direction.
+  // @param theBoxIndex The index of the box to be added.
+  void AppendSliceY(const Standard_Integer theVoxelIndexMin,
+                    const Standard_Integer theVoxelIndexMax,
+                    const Standard_Integer theBoxIndex);
+
+  // Appends a slice of the voxel grid in the Z direction.
+  // This method is used to store the indices of boxes that occupy a specific voxel index in the Z
+  // direction.
+  // @param theVoxelIndexMin The minimum voxel index in the Z direction.
+  // @param theVoxelIndexMax The maximum voxel index in the Z direction.
+  // @param theBoxIndex The index of the box to be added.
+  void AppendSliceZ(const Standard_Integer theVoxelIndexMin,
+                    const Standard_Integer theVoxelIndexMax,
+                    const Standard_Integer theBoxIndex);
+
+  // Marks the voxel at the specified coordinates as occupied.
+  // @param theX The x-coordinate of the voxel to mark.
+  // @param theY The y-coordinate of the voxel to mark.
+  // @param theZ The z-coordinate of the voxel to mark.
+  inline void SetOccupied(const Standard_Integer theX,
+                          const Standard_Integer theY,
+                          const Standard_Integer theZ)
+  {
+    myVoxelBits[toIndex(theX, theY, theZ)] = true;
+  }
+
+  // Returns true if the voxel at the specified coordinates is marked as occupied.
+  // @param theX The x-coordinate of the voxel to check.
+  // @param theY The y-coordinate of the voxel to check.
+  // @param theZ The z-coordinate of the voxel to check.
+  // @return True if the voxel is occupied, false otherwise.
+  inline bool IsOccupied(const Standard_Integer theX,
+                         const Standard_Integer theY,
+                         const Standard_Integer theZ) const
+  {
+    return myVoxelBits[toIndex(theX, theY, theZ)];
+  }
+
+  // Converts the 3D grid coordinates to a 1D index.
+  // The index is calculated based on the formula:
+  // index = x * dimensionSize^2 + y * dimensionSize + z
+  // This formula maps the 3D coordinates to a unique index in the 1D array.
+  // @param theX The x-coordinate in the grid.
+  // @param theY The y-coordinate in the grid.
+  // @param theZ The z-coordinate in the grid.
+  // @return The calculated index in the 1D array.
+  inline Standard_Integer toIndex(const Standard_Integer theX,
+                                  const Standard_Integer theY,
+                                  const Standard_Integer theZ) const
+  {
+    return theX * myResolution * myResolution + theY * myResolution + theZ;
   }
 
 private:
-  BSB_T3Bits(const BSB_T3Bits&);
-  BSB_T3Bits& operator=(const BSB_T3Bits&);
+  const Standard_Integer myResolution; //< Number of voxels in one dimension of the grid.
+  VectorBool myVoxelBits; //< Array of bits representing the occupancy of voxels in the grid.
+  VoxelMap   mySlicesX;   //< Map of voxel indices to lists of box indices for the X-axis.
+  VoxelMap   mySlicesY;   //< Map of voxel indices to lists of box indices for the Y-axis.
+  VoxelMap   mySlicesZ;   //< Map of voxel indices to lists of box indices for the Z-axis.
 };
 
-//=======================================================================
-BSB_T3Bits::~BSB_T3Bits()
+IMPLEMENT_STANDARD_RTTIEXT(Bnd_VoxelGrid, Standard_Transient)
+
+//==================================================================================================
+
+Bnd_VoxelGrid::Bnd_VoxelGrid(const Standard_Integer theResolution)
+    : myResolution(theResolution),
+      myVoxelBits(theResolution * theResolution * theResolution),
+      mySlicesX(),
+      mySlicesY(),
+      mySlicesZ()
 {
-  if (p)
-  {
-    delete[] p;
-    p = 0;
-  }
-#if DEBUG
-  printf("\n BASE:%d\n", _BASE);
-#endif
-  for (Standard_Integer i = 0; i <= ssize; i++)
-  {
-    if (axisX[i])
-    {
-      delete[] axisX[i];
-      axisX[i] = 0;
-    }
-    if (axisY[i])
-    {
-      delete[] axisY[i];
-      axisY[i] = 0;
-    }
-    if (axisZ[i])
+}
+
+//==================================================================================================
+
+void Bnd_VoxelGrid::AddBox(const Standard_Integer                 theBoxIndex,
+                           const std::array<Standard_Integer, 6>& theVoxelBox)
+{
+  const Standard_Integer aMinVoxelX = theVoxelBox[0];
+  const Standard_Integer aMinVoxelY = theVoxelBox[1];
+  const Standard_Integer aMinVoxelZ = theVoxelBox[2];
+  const Standard_Integer aMaxVoxelX = theVoxelBox[3];
+  const Standard_Integer aMaxVoxelY = theVoxelBox[4];
+  const Standard_Integer aMaxVoxelZ = theVoxelBox[5];
+
+  AppendSliceX(aMinVoxelX, aMaxVoxelX, theBoxIndex);
+  AppendSliceY(aMinVoxelY, aMaxVoxelY, theBoxIndex);
+  AppendSliceZ(aMinVoxelZ, aMaxVoxelZ, theBoxIndex);
+  // Fill table with bits.
+  for (Standard_Integer aVoxelX = aMinVoxelX; aVoxelX <= aMaxVoxelX; ++aVoxelX)
+  {
+    for (Standard_Integer aVoxelY = aMinVoxelY; aVoxelY <= aMaxVoxelY; ++aVoxelY)
     {
-      delete[] axisZ[i];
-      axisZ[i] = 0;
+      for (Standard_Integer aVoxelZ = aMinVoxelZ; aVoxelZ <= aMaxVoxelZ; ++aVoxelZ)
+      {
+        SetOccupied(aVoxelX, aVoxelY, aVoxelZ);
+      }
     }
   }
-  free(axisX);
-  axisX = 0;
-  free(axisY);
-  axisY = 0;
-  free(axisZ);
-  axisZ = 0;
-  if (ToTest)
-  {
-    delete[] ToTest;
-    ToTest = 0;
-  }
 }
 
-//=======================================================================
-BSB_T3Bits::BSB_T3Bits(int size)
-    : ind(0),
-      Xmin(0),
-      Xmax(0),
-      Ymin(0),
-      Ymax(0),
-      Zmin(0),
-      Zmax(0)
+//==================================================================================================
+
+const Bnd_VoxelGrid::VectorInt* Bnd_VoxelGrid::GetSliceX(const Standard_Integer theVoxelIndex) const
 {
-  switch (size)
-  {
-    case 128: {
-      _DECAL  = 7;
-      _DECAL2 = 14;
-      _BASE   = 128;
-      _BASEM1 = 127;
-      break;
-    }
-    case 64: {
-      _DECAL  = 6;
-      _DECAL2 = 12;
-      _BASE   = 64;
-      _BASEM1 = 63;
-      break;
-    }
-    case 32: {
-      _DECAL  = 5;
-      _DECAL2 = 10;
-      _BASE   = 32;
-      _BASEM1 = 31;
-      break;
-    }
-    case 16: {
-      _DECAL  = 4;
-      _DECAL2 = 8;
-      _BASE   = 16;
-      _BASEM1 = 15;
-      break;
-    }
-    default: {
-      _DECAL  = 3;
-      _DECAL2 = 6;
-      _BASE   = 8;
-      _BASEM1 = 7;
-      break;
-    }
-  }
-  Standard_Integer i;
-  unsigned int     nb = (size * size * size) >> 5;
-  Isize               = nb;
-  ssize               = size;
-  p                   = new unsigned int[nb];
-  do
-  {
-    p[--nb] = 0;
-  } while (nb);
+  auto it = mySlicesX.find(theVoxelIndex);
+  return it != mySlicesX.end() ? &it->second : nullptr;
+}
 
-  axisX = (Standard_Integer**)malloc((size + 1) * sizeof(Standard_Integer*));
-  axisY = (Standard_Integer**)malloc((size + 1) * sizeof(Standard_Integer*));
-  axisZ = (Standard_Integer**)malloc((size + 1) * sizeof(Standard_Integer*));
+//==================================================================================================
 
-  axisX[0] = new Standard_Integer[_BASE + 1];
-  axisY[0] = new Standard_Integer[_BASE + 1];
-  axisZ[0] = new Standard_Integer[_BASE + 1];
+const Bnd_VoxelGrid::VectorInt* Bnd_VoxelGrid::GetSliceY(const Standard_Integer theVoxelIndex) const
+{
+  auto it = mySlicesY.find(theVoxelIndex);
+  return it != mySlicesY.end() ? &it->second : nullptr;
+}
 
-  for (i = 0; i < (_BASE + 1); i++)
-  {
-    axisX[0][i] = 0;
-    axisY[0][i] = 0;
-    axisZ[0][i] = 0;
-  }
+//==================================================================================================
 
-  for (i = 1; i <= size; i++)
-  {
-    axisX[i]    = new Standard_Integer[DIMAXIS];
-    axisY[i]    = new Standard_Integer[DIMAXIS];
-    axisZ[i]    = new Standard_Integer[DIMAXIS];
-    axisX[i][0] = DIMAXIS;
-    axisY[i][0] = DIMAXIS;
-    axisZ[i][0] = DIMAXIS;
-    axisX[i][1] = axisY[i][1] = axisZ[i][1] = -1;
-  }
-  ToTest = 0;
+const Bnd_VoxelGrid::VectorInt* Bnd_VoxelGrid::GetSliceZ(const Standard_Integer theVoxelIndex) const
+{
+  auto it = mySlicesZ.find(theVoxelIndex);
+  return it != mySlicesZ.end() ? &it->second : nullptr;
 }
 
-//=======================================================================
-void BSB_T3Bits::AppendAxisZ(const Standard_Integer i, const Standard_Integer v)
+//==================================================================================================
+
+bool Bnd_VoxelGrid::IsOccupied(const std::array<Standard_Integer, 6>& theVoxelBox) const
 {
-  Standard_Integer n = axisZ[0][i];
-  n++;
-  if (n < axisZ[i][0])
-  {
-    axisZ[i][n] = v;
-  }
-  else
+  const Standard_Integer aMinVoxelX = theVoxelBox[0];
+  const Standard_Integer aMinVoxelY = theVoxelBox[1];
+  const Standard_Integer aMinVoxelZ = theVoxelBox[2];
+  const Standard_Integer aMaxVoxelX = theVoxelBox[3];
+  const Standard_Integer aMaxVoxelY = theVoxelBox[4];
+  const Standard_Integer aMaxVoxelZ = theVoxelBox[5];
+
+  for (Standard_Integer aVoxelX = aMinVoxelX; aVoxelX <= aMaxVoxelX; ++aVoxelX)
   {
-    Standard_Integer  s  = axisZ[i][0];
-    Standard_Integer* nt = new Standard_Integer[s + s];
-    nt[0]                = s + s;
-    for (Standard_Integer j = 1; j < s; j++)
+    for (Standard_Integer aVoxelY = aMinVoxelY; aVoxelY <= aMaxVoxelY; ++aVoxelY)
     {
-      nt[j] = axisZ[i][j];
+      for (Standard_Integer aVoxelZ = aMinVoxelZ; aVoxelZ <= aMaxVoxelZ; ++aVoxelZ)
+      {
+        if (IsOccupied(aVoxelX, aVoxelY, aVoxelZ))
+        {
+          return true; // Found an occupied voxel.
+        }
+      }
     }
-    nt[n] = v;
-    delete[] axisZ[i];
-    axisZ[i] = nt;
   }
-  axisZ[0][i] = n;
+  return false; // No occupied voxels found.
 }
 
-//=======================================================================
-void BSB_T3Bits::AppendAxisY(const Standard_Integer i, const Standard_Integer v)
+//==================================================================================================
+
+void Bnd_VoxelGrid::AppendSliceX(const Standard_Integer theVoxelIndexMin,
+                                 const Standard_Integer theVoxelIndexMax,
+                                 const Standard_Integer theBoxIndex)
 {
-  Standard_Integer n = axisY[0][i];
-  n++;
-  if (n < axisY[i][0])
-  {
-    axisY[i][n] = v;
-  }
-  else
+  for (Standard_Integer i = theVoxelIndexMin; i <= theVoxelIndexMax; ++i)
   {
-    Standard_Integer  s  = axisY[i][0];
-    Standard_Integer* nt = new Standard_Integer[s + s];
-    nt[0]                = s + s;
-    for (Standard_Integer j = 1; j < s; j++)
-    {
-      nt[j] = axisY[i][j];
-    }
-    nt[n] = v;
-    delete[] axisY[i];
-    axisY[i] = nt;
+    mySlicesX[i].push_back(theBoxIndex);
   }
-  axisY[0][i] = n;
 }
 
-//=======================================================================
-void BSB_T3Bits::AppendAxisX(const Standard_Integer i, const Standard_Integer v)
-{
-  Standard_Integer n = axisX[0][i];
+//==================================================================================================
 
-  n++;
-  if (n < axisX[i][0])
+void Bnd_VoxelGrid::AppendSliceY(const Standard_Integer theVoxelIndexMin,
+                                 const Standard_Integer theVoxelIndexMax,
+                                 const Standard_Integer theBoxIndex)
+{
+  for (Standard_Integer i = theVoxelIndexMin; i <= theVoxelIndexMax; ++i)
   {
-    axisX[i][n] = v;
+    mySlicesY[i].push_back(theBoxIndex);
   }
-  else
-  {
-    //-- it is required to extend
-    Standard_Integer  s  = axisX[i][0];
-    Standard_Integer* nt = new Standard_Integer[s + s];
-    nt[0]                = s + s;
-    for (Standard_Integer j = 1; j < s; j++)
-    {
-      nt[j] = axisX[i][j];
-    }
-    nt[n] = v;
-    delete[] axisX[i];
-    axisX[i] = nt;
-  }
-  axisX[0][i] = n;
 }
 
-//=================================================================================================
+//==================================================================================================
 
-Bnd_BoundSortBox::Bnd_BoundSortBox()
-    : myBox(),
-      myBndComponents(nullptr),
-      Xmin(0.),
-      Ymin(0.),
-      Zmin(0.),
-      deltaX(0.),
-      deltaY(0.),
-      deltaZ(0.),
-      discrX(0),
-      discrY(0),
-      discrZ(0),
-      theFound(0),
-      Crible(),
-      lastResult(),
-      TabBits(0)
+void Bnd_VoxelGrid::AppendSliceZ(const Standard_Integer theVoxelIndexMin,
+                                 const Standard_Integer theVoxelIndexMax,
+                                 const Standard_Integer theBoxIndex)
 {
-#if DEBUG
-  NBCOMPARE       = 0L;
-  NBBOITES        = 0L;
-  NBBOITESATESTER = 0L;
-  APPELREJECTION  = 0L;
-  REJECTNIV0      = 0L;
-  REJECTNIV1      = 0L;
-#endif
+  for (Standard_Integer i = theVoxelIndexMin; i <= theVoxelIndexMax; ++i)
+  {
+    mySlicesZ[i].push_back(theBoxIndex);
+  }
 }
 
-//=================================================================================================
+//==================================================================================================
 
-void Bnd_BoundSortBox::Initialize(const Bnd_Box&                  CompleteBox,
-                                  const Handle(Bnd_HArray1OfBox)& SetOfBox)
+Bnd_BoundSortBox::Bnd_BoundSortBox()
+    : myEnclosingBox(),
+      myBoxes(nullptr),
+      myCoeffX(0.),
+      myCoeffY(0.),
+      myCoeffZ(0.),
+      myResolution(0),
+      myLastResult(),
+      myLargeBoxes(),
+      myVoxelGrid(nullptr)
 {
-  myBox                        = CompleteBox;
-  myBndComponents              = SetOfBox;
-  const Bnd_Array1OfBox& taBox = myBndComponents->Array1();
-  discrX = discrY = discrZ = ComputeSize(taBox.Upper() - taBox.Lower());
-  Standard_Real Xmax, Ymax, Zmax;
-  if (CompleteBox.IsVoid())
-    return;
-  CompleteBox.Get(Xmin, Ymin, Zmin, Xmax, Ymax, Zmax);
-  deltaX = (Xmax - Xmin == 0. ? 0. : discrX / (Xmax - Xmin));
-  deltaY = (Ymax - Ymin == 0. ? 0. : discrY / (Ymax - Ymin));
-  deltaZ = (Zmax - Zmin == 0. ? 0. : discrZ / (Zmax - Zmin));
-  SortBoxes();
 }
 
-//=================================================================================================
+//==================================================================================================
 
-void Bnd_BoundSortBox::Initialize(const Handle(Bnd_HArray1OfBox)& SetOfBox)
+void Bnd_BoundSortBox::Initialize(const Handle(Bnd_HArray1OfBox)& theSetOfBoxes)
 {
-  myBndComponents              = SetOfBox;
-  const Bnd_Array1OfBox& taBox = myBndComponents->Array1();
-  Standard_Integer       i0, i1;
-  i0     = taBox.Lower();
-  i1     = taBox.Upper();
-  discrX = discrY = discrZ = ComputeSize(i1 - i0);
-  Standard_Integer labox;
-  for (labox = i0; labox <= i1; labox++)
+  myBoxes = theSetOfBoxes;
+
+  for (Standard_Integer aBoxIndex = myBoxes->Lower(); aBoxIndex <= myBoxes->Upper(); ++aBoxIndex)
   {
-    if (!taBox(labox).IsVoid())
+    const Bnd_Box& aBox = myBoxes->Value(aBoxIndex);
+    if (!aBox.IsVoid())
     {
-      myBox.Add(taBox(labox));
+      myEnclosingBox.Add(aBox);
     }
   }
-  Standard_Real Xmax, Ymax, Zmax;
-  if (myBox.IsVoid())
+
+  myResolution = getBnd_VoxelGridResolution(myBoxes->Size());
+
+  if (myEnclosingBox.IsVoid())
+  {
     return;
-  myBox.Get(Xmin, Ymin, Zmin, Xmax, Ymax, Zmax);
-  deltaX = (Xmax - Xmin == 0. ? 0. : discrX / (Xmax - Xmin));
-  deltaY = (Ymax - Ymin == 0. ? 0. : discrY / (Ymax - Ymin));
-  deltaZ = (Zmax - Zmin == 0. ? 0. : discrZ / (Zmax - Zmin));
-  SortBoxes();
+  }
+
+  calculateCoefficients();
+  resetVoxelGrid();
+
+  sortBoxes();
 }
 
-//=================================================================================================
+//==================================================================================================
 
-void Bnd_BoundSortBox::SortBoxes()
+void Bnd_BoundSortBox::Initialize(const Bnd_Box&                  theEnclosingBox,
+                                  const Handle(Bnd_HArray1OfBox)& theSetOfBoxes)
 {
-  Standard_Integer       labox;
-  Standard_Integer       lacaseX, firstcaseX, lastcaseX;
-  Standard_Integer       lacaseY, firstcaseY, lastcaseY;
-  Standard_Integer       lacaseZ, firstcaseZ, lastcaseZ;
-  Standard_Real          xmin, ymin, zmin, xmax, ymax, zmax;
-  const Bnd_Array1OfBox& taBox = myBndComponents->Array1();
-  Standard_Integer       i0    = taBox.Lower();
-  Standard_Integer       i1    = taBox.Upper();
-  BSB_T3Bits*            Map   = 0;
-  if (TabBits)
-  {
-    BSB_T3Bits* _Map = (BSB_T3Bits*)TabBits;
-    delete _Map;
-  }
-  Map     = new BSB_T3Bits(discrX);
-  TabBits = (void*)Map;
-  if (Map->ToTest == 0)
-  {
-    Standard_Integer s = i1 - i0;
-    if (s < 2)
-      s = 2;
-    Map->ToTest = new Standard_Integer[s];
-    for (Standard_Integer i = 0; i < s; i++)
-    {
-      Map->ToTest[i] = i0 - 1;
-    }
-  }
-  Standard_Real _Xmax, _Xmin, _Ymax, _Ymin, _Zmin, _Zmax;
-  myBox.Get(_Xmin, _Ymin, _Zmin, _Xmax, _Ymax, _Zmax);
-  Map->Xmax = _Xmax;
-  Map->Ymax = _Ymax;
-  Map->Zmax = _Zmax;
-  Map->Xmin = _Xmin;
-  Map->Ymin = _Ymin;
-  Map->Zmin = _Zmin;
-  for (labox = i0; labox <= i1; labox++)
+  myBoxes        = theSetOfBoxes;
+  myEnclosingBox = theEnclosingBox;
+  myResolution   = getBnd_VoxelGridResolution(myBoxes->Size());
+
+  if (myEnclosingBox.IsVoid())
   {
-    if (!taBox(labox).IsVoid())
-    {
-      taBox(labox).Get(xmin, ymin, zmin, xmax, ymax, zmax);
-      if (xmin > Xmin)
-        firstcaseX = (Standard_Integer)((xmin - Xmin) * deltaX) - 1;
-      else
-        firstcaseX = 1;
-      if (ymin > Ymin)
-        firstcaseY = (Standard_Integer)((ymin - Ymin) * deltaY) - 1;
-      else
-        firstcaseY = 1;
-      if (zmin > Zmin)
-        firstcaseZ = (Standard_Integer)((zmin - Zmin) * deltaZ) - 1;
-      else
-        firstcaseZ = 1;
-      if (xmax < _Xmax)
-        lastcaseX = (Standard_Integer)((xmax - Xmin) * deltaX) + 1;
-      else
-        lastcaseX = discrX;
-      if (ymax < _Ymax)
-        lastcaseY = (Standard_Integer)((ymax - Ymin) * deltaY) + 1;
-      else
-        lastcaseY = discrY;
-      if (zmax < _Zmax)
-        lastcaseZ = (Standard_Integer)((zmax - Zmin) * deltaZ) + 1;
-      else
-        lastcaseZ = discrZ;
-      if (firstcaseX < 1)
-        firstcaseX = 1;
-      else if (firstcaseX > discrX)
-        firstcaseX = discrX;
-      if (firstcaseY < 1)
-        firstcaseY = 1;
-      else if (firstcaseY > discrY)
-        firstcaseY = discrY;
-      if (firstcaseZ < 1)
-        firstcaseZ = 1;
-      else if (firstcaseZ > discrZ)
-        firstcaseZ = discrZ;
-
-      if (lastcaseX < 1)
-        lastcaseX = 1;
-      else if (lastcaseX > discrX)
-        lastcaseX = discrX;
-      if (lastcaseY < 1)
-        lastcaseY = 1;
-      else if (lastcaseY > discrY)
-        lastcaseY = discrY;
-      if (lastcaseZ < 1)
-        lastcaseZ = 1;
-      else if (lastcaseZ > discrZ)
-        lastcaseZ = discrZ;
-
-      Standard_Integer n = (lastcaseX - firstcaseX);
-      if (n > (lastcaseY - firstcaseY))
-        n = lastcaseY - firstcaseY;
-      if (n > (lastcaseZ - firstcaseZ))
-        n = lastcaseZ - firstcaseZ;
-#if DEBUG
-      NBBOITES++;
-#endif
-      n <<= 2;
-      if (n > discrX)
-      {
-#if DEBUG
-        NBBOITESATESTER++;
-#endif
-        for (Standard_Integer i = 0; i < (i1 - i0); i++)
-        {
-          if (Map->ToTest[i] < i0)
-          {
-            Map->ToTest[i] = labox;
-            break;
-          }
-        }
-      }
-      else
-      {
-        for (lacaseX = firstcaseX; lacaseX <= lastcaseX; lacaseX++)
-        {
-          Map->AppendAxisX(lacaseX, labox);
-        }
-        for (lacaseY = firstcaseY; lacaseY <= lastcaseY; lacaseY++)
-        {
-          Map->AppendAxisY(lacaseY, labox);
-        }
-        for (lacaseZ = firstcaseZ; lacaseZ <= lastcaseZ; lacaseZ++)
-        {
-          Map->AppendAxisZ(lacaseZ, labox);
-        }
-        //------------------------------------------------------------
-        //-- fill table with bits
-        //--
-        if (Map)
-        {
-          for (lacaseX = firstcaseX; lacaseX <= lastcaseX; lacaseX++)
-          {
-            for (lacaseY = firstcaseY; lacaseY <= lastcaseY; lacaseY++)
-            {
-              for (lacaseZ = firstcaseZ; lacaseZ <= lastcaseZ; lacaseZ++)
-              {
-                unsigned int t = Map->GrilleInteger(lacaseX - 1, lacaseY - 1, lacaseZ - 1);
-                Map->Add(t);
-              }
-            }
-          }
-        }
-      }
-    }
+    return;
   }
+
+  calculateCoefficients();
+  resetVoxelGrid();
+
+  sortBoxes();
 }
 
-//=================================================================================================
+//==================================================================================================
 
-void Bnd_BoundSortBox::Initialize(const Bnd_Box& CompleteBox, const Standard_Integer nbComponents)
+void Bnd_BoundSortBox::Initialize(const Bnd_Box& theEnclosingBox, const Standard_Integer theNbBoxes)
 {
-  Standard_NullValue_Raise_if(nbComponents <= 0, "BoundSortBox nul!");
-  myBox           = CompleteBox;
-  myBndComponents = new Bnd_HArray1OfBox(1, nbComponents);
-
+  Standard_NullValue_Raise_if(theNbBoxes <= 0, "Unexpected: theNbBoxes <= 0");
+  myBoxes = new Bnd_HArray1OfBox(1, theNbBoxes);
   //***>>> JCD - 04.08.2000 - Array initialization is missing...
   Bnd_Box emptyBox;
-  myBndComponents->Init(emptyBox);
+  myBoxes->Init(emptyBox);
   //***<<< JCD - End
+  myEnclosingBox = theEnclosingBox;
+  myResolution   = getBnd_VoxelGridResolution(theNbBoxes);
 
-  discrX = discrY = discrZ = ComputeSize(nbComponents);
-  Standard_Real Xmax, Ymax, Zmax;
-
-  if (CompleteBox.IsVoid())
-    return;
-  CompleteBox.Get(Xmin, Ymin, Zmin, Xmax, Ymax, Zmax);
-  myBox.Get(Xmin, Ymin, Zmin, Xmax, Ymax, Zmax);
-  deltaX = (Xmax - Xmin == 0. ? 0. : discrX / (Xmax - Xmin));
-  deltaY = (Ymax - Ymin == 0. ? 0. : discrY / (Ymax - Ymin));
-  deltaZ = (Zmax - Zmin == 0. ? 0. : discrZ / (Zmax - Zmin));
-  if (TabBits)
+  if (myEnclosingBox.IsVoid())
   {
-    BSB_T3Bits* _Map = (BSB_T3Bits*)TabBits;
-    delete _Map;
-    TabBits = 0;
+    return;
   }
-  BSB_T3Bits* Map = 0;
-  Map             = new BSB_T3Bits(discrX);
-  TabBits         = (void*)Map;
+
+  calculateCoefficients();
+  resetVoxelGrid();
 }
 
-//=================================================================================================
+//==================================================================================================
 
-void Bnd_BoundSortBox::Add(const Bnd_Box& theBox, const Standard_Integer boxIndex)
+void Bnd_BoundSortBox::Add(const Bnd_Box& theBox, const Standard_Integer theIndex)
 {
-  Standard_MultiplyDefined_Raise_if(!(myBndComponents->Value(boxIndex).IsVoid()),
+  Standard_MultiplyDefined_Raise_if(!(myBoxes->Value(theIndex).IsVoid()),
                                     " This box is already defined !");
-  if (!theBox.IsVoid())
+  if (theBox.IsVoid())
   {
-    Standard_Integer i0 = myBndComponents->Lower();
-    Standard_Integer i1 = myBndComponents->Upper();
-    Standard_Integer theGapX, firstGapX, lastGapX;
-    Standard_Integer theGapY, firstGapY, lastGapY;
-    Standard_Integer theGapZ, firstGapZ, lastGapZ;
-    Standard_Real    xmin, ymin, zmin, xmax, ymax, zmax;
-    myBndComponents->SetValue(boxIndex, theBox);
-    theBox.Get(xmin, ymin, zmin, xmax, ymax, zmax);
-    BSB_T3Bits* Map = (BSB_T3Bits*)TabBits;
-    if (Map->ToTest == 0)
-    {
-      Standard_Integer s = i1 - i0;
-      if (s < 2)
-        s = 2;
-      Map->ToTest = new Standard_Integer[s];
-      for (Standard_Integer i = 0; i < s; i++)
-      {
-        Map->ToTest[i] = i0 - 1;
-      }
-    }
-    Standard_Real _Xmax, _Ymax, _Zmax;
-    _Xmax = Map->Xmax;
-    _Ymax = Map->Ymax;
-    _Zmax = Map->Zmax;
-    if (xmin > Xmin)
-      firstGapX = (Standard_Integer)((xmin - Xmin) * deltaX) - 1;
-    else
-      firstGapX = 1;
-    if (ymin > Ymin)
-      firstGapY = (Standard_Integer)((ymin - Ymin) * deltaY) - 1;
-    else
-      firstGapY = 1;
-    if (zmin > Zmin)
-      firstGapZ = (Standard_Integer)((zmin - Zmin) * deltaZ) - 1;
-    else
-      firstGapZ = 1;
-    if (xmax < _Xmax)
-      lastGapX = (Standard_Integer)((xmax - Xmin) * deltaX) + 1;
-    else
-      lastGapX = discrX;
-    if (ymax < _Ymax)
-      lastGapY = (Standard_Integer)((ymax - Ymin) * deltaY) + 1;
-    else
-      lastGapY = discrY;
-    if (zmax < _Zmax)
-      lastGapZ = (Standard_Integer)((zmax - Zmin) * deltaZ) + 1;
-    else
-      lastGapZ = discrZ;
-    if (firstGapX < 1)
-      firstGapX = 1;
-    else if (firstGapX > discrX)
-      firstGapX = discrX;
-    if (firstGapY < 1)
-      firstGapY = 1;
-    else if (firstGapY > discrY)
-      firstGapY = discrY;
-    if (firstGapZ < 1)
-      firstGapZ = 1;
-    else if (firstGapZ > discrZ)
-      firstGapZ = discrZ;
-
-    if (lastGapX < 1)
-      lastGapX = 1;
-    else if (lastGapX > discrX)
-      lastGapX = discrX;
-    if (lastGapY < 1)
-      lastGapY = 1;
-    else if (lastGapY > discrY)
-      lastGapY = discrY;
-    if (lastGapZ < 1)
-      lastGapZ = 1;
-    else if (lastGapZ > discrZ)
-      lastGapZ = discrZ;
-    Standard_Integer n = (lastGapX - firstGapX);
-    if (n > (lastGapY - firstGapY))
-      n = lastGapY - firstGapY;
-    if (n > (lastGapZ - firstGapZ))
-      n = lastGapZ - firstGapZ;
-    n <<= 2;
-#if DEBUG
-    NBBOITES++;
-#endif
-    if (n > discrX)
-    {
-#if DEBUG
-      NBBOITESATESTER++;
-#endif
-      for (Standard_Integer i = 0; i < (i1 - i0); i++)
-      {
-        if (Map->ToTest[i] < i0)
-        {
-          Map->ToTest[i] = boxIndex;
-          break;
-        }
-      }
-    }
-    for (theGapY = firstGapY; theGapY <= lastGapY; theGapY++)
-    {
-      Map->AppendAxisY(theGapY, boxIndex);
-    }
-    for (theGapX = firstGapX; theGapX <= lastGapX; theGapX++)
-    {
-      Map->AppendAxisX(theGapX, boxIndex);
-    }
-    for (theGapZ = firstGapZ; theGapZ <= lastGapZ; theGapZ++)
-    {
-      Map->AppendAxisZ(theGapZ, boxIndex);
-    }
-    //------------------------------------------------------------
-    //-- fill table with bits
-    //--
-    if (TabBits)
-    {
-      Map = (BSB_T3Bits*)TabBits;
-      for (theGapX = firstGapX; theGapX <= lastGapX; theGapX++)
-      {
-        for (theGapY = firstGapY; theGapY <= lastGapY; theGapY++)
-        {
-          for (theGapZ = firstGapZ; theGapZ <= lastGapZ; theGapZ++)
-          {
-            unsigned int t = Map->GrilleInteger(theGapX - 1, theGapY - 1, theGapZ - 1);
-            Map->Add(t);
-          }
-        }
-      }
-    }
+    return;
   }
+
+  myBoxes->SetValue(theIndex, theBox);
+
+  addBox(theBox, theIndex);
 }
 
-//=======================================================================
-#if VERIFICATION
-static void VerifCompare(const TColStd_ListOfInteger& lastResult,
-                         const Bnd_Box&               theBox,
-                         const Bnd_Array1OfBox&       taBox)
+//==================================================================================================
+
+const TColStd_ListOfInteger& Bnd_BoundSortBox::Compare(const Bnd_Box& theBox)
+
 {
-  static int       Verif = 1;
-  Standard_Integer i;
+  myLastResult.Clear();
 
-  if (Verif)
+  if (theBox.IsVoid() || theBox.IsOut(myEnclosingBox))
   {
-    Standard_Integer i0, i1;
-    i0        = taBox.Lower();
-    i1        = taBox.Upper();
-    char* qwe = new char[i1 + 1]; //-- $$$$$$$ ATTENTION IF I0 < 0
-    for (i = i0; i <= i1; i++)
-      qwe[i] = '\0';
-    TColStd_ListIteratorOfListOfInteger theList(lastResult);
-    for (; theList.More(); theList.Next())
-    {
-      qwe[theList.Value()] = (char)1;
-    }
-    Standard_Integer labox;
-    for (labox = i0; labox <= i1; labox++)
-    {
-      if (!taBox(labox).IsOut(theBox))
-      {
-        qwe[labox] += 2;
-      }
-    }
-    for (i = i0; i <= i1; i++)
+    return myLastResult;
+  }
+
+  // Processing the large boxes. These boxes are not voxelized and are checked
+  // against the given box directly which is presumably faster.
+  for (auto& aBoxIndex : myLargeBoxes)
+  {
+    const Bnd_Box& aBox = myBoxes->Value(aBoxIndex);
+    if (!aBox.IsOut(theBox))
     {
-      if (qwe[i] == 2)
-      {
-        printf("\nPb with box: %d ", i);
-      }
-      else if (qwe[i] == 1)
-      {
-        printf("\n false rejection by %d \n", i);
-      }
+      myLastResult.Append(aBoxIndex);
     }
-    delete[] qwe;
   }
-}
-#endif
-//=================================================================================================
 
-const TColStd_ListOfInteger& Bnd_BoundSortBox::Compare(const Bnd_Box& theBox)
+  // Obtaining the box voxel coordinates.
+  auto&& [aMinVoxelX, aMinVoxelY, aMinVoxelZ, aMaxVoxelX, aMaxVoxelY, aMaxVoxelZ] =
+    getBoundingVoxels(theBox);
 
-{
-  Standard_Integer lacase;
-#if DEBUG
-  NBCOMPARE++;
-#endif
-  lastResult.Clear();
-  if (theBox.IsVoid())
-    return lastResult;
-  if (theBox.IsOut(myBox))
+  // Check if the given box has any intersections within the voxel map.
+  // If not, there is nothing to check.
+  if (!myVoxelGrid->IsOccupied(
+        {aMinVoxelX, aMinVoxelY, aMinVoxelZ, aMaxVoxelX, aMaxVoxelY, aMaxVoxelZ}))
   {
-#if DEBUG
-    REJECTNIV0++;
-#endif
-    return lastResult;
+    return myLastResult;
   }
-  const Bnd_Array1OfBox& taBox = myBndComponents->Array1();
-  //-- Rejection with the table of bits
-  Standard_Boolean touch = Standard_True;
-  touch                  = Standard_False;
-  Standard_Real _Xmax, _Ymax, _Zmax;
-  BSB_T3Bits*   Map = (BSB_T3Bits*)TabBits;
-  Standard_Real xmin, ymin, zmin, xmax, ymax, zmax;
-  _Xmax = Map->Xmax;
-  _Ymax = Map->Ymax;
-  _Zmax = Map->Zmax;
-  theBox.Get(xmin, ymin, zmin, xmax, ymax, zmax);
-  Standard_Integer i0, i1, j0, j1, k0, k1;
-  if (xmin > Xmin)
-    i0 = (Standard_Integer)((xmin - Xmin) * deltaX) - 1;
-  else
-    i0 = 1;
-  if (ymin > Ymin)
-    j0 = (Standard_Integer)((ymin - Ymin) * deltaY) - 1;
-  else
-    j0 = 1;
-  if (zmin > Zmin)
-    k0 = (Standard_Integer)((zmin - Zmin) * deltaZ) - 1;
-  else
-    k0 = 1;
-  if (xmax < _Xmax)
-    i1 = (Standard_Integer)((xmax - Xmin) * deltaX) + 1;
-  else
-    i1 = discrX;
-  if (ymax < _Ymax)
-    j1 = (Standard_Integer)((ymax - Ymin) * deltaY) + 1;
-  else
-    j1 = discrY;
-  if (zmax < _Zmax)
-    k1 = (Standard_Integer)((zmax - Zmin) * deltaZ) + 1;
-  else
-    k1 = discrZ;
-  if (i0 < 1)
-    i0 = 1;
-  else if (i0 > discrX)
-    i0 = discrX;
-  if (j0 < 1)
-    j0 = 1;
-  else if (j0 > discrY)
-    j0 = discrY;
-  if (k0 < 1)
-    k0 = 1;
-  else if (k0 > discrZ)
-    k0 = discrZ;
-
-  if (i1 < 1)
-    i1 = 1;
-  else if (i1 > discrX)
-    i1 = discrX;
-  if (j1 < 1)
-    j1 = 1;
-  else if (j1 > discrY)
-    j1 = discrY;
-  if (k1 < 1)
-    k1 = 1;
-  else if (k1 > discrZ)
-    k1 = discrZ;
-  i0--;
-  j0--;
-  k0--;
-  i1--;
-  j1--;
-  k1--;
-  for (Standard_Integer i = i0; touch == Standard_False && i <= i1; i++)
+
+  // This vector is a structure to store the indices of boxes that occupy the same voxels
+  // as the given box. Index of element in vector corresponds to the index of the box in
+  // the array of boxes. The value of the element is a bit mask indicating which axes
+  // the box occupies. The first bit (0b01) indicates the X-axis, and the second bit (0b10)
+  // indicates the Y-axis. If both bits are set (0b11), and the box also occupies the Z-axis,
+  // we check for actual intersection with the given box and add it to the result if
+  // intersection occurs.
+  std::vector<uint8_t> aResultIndices(myBoxes->Upper() + 1, 0);
+  constexpr uint8_t    anOccupiedX  = 0b01;
+  constexpr uint8_t    anOccupiedY  = 0b10;
+  constexpr uint8_t    anOccupiedXY = 0b11;
+
+  // Checking the voxels along X-axis.
+  for (Standard_Integer aVoxelX = aMinVoxelX; aVoxelX <= aMaxVoxelX; ++aVoxelX)
   {
-    for (Standard_Integer j = j0; touch == Standard_False && j <= j1; j++)
+    const Bnd_VoxelGrid::VectorInt* aBoxIndices = myVoxelGrid->GetSliceX(aVoxelX);
+    if (aBoxIndices == nullptr)
     {
-      for (Standard_Integer k = k0; touch == Standard_False && k <= k1; k++)
-      {
-        unsigned int t = Map->GrilleInteger(i, j, k);
-        if (Map->Val(t))
-        {
-          touch = Standard_True;
-        }
-      }
+      continue;
     }
-  }
-  //-- processing of systematically tested boxes
-  if (Map->ToTest)
-  {
-    Standard_Integer l0 = taBox.Lower();
-    Standard_Integer l1 = taBox.Upper();
-    l1 -= l0;
-    for (Standard_Integer l = 0; Map->ToTest[l] >= l0 && l < (l1 - l0); l++)
+    for (const auto& aBoxIndex : *aBoxIndices)
     {
-      if (Map->ToTest[l] >= l0)
-      {
-        if (!taBox(Map->ToTest[l]).IsOut(theBox))
-        {
-          lastResult.Append(Map->ToTest[l]);
-        }
-      }
+      // Set the first bit (0b01) to indicate that the box occupies the X-axis.
+      aResultIndices[aBoxIndex] |= anOccupiedX;
     }
   }
-  if (touch == Standard_False)
-  {
-#if DEBUG
-    REJECTNIV1++;
-#endif
-#if VERIFICATION
-    VerifCompare(lastResult, theBox, taBox);
-#endif
-    return (lastResult);
-  }
-  //------------------------
-  //-- classic processing --
-  //------------------------
-  i0++;
-  i1++;
-  j0++;
-  j1++;
-  k0++;
-  k1++;
-  Crible.Clear();
-  theFound               = 6;
-  Standard_Integer cardY = 0;
-  for (lacase = j0; lacase <= j1; lacase++)
+
+  // Checking the voxels along Y-axis.
+  for (Standard_Integer aVoxelY = aMinVoxelY; aVoxelY <= aMaxVoxelY; ++aVoxelY)
   {
-    Standard_Integer nby = Map->NbAxisY(lacase);
-    while (nby > 0)
+    const Bnd_VoxelGrid::VectorInt* aBoxIndices = myVoxelGrid->GetSliceY(aVoxelY);
+    if (aBoxIndices == nullptr)
     {
-      cardY++;
-      Crible.Bind(Map->axisY[lacase][nby], 4);
-      nby--;
+      continue;
     }
-  }
-  if (cardY == 0)
-  {
-#if VERIFICATION
-    VerifCompare(lastResult, theBox, taBox);
-#endif
-    return lastResult;
-  }
-  Standard_Integer cardZ = 0;
-  for (lacase = k0; lacase <= k1; lacase++)
-  {
-    Standard_Integer nbz = Map->NbAxisZ(lacase);
-    while (nbz > 0)
+    for (const auto& aBoxIndex : *aBoxIndices)
     {
-      cardZ++;
-      if (Crible.IsBound(Map->axisZ[lacase][nbz]))
-      {
-        Crible.Bind(Map->axisZ[lacase][nbz], 6);
-      }
-      nbz--;
+      // Set the second bit (0b10) to indicate that the box occupies the Y-axis.
+      aResultIndices[aBoxIndex] |= anOccupiedY;
     }
   }
-  if (cardZ == 0)
-  {
-#if VERIFICATION
-    VerifCompare(lastResult, theBox, taBox);
-#endif
-    return lastResult;
-  }
-  for (lacase = i0; lacase <= i1; lacase++)
+
+  // Checking the voxels along Z-axis.
+  for (Standard_Integer aVoxelZ = aMinVoxelZ; aVoxelZ <= aMaxVoxelZ; ++aVoxelZ)
   {
-    Standard_Integer nbx = Map->NbAxisX(lacase);
-    while (nbx > 0)
+    const Bnd_VoxelGrid::VectorInt* aBoxIndices = myVoxelGrid->GetSliceZ(aVoxelZ);
+    if (aBoxIndices == nullptr)
+    {
+      continue;
+    }
+    for (const auto& aBoxIndex : *aBoxIndices)
     {
-      Standard_Integer x = Map->axisX[lacase][nbx];
-      if (Crible.IsBound(x))
+      // If box also occupies X and Y axes, we will check for intersection with the given box.
+      if (aResultIndices[aBoxIndex] == anOccupiedXY)
       {
-        if (Crible(x) == theFound)
+        // Clear the bit mask for this box to avoid checking and adding it multiple times.
+        aResultIndices[aBoxIndex] = 0;
+        // Perform actual intersection check.
+        if (!myBoxes->Value(aBoxIndex).IsOut(theBox))
         {
-          Crible.UnBind(x);
-          if (!taBox(x).IsOut(theBox))
-          {
-            lastResult.Append(x);
-          }
+          // Add to the result if intersection occurs.
+          myLastResult.Append(aBoxIndex);
         }
       }
-      nbx--;
     }
   }
-#if VERIFICATION
-  VerifCompare(lastResult, theBox, taBox);
-#endif
-  return lastResult;
-}
 
-//=================================================================================================
-
-void Bnd_BoundSortBox::Dump() const {}
+  return myLastResult;
+}
 
-//=================================================================================================
+//==================================================================================================
 
 const TColStd_ListOfInteger& Bnd_BoundSortBox::Compare(const gp_Pln& thePlane)
 
 {
-  lastResult.Clear();
-  Standard_Integer       i;
-  const Bnd_Array1OfBox& boxes = myBndComponents->Array1();
-  for (i = boxes.Lower(); i <= boxes.Upper(); i++)
+  myLastResult.Clear();
+  for (Standard_Integer aBoxIndex = myBoxes->Lower(); aBoxIndex <= myBoxes->Upper(); ++aBoxIndex)
   {
-    if (!boxes(i).IsOut(thePlane))
-      lastResult.Append(i);
+    const Bnd_Box& aBox = myBoxes->Value(aBoxIndex);
+    if (!aBox.IsOut(thePlane))
+    {
+      myLastResult.Append(aBoxIndex);
+    }
   }
-  return lastResult;
+  return myLastResult;
+}
+
+//==================================================================================================
+
+void Bnd_BoundSortBox::calculateCoefficients()
+{
+  Standard_Real aXmin, aYmin, aZmin, aXmax, aYmax, aZmax;
+  myEnclosingBox.Get(aXmin, aYmin, aZmin, aXmax, aYmax, aZmax);
+  myCoeffX = (aXmax - aXmin == 0. ? 0. : myResolution / (aXmax - aXmin));
+  myCoeffY = (aYmax - aYmin == 0. ? 0. : myResolution / (aYmax - aYmin));
+  myCoeffZ = (aZmax - aZmin == 0. ? 0. : myResolution / (aZmax - aZmin));
 }
 
-//=======================================================================
-void Bnd_BoundSortBox::Destroy()
+//==================================================================================================
+
+void Bnd_BoundSortBox::resetVoxelGrid()
 {
-#if DEBUG
-  printf(
-    "\nDESTROY NBCOMPARE:%lu  REJECTNIV0:%lu  REJECTIONSOK=%lu  NBBOITES:%lu NBBOITESATESTER:%lu\n",
-    NBCOMPARE,
-    REJECTNIV0,
-    REJECTNIV1,
-    NBBOITES,
-    NBBOITESATESTER);
-#endif
-  BSB_T3Bits* Map = (BSB_T3Bits*)TabBits;
-  if (Map)
+  myVoxelGrid = new Bnd_VoxelGrid(myResolution);
+  myLargeBoxes.clear();
+  myLargeBoxes.reserve(std::max(myBoxes->Size(), 2));
+}
+
+//==================================================================================================
+
+void Bnd_BoundSortBox::sortBoxes()
+{
+  for (Standard_Integer aBoxIndex = myBoxes->Lower(); aBoxIndex <= myBoxes->Upper(); ++aBoxIndex)
   {
-    delete Map;
-    Map = 0;
+    addBox(myBoxes->Value(aBoxIndex), aBoxIndex);
   }
 }
 
-//=======================================================================
+//==================================================================================================
+
+std::array<Standard_Integer, 6> Bnd_BoundSortBox::getBoundingVoxels(const Bnd_Box& theBox) const
+{
+  // Start point of the voxel grid.
+  const gp_Pnt aGridStart = myEnclosingBox.CornerMin();
+
+  Standard_Real aXMin, aYMin, aZMin, aXMax, aYMax, aZmax;
+  theBox.Get(aXMin, aYMin, aZMin, aXMax, aYMax, aZmax);
+
+  // Calculate the voxel indices for the bounding box.
+  // The math is as follows:
+  // Voxel grid covers the space defined by the enclosing box: from
+  // myEnclosingBox.CornerMin() to myEnclosingBox.CornerMax().
+  // Therefore, we can say that for the particular dimension:
+  // Vmax / Lmax == V / L, where
+  // Vmax is the number of voxels in the grid along that dimension,
+  // Lmax is the length of the enclosing bounding box along that dimension,
+  // V is the voxel coordinate along that dimension that we want to calculate,
+  // L is the distance from myEnclosingBox.CornerMin() to the coordinate of the box
+  //   along that dimension.
+  // Rearranging gives us:
+  // V = (Vmax / Lmax) * L
+  // Vmax / Lmax is myCoeff(X|Y|Z), precalculated during initialization.
+  // L can be simply calculated by substracting the respective coordinate of
+  // myEnclosingBox.CornerMin() from the coordinate of the box.
+  // Note: adding and substracting one to make sure that that the box is inside the
+  // voxel coordinates. Just a safety measure.
+  const Standard_Integer aXMinIndex =
+    std::clamp(static_cast<Standard_Integer>((aXMin - aGridStart.X()) * myCoeffX) - 1,
+               0,
+               myResolution - 1);
+  const Standard_Integer aYMinIndex =
+    std::clamp(static_cast<Standard_Integer>((aYMin - aGridStart.Y()) * myCoeffY) - 1,
+               0,
+               myResolution - 1);
+  const Standard_Integer aZMinIndex =
+    std::clamp(static_cast<Standard_Integer>((aZMin - aGridStart.Z()) * myCoeffZ) - 1,
+               0,
+               myResolution - 1);
+  const Standard_Integer aXMaxIndex =
+    std::clamp(static_cast<Standard_Integer>((aXMax - aGridStart.X()) * myCoeffX) + 1,
+               0,
+               myResolution - 1);
+  const Standard_Integer aYMaxIndex =
+    std::clamp(static_cast<Standard_Integer>((aYMax - aGridStart.Y()) * myCoeffY) + 1,
+               0,
+               myResolution - 1);
+  const Standard_Integer aZMaxIndex =
+    std::clamp(static_cast<Standard_Integer>((aZmax - aGridStart.Z()) * myCoeffZ) + 1,
+               0,
+               myResolution - 1);
+
+  return {aXMinIndex, aYMinIndex, aZMinIndex, aXMaxIndex, aYMaxIndex, aZMaxIndex};
+}
+
+//==================================================================================================
+
+void Bnd_BoundSortBox::addBox(const Bnd_Box& theBox, const Standard_Integer theIndex)
+{
+  if (theBox.IsVoid())
+  {
+    return;
+  }
+
+  auto&& [aMinVoxelX, aMinVoxelY, aMinVoxelZ, aMaxVoxelX, aMaxVoxelY, aMaxVoxelZ] =
+    getBoundingVoxels(theBox);
+
+  const Standard_Integer aBoxMinSide =
+    std::min({aMaxVoxelX - aMinVoxelX, aMaxVoxelY - aMinVoxelY, aMaxVoxelZ - aMinVoxelZ});
+
+  // If the box spans a significant portion of the grid,
+  // it is more efficient to test it normally rather than using voxel-based checks.
+  if (aBoxMinSide * 4 > myResolution)
+  {
+    myLargeBoxes.push_back(theIndex);
+  }
+  else
+  {
+    myVoxelGrid->AddBox(theIndex,
+                        {aMinVoxelX, aMinVoxelY, aMinVoxelZ, aMaxVoxelX, aMaxVoxelY, aMaxVoxelZ});
+  }
+}
index 554aae6137e8324bbf88bbe027c5e609c0e11067..dc21c13592ee5e67d0edb0d7ff0260a379c6bd3b 100644 (file)
 #include <Standard_Integer.hxx>
 #include <TColStd_DataMapOfIntegerInteger.hxx>
 #include <TColStd_ListOfInteger.hxx>
+
+#include <array>
+#include <vector>
+
 class gp_Pln;
+class Bnd_VoxelGrid;
 
 //! A tool to compare a bounding box or a plane with a set of
 //! bounding boxes. It sorts the set of bounding boxes to give
@@ -35,90 +40,111 @@ class gp_Pln;
 //! while the box being compared bounds a shape to be
 //! compared. The resulting list of intersecting boxes therefore
 //! gives the list of items which potentially intersect the shape to be compared.
+//! How to use this class:
+//! - Create an instance of this class.
+//! - Initialize it with the set of boxes to be sorted using one of the
+//!   Initialize() methods.
+//! - Call the Compare() method with the box or plane to be compared.
+//!   Compare() will return the list of indices of the boxes which intersect
+//!   the box or plane passed as argument.
 class Bnd_BoundSortBox
 {
 public:
   DEFINE_STANDARD_ALLOC
 
+private:
+  using VectorInt = std::vector<Standard_Integer, NCollection_Allocator<Standard_Integer>>;
+
+public:
   //! Constructs an empty comparison algorithm for bounding boxes.
   //! The bounding boxes are then defined using the Initialize function.
   Standard_EXPORT Bnd_BoundSortBox();
 
-  //! Initializes this comparison algorithm with
-  //! -   the set of bounding boxes SetOfBox.
-  Standard_EXPORT void Initialize(const Bnd_Box&                  CompleteBox,
-                                  const Handle(Bnd_HArray1OfBox)& SetOfBox);
-
-  //! Initializes this comparison algorithm with
-  //! -   the set of bounding boxes SetOfBox, where
-  //! CompleteBox is given as the global bounding box of SetOfBox.
-  Standard_EXPORT void Initialize(const Handle(Bnd_HArray1OfBox)& SetOfBox);
-
-  //! Initializes this comparison algorithm, giving it only
-  //! -   the maximum number nbComponents
-  //! of the bounding boxes to be managed. Use the Add
-  //! function to define the array of bounding boxes to be sorted by this algorithm.
-  Standard_EXPORT void Initialize(const Bnd_Box& CompleteBox, const Standard_Integer nbComponents);
-
-  //! Adds the bounding box theBox at position boxIndex in
-  //! the array of boxes to be sorted by this comparison algorithm.
-  //! This function is used only in conjunction with the third
-  //! syntax described in the synopsis of Initialize.
-  //!
+  //! Initializes this comparison algorithm with the set of boxes.
+  //! @param theSetOfBoxes The set of bounding boxes to be used by this algorithm.
+  Standard_EXPORT void Initialize(const Handle(Bnd_HArray1OfBox)& theSetOfBoxes);
+
+  //! Initializes this comparison algorithm with the set of boxes and the bounding box
+  //! that encloses all those boxes. This version of initialization can be used if complete
+  //! box is known in advance to avoid calculating it again inside the algorithm.
+  //! @param theEnclosingBox The bounding box that contains all the boxes in @p theSetOfBoxes.
+  //! @param theSetOfBoxes The set of bounding boxes to be used by this algorithm.
+  Standard_EXPORT void Initialize(const Bnd_Box&                  theEnclosingBox,
+                                  const Handle(Bnd_HArray1OfBox)& theSetOfBoxes);
+
+  //! Initializes this comparison algorithm with the bounding box that encloses all the boxes
+  //! that will be used by this algorithm. and the expected number of those boxes.
+  //! Boxes to be considered can then be added using the Add() method.
+  //! @param theEnclosingBox The bounding box that contains all the boxes to be sorted.
+  //! @param theNbComponents The number of components to be added.
+  Standard_EXPORT void Initialize(const Bnd_Box&         theEnclosingBox,
+                                  const Standard_Integer theNbBoxes);
+
+  //! Adds the bounding box theBox at position boxIndex in the internal array of boxes
+  //! to be sorted by this comparison algorithm. This function is used only in
+  //! conjunction with the Initialize(const Bnd_Box&, const Standard_Integer) method.
   //! Exceptions:
-  //!
-  //! - Standard_OutOfRange if boxIndex is not in the
-  //! range [ 1,nbComponents ] where
-  //! nbComponents is the maximum number of bounding
-  //! boxes declared for this comparison algorithm at
-  //! initialization.
-  //!
-  //! - Standard_MultiplyDefined if a box already exists at
-  //! position boxIndex in the array of boxes to be sorted by
-  //! this comparison algorithm.
-  Standard_EXPORT void Add(const Bnd_Box& theBox, const Standard_Integer boxIndex);
-
-  //! Compares the bounding box theBox,
-  //! with the set of bounding boxes to be sorted by this
-  //! comparison algorithm, and returns the list of intersecting
-  //! bounding boxes as a list of indexes on the array of
-  //! bounding boxes used by this algorithm.
+  //! - Standard_OutOfRange if boxIndex is not in the range [ 1,nbComponents ] where
+  //!   nbComponents is the maximum number of bounding boxes declared for this algorithm at
+  //!   initialization.
+  //! - Standard_MultiplyDefined if a box already exists at position @p theIndex in the
+  //!   internal array of boxes.
+  //! @param theBox The bounding box to be added.
+  //! @param theIndex The index of the bounding box in the internal array where the box
+  //!        will be added. The index is 1-based.
+  Standard_EXPORT void Add(const Bnd_Box& theBox, const Standard_Integer theIndex);
+
+  //! Compares the bounding box theBox, with the set of bounding boxes provided to this
+  //! algorithm at initialization, and returns the list of indices of bounding boxes
+  //! that intersect the @p theBox or are inside it.
+  //! The indices correspond to the indices of the bounding boxes in the array provided
+  //! to this algorithm at initialization.
+  //! @param theBox The bounding box to be compared.
+  //! @return The list of indices of bounding boxes that intersect the bounding box theBox
+  //!         or are inside it.
   Standard_EXPORT const TColStd_ListOfInteger& Compare(const Bnd_Box& theBox);
 
-  //! Compares the plane P
-  //! with the set of bounding boxes to be sorted by this
-  //! comparison algorithm, and returns the list of intersecting
-  //! bounding boxes as a list of indexes on the array of
-  //! bounding boxes used by this algorithm.
-  Standard_EXPORT const TColStd_ListOfInteger& Compare(const gp_Pln& P);
-
-  Standard_EXPORT void Dump() const;
-
-  Standard_EXPORT void Destroy();
-
-  ~Bnd_BoundSortBox() { Destroy(); }
+  //! Compares the plane @p thePlane with the set of bounding boxes provided to this
+  //! algorithm at initialization, and returns the list of indices of bounding boxes
+  //! that intersect the @p thePlane.
+  //! The indices correspond to the indices of the bounding boxes in the array provided
+  //! to this algorithm at initialization.
+  //! @param thePlane The plane to be compared.
+  //! @return The list of indices of bounding boxes that intersect the plane thePlane.
+  Standard_EXPORT const TColStd_ListOfInteger& Compare(const gp_Pln& thePlane);
 
-protected:
 private:
-  //! Prepares  BoundSortBox and  sorts   the  boxes of
-  //! <SetOfBox> .
-  Standard_EXPORT void SortBoxes();
-
-  Bnd_Box                         myBox;
-  Handle(Bnd_HArray1OfBox)        myBndComponents;
-  Standard_Real                   Xmin;
-  Standard_Real                   Ymin;
-  Standard_Real                   Zmin;
-  Standard_Real                   deltaX;
-  Standard_Real                   deltaY;
-  Standard_Real                   deltaZ;
-  Standard_Integer                discrX;
-  Standard_Integer                discrY;
-  Standard_Integer                discrZ;
-  Standard_Integer                theFound;
-  TColStd_DataMapOfIntegerInteger Crible;
-  TColStd_ListOfInteger           lastResult;
-  Standard_Address                TabBits;
+  //! Precalculates the coefficients for the voxel grid based on the enclosing box dimensions.
+  //! The coefficients will be used to map the box coordinates to the voxel grid.
+  void calculateCoefficients();
+
+  //! Resets the voxel grid and clears the list of large boxes.
+  void resetVoxelGrid();
+
+  //! Performs the sorting of the boxes in the voxel grid.
+  //! This method is called after the boxes have been added to the voxel grid.
+  void sortBoxes();
+
+  //! Returns indices of voxels that contain minimum and maximum points of the box.
+  //! @param theBox The bounding box to be compared.
+  //! @return The indices of the voxels that contain the minimum and maximum points of the box
+  //!         in the order: [minX, minY, minZ, maxX, maxY, maxZ].
+  std::array<Standard_Integer, 6> getBoundingVoxels(const Bnd_Box& theBox) const;
+
+  //! Adds the box stored in myBoxes to the voxel map.
+  //! @param theBox The bounding box to be added.
+  //! @param theIndex The index of the bounding box in myBoxes.
+  void addBox(const Bnd_Box& theBox, const Standard_Integer theIndex);
+
+  Bnd_Box myEnclosingBox;            //!< The bounding box that contains all the boxes to be sorted.
+  Handle(Bnd_HArray1OfBox) myBoxes;  //!< The set of bounding boxes to be sorted.
+  Standard_Real            myCoeffX; //!< Coefficient for X direction.
+  Standard_Real            myCoeffY; //!< Coefficient for Y direction.
+  Standard_Real            myCoeffZ; //!< Coefficient for Z direction.
+  Standard_Integer         myResolution; //!< The number of voxels in each direction.
+  TColStd_ListOfInteger    myLastResult; //!< The last result of the Compare() method.
+  VectorInt                myLargeBoxes; //!< The list of large boxes.
+  Handle(Bnd_VoxelGrid)    myVoxelGrid;  //!< The voxel grid used for sorting the boxes.
 };
 
 #endif // _Bnd_BoundSortBox_HeaderFile
diff --git a/src/FoundationClasses/TKMath/GTests/Bnd_BoundSortBox_Test.cxx b/src/FoundationClasses/TKMath/GTests/Bnd_BoundSortBox_Test.cxx
new file mode 100644 (file)
index 0000000..309957b
--- /dev/null
@@ -0,0 +1,266 @@
+#include <gtest/gtest.h>
+
+#include <Bnd_BoundSortBox.hxx>
+#include <Bnd_Box.hxx>
+#include <Bnd_HArray1OfBox.hxx>
+#include <gp_Pln.hxx>
+#include <gp_Pnt.hxx>
+#include <gp_Dir.hxx>
+#include <TColStd_ListOfInteger.hxx>
+
+#include <random>
+#include <chrono>
+
+// Helper function to create a box from min and max points
+static Bnd_Box CreateBox(const Standard_Real xmin,
+                         const Standard_Real ymin,
+                         const Standard_Real zmin,
+                         const Standard_Real xmax,
+                         const Standard_Real ymax,
+                         const Standard_Real zmax)
+{
+  Bnd_Box box;
+  box.Update(xmin, ymin, zmin, xmax, ymax, zmax);
+  return box;
+}
+
+// Test fixture for Bnd_BoundSortBox tests
+class Bnd_BoundSortBoxTest : public ::testing::Test
+{
+protected:
+  void SetUp() override
+  {
+    // Setup common testing environment
+    mySmallBox           = CreateBox(0.0, 0.0, 0.0, 1.0, 1.0, 1.0);
+    myLargeBox           = CreateBox(-10.0, -10.0, -10.0, 10.0, 10.0, 10.0);
+    myOffsetBox          = CreateBox(5.0, 5.0, 5.0, 7.0, 7.0, 7.0);
+    myNonIntersectingBox = CreateBox(20.0, 20.0, 20.0, 30.0, 30.0, 30.0);
+
+    // Create array of test boxes
+    myBoxes = new Bnd_HArray1OfBox(1, 4);
+    myBoxes->SetValue(1, mySmallBox);
+    myBoxes->SetValue(2, myLargeBox);
+    myBoxes->SetValue(3, myOffsetBox);
+    myBoxes->SetValue(4, myNonIntersectingBox);
+
+    // Create a global bounding box that contains all test boxes
+    myGlobalBox = CreateBox(-20.0, -20.0, -20.0, 40.0, 40.0, 40.0);
+  }
+
+  // Common test data
+  Bnd_Box                  mySmallBox;
+  Bnd_Box                  myLargeBox;
+  Bnd_Box                  myOffsetBox;
+  Bnd_Box                  myNonIntersectingBox;
+  Bnd_Box                  myGlobalBox;
+  Handle(Bnd_HArray1OfBox) myBoxes;
+};
+
+//==================================================================================================
+
+// Test initialization with set of boxes
+TEST_F(Bnd_BoundSortBoxTest, InitializeWithBoxes)
+{
+  Bnd_BoundSortBox sortBox;
+  sortBox.Initialize(myBoxes);
+
+  // Test comparing with a box that intersects mySmallBox
+  Bnd_Box                      testBox = CreateBox(0.5, 0.5, 0.5, 1.5, 1.5, 1.5);
+  const TColStd_ListOfInteger& result  = sortBox.Compare(testBox);
+
+  EXPECT_EQ(2, result.Extent()) << "Expected to find 2 intersections";
+
+  // Check that box indices 1 (mySmallBox) and 2 (myLargeBox) are in the result
+  bool foundSmall = false;
+  bool foundLarge = false;
+  for (TColStd_ListOfInteger::Iterator it(result); it.More(); it.Next())
+  {
+    if (it.Value() == 1)
+      foundSmall = true;
+    if (it.Value() == 2)
+      foundLarge = true;
+  }
+  EXPECT_TRUE(foundSmall) << "Small box (index 1) should be in the result";
+  EXPECT_TRUE(foundLarge) << "Large box (index 2) should be in the result";
+}
+
+//==================================================================================================
+
+// Test initialization with provided enclosing box
+TEST_F(Bnd_BoundSortBoxTest, InitializeWithEnclosingBox)
+{
+  Bnd_BoundSortBox sortBox;
+  sortBox.Initialize(myGlobalBox, myBoxes);
+
+  // Test comparing with myOffsetBox
+  const TColStd_ListOfInteger& result = sortBox.Compare(myOffsetBox);
+
+  EXPECT_EQ(2, result.Extent()) << "Expected to find 2 intersections";
+
+  // Check that box indices 2 (myLargeBox) and 3 (myOffsetBox) are in the result
+  bool foundLarge  = false;
+  bool foundOffset = false;
+  for (TColStd_ListOfInteger::Iterator it(result); it.More(); it.Next())
+  {
+    if (it.Value() == 2)
+      foundLarge = true;
+    if (it.Value() == 3)
+      foundOffset = true;
+  }
+  EXPECT_TRUE(foundLarge) << "Large box (index 2) should be in the result";
+  EXPECT_TRUE(foundOffset) << "Offset box (index 3) should be in the result";
+}
+
+//==================================================================================================
+
+// Test initialization with enclosing box and expected count
+TEST_F(Bnd_BoundSortBoxTest, InitializeWithCount)
+{
+  Bnd_BoundSortBox sortBox;
+  sortBox.Initialize(myGlobalBox, 3);
+
+  // Add boxes manually
+  sortBox.Add(mySmallBox, 1);
+  sortBox.Add(myLargeBox, 2);
+  sortBox.Add(myNonIntersectingBox, 3);
+
+  // Test comparing with a box that should only intersect myLargeBox
+  Bnd_Box                      testBox = CreateBox(-5.0, -5.0, -5.0, -2.0, -2.0, -2.0);
+  const TColStd_ListOfInteger& result  = sortBox.Compare(testBox);
+
+  EXPECT_EQ(1, result.Extent()) << "Expected to find 1 intersection";
+
+  // Verify that only the large box was found
+  EXPECT_EQ(2, result.First()) << "Large box (index 2) should be the only result";
+}
+
+//==================================================================================================
+
+// Test comparing with a plane
+TEST_F(Bnd_BoundSortBoxTest, CompareWithPlane)
+{
+  Bnd_BoundSortBox sortBox;
+  sortBox.Initialize(myBoxes);
+
+  // Create a plane that intersects the large box but not others
+  gp_Pnt point(0.0, 0.0, 9.0);
+  gp_Dir direction(0.0, 0.0, 1.0);
+  gp_Pln plane(point, direction);
+
+  const TColStd_ListOfInteger& result = sortBox.Compare(plane);
+
+  // Only the large box should intersect this plane
+  EXPECT_EQ(1, result.Extent()) << "Expected to find 1 intersection";
+  EXPECT_EQ(2, result.First()) << "Large box (index 2) should be the only result";
+}
+
+//==================================================================================================
+
+// Test with void boxes
+TEST_F(Bnd_BoundSortBoxTest, VoidBoxes)
+{
+  Handle(Bnd_HArray1OfBox) boxes = new Bnd_HArray1OfBox(1, 2);
+  Bnd_Box                  void_box; // Default constructed box is void
+  boxes->SetValue(1, void_box);
+  boxes->SetValue(2, mySmallBox);
+
+  Bnd_BoundSortBox sortBox;
+  sortBox.Initialize(boxes);
+
+  // Test comparing with mySmallBox - should only find itself
+  const TColStd_ListOfInteger& result = sortBox.Compare(mySmallBox);
+
+  EXPECT_EQ(1, result.Extent()) << "Expected to find 1 intersection";
+  EXPECT_EQ(2, result.First()) << "Small box (index 2) should be the only result";
+
+  // Test comparing with void box - should find nothing
+  const TColStd_ListOfInteger& void_result = sortBox.Compare(void_box);
+  EXPECT_EQ(0, void_result.Extent()) << "Expected to find 0 intersections with void box";
+}
+
+//==================================================================================================
+
+// Test with touching boxes
+TEST_F(Bnd_BoundSortBoxTest, TouchingBoxes)
+{
+  // Create boxes that touch at a single point
+  Bnd_Box box1 = CreateBox(0.0, 0.0, 0.0, 1.0, 1.0, 1.0);
+  Bnd_Box box2 = CreateBox(1.0, 1.0, 1.0, 2.0, 2.0, 2.0); // Touches box1 at (1,1,1)
+
+  Handle(Bnd_HArray1OfBox) boxes = new Bnd_HArray1OfBox(1, 2);
+  boxes->SetValue(1, box1);
+  boxes->SetValue(2, box2);
+
+  Bnd_BoundSortBox sortBox;
+  sortBox.Initialize(boxes);
+
+  // Test comparing with box1
+  const TColStd_ListOfInteger& result1 = sortBox.Compare(box1);
+  EXPECT_EQ(2, result1.Extent()) << "Expected to find 2 intersections";
+
+  // Test comparing with box2
+  const TColStd_ListOfInteger& result2 = sortBox.Compare(box2);
+  EXPECT_EQ(2, result2.Extent()) << "Expected to find 2 intersections";
+}
+
+//==================================================================================================
+
+// Test with boxes that are far apart
+TEST_F(Bnd_BoundSortBoxTest, DisjointBoxes)
+{
+  Bnd_Box farBox = CreateBox(100.0, 100.0, 100.0, 110.0, 110.0, 110.0);
+
+  Handle(Bnd_HArray1OfBox) boxes = new Bnd_HArray1OfBox(1, 2);
+  boxes->SetValue(1, mySmallBox);
+  boxes->SetValue(2, farBox);
+
+  // Create enclosing box large enough
+  Bnd_Box enclosingBox = CreateBox(-10.0, -10.0, -10.0, 120.0, 120.0, 120.0);
+
+  Bnd_BoundSortBox sortBox;
+  sortBox.Initialize(enclosingBox, boxes);
+
+  // Test comparing with a box near mySmallBox
+  Bnd_Box                      testBox = CreateBox(0.5, 0.5, 0.5, 1.5, 1.5, 1.5);
+  const TColStd_ListOfInteger& result  = sortBox.Compare(testBox);
+
+  EXPECT_EQ(1, result.Extent()) << "Expected to find 1 intersection";
+  EXPECT_EQ(1, result.First()) << "Small box (index 1) should be the only result";
+}
+
+//==================================================================================================
+
+// Test with degenerate boxes (points, lines, planes)
+TEST_F(Bnd_BoundSortBoxTest, DegenerateBoxes)
+{
+  // Create degenerate boxes
+  Bnd_Box pointBox;
+  pointBox.Update(1.0, 1.0, 1.0, 1.0, 1.0, 1.0); // Point at (1,1,1)
+
+  Bnd_Box lineBox;
+  lineBox.Update(2.0, 0.0, 0.0, 5.0, 0.0, 0.0); // Line along X from (2,0,0) to (5,0,0)
+
+  Bnd_Box planeBox;
+  planeBox.Update(0.0, 0.0, 3.0, 3.0, 3.0, 3.0); // XY plane at Z=3
+
+  Handle(Bnd_HArray1OfBox) boxes = new Bnd_HArray1OfBox(1, 3);
+  boxes->SetValue(1, pointBox);
+  boxes->SetValue(2, lineBox);
+  boxes->SetValue(3, planeBox);
+
+  Bnd_BoundSortBox sortBox;
+  sortBox.Initialize(boxes);
+
+  // Test with a box that should intersect all three degenerate boxes
+  Bnd_Box                      testBox = CreateBox(0.0, 0.0, 0.0, 6.0, 6.0, 6.0);
+  const TColStd_ListOfInteger& result  = sortBox.Compare(testBox);
+
+  EXPECT_EQ(3, result.Extent()) << "Expected to find 3 intersections with degenerate boxes";
+
+  // Test with a box that should intersect only the point
+  Bnd_Box                      pointTestBox = CreateBox(0.5, 0.5, 0.5, 1.5, 1.5, 1.5);
+  const TColStd_ListOfInteger& pointResult  = sortBox.Compare(pointTestBox);
+
+  EXPECT_EQ(1, pointResult.Extent()) << "Expected to find only the point box";
+  EXPECT_EQ(1, pointResult.First()) << "Point box (index 1) should be the only result";
+}
index 811a769e560a3517b57681903ec63a64de1bebf6..84519823cd61cd71eff7d92e61d6cecd7444a9e7 100644 (file)
@@ -2,5 +2,6 @@
 set(OCCT_TKMath_GTests_FILES_LOCATION "${CMAKE_CURRENT_LIST_DIR}")
 
 set(OCCT_TKMath_GTests_FILES
+  Bnd_BoundSortBox_Test.cxx
   ElCLib_Test.cxx
 )