#include <Standard_Macro.hxx>
#include <Standard_Dump.hxx>
#include <Standard_ShortReal.hxx>
-#include <Graphic3d_Vec4.hxx>
-#include <Graphic3d_Vec3.hxx>
#include <limits>
//! 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())
{
}
// 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
// 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);
}
};
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());
+}