mirror of
https://github.com/Open-Cascade-SAS/OCCT.git
synced 2026-05-10 09:30:48 +08:00
Foundation Classes - Refactor BVH_Box to use generic vector types and add transformation tests (#858)
- Replaced `Graphic3d_Vec3d` and `Graphic3d_Vec4d` with generic `BVH_VecNt` and `BVH::VectorType<T, 4>::Type` - Changed `Standard_Integer` and `Standard_Real` to template type `T` and `int` for generic implementation - Added comprehensive test coverage for float-precision transformations
This commit is contained in:
@@ -21,8 +21,6 @@
|
||||
#include <Standard_Macro.hxx>
|
||||
#include <Standard_Dump.hxx>
|
||||
#include <Standard_ShortReal.hxx>
|
||||
#include <Graphic3d_Vec4.hxx>
|
||||
#include <Graphic3d_Vec3.hxx>
|
||||
|
||||
#include <limits>
|
||||
|
||||
@@ -68,6 +66,8 @@ public:
|
||||
//! given transformation to this box.
|
||||
BVH_Box<T, 3> Transformed(const NCollection_Mat4<T>& theTransform) const
|
||||
{
|
||||
using BVH_VecNt = typename BVH_Box<T, 3>::BVH_VecNt;
|
||||
|
||||
const BVH_Box<T, 3>* aThis = static_cast<const BVH_Box<T, 3>*>(this);
|
||||
if (theTransform.IsIdentity())
|
||||
{
|
||||
@@ -80,15 +80,13 @@ public:
|
||||
}
|
||||
|
||||
// Untransformed AABB min and max points
|
||||
Graphic3d_Vec3d anOldMinPnt = aThis->CornerMin();
|
||||
Graphic3d_Vec3d anOldMaxPnt = aThis->CornerMax();
|
||||
const BVH_VecNt& anOldMinPnt = aThis->CornerMin();
|
||||
const BVH_VecNt& anOldMaxPnt = aThis->CornerMax();
|
||||
|
||||
// Define an empty AABB located in the transformation translation point
|
||||
Graphic3d_Vec4d aTranslation = theTransform.GetColumn(3);
|
||||
Graphic3d_Vec3d aNewMinPnt =
|
||||
Graphic3d_Vec3d(aTranslation.x(), aTranslation.y(), aTranslation.z());
|
||||
Graphic3d_Vec3d aNewMaxPnt =
|
||||
Graphic3d_Vec3d(aTranslation.x(), aTranslation.y(), aTranslation.z());
|
||||
const typename BVH::VectorType<T, 4>::Type aTranslation = theTransform.GetColumn(3);
|
||||
BVH_VecNt aNewMinPnt = BVH_VecNt(aTranslation.x(), aTranslation.y(), aTranslation.z());
|
||||
BVH_VecNt aNewMaxPnt = BVH_VecNt(aTranslation.x(), aTranslation.y(), aTranslation.z());
|
||||
|
||||
// This implements James Arvo's algorithm for transforming an axis-aligned bounding box (AABB)
|
||||
// under an affine transformation. For each row of the transformation matrix, we compute
|
||||
@@ -96,21 +94,20 @@ public:
|
||||
// minimum and maximum values to form the new bounding box. This ensures that the transformed
|
||||
// box tightly encloses the original box after transformation, accounting for rotation and
|
||||
// scaling.
|
||||
for (Standard_Integer aCol = 0; aCol < 3; ++aCol)
|
||||
for (int aCol = 0; aCol < 3; ++aCol)
|
||||
{
|
||||
for (Standard_Integer aRow = 0; aRow < 3; ++aRow)
|
||||
for (int aRow = 0; aRow < 3; ++aRow)
|
||||
{
|
||||
Standard_Real aMatValue = theTransform.GetValue(aRow, aCol);
|
||||
Standard_Real anOffset1 = aMatValue * anOldMinPnt.GetData()[aCol];
|
||||
Standard_Real anOffset2 = aMatValue * anOldMaxPnt.GetData()[aCol];
|
||||
const T aMatValue = theTransform.GetValue(aRow, aCol);
|
||||
const T anOffset1 = aMatValue * anOldMinPnt.GetData()[aCol];
|
||||
const T anOffset2 = aMatValue * anOldMaxPnt.GetData()[aCol];
|
||||
|
||||
aNewMinPnt.ChangeData()[aRow] += (std::min)(anOffset1, anOffset2);
|
||||
aNewMaxPnt.ChangeData()[aRow] += (std::max)(anOffset1, anOffset2);
|
||||
}
|
||||
}
|
||||
|
||||
BVH_Box<T, 3> aResultBox(aNewMinPnt, aNewMaxPnt);
|
||||
return aResultBox;
|
||||
return BVH_Box<T, 3>(aNewMinPnt, aNewMaxPnt);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1043,3 +1043,93 @@ TEST(BVH_BoxTest, Constexpr_DefaultInvalid)
|
||||
|
||||
static_assert(!aBox.IsValid(), "Default constexpr box should be invalid");
|
||||
}
|
||||
|
||||
// =======================================================================================
|
||||
// Tests for Transform/Transformed with float precision
|
||||
// =======================================================================================
|
||||
|
||||
TEST(BVH_BoxTest, Transform_Float_Identity)
|
||||
{
|
||||
BVH_Box<Standard_ShortReal, 3> aBox(BVH_Vec3f(0.0f, 0.0f, 0.0f), BVH_Vec3f(1.0f, 1.0f, 1.0f));
|
||||
|
||||
NCollection_Mat4<Standard_ShortReal> aIdentity;
|
||||
aIdentity.InitIdentity();
|
||||
|
||||
aBox.Transform(aIdentity);
|
||||
|
||||
EXPECT_NEAR(aBox.CornerMin().x(), 0.0f, 1e-5f);
|
||||
EXPECT_NEAR(aBox.CornerMax().x(), 1.0f, 1e-5f);
|
||||
}
|
||||
|
||||
TEST(BVH_BoxTest, Transform_Float_Translation)
|
||||
{
|
||||
BVH_Box<Standard_ShortReal, 3> aBox(BVH_Vec3f(0.0f, 0.0f, 0.0f), BVH_Vec3f(1.0f, 1.0f, 1.0f));
|
||||
|
||||
NCollection_Mat4<Standard_ShortReal> aTransform;
|
||||
aTransform.InitIdentity();
|
||||
aTransform.SetColumn(3, NCollection_Vec3<Standard_ShortReal>(5.0f, 10.0f, 15.0f));
|
||||
|
||||
aBox.Transform(aTransform);
|
||||
|
||||
EXPECT_NEAR(aBox.CornerMin().x(), 5.0f, 1e-5f);
|
||||
EXPECT_NEAR(aBox.CornerMin().y(), 10.0f, 1e-5f);
|
||||
EXPECT_NEAR(aBox.CornerMin().z(), 15.0f, 1e-5f);
|
||||
EXPECT_NEAR(aBox.CornerMax().x(), 6.0f, 1e-5f);
|
||||
EXPECT_NEAR(aBox.CornerMax().y(), 11.0f, 1e-5f);
|
||||
EXPECT_NEAR(aBox.CornerMax().z(), 16.0f, 1e-5f);
|
||||
}
|
||||
|
||||
TEST(BVH_BoxTest, Transform_Float_Scale)
|
||||
{
|
||||
BVH_Box<Standard_ShortReal, 3> aBox(BVH_Vec3f(0.0f, 0.0f, 0.0f), BVH_Vec3f(1.0f, 1.0f, 1.0f));
|
||||
|
||||
NCollection_Mat4<Standard_ShortReal> aTransform;
|
||||
aTransform.InitIdentity();
|
||||
aTransform.SetValue(0, 0, 2.0f); // Scale X by 2
|
||||
aTransform.SetValue(1, 1, 3.0f); // Scale Y by 3
|
||||
aTransform.SetValue(2, 2, 4.0f); // Scale Z by 4
|
||||
|
||||
aBox.Transform(aTransform);
|
||||
|
||||
EXPECT_NEAR(aBox.CornerMin().x(), 0.0f, 1e-5f);
|
||||
EXPECT_NEAR(aBox.CornerMax().x(), 2.0f, 1e-5f);
|
||||
EXPECT_NEAR(aBox.CornerMax().y(), 3.0f, 1e-5f);
|
||||
EXPECT_NEAR(aBox.CornerMax().z(), 4.0f, 1e-5f);
|
||||
}
|
||||
|
||||
TEST(BVH_BoxTest, Transformed_Float_Translation)
|
||||
{
|
||||
BVH_Box<Standard_ShortReal, 3> aBox(BVH_Vec3f(0.0f, 0.0f, 0.0f), BVH_Vec3f(1.0f, 1.0f, 1.0f));
|
||||
|
||||
NCollection_Mat4<Standard_ShortReal> aTransform;
|
||||
aTransform.InitIdentity();
|
||||
aTransform.SetColumn(3, NCollection_Vec3<Standard_ShortReal>(10.0f, 20.0f, 30.0f));
|
||||
|
||||
BVH_Box<Standard_ShortReal, 3> aTransformed = aBox.Transformed(aTransform);
|
||||
|
||||
// Original should be unchanged
|
||||
EXPECT_NEAR(aBox.CornerMin().x(), 0.0f, 1e-5f);
|
||||
EXPECT_NEAR(aBox.CornerMax().x(), 1.0f, 1e-5f);
|
||||
|
||||
// Transformed should be translated
|
||||
EXPECT_NEAR(aTransformed.CornerMin().x(), 10.0f, 1e-5f);
|
||||
EXPECT_NEAR(aTransformed.CornerMin().y(), 20.0f, 1e-5f);
|
||||
EXPECT_NEAR(aTransformed.CornerMin().z(), 30.0f, 1e-5f);
|
||||
EXPECT_NEAR(aTransformed.CornerMax().x(), 11.0f, 1e-5f);
|
||||
EXPECT_NEAR(aTransformed.CornerMax().y(), 21.0f, 1e-5f);
|
||||
EXPECT_NEAR(aTransformed.CornerMax().z(), 31.0f, 1e-5f);
|
||||
}
|
||||
|
||||
TEST(BVH_BoxTest, Transform_Float_InvalidBox)
|
||||
{
|
||||
BVH_Box<Standard_ShortReal, 3> aBox; // Invalid box
|
||||
|
||||
NCollection_Mat4<Standard_ShortReal> aTransform;
|
||||
aTransform.InitIdentity();
|
||||
aTransform.SetColumn(3, NCollection_Vec3<Standard_ShortReal>(10.0f, 20.0f, 30.0f));
|
||||
|
||||
aBox.Transform(aTransform);
|
||||
|
||||
// Should remain invalid
|
||||
EXPECT_FALSE(aBox.IsValid());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user