1
0
mirror of https://git.dev.opencascade.org/repos/occt.git synced 2025-05-21 10:55:33 +03:00

Modeling - Bnd_BoundSortBox::Compare fails in some cases #518

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.
This commit is contained in:
Dmitrii Kulikov 2025-05-16 16:04:48 +01:00 committed by GitHub
parent 3e80fad177
commit 2eeb0ed5ba
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 896 additions and 981 deletions

File diff suppressed because it is too large Load Diff

View File

@ -26,7 +26,12 @@
#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 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 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 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, 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);
//! 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 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.
//!
//! 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);
//! - 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 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.
//! 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);
//! 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);
Standard_EXPORT void Dump() const;
Standard_EXPORT void Destroy();
~Bnd_BoundSortBox() { Destroy(); }
protected:
private:
//! Prepares BoundSortBox and sorts the boxes of
//! <SetOfBox> .
Standard_EXPORT void SortBoxes();
//! 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();
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;
//! 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

View File

@ -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";
}

View 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
)