// Created on: 2013-12-20 // Created by: Denis BOGOLEPOV // Copyright (c) 2013-2014 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 BVH_Box_HeaderFile #define BVH_Box_HeaderFile #include "BVH_Constants.hxx" #include "BVH_Types.hxx" #include "Standard_Macro.hxx" #include "Standard_Dump.hxx" #include "Standard_ShortReal.hxx" #include //! Base class for BVH_Box (CRTP idiom is used). //! @tparam T Numeric data type //! @tparam N Vector dimension //! @tparam TheDerivedBox Template of derived class that defined axis aligned bounding box. template class TheDerivedBox> class BVH_BaseBox { }; // forward declaration template class BVH_Box; //! Partial template specialization for BVH_Box when N = 3. template class BVH_BaseBox { public: //! Transforms this box with given transformation. void Transform(const NCollection_Mat4& theTransform) { if (theTransform.IsIdentity()) { return; } BVH_Box* aThis = static_cast*>(this); if (!aThis->IsValid()) { return; } BVH_Box aBox = Transformed(theTransform); aThis->CornerMin() = aBox.CornerMin(); aThis->CornerMax() = aBox.CornerMax(); } //! Returns a box which is the result of applying the //! given transformation to this box. BVH_Box Transformed(const NCollection_Mat4& theTransform) const { using BVH_VecNt = typename BVH_Box::BVH_VecNt; const BVH_Box* aThis = static_cast*>(this); if (theTransform.IsIdentity()) { return *aThis; } if (!aThis->IsValid()) { return *aThis; } // Untransformed AABB min and max points const BVH_VecNt& anOldMinPnt = aThis->CornerMin(); const BVH_VecNt& anOldMaxPnt = aThis->CornerMax(); // Define an empty AABB located in the transformation translation point const typename BVH::VectorType::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 // the products of the min and max coordinates with the matrix elements, and select the // 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 (int aCol = 0; aCol < 3; ++aCol) { for (int aRow = 0; aRow < 3; ++aRow) { 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); } } return BVH_Box(aNewMinPnt, aNewMaxPnt); } }; //! Defines axis aligned bounding box (AABB) based on BVH vectors. //! \tparam T Numeric data type //! \tparam N Vector dimension template class BVH_Box : public BVH_BaseBox { public: typedef typename BVH::VectorType::Type BVH_VecNt; private: //! Returns the minimum point sentinel value for invalid box. static constexpr T minSentinel() noexcept { return (std::numeric_limits::max)(); } //! Returns the maximum point sentinel value for invalid box. static constexpr T maxSentinel() noexcept { return (std::numeric_limits::lowest)(); } public: //! Creates uninitialized bounding box. constexpr BVH_Box() noexcept : myMinPoint(BVH_VecNt(minSentinel())), myMaxPoint(BVH_VecNt(maxSentinel())) { } //! Creates bounding box of given point. constexpr BVH_Box(const BVH_VecNt& thePoint) noexcept : myMinPoint(thePoint), myMaxPoint(thePoint) { } //! Creates bounding box from corner points. constexpr BVH_Box(const BVH_VecNt& theMinPoint, const BVH_VecNt& theMaxPoint) noexcept : myMinPoint(theMinPoint), myMaxPoint(theMaxPoint) { } public: //! Clears bounding box. constexpr void Clear() noexcept { myMinPoint = BVH_VecNt(minSentinel()); myMaxPoint = BVH_VecNt(maxSentinel()); } //! Is bounding box valid? constexpr bool IsValid() const noexcept { return myMinPoint[0] <= myMaxPoint[0]; } //! Appends new point to the bounding box. void Add(const BVH_VecNt& thePoint); //! Combines bounding box with another one. void Combine(const BVH_Box& theBox); //! Returns minimum point of bounding box. constexpr const BVH_VecNt& CornerMin() const noexcept { return myMinPoint; } //! Returns maximum point of bounding box. constexpr const BVH_VecNt& CornerMax() const noexcept { return myMaxPoint; } //! Returns minimum point of bounding box. constexpr BVH_VecNt& CornerMin() noexcept { return myMinPoint; } //! Returns maximum point of bounding box. constexpr BVH_VecNt& CornerMax() noexcept { return myMaxPoint; } //! Returns surface area of bounding box. //! If the box is degenerated into line, returns the perimeter instead. T Area() const; //! Returns diagonal of bounding box. constexpr BVH_VecNt Size() const { return myMaxPoint - myMinPoint; } //! Returns center of bounding box. constexpr BVH_VecNt Center() const { return (myMinPoint + myMaxPoint) * static_cast(0.5); } //! Returns center of bounding box along the given axis. inline T Center(const int theAxis) const; //! Dumps the content of me into the stream void DumpJson(Standard_OStream& theOStream, int theDepth = -1) const { (void)theDepth; const int anIsValid = IsValid() ? 1 : 0; OCCT_DUMP_FIELD_VALUE_NUMERICAL(theOStream, anIsValid) constexpr int n = (N < 3) ? N : 3; if constexpr (n == 1) { OCCT_DUMP_FIELD_VALUE_NUMERICAL(theOStream, myMinPoint[0]) OCCT_DUMP_FIELD_VALUE_NUMERICAL(theOStream, myMaxPoint[0]) } else if constexpr (n == 2) { OCCT_DUMP_FIELD_VALUES_NUMERICAL(theOStream, "MinPoint", n, myMinPoint[0], myMinPoint[1]) OCCT_DUMP_FIELD_VALUES_NUMERICAL(theOStream, "MaxPoint", n, myMaxPoint[0], myMaxPoint[1]) } else if constexpr (n == 3) { OCCT_DUMP_FIELD_VALUES_NUMERICAL(theOStream, "MinPoint", n, myMinPoint[0], myMinPoint[1], myMinPoint[2]) OCCT_DUMP_FIELD_VALUES_NUMERICAL(theOStream, "MaxPoint", n, myMaxPoint[0], myMaxPoint[1], myMaxPoint[2]) } } //! Inits the content of me from the stream bool InitFromJson(const Standard_SStream& theSStream, int& theStreamPos) { int aPos = theStreamPos; int anIsValid = 0; TCollection_AsciiString aStreamStr = Standard_Dump::Text(theSStream); OCCT_INIT_FIELD_VALUE_INTEGER(aStreamStr, aPos, anIsValid); if (anIsValid == 0) { Clear(); // Set to invalid state using sentinel values theStreamPos = aPos; return true; } constexpr int n = (N < 3) ? N : 3; if constexpr (n == 1) { double aMinValue, aMaxValue; OCCT_INIT_FIELD_VALUE_REAL(aStreamStr, aPos, aMinValue); OCCT_INIT_FIELD_VALUE_REAL(aStreamStr, aPos, aMaxValue); myMinPoint[0] = (T)aMinValue; myMaxPoint[0] = (T)aMaxValue; } else if constexpr (n == 2) { double aValue1, aValue2; OCCT_INIT_VECTOR_CLASS(aStreamStr, "MinPoint", aPos, n, &aValue1, &aValue2); myMinPoint[0] = (T)aValue1; myMinPoint[1] = (T)aValue2; OCCT_INIT_VECTOR_CLASS(aStreamStr, "MaxPoint", aPos, n, &aValue1, &aValue2); myMaxPoint[0] = (T)aValue1; myMaxPoint[1] = (T)aValue2; } else if constexpr (n == 3) { double aValue1, aValue2, aValue3; OCCT_INIT_VECTOR_CLASS(aStreamStr, "MinPoint", aPos, n, &aValue1, &aValue2, &aValue3); myMinPoint[0] = (T)aValue1; myMinPoint[1] = (T)aValue2; myMinPoint[2] = (T)aValue3; OCCT_INIT_VECTOR_CLASS(aStreamStr, "MaxPoint", aPos, n, &aValue1, &aValue2, &aValue3); myMaxPoint[0] = (T)aValue1; myMaxPoint[1] = (T)aValue2; myMaxPoint[2] = (T)aValue3; } // For N > 3, initialize remaining dimensions to unbounded range // so they don't affect intersection checks for (int i = n; i < N; ++i) { myMinPoint[i] = (std::numeric_limits::lowest)(); myMaxPoint[i] = (std::numeric_limits::max)(); } theStreamPos = aPos; return true; } public: //! Checks if the Box is out of the other box. constexpr bool IsOut(const BVH_Box& theOther) const { if (!theOther.IsValid()) return true; return IsOut(theOther.myMinPoint, theOther.myMaxPoint); } //! Checks if the Box is out of the other box defined by two points. constexpr bool IsOut(const BVH_VecNt& theMinPoint, const BVH_VecNt& theMaxPoint) const { if (!IsValid()) return true; for (int i = 0; i < N; ++i) { if (myMinPoint[i] > theMaxPoint[i] || myMaxPoint[i] < theMinPoint[i]) return true; } return false; } //! Checks if the Box fully contains the other box. constexpr bool Contains(const BVH_Box& theOther, bool& hasOverlap) const { hasOverlap = false; if (!theOther.IsValid()) return false; return Contains(theOther.myMinPoint, theOther.myMaxPoint, hasOverlap); } //! Checks if the Box is fully contains the other box. constexpr bool Contains(const BVH_VecNt& theMinPoint, const BVH_VecNt& theMaxPoint, bool& hasOverlap) const { hasOverlap = false; if (!IsValid()) return false; bool isInside = true; for (int i = 0; i < N; ++i) { hasOverlap = (myMinPoint[i] <= theMaxPoint[i] && myMaxPoint[i] >= theMinPoint[i]); if (!hasOverlap) return false; isInside = isInside && (myMinPoint[i] <= theMinPoint[i] && myMaxPoint[i] >= theMaxPoint[i]); } return isInside; } //! Checks if the Point is out of the box. constexpr bool IsOut(const BVH_VecNt& thePoint) const { if (!IsValid()) return true; for (int i = 0; i < N; ++i) { if (thePoint[i] < myMinPoint[i] || thePoint[i] > myMaxPoint[i]) return true; } return false; } protected: BVH_VecNt myMinPoint; //!< Minimum point of bounding box (max when invalid) BVH_VecNt myMaxPoint; //!< Maximum point of bounding box (lowest when invalid) }; namespace BVH { //! Tool class for calculating box center along the given axis. //! \tparam T Numeric data type //! \tparam N Vector dimension template struct CenterAxis { //! Returns the center of the box along the specified axis using array access. static inline T Center(const BVH_Box& theBox, const int theAxis) { return (theBox.CornerMin()[theAxis] + theBox.CornerMax()[theAxis]) * static_cast(0.5); } }; //! Tool class for calculating surface area of the box. //! For N=1, computes length (degenerate case). //! For N=2, computes area (or perimeter for degenerate boxes). //! For N>=3, computes 3D surface area using X, Y, Z components only. //! The W component (4th dimension) is intentionally ignored as BVH surface area //! heuristic (SAH) operates in 3D geometric space regardless of additional dimensions. //! \tparam T Numeric data type //! \tparam N Vector dimension template struct SurfaceCalculator { static inline T Area(const typename BVH_Box::BVH_VecNt& theSize) { if constexpr (N == 1) { // For 1D, return the length return std::abs(theSize[0]); } else if constexpr (N == 2) { const T anArea = std::abs(theSize.x() * theSize.y()); if (anArea < std::numeric_limits::epsilon()) { return std::abs(theSize.x()) + std::abs(theSize.y()); } return anArea; } else { // For N >= 3, compute standard 3D surface area. const T anArea = (std::abs(theSize.x() * theSize.y()) + std::abs(theSize.x() * theSize.z()) + std::abs(theSize.z() * theSize.y())) * static_cast(2.0); if (anArea < std::numeric_limits::epsilon()) { return std::abs(theSize.x()) + std::abs(theSize.y()) + std::abs(theSize.z()); } return anArea; } } }; //! Tool class for computing component-wise vector minimum and maximum. //! \tparam T Numeric data type //! \tparam N Vector dimension template struct BoxMinMax { typedef typename BVH::VectorType::Type BVH_VecNt; //! Computes component-wise minimum in-place. static inline void CwiseMin(BVH_VecNt& theVec1, const BVH_VecNt& theVec2) { for (int i = 0; i < N; ++i) { theVec1[i] = (std::min)(theVec1[i], theVec2[i]); } } //! Computes component-wise maximum in-place. static inline void CwiseMax(BVH_VecNt& theVec1, const BVH_VecNt& theVec2) { for (int i = 0; i < N; ++i) { theVec1[i] = (std::max)(theVec1[i], theVec2[i]); } } }; } // namespace BVH //================================================================================================= template void BVH_Box::Add(const BVH_VecNt& thePoint) { BVH::BoxMinMax::CwiseMin(myMinPoint, thePoint); BVH::BoxMinMax::CwiseMax(myMaxPoint, thePoint); } //================================================================================================= template void BVH_Box::Combine(const BVH_Box& theBox) { if (theBox.IsValid()) { BVH::BoxMinMax::CwiseMin(myMinPoint, theBox.myMinPoint); BVH::BoxMinMax::CwiseMax(myMaxPoint, theBox.myMaxPoint); } } //================================================================================================= template T BVH_Box::Area() const { return !IsValid() ? static_cast(0.0) : BVH::SurfaceCalculator::Area(myMaxPoint - myMinPoint); } //================================================================================================= template T BVH_Box::Center(const int theAxis) const { return BVH::CenterAxis::Center(*this, theAxis); } #endif // _BVH_Box_Header