Modeling - Optimize interference detection in polyhedra (#924)

- Introduced `IntPatch_PolyhedronBVH` to wrap polyhedra as BVH primitive sets
- Implemented `IntPatch_BVHTraversal` for efficient dual-tree traversal to find candidate triangle pairs
- Refactored `IntPatch_InterferencePolyhedron::Interference()` to use BVH-based detection
This commit is contained in:
Pasukhin Dmitry
2026-04-24 16:15:40 +01:00
committed by GitHub
parent b7f849a23a
commit b91b6d48b4
8 changed files with 743 additions and 80 deletions

View File

@@ -20,6 +20,7 @@ set(OCCT_TKGeomAlgo_GTests_FILES
IntCurveSurface_ThePolygonOfHInter_Test.cxx
Intf_Tool_Test.cxx
IntPatch_Polyhedron_Test.cxx
IntPatch_PolyhedronBVH_Test.cxx
IntPolyh_Intersection_Test.cxx
IntPolyh_Point_Test.cxx
IntSurf_LineOn2S_Test.cxx

View File

@@ -0,0 +1,239 @@
// Copyright (c) 2024 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.
#include <gtest/gtest.h>
#include <Adaptor3d_CurveOnSurface.hxx>
#include <GeomAdaptor_Surface.hxx>
#include <Geom_SphericalSurface.hxx>
#include <Geom_CylindricalSurface.hxx>
#include <Geom_Plane.hxx>
#include <gp_Ax3.hxx>
#include <gp_Sphere.hxx>
#include <gp_Cylinder.hxx>
#include <gp_Pln.hxx>
#include <IntPatch_BVHTraversal.hxx>
#include <IntPatch_InterferencePolyhedron.hxx>
#include <IntPatch_Polyhedron.hxx>
#include <IntPatch_PolyhedronBVH.hxx>
#include <IntPatch_PolyhedronTool.hxx>
// Test fixture for IntPatch_PolyhedronBVH tests
class IntPatch_PolyhedronBVHTest : public testing::Test
{
protected:
void SetUp() override
{
// Create a sphere surface
const gp_Sphere aSphere(gp_Ax3(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), 1.0);
const occ::handle<Geom_SphericalSurface> aSphereSurf = new Geom_SphericalSurface(aSphere);
mySphereAdaptor = new GeomAdaptor_Surface(aSphereSurf);
// Create a cylinder surface
const gp_Cylinder aCyl(gp_Ax3(gp_Pnt(0.5, 0, 0), gp_Dir(0, 0, 1)), 0.8);
const occ::handle<Geom_CylindricalSurface> aCylSurf = new Geom_CylindricalSurface(aCyl);
myCylAdaptor = new GeomAdaptor_Surface(aCylSurf, 0, 2 * M_PI, -1, 1);
}
occ::handle<GeomAdaptor_Surface> mySphereAdaptor;
occ::handle<GeomAdaptor_Surface> myCylAdaptor;
};
// Test that PolyhedronBVH can be constructed and has correct size
TEST_F(IntPatch_PolyhedronBVHTest, Construction)
{
constexpr int aNbU = 10;
constexpr int aNbV = 10;
const IntPatch_Polyhedron aPoly(mySphereAdaptor, aNbU, aNbV);
const IntPatch_PolyhedronBVH aBVH(aPoly);
EXPECT_TRUE(aBVH.IsInitialized());
EXPECT_GT(aBVH.Size(), 0);
EXPECT_LE(aBVH.Size(), IntPatch_PolyhedronTool::NbTriangles(aPoly));
}
// Test that Box() returns valid bounding boxes
TEST_F(IntPatch_PolyhedronBVHTest, Box)
{
constexpr int aNbU = 5;
constexpr int aNbV = 5;
const IntPatch_Polyhedron aPoly(mySphereAdaptor, aNbU, aNbV);
const IntPatch_PolyhedronBVH aBVH(aPoly);
// Check that all boxes are valid
for (int i = 0; i < aBVH.Size(); ++i)
{
const BVH_Box<double, 3> aBox = aBVH.Box(i);
EXPECT_TRUE(aBox.IsValid()) << "Box " << i << " is not valid";
}
}
// Test that Center() returns valid centroid coordinates
TEST_F(IntPatch_PolyhedronBVHTest, Center)
{
constexpr int aNbU = 5;
const int aNbV = 5;
const IntPatch_Polyhedron aPoly(mySphereAdaptor, aNbU, aNbV);
const IntPatch_PolyhedronBVH aBVH(aPoly);
// Check that centroids are within the overall bounding box
const Bnd_Box& aBounding = IntPatch_PolyhedronTool::Bounding(aPoly);
double aXmin, aYmin, aZmin, aXmax, aYmax, aZmax;
aBounding.Get(aXmin, aYmin, aZmin, aXmax, aYmax, aZmax);
for (int i = 0; i < aBVH.Size(); ++i)
{
const double aCx = aBVH.Center(i, 0);
const double aCy = aBVH.Center(i, 1);
const double aCz = aBVH.Center(i, 2);
EXPECT_GE(aCx, aXmin - 1e-10) << "Center X of triangle " << i << " is below min";
EXPECT_LE(aCx, aXmax + 1e-10) << "Center X of triangle " << i << " is above max";
EXPECT_GE(aCy, aYmin - 1e-10) << "Center Y of triangle " << i << " is below min";
EXPECT_LE(aCy, aYmax + 1e-10) << "Center Y of triangle " << i << " is above max";
EXPECT_GE(aCz, aZmin - 1e-10) << "Center Z of triangle " << i << " is below min";
EXPECT_LE(aCz, aZmax + 1e-10) << "Center Z of triangle " << i << " is above max";
}
}
// Test that OriginalIndex() returns valid 1-based indices
TEST_F(IntPatch_PolyhedronBVHTest, OriginalIndex)
{
constexpr int aNbU = 5;
constexpr int aNbV = 5;
const IntPatch_Polyhedron aPoly(mySphereAdaptor, aNbU, aNbV);
IntPatch_PolyhedronBVH aBVH(aPoly);
const int aNbTri = aBVH.Size();
const int aNbPolyTri = IntPatch_PolyhedronTool::NbTriangles(aPoly);
// Before BVH build, indices should be sequential
for (int i = 0; i < aNbTri; ++i)
{
const int anOrigIdx = aBVH.OriginalIndex(i);
EXPECT_GE(anOrigIdx, 1) << "Original index should be >= 1";
EXPECT_LE(anOrigIdx, aNbPolyTri) << "Original index should be <= NbTriangles";
}
// Force BVH build
aBVH.BVH();
// After BVH build, each original index should appear exactly once
std::vector<bool> aUsed(aNbPolyTri + 1, false);
for (int i = 0; i < aNbTri; ++i)
{
const int anOrigIdx = aBVH.OriginalIndex(i);
EXPECT_FALSE(aUsed[anOrigIdx]) << "Original index " << anOrigIdx << " used more than once";
aUsed[anOrigIdx] = true;
}
}
// Test BVH traversal finds overlapping triangles
TEST_F(IntPatch_PolyhedronBVHTest, Traversal)
{
constexpr int aNbU = 10;
constexpr int aNbV = 10;
const IntPatch_Polyhedron aPoly1(mySphereAdaptor, aNbU, aNbV);
const IntPatch_Polyhedron aPoly2(myCylAdaptor, aNbU, aNbV);
IntPatch_PolyhedronBVH aSet1(aPoly1);
IntPatch_PolyhedronBVH aSet2(aPoly2);
IntPatch_BVHTraversal aTraversal;
const int aNbPairs = aTraversal.Perform(aSet1, aSet2, false);
// The sphere and cylinder should have some overlapping triangles
EXPECT_GT(aNbPairs, 0) << "Expected some overlapping triangle pairs";
EXPECT_EQ(aNbPairs, static_cast<int>(aTraversal.Pairs().Size()));
// Verify all pairs have valid indices
for (const auto& aPair : aTraversal.Pairs())
{
EXPECT_GE(aPair.First, 1);
EXPECT_LE(aPair.First, IntPatch_PolyhedronTool::NbTriangles(aPoly1));
EXPECT_GE(aPair.Second, 1);
EXPECT_LE(aPair.Second, IntPatch_PolyhedronTool::NbTriangles(aPoly2));
}
}
// Test self-interference mode
TEST_F(IntPatch_PolyhedronBVHTest, SelfInterference)
{
constexpr int aNbU = 5;
constexpr int aNbV = 5;
const IntPatch_Polyhedron aPoly(mySphereAdaptor, aNbU, aNbV);
IntPatch_PolyhedronBVH aSet(aPoly);
IntPatch_BVHTraversal aTraversal;
aTraversal.Perform(aSet, aSet, true); // self-interference mode
// In self-interference mode, First < Second for all pairs
for (const auto& aPair : aTraversal.Pairs())
{
EXPECT_LT(aPair.First, aPair.Second) << "Self-interference should have First < Second";
}
}
// Test that IntPatch_InterferencePolyhedron works with BVH
TEST_F(IntPatch_PolyhedronBVHTest, InterferencePolyhedron)
{
constexpr int aNbU = 10;
constexpr int aNbV = 10;
const IntPatch_Polyhedron aPoly1(mySphereAdaptor, aNbU, aNbV);
const IntPatch_Polyhedron aPoly2(myCylAdaptor, aNbU, aNbV);
// Create interference and check it completes without error
const IntPatch_InterferencePolyhedron anInterf(aPoly1, aPoly2);
// The result should have some section points or lines
// (exact number depends on geometry, just verify it runs)
const int aNbPoints = anInterf.NbSectionPoints();
const int aNbLines = anInterf.NbSectionLines();
const int aNbZones = anInterf.NbTangentZones();
// At least some intersection should be found
EXPECT_TRUE(aNbPoints > 0 || aNbLines > 0 || aNbZones > 0)
<< "Expected some intersection results";
}
// Test with non-overlapping surfaces
TEST_F(IntPatch_PolyhedronBVHTest, NoOverlap)
{
// Create a plane far from the sphere
const gp_Pln aPlane(gp_Pnt(10, 10, 10), gp_Dir(1, 0, 0));
const occ::handle<Geom_Plane> aPlaneSurf = new Geom_Plane(aPlane);
const occ::handle<GeomAdaptor_Surface> aPlaneAdaptor =
new GeomAdaptor_Surface(aPlaneSurf, -1, 1, -1, 1);
const int aNbU = 5;
const int aNbV = 5;
const IntPatch_Polyhedron aPoly1(mySphereAdaptor, aNbU, aNbV);
const IntPatch_Polyhedron aPoly2(aPlaneAdaptor, aNbU, aNbV);
IntPatch_PolyhedronBVH aSet1(aPoly1);
IntPatch_PolyhedronBVH aSet2(aPoly2);
IntPatch_BVHTraversal aTraversal;
const int aNbPairs = aTraversal.Perform(aSet1, aSet2, false);
// Far-away surfaces should have no overlapping boxes
EXPECT_EQ(aNbPairs, 0) << "Expected no overlapping triangle pairs for distant surfaces";
}

View File

@@ -10,6 +10,8 @@ set(OCCT_IntPatch_FILES
IntPatch_ArcFunction.cxx
IntPatch_ArcFunction.hxx
IntPatch_ArcFunction.lxx
IntPatch_BVHTraversal.cxx
IntPatch_BVHTraversal.hxx
IntPatch_CSFunction.cxx
IntPatch_CSFunction.hxx
IntPatch_CurvIntSurf.hxx
@@ -54,6 +56,8 @@ set(OCCT_IntPatch_FILES
IntPatch_Polygo.lxx
IntPatch_Polyhedron.cxx
IntPatch_Polyhedron.hxx
IntPatch_PolyhedronBVH.cxx
IntPatch_PolyhedronBVH.hxx
IntPatch_PolyhedronTool.hxx
IntPatch_PolyhedronTool.lxx
IntPatch_PrmPrmIntersection.cxx

View File

@@ -0,0 +1,116 @@
// Copyright (c) 2024 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.
#include <IntPatch_BVHTraversal.hxx>
#include <IntPatch_PolyhedronBVH.hxx>
//==================================================================================================
IntPatch_BVHTraversal::IntPatch_BVHTraversal()
: BVH_PairTraverse<double, 3>(),
mySet1(nullptr),
mySet2(nullptr),
mySelfInterference(false)
{
}
//==================================================================================================
IntPatch_BVHTraversal::~IntPatch_BVHTraversal() {}
//==================================================================================================
int IntPatch_BVHTraversal::Perform(IntPatch_PolyhedronBVH& theSet1,
IntPatch_PolyhedronBVH& theSet2,
bool theSelfInterference)
{
myPairs.Clear();
mySet1 = &theSet1;
mySet2 = &theSet2;
mySelfInterference = theSelfInterference;
if (!theSet1.IsInitialized() || !theSet2.IsInitialized())
{
return 0;
}
if (theSet1.Size() == 0 || theSet2.Size() == 0)
{
return 0;
}
// Get BVH trees (builds them if necessary)
const opencascade::handle<BVH_Tree<double, 3>>& aBVH1 = theSet1.BVH();
const opencascade::handle<BVH_Tree<double, 3>>& aBVH2 = theSet2.BVH();
if (aBVH1.IsNull() || aBVH2.IsNull())
{
return 0;
}
// Perform dual-tree traversal
return Select(aBVH1, aBVH2);
}
//==================================================================================================
bool IntPatch_BVHTraversal::RejectNode(const BVH_Vec3d& theCMin1,
const BVH_Vec3d& theCMax1,
const BVH_Vec3d& theCMin2,
const BVH_Vec3d& theCMax2,
double& /*theMetric*/) const
{
// AABB overlap test: reject if boxes don't overlap
// Two boxes overlap if and only if they overlap on all three axes
if (theCMin1.x() > theCMax2.x() || theCMax1.x() < theCMin2.x())
{
return true; // No overlap on X axis
}
if (theCMin1.y() > theCMax2.y() || theCMax1.y() < theCMin2.y())
{
return true; // No overlap on Y axis
}
if (theCMin1.z() > theCMax2.z() || theCMax1.z() < theCMin2.z())
{
return true; // No overlap on Z axis
}
return false; // Boxes overlap
}
//==================================================================================================
bool IntPatch_BVHTraversal::Accept(const int theIndex1, const int theIndex2)
{
if (mySet1 == nullptr || mySet2 == nullptr)
{
return false;
}
// Get original 1-based triangle indices
const int anOrigIdx1 = mySet1->OriginalIndex(theIndex1);
const int anOrigIdx2 = mySet2->OriginalIndex(theIndex2);
// In self-interference mode, skip pairs where first index >= second index
// to avoid testing the same pair twice (and to avoid self-intersection)
if (mySelfInterference && anOrigIdx1 >= anOrigIdx2)
{
return false;
}
// Store the pair
myPairs.Append(TrianglePair(anOrigIdx1, anOrigIdx2));
return true;
}

View File

@@ -0,0 +1,95 @@
// Copyright (c) 2024 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.
#ifndef IntPatch_BVHTraversal_HeaderFile
#define IntPatch_BVHTraversal_HeaderFile
#include <BVH_Traverse.hxx>
#include <NCollection_Vector.hxx>
class IntPatch_PolyhedronBVH;
//! Performs BVH tree traversal of two polyhedra to find candidate triangle pairs
//! for intersection testing. This class implements the BVH_PairTraverse interface
//! to efficiently find potentially intersecting triangles using bounding box tests.
//!
//! The traversal collects pairs of original (1-based) triangle indices that have
//! overlapping bounding boxes, which should then be tested for actual geometric
//! intersection using IntPatch_InterferencePolyhedron::Intersect().
class IntPatch_BVHTraversal : public BVH_PairTraverse<double, 3>
{
public:
//! Pair of triangle indices (both 1-based, original indices in polyhedra).
struct TrianglePair
{
int First; //!< Triangle index in first polyhedron (1-based)
int Second; //!< Triangle index in second polyhedron (1-based)
TrianglePair(int theFirst = 0, int theSecond = 0)
: First(theFirst),
Second(theSecond)
{
}
};
public:
//! Creates an empty traversal object.
Standard_EXPORT IntPatch_BVHTraversal();
//! Destructor.
Standard_EXPORT virtual ~IntPatch_BVHTraversal();
//! Performs BVH traversal and collects candidate triangle pairs.
//! @param[in] theSet1 BVH set for the first polyhedron
//! @param[in] theSet2 BVH set for the second polyhedron
//! @param[in] theSelfInterference if true, skip pairs where first index >= second index
//! (used for self-intersection where we don't want to test same pair twice)
//! @return number of collected pairs
Standard_EXPORT int Perform(IntPatch_PolyhedronBVH& theSet1,
IntPatch_PolyhedronBVH& theSet2,
bool theSelfInterference = false);
//! Returns the collected triangle pairs.
const NCollection_Vector<TrianglePair>& Pairs() const { return myPairs; }
//! Clears the collected pairs.
void Clear() { myPairs.Clear(); }
public: //! @name BVH_PairTraverse interface implementation
//! Rejects pair of nodes if their bounding boxes don't overlap.
//! @param[in] theCMin1 minimum corner of the first node's bounding box
//! @param[in] theCMax1 maximum corner of the first node's bounding box
//! @param[in] theCMin2 minimum corner of the second node's bounding box
//! @param[in] theCMax2 maximum corner of the second node's bounding box
//! @param[out] theMetric unused metric parameter
//! @return true if the pair should be rejected (no overlap), false otherwise
Standard_EXPORT virtual bool RejectNode(const BVH_Vec3d& theCMin1,
const BVH_Vec3d& theCMax1,
const BVH_Vec3d& theCMin2,
const BVH_Vec3d& theCMax2,
double& theMetric) const override;
//! Accepts a pair of leaf elements and stores their original indices.
//! @param[in] theIndex1 0-based index in the first BVH set
//! @param[in] theIndex2 0-based index in the second BVH set
//! @return true (always accepts the pair)
Standard_EXPORT virtual bool Accept(const int theIndex1, const int theIndex2) override;
private:
IntPatch_PolyhedronBVH* mySet1; //!< First BVH set
IntPatch_PolyhedronBVH* mySet2; //!< Second BVH set
bool mySelfInterference; //!< Self-interference mode flag
NCollection_Vector<TrianglePair> myPairs; //!< Collected triangle pairs
};
#endif // IntPatch_BVHTraversal_HeaderFile

View File

@@ -14,7 +14,6 @@
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.
#include <Bnd_BoundSortBox.hxx>
#include <Bnd_Box.hxx>
#include <NCollection_Array1.hxx>
#include <NCollection_HArray1.hxx>
@@ -22,8 +21,10 @@
#include <gp_Vec.hxx>
#include <gp_XYZ.hxx>
#include <Intf.hxx>
#include <IntPatch_BVHTraversal.hxx>
#include <IntPatch_InterferencePolyhedron.hxx>
#include <IntPatch_Polyhedron.hxx>
#include <IntPatch_PolyhedronBVH.hxx>
#include <IntPatch_PolyhedronTool.hxx>
#include <NCollection_LocalArray.hxx>
#include <Standard_Integer.hxx>
@@ -117,89 +118,22 @@ void IntPatch_InterferencePolyhedron::Perform(const IntPatch_Polyhedron& Objet)
void IntPatch_InterferencePolyhedron::Interference(const IntPatch_Polyhedron&) {}
void IntPatch_InterferencePolyhedron::Interference(const IntPatch_Polyhedron& FirstPol,
const IntPatch_Polyhedron& SeconPol)
void IntPatch_InterferencePolyhedron::Interference(const IntPatch_Polyhedron& theFirstPol,
const IntPatch_Polyhedron& theSecondPol)
{
bool gridOnFirst = true;
int NbTrianglesFirstPol = IntPatch_PolyhedronTool::NbTriangles(FirstPol);
int NbTrianglesSecondPol = IntPatch_PolyhedronTool::NbTriangles(SeconPol);
int iFirst, iSecon;
// Build BVH sets for both polyhedra
IntPatch_PolyhedronBVH aSet1(theFirstPol);
IntPatch_PolyhedronBVH aSet2(theSecondPol);
//------------------------------------------------------------------------------------------
//-- the same number of triangles it is necessary to test better on
//-- the size of boxes.
//--
//-- the second is chosen if nbTri1 > 2*nbTri2 or if VolBoit1 > 2*VolBoit2
//--
//--if (!SelfIntf && NbTrianglesFirstPol>NbTrianglesSecondPol)
//-- gridOnFirst=false;
// Find candidate triangle pairs via BVH traversal
IntPatch_BVHTraversal aTraversal;
aTraversal.Perform(aSet1, aSet2, SelfIntf);
if (!SelfIntf)
// Process each candidate pair
const NCollection_Vector<IntPatch_BVHTraversal::TrianglePair>& aPairs = aTraversal.Pairs();
for (const auto& aPair : aPairs)
{
if (NbTrianglesFirstPol > NbTrianglesSecondPol + NbTrianglesSecondPol)
gridOnFirst = false;
double vol1, vol2, Xmin, Ymin, Zmin, Xmax, Ymax, Zmax;
IntPatch_PolyhedronTool::Bounding(FirstPol).Get(Xmin, Ymin, Zmin, Xmax, Ymax, Zmax);
vol1 = (Xmax - Xmin) * (Ymax - Ymin) * (Zmax - Zmin);
IntPatch_PolyhedronTool::Bounding(SeconPol).Get(Xmin, Ymin, Zmin, Xmax, Ymax, Zmax);
vol2 = (Xmax - Xmin) * (Ymax - Ymin) * (Zmax - Zmin);
if (vol1 > 8.0 * vol2)
gridOnFirst = false;
}
if (gridOnFirst)
{
Bnd_BoundSortBox TheGridFirst;
TheGridFirst.Initialize(IntPatch_PolyhedronTool::Bounding(FirstPol),
IntPatch_PolyhedronTool::ComponentsBounding(FirstPol));
for (iSecon = 1; iSecon <= NbTrianglesSecondPol; iSecon++)
{
NCollection_List<int>::Iterator iLoI(
TheGridFirst.Compare(IntPatch_PolyhedronTool::ComponentsBounding(SeconPol)->Value(iSecon)));
while (iLoI.More())
{
iFirst = iLoI.Value();
if (SelfIntf)
{
if (iFirst < iSecon)
Intersect(iFirst, FirstPol, iSecon, SeconPol);
}
else
Intersect(iFirst, FirstPol, iSecon, SeconPol);
iLoI.Next();
}
}
}
else
{
Bnd_BoundSortBox TheGridSecond;
TheGridSecond.Initialize(IntPatch_PolyhedronTool::Bounding(SeconPol),
IntPatch_PolyhedronTool::ComponentsBounding(SeconPol));
for (iFirst = 1; iFirst <= NbTrianglesFirstPol; iFirst++)
{
NCollection_List<int>::Iterator iLoI(TheGridSecond.Compare(
IntPatch_PolyhedronTool::ComponentsBounding(FirstPol)->Value(iFirst)));
while (iLoI.More())
{
iSecon = iLoI.Value();
if (SelfIntf)
{
if (iFirst < iSecon)
Intersect(iFirst, FirstPol, iSecon, SeconPol);
}
else
Intersect(iFirst, FirstPol, iSecon, SeconPol);
iLoI.Next();
}
}
Intersect(aPair.First, theFirstPol, aPair.Second, theSecondPol);
}
}

View File

@@ -0,0 +1,189 @@
// Copyright (c) 2024 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.
#include <IntPatch_PolyhedronBVH.hxx>
#include <Bnd_Box.hxx>
#include <IntPatch_Polyhedron.hxx>
#include <IntPatch_PolyhedronTool.hxx>
#include <gp_Pnt.hxx>
#include <algorithm>
//==================================================================================================
IntPatch_PolyhedronBVH::IntPatch_PolyhedronBVH()
: BVH_PrimitiveSet<double, 3>(
new BVH_LinearBuilder<double, 3>(BVH_Constants_LeafNodeSizeDefault,
BVH_Constants_MaxTreeDepth)),
myPoly(nullptr)
{
}
//==================================================================================================
IntPatch_PolyhedronBVH::IntPatch_PolyhedronBVH(const IntPatch_Polyhedron& thePoly)
: BVH_PrimitiveSet<double, 3>(
new BVH_LinearBuilder<double, 3>(BVH_Constants_LeafNodeSizeDefault,
BVH_Constants_MaxTreeDepth)),
myPoly(nullptr)
{
Init(thePoly);
}
//==================================================================================================
IntPatch_PolyhedronBVH::~IntPatch_PolyhedronBVH()
{
//
}
//==================================================================================================
void IntPatch_PolyhedronBVH::Init(const IntPatch_Polyhedron& thePoly)
{
myPoly = &thePoly;
const int aNbTriangles = IntPatch_PolyhedronTool::NbTriangles(thePoly);
// Filter out degenerate triangles (those with void component bounding boxes)
// by only including triangles with valid boxes in the index mapping.
// This matches the behavior of the former Bnd_BoundSortBox-based approach,
// which never matched void boxes.
const occ::handle<NCollection_HArray1<Bnd_Box>>& aCompBnd =
IntPatch_PolyhedronTool::ComponentsBounding(thePoly);
myIndexMap.Clear();
for (int anIdx = 1; anIdx <= aNbTriangles; ++anIdx)
{
if (!aCompBnd->Value(anIdx).IsVoid())
{
myIndexMap.Append(anIdx); // Store 1-based original index
}
}
// Mark as dirty to trigger BVH rebuild
MarkDirty();
}
//==================================================================================================
void IntPatch_PolyhedronBVH::Clear()
{
myPoly = nullptr;
myIndexMap.Clear();
MarkDirty();
}
//==================================================================================================
int IntPatch_PolyhedronBVH::Size() const
{
if (myPoly == nullptr)
{
return 0;
}
return static_cast<int>(myIndexMap.Size());
}
//==================================================================================================
BVH_Box<double, 3> IntPatch_PolyhedronBVH::Box(const int theIndex) const
{
BVH_Box<double, 3> aBox;
if (myPoly == nullptr || theIndex < 0 || theIndex >= Size())
{
return aBox;
}
// Get original 1-based triangle index
const int anOrigIdx = myIndexMap.Value(theIndex);
// Use pre-computed component bounding boxes from the polyhedron.
// These boxes include deflection enlargement, matching the behavior
// of the former Bnd_BoundSortBox-based approach. Degenerate triangles
// (with void boxes) are already excluded during Init().
const occ::handle<NCollection_HArray1<Bnd_Box>>& aCompBnd =
IntPatch_PolyhedronTool::ComponentsBounding(*myPoly);
const Bnd_Box& aBndBox = aCompBnd->Value(anOrigIdx);
double aXmin, aYmin, aZmin, aXmax, aYmax, aZmax;
aBndBox.Get(aXmin, aYmin, aZmin, aXmax, aYmax, aZmax);
aBox.Add(BVH_Vec3d(aXmin, aYmin, aZmin));
aBox.Add(BVH_Vec3d(aXmax, aYmax, aZmax));
return aBox;
}
//==================================================================================================
double IntPatch_PolyhedronBVH::Center(const int theIndex, const int theAxis) const
{
if (myPoly == nullptr || theIndex < 0 || theIndex >= Size())
{
return 0.0;
}
// Get original 1-based triangle index
const int anOrigIdx = myIndexMap.Value(theIndex);
// Get triangle vertex indices
int aP1, aP2, aP3;
IntPatch_PolyhedronTool::Triangle(*myPoly, anOrigIdx, aP1, aP2, aP3);
// Get vertex coordinates
const gp_Pnt& aPnt1 = IntPatch_PolyhedronTool::Point(*myPoly, aP1);
const gp_Pnt& aPnt2 = IntPatch_PolyhedronTool::Point(*myPoly, aP2);
const gp_Pnt& aPnt3 = IntPatch_PolyhedronTool::Point(*myPoly, aP3);
// Compute centroid coordinate along specified axis
switch (theAxis)
{
case 0:
return (aPnt1.X() + aPnt2.X() + aPnt3.X()) / 3.0;
case 1:
return (aPnt1.Y() + aPnt2.Y() + aPnt3.Y()) / 3.0;
case 2:
return (aPnt1.Z() + aPnt2.Z() + aPnt3.Z()) / 3.0;
default:
return 0.0;
}
}
//==================================================================================================
void IntPatch_PolyhedronBVH::Swap(const int theIndex1, const int theIndex2)
{
if (theIndex1 == theIndex2)
{
return;
}
// Swap indices in the mapping
const int aTmp = myIndexMap.Value(theIndex1);
myIndexMap.SetValue(theIndex1, myIndexMap.Value(theIndex2));
myIndexMap.SetValue(theIndex2, aTmp);
}
//==================================================================================================
int IntPatch_PolyhedronBVH::OriginalIndex(const int theIndex) const
{
if (theIndex < 0 || theIndex >= Size())
{
return 0;
}
return myIndexMap.Value(theIndex);
}

View File

@@ -0,0 +1,85 @@
// Copyright (c) 2024 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.
#ifndef IntPatch_PolyhedronBVH_HeaderFile
#define IntPatch_PolyhedronBVH_HeaderFile
#include <BVH_PrimitiveSet.hxx>
#include <BVH_LinearBuilder.hxx>
#include <NCollection_Vector.hxx>
class IntPatch_Polyhedron;
//! Wraps IntPatch_Polyhedron as a BVH_PrimitiveSet for efficient spatial queries.
//! This class provides a BVH (Bounding Volume Hierarchy) representation of a polyhedron's
//! triangles, enabling O(log n) spatial queries instead of linear search.
//!
//! The class stores a reference to the polyhedron (no data copy) and maintains
//! an index mapping to track triangle reordering during BVH construction.
class IntPatch_PolyhedronBVH : public BVH_PrimitiveSet<double, 3>
{
public:
//! Creates an empty BVH set.
Standard_EXPORT IntPatch_PolyhedronBVH();
//! Creates BVH set from the given polyhedron.
//! @param[in] thePoly the polyhedron to wrap (must remain valid during BVH lifetime)
Standard_EXPORT IntPatch_PolyhedronBVH(const IntPatch_Polyhedron& thePoly);
//! Destructor.
Standard_EXPORT virtual ~IntPatch_PolyhedronBVH();
//! Initializes BVH set from the given polyhedron.
//! @param[in] thePoly the polyhedron to wrap (must remain valid during BVH lifetime)
Standard_EXPORT void Init(const IntPatch_Polyhedron& thePoly);
//! Clears the BVH set.
Standard_EXPORT void Clear();
public: //! @name BVH_Set interface implementation
// Make inherited Box() method visible
using BVH_PrimitiveSet<double, 3>::Box;
//! Returns the total number of triangles.
Standard_EXPORT virtual int Size() const override;
//! Returns AABB of the triangle with the given index.
//! @param[in] theIndex 0-based triangle index (after BVH reordering)
Standard_EXPORT virtual BVH_Box<double, 3> Box(const int theIndex) const override;
//! Returns centroid coordinate of the triangle along the given axis.
//! @param[in] theIndex 0-based triangle index (after BVH reordering)
//! @param[in] theAxis axis index (0=X, 1=Y, 2=Z)
Standard_EXPORT virtual double Center(const int theIndex, const int theAxis) const override;
//! Swaps two triangles in the set (used during BVH construction).
//! @param[in] theIndex1 first triangle index
//! @param[in] theIndex2 second triangle index
Standard_EXPORT virtual void Swap(const int theIndex1, const int theIndex2) override;
public: //! @name Additional methods
//! Returns the original (1-based) triangle index in the polyhedron
//! for the given 0-based index after BVH reordering.
//! @param[in] theIndex 0-based triangle index (after BVH reordering)
//! @return 1-based original triangle index in the polyhedron
Standard_EXPORT int OriginalIndex(const int theIndex) const;
//! Returns true if the BVH set is initialized.
bool IsInitialized() const { return myPoly != nullptr; }
private:
const IntPatch_Polyhedron* myPoly; //!< Reference to the wrapped polyhedron
NCollection_Vector<int> myIndexMap; //!< Maps current indices to original 1-based indices
};
#endif // IntPatch_PolyhedronBVH_HeaderFile