Foundation Classes - Bnd package improvements (#1051)

Bug fixes:
- Bnd_Box::Add(Bnd_Box) - fix unconditional bounds merge before open direction check
- Bnd_Box::IsOut(gp_Pln) - use GetXMin()/GetXMax() accessors (include Gap) instead of raw fields
- Bnd_Box::Distance() - add void box check to avoid invalid results
- Bnd_Range::Common() - add missing return statement
- Bnd_Sphere::SquareDistances() - fix radius comparison (was comparing distance vs radius instead of squared values)
- Bnd_OBB: OBBTool::ProcessTriangle - add safety check for degenerate cross products before normalization
- Bnd_OBB: OBBTool::ProcessDiTetrahedron - fix off-by-one in bounds check (use < instead of <=)

Performance optimizations:
- Bnd_Box::IsOut(Bnd_Box) - add early return fast path for non-open boxes
- Bnd_Box2d::IsOut(Bnd_Box2d) - add early return fast path for non-open boxes

API improvements:
- Add Contains()/Intersects() semantic wrappers (inverse of IsOut) to Bnd_Box, Bnd_Box2d, Bnd_OBB, Bnd_Range
- Add Center() returning std::optional to Bnd_Box, Bnd_Box2d, Bnd_Range
- Add Min()/Max()/Get() returning std::optional to Bnd_Range
- Add Bnd_Range::Bounds struct for C++17 structured bindings via Get()
- Add Bnd_OBB::HalfSizes struct and GetHalfSizes() for structured bindings
- Add Bnd_Box2d::Distance() method
- Replace Bnd_Range::IsIntersected magic int returns with IntersectStatus enum

Code modernization:
- Add [[nodiscard]] and noexcept annotations across all Bnd classes
- Migrate Bnd_Sphere inline methods from .lxx to header, delete Bnd_Sphere.lxx
- Remove unused includes (iomanip, fstream, legacy Standard_*.hxx headers)
- Fix Bnd_Box.hxx "Ymix" typo in IsOpenYmin() comment
- Clean up DET-style comments in Bnd_Box.cxx
- Update IntPatch_WLineTool.cxx to use IntersectStatus enum
This commit is contained in:
Pasukhin Dmitry
2026-02-12 23:02:36 +00:00
committed by GitHub
parent 27788ce7a4
commit 4df229f2c4
19 changed files with 1607 additions and 172 deletions

View File

@@ -24,8 +24,6 @@
#include <Standard_Dump.hxx>
#include <Standard_Macro.hxx>
#include <iostream>
#include <iomanip>
#include <fstream>
namespace
{
@@ -289,6 +287,19 @@ gp_Pnt Bnd_Box::CornerMax() const
//=================================================================================================
std::optional<gp_Pnt> Bnd_Box::Center() const
{
if (IsVoid())
{
return std::nullopt;
}
return gp_Pnt(0.5 * (GetXMin() + GetXMax()),
0.5 * (GetYMin() + GetYMax()),
0.5 * (GetZMin() + GetZMax()));
}
//=================================================================================================
bool Bnd_Box::IsXThin(const double tol) const
{
if (IsWhole())
@@ -460,18 +471,6 @@ void Bnd_Box::Add(const Bnd_Box& Other)
return;
}
if (Xmin > Other.Xmin)
Xmin = Other.Xmin;
if (Xmax < Other.Xmax)
Xmax = Other.Xmax;
if (Ymin > Other.Ymin)
Ymin = Other.Ymin;
if (Ymax < Other.Ymax)
Ymax = Other.Ymax;
if (Zmin > Other.Zmin)
Zmin = Other.Zmin;
if (Zmax < Other.Zmax)
Zmax = Other.Zmax;
Gap = std::max(Gap, Other.Gap);
if (IsWhole())
@@ -484,18 +483,48 @@ void Bnd_Box::Add(const Bnd_Box& Other)
return;
}
if (Other.IsOpenXmin())
OpenXmin();
if (Other.IsOpenXmax())
OpenXmax();
if (Other.IsOpenYmin())
OpenYmin();
if (Other.IsOpenYmax())
OpenYmax();
if (Other.IsOpenZmin())
OpenZmin();
if (Other.IsOpenZmax())
OpenZmax();
if (!IsOpenXmin())
{
if (Other.IsOpenXmin())
OpenXmin();
else if (Xmin > Other.Xmin)
Xmin = Other.Xmin;
}
if (!IsOpenXmax())
{
if (Other.IsOpenXmax())
OpenXmax();
else if (Xmax < Other.Xmax)
Xmax = Other.Xmax;
}
if (!IsOpenYmin())
{
if (Other.IsOpenYmin())
OpenYmin();
else if (Ymin > Other.Ymin)
Ymin = Other.Ymin;
}
if (!IsOpenYmax())
{
if (Other.IsOpenYmax())
OpenYmax();
else if (Ymax < Other.Ymax)
Ymax = Other.Ymax;
}
if (!IsOpenZmin())
{
if (Other.IsOpenZmin())
OpenZmin();
else if (Zmin > Other.Zmin)
Zmin = Other.Zmin;
}
if (!IsOpenZmax())
{
if (Other.IsOpenZmax())
OpenZmax();
else if (Zmax < Other.Zmax)
Zmax = Other.Zmax;
}
}
//=================================================================================================
@@ -579,21 +608,27 @@ bool Bnd_Box::IsOut(const gp_Pln& P) const
{
double A, B, C, D;
P.Coefficients(A, B, C, D);
double d = A * (Xmin - Gap) + B * (Ymin - Gap) + C * (Zmin - Gap) + D;
bool plus = d > 0;
if (plus != ((A * (Xmin - Gap) + B * (Ymin - Gap) + C * (Zmax + Gap) + D) > 0))
const double aXmin = GetXMin();
const double aXmax = GetXMax();
const double aYmin = GetYMin();
const double aYmax = GetYMax();
const double aZmin = GetZMin();
const double aZmax = GetZMax();
double d = A * aXmin + B * aYmin + C * aZmin + D;
bool plus = d > 0;
if (plus != ((A * aXmin + B * aYmin + C * aZmax + D) > 0))
return false;
if (plus != ((A * (Xmin - Gap) + B * (Ymax + Gap) + C * (Zmin - Gap) + D) > 0))
if (plus != ((A * aXmin + B * aYmax + C * aZmin + D) > 0))
return false;
if (plus != ((A * (Xmin - Gap) + B * (Ymax + Gap) + C * (Zmax + Gap) + D) > 0))
if (plus != ((A * aXmin + B * aYmax + C * aZmax + D) > 0))
return false;
if (plus != ((A * (Xmax + Gap) + B * (Ymin - Gap) + C * (Zmin - Gap) + D) > 0))
if (plus != ((A * aXmax + B * aYmin + C * aZmin + D) > 0))
return false;
if (plus != ((A * (Xmax + Gap) + B * (Ymin - Gap) + C * (Zmax + Gap) + D) > 0))
if (plus != ((A * aXmax + B * aYmin + C * aZmax + D) > 0))
return false;
if (plus != ((A * (Xmax + Gap) + B * (Ymax + Gap) + C * (Zmin - Gap) + D) > 0))
if (plus != ((A * aXmax + B * aYmax + C * aZmin + D) > 0))
return false;
return plus == ((A * (Xmax + Gap) + B * (Ymax + Gap) + C * (Zmax + Gap) + D) > 0);
return plus == ((A * aXmax + B * aYmax + C * aZmax + D) > 0);
}
}
@@ -642,10 +677,10 @@ bool Bnd_Box::IsOut(const gp_Lin& L) const
{
par1 = (myYmin - L.Location().XYZ().Y()) / L.Direction().XYZ().Y();
par2 = (myYmax - L.Location().XYZ().Y()) / L.Direction().XYZ().Y();
//=================DET change 06/03/01====================
// Check if parameter ranges from this axis are disjoint (early exit)
if (parmax < std::min(par1, par2) || parmin > std::max(par1, par2))
return true;
//========================================================
parmin = std::max(parmin, std::min(par1, par2));
parmax = std::min(parmax, std::max(par1, par2));
yToSet = true;
@@ -665,10 +700,10 @@ bool Bnd_Box::IsOut(const gp_Lin& L) const
{
par1 = (myZmin - L.Location().XYZ().Z()) / L.Direction().XYZ().Z();
par2 = (myZmax - L.Location().XYZ().Z()) / L.Direction().XYZ().Z();
//=================DET change 06/03/01====================
// Check if parameter ranges from this axis are disjoint (early exit)
if (parmax < std::min(par1, par2) || parmin > std::max(par1, par2))
return true;
//========================================================
parmin = std::max(parmin, std::min(par1, par2));
parmax = std::min(parmax, std::max(par1, par2));
par1 = L.Location().XYZ().Z() + parmin * L.Direction().XYZ().Z();
@@ -995,13 +1030,15 @@ bool Bnd_Box::IsOut(const gp_Pnt& P1, const gp_Pnt& P2, const gp_Dir& D) const
return true;
}
//=======================================================================
// function : Distance
// purpose : computes the minimum distance between two boxes
//=======================================================================
//=================================================================================================
double Bnd_Box::Distance(const Bnd_Box& Other) const
{
if (IsVoid() || Other.IsVoid())
{
return 0.0;
}
double aXMinB1, aYMinB1, aZMinB1, aXMaxB1, aYMaxB1, aZMaxB1;
double aXMinB2, aYMinB2, aZMinB2, aXMaxB2, aYMaxB2, aZMaxB2;

View File

@@ -22,11 +22,10 @@
#include <Standard_Handle.hxx>
#include <gp_Pnt.hxx>
#include <Standard_Real.hxx>
#include <Standard_Boolean.hxx>
#include <algorithm>
#include <cmath>
#include <optional>
class gp_Pnt;
class gp_Dir;
@@ -218,6 +217,12 @@ public:
//! if IsVoid()
[[nodiscard]] Standard_EXPORT gp_Pnt CornerMax() const;
//! Returns the center of this bounding box. The gap is included.
//! If this bounding box is infinite (i.e. "open"), returned values
//! may be equal to +/- Precision::Infinite().
//! Returns std::nullopt if the box is void.
[[nodiscard]] Standard_EXPORT std::optional<gp_Pnt> Center() const;
//! The Box will be infinitely long in the Xmin
//! direction.
void OpenXmin() noexcept { Flags |= XminMask; }
@@ -251,7 +256,7 @@ public:
//! Returns true if this bounding box is open in the Xmax direction.
[[nodiscard]] bool IsOpenXmax() const noexcept { return (Flags & XmaxMask) != 0; }
//! Returns true if this bounding box is open in the Ymix direction.
//! Returns true if this bounding box is open in the Ymin direction.
[[nodiscard]] bool IsOpenYmin() const noexcept { return (Flags & YminMask) != 0; }
//! Returns true if this bounding box is open in the Ymax direction.
@@ -333,6 +338,12 @@ public:
const gp_Pnt& P2,
const gp_Dir& D) const;
//! Returns True if the point is inside or on the boundary of this box.
[[nodiscard]] bool Contains(const gp_Pnt& theP) const { return !IsOut(theP); }
//! Returns True if the other box intersects or is inside this box.
[[nodiscard]] bool Intersects(const Bnd_Box& theOther) const { return !IsOut(theOther); }
//! Computes the minimum distance between two boxes.
[[nodiscard]] Standard_EXPORT double Distance(const Bnd_Box& Other) const;

View File

@@ -21,8 +21,6 @@
#include <Standard_ConstructionError.hxx>
#include <Standard_Macro.hxx>
#include <iostream>
#include <iomanip>
#include <fstream>
namespace
{
@@ -383,32 +381,92 @@ bool Bnd_Box2d::IsOut(const gp_Pnt2d& theP0, const gp_Pnt2d& theP1) const
bool Bnd_Box2d::IsOut(const Bnd_Box2d& Other) const
{
if (IsWhole())
return false;
else if (IsVoid())
return true;
else if (Other.IsWhole())
return false;
else if (Other.IsVoid())
return true;
else
// Fast path for non-open, non-void, non-whole boxes (most common case)
if (!Flags && !Other.Flags)
{
double OXmin, OXmax, OYmin, OYmax;
Other.Get(OXmin, OYmin, OXmax, OYmax);
if (!(Flags & XminMask) && (OXmax < (Xmin - Gap)))
const double aDelta = Other.Gap + Gap;
if (Xmin - Other.Xmax > aDelta)
return true;
else if (!(Flags & XmaxMask) && (OXmin > (Xmax + Gap)))
if (Other.Xmin - Xmax > aDelta)
return true;
else if (!(Flags & YminMask) && (OYmax < (Ymin - Gap)))
if (Ymin - Other.Ymax > aDelta)
return true;
else if (!(Flags & YmaxMask) && (OYmin > (Ymax + Gap)))
if (Other.Ymin - Ymax > aDelta)
return true;
return false;
}
// Handle special cases
if (IsVoid() || Other.IsVoid())
return true;
if (IsWhole() || Other.IsWhole())
return false;
double OXmin, OXmax, OYmin, OYmax;
Other.Get(OXmin, OYmin, OXmax, OYmax);
if (!(Flags & XminMask) && (OXmax < (Xmin - Gap)))
return true;
if (!(Flags & XmaxMask) && (OXmin > (Xmax + Gap)))
return true;
if (!(Flags & YminMask) && (OYmax < (Ymin - Gap)))
return true;
if (!(Flags & YmaxMask) && (OYmin > (Ymax + Gap)))
return true;
return false;
}
//=================================================================================================
std::optional<gp_Pnt2d> Bnd_Box2d::Center() const
{
if (IsVoid())
{
return std::nullopt;
}
return gp_Pnt2d(0.5 * (GetXMin() + GetXMax()), 0.5 * (GetYMin() + GetYMax()));
}
//=================================================================================================
double Bnd_Box2d::Distance(const Bnd_Box2d& theOther) const
{
if (IsVoid() || theOther.IsVoid())
{
return 0.0;
}
double aXMin1, aYMin1, aXMax1, aYMax1;
double aXMin2, aYMin2, aXMax2, aYMax2;
Get(aXMin1, aYMin1, aXMax1, aYMax1);
theOther.Get(aXMin2, aYMin2, aXMax2, aYMax2);
// Compute squared distance per axis
auto distAxis = [](const double theMin1,
const double theMax1,
const double theMin2,
const double theMax2) -> double {
if (theMin1 > theMax2)
{
const double aD = theMin1 - theMax2;
return aD * aD;
}
if (theMin2 > theMax1)
{
const double aD = theMin2 - theMax1;
return aD * aD;
}
return 0.0;
};
const double aDx = distAxis(aXMin1, aXMax1, aXMin2, aXMax2);
const double aDy = distAxis(aYMin1, aYMax1, aYMin2, aYMax2);
return std::sqrt(aDx + aDy);
}
//=================================================================================================
void Bnd_Box2d::Dump() const
{
std::cout << "Box2d : ";

View File

@@ -23,12 +23,9 @@
#include <Standard_DefineAlloc.hxx>
#include <Standard_Handle.hxx>
#include <Standard_Real.hxx>
#include <Standard_Integer.hxx>
#include <Standard_Boolean.hxx>
#include <algorithm>
#include <cmath>
#include <optional>
class gp_Dir2d;
class gp_Trsf2d;
@@ -169,6 +166,12 @@ public:
//! Returns the Ymax value (IsOpenYmax() ? Precision::Infinite() : Ymax + GetGap()).
[[nodiscard]] Standard_EXPORT double GetYMax() const;
//! Returns the center of this 2D bounding box. The gap is included.
//! If this bounding box is infinite (i.e. "open"), returned values
//! may be equal to +/- Precision::Infinite().
//! Returns std::nullopt if the box is void.
[[nodiscard]] Standard_EXPORT std::optional<gp_Pnt2d> Center() const;
//! The Box will be infinitely long in the Xmin direction.
void OpenXmin() noexcept { Flags |= XminMask; }
@@ -238,6 +241,15 @@ public:
//! Returns True if <Box2d> is out <me>.
[[nodiscard]] Standard_EXPORT bool IsOut(const Bnd_Box2d& Other) const;
//! Returns True if the 2d point is inside or on the boundary of this box.
[[nodiscard]] bool Contains(const gp_Pnt2d& theP) const { return !IsOut(theP); }
//! Returns True if the other 2d box intersects or is inside this box.
[[nodiscard]] bool Intersects(const Bnd_Box2d& theOther) const { return !IsOut(theOther); }
//! Computes the minimum distance between two 2D boxes.
[[nodiscard]] Standard_EXPORT double Distance(const Bnd_Box2d& theOther) const;
//! Returns True if transformed <Box2d> is out <me>.
[[nodiscard]] bool IsOut(const Bnd_Box2d& theOther, const gp_Trsf2d& theTrsf) const noexcept
{

View File

@@ -596,8 +596,19 @@ void OBBTool::ProcessTriangle(const int theIdx1,
aZAxis /= std::sqrt(aSqMod);
gp_XYZ aXAxis[aNbAxes];
bool aXAxisValid[aNbAxes];
for (int i = 0; i < aNbAxes; i++)
aXAxis[i] = aYAxis[i].Crossed(aZAxis).Normalized();
{
const gp_XYZ aCross = aYAxis[i].Crossed(aZAxis);
const double aCrossSqMod = aCross.SquareModulus();
if (aCrossSqMod < Precision::SquareConfusion())
{
aXAxisValid[i] = false;
continue;
}
aXAxis[i] = aCross / std::sqrt(aCrossSqMod);
aXAxisValid[i] = true;
}
if (theIsBuiltTrg)
FillToTriangle5(aZAxis, myLExtremalPoints[theIdx1]);
@@ -612,6 +623,9 @@ void OBBTool::ProcessTriangle(const int theIdx1,
int aMinIdx = -1;
for (int anAxeInd = 0; anAxeInd < aNbAxes; anAxeInd++)
{
if (!aXAxisValid[anAxeInd])
continue;
const gp_XYZ& aAX = aXAxis[anAxeInd];
// Compute params on XAxis
FindMinMax(aAX, aParams[0], aParams[1]);
@@ -662,14 +676,14 @@ void OBBTool::ProcessDiTetrahedron()
// Use the standard DiTo approach
ProcessTriangle(myTriIdx[0], myTriIdx[1], myTriIdx[2], true);
if (myTriIdx[3] <= myNbExtremalPoints)
if (myTriIdx[3] < myNbExtremalPoints)
{
ProcessTriangle(myTriIdx[0], myTriIdx[1], myTriIdx[3], false);
ProcessTriangle(myTriIdx[1], myTriIdx[2], myTriIdx[3], false);
ProcessTriangle(myTriIdx[0], myTriIdx[2], myTriIdx[3], false);
}
if (myTriIdx[4] <= myNbExtremalPoints)
if (myTriIdx[4] < myNbExtremalPoints)
{
ProcessTriangle(myTriIdx[0], myTriIdx[1], myTriIdx[4], false);
ProcessTriangle(myTriIdx[1], myTriIdx[2], myTriIdx[4], false);

View File

@@ -18,7 +18,6 @@
#include <Standard.hxx>
#include <Standard_DefineAlloc.hxx>
#include <Standard_Handle.hxx>
#include <Standard_Real.hxx>
#include <Bnd_Box.hxx>
#include <gp_Ax3.hxx>
@@ -39,6 +38,18 @@ class Bnd_OBB
public:
DEFINE_STANDARD_ALLOC
//! Structure containing the OBB half-size dimensions.
//! Can be used with C++17 structured bindings:
//! @code
//! auto [aHX, aHY, aHZ] = anOBB.GetHalfSizes();
//! @endcode
struct HalfSizes
{
double X; //!< Half-size along X axis
double Y; //!< Half-size along Y axis
double Z; //!< Half-size along Z axis
};
//! Empty constructor
Bnd_OBB()
: myIsAABox(false)
@@ -144,31 +155,44 @@ public:
//! gp_Trsf aLoc;
//! aLoc.SetTransformation (theOBB.Position(), gp::XOY());
//! @endcode
gp_Ax3 Position() const { return gp_Ax3(myCenter, ZDirection(), XDirection()); }
[[nodiscard]] gp_Ax3 Position() const { return gp_Ax3(myCenter, ZDirection(), XDirection()); }
//! Returns the center of OBB
const gp_XYZ& Center() const { return myCenter; }
[[nodiscard]] const gp_XYZ& Center() const noexcept { return myCenter; }
//! Returns the X Direction of OBB
const gp_XYZ& XDirection() const { return myAxes[0]; }
[[nodiscard]] const gp_XYZ& XDirection() const noexcept { return myAxes[0]; }
//! Returns the Y Direction of OBB
const gp_XYZ& YDirection() const { return myAxes[1]; }
[[nodiscard]] const gp_XYZ& YDirection() const noexcept { return myAxes[1]; }
//! Returns the Z Direction of OBB
const gp_XYZ& ZDirection() const { return myAxes[2]; }
[[nodiscard]] const gp_XYZ& ZDirection() const noexcept { return myAxes[2]; }
//! Returns the X Dimension of OBB
double XHSize() const { return myHDims[0]; }
[[nodiscard]] double XHSize() const noexcept { return myHDims[0]; }
//! Returns the Y Dimension of OBB
double YHSize() const { return myHDims[1]; }
[[nodiscard]] double YHSize() const noexcept { return myHDims[1]; }
//! Returns the Z Dimension of OBB
double ZHSize() const { return myHDims[2]; }
[[nodiscard]] double ZHSize() const noexcept { return myHDims[2]; }
//! Returns the half-size dimensions of the OBB as a HalfSizes structure.
//! Can be used with C++17 structured bindings:
//! @code
//! auto [aHX, aHY, aHZ] = anOBB.GetHalfSizes();
//! @endcode
[[nodiscard]] HalfSizes GetHalfSizes() const noexcept
{
return {myHDims[0], myHDims[1], myHDims[2]};
}
//! Checks if the box is empty.
bool IsVoid() const { return ((myHDims[0] < 0.0) || (myHDims[1] < 0.0) || (myHDims[2] < 0.0)); }
[[nodiscard]] bool IsVoid() const noexcept
{
return ((myHDims[0] < 0.0) || (myHDims[1] < 0.0) || (myHDims[2] < 0.0));
}
//! Clears this box
void SetVoid()
@@ -182,7 +206,7 @@ public:
void SetAABox(const bool& theFlag) { myIsAABox = theFlag; }
//! Returns TRUE if the box is axes aligned
bool IsAABox() const { return myIsAABox; }
[[nodiscard]] bool IsAABox() const noexcept { return myIsAABox; }
//! Enlarges the box with the given value
void Enlarge(const double theGapAdd)
@@ -230,19 +254,25 @@ public:
}
//! Returns square diagonal of this box
double SquareExtent() const
[[nodiscard]] double SquareExtent() const noexcept
{
return 4.0 * (myHDims[0] * myHDims[0] + myHDims[1] * myHDims[1] + myHDims[2] * myHDims[2]);
}
//! Check if the box do not interfere the other box.
Standard_EXPORT bool IsOut(const Bnd_OBB& theOther) const;
[[nodiscard]] Standard_EXPORT bool IsOut(const Bnd_OBB& theOther) const;
//! Check if the point is inside of <this>.
Standard_EXPORT bool IsOut(const gp_Pnt& theP) const;
[[nodiscard]] Standard_EXPORT bool IsOut(const gp_Pnt& theP) const;
//! Returns True if the point is inside or on the boundary of this OBB.
[[nodiscard]] bool Contains(const gp_Pnt& theP) const { return !IsOut(theP); }
//! Returns True if the other OBB intersects or is inside this OBB.
[[nodiscard]] bool Intersects(const Bnd_OBB& theOther) const { return !IsOut(theOther); }
//! Check if the theOther is completely inside *this.
Standard_EXPORT bool IsCompletelyInside(const Bnd_OBB& theOther) const;
[[nodiscard]] Standard_EXPORT bool IsCompletelyInside(const Bnd_OBB& theOther) const;
//! Rebuilds this in order to include all previous objects
//! (which it was created from) and theOther.

View File

@@ -23,6 +23,7 @@ void Bnd_Range::Common(const Bnd_Range& theOther)
if (theOther.IsVoid())
{
SetVoid();
return;
}
if (IsVoid())
@@ -55,10 +56,11 @@ bool Bnd_Range::Union(const Bnd_Range& theOther)
//=================================================================================================
int Bnd_Range::IsIntersected(const double theVal, const double thePeriod) const
Bnd_Range::IntersectStatus Bnd_Range::IsIntersected(const double theVal,
const double thePeriod) const
{
if (IsVoid())
return false;
return IntersectStatus_Out;
const double aPeriod = std::abs(thePeriod);
const double aDF = myFirst - theVal, aDL = myLast - theVal;
@@ -67,12 +69,12 @@ int Bnd_Range::IsIntersected(const double theVal, const double thePeriod) const
{
const double aDelta = aDF * aDL;
if (IsEqual(aDelta, 0.0))
return 2;
return IntersectStatus_Boundary;
if (aDelta > 0.0)
return 0;
return IntersectStatus_Out;
return 1;
return IntersectStatus_In;
}
// If <this> intersects theVal then there exists an integer
@@ -97,20 +99,20 @@ int Bnd_Range::IsIntersected(const double theVal, const double thePeriod) const
{ // Interval (myFirst, myLast] intersects seam-edge
if (IsEqual(aVal2, static_cast<double>(aPar2)))
{ // aVal2 is an integer number => myLast lies ON the "seam-edge"
return 2;
return IntersectStatus_Boundary;
}
return 1;
return IntersectStatus_In;
}
// Here, aPar1 == aPar2.
if (IsEqual(aVal1, static_cast<double>(aPar1)))
{ // aVal1 is an integer number => myFirst lies ON the "seam-edge"
return 2;
return IntersectStatus_Boundary;
}
return 0;
return IntersectStatus_Out;
}
//=================================================================================================
@@ -120,7 +122,7 @@ void Bnd_Range::Split(const double theVal,
const double thePeriod) const
{
const double aPeriod = std::abs(thePeriod);
if (IsIntersected(theVal, aPeriod) != 1)
if (IsIntersected(theVal, aPeriod) != IntersectStatus_In)
{
theList.Append(*this);
return;

View File

@@ -20,12 +20,25 @@
#include <NCollection_List.hxx>
#include <optional>
//! This class describes a range in 1D space restricted
//! by two real values.
//! A range can be void indicating there is no point included in the range.
class Bnd_Range
{
public:
//! Structure containing the range bounds (Min, Max).
//! Can be used with C++17 structured bindings:
//! @code
//! auto [aMin, aMax] = aRange.Get();
//! @endcode
struct Bounds
{
double Min; //!< Minimum value of the range
double Max; //!< Maximum value of the range
};
//! Default constructor. Creates VOID range.
Bnd_Range()
: myFirst(0.0),
@@ -50,7 +63,7 @@ public:
//! Returns false if the operation cannot be done (e.g.
//! input arguments are empty or separated).
//! @sa use method ::Add() to merge two ranges unconditionally
Standard_EXPORT bool Union(const Bnd_Range& theOther);
[[nodiscard]] Standard_EXPORT bool Union(const Bnd_Range& theOther);
//! Splits <this> to several sub-ranges by theVal value
//! (e.g. range [3, 15] will be split by theVal==5 to the two
@@ -66,16 +79,22 @@ public:
NCollection_List<Bnd_Range>& theList,
const double thePeriod = 0.0) const;
//! Status of intersection check with a periodic value.
//! @sa IsIntersected()
enum IntersectStatus
{
IntersectStatus_Out = 0, //!< No intersection with theVal+k*thePeriod
IntersectStatus_In = 1, //!< Range strictly contains theVal+k*thePeriod
IntersectStatus_Boundary = 2 //!< Range boundary coincides with theVal+k*thePeriod
};
//! Checks if <this> intersects values like
//! theVal+k*thePeriod, where k is an integer number (k = 0, +/-1, +/-2, ...).
//! Returns:
//! 0 - if <this> does not intersect the theVal+k*thePeriod.
//! 1 - if <this> intersects theVal+k*thePeriod.
//! 2 - if myFirst or/and myLast are equal to theVal+k*thePeriod.
//!
//! ATTENTION!!!
//! If (myFirst == myLast) then this function will return only either 0 or 2.
Standard_EXPORT int IsIntersected(const double theVal, const double thePeriod = 0.0) const;
//! If (myFirst == myLast) then this function will return only either Out or Boundary.
Standard_EXPORT IntersectStatus IsIntersected(const double theVal,
const double thePeriod = 0.0) const;
//! Extends <this> to include theParameter
void Add(const double theParameter)
@@ -101,6 +120,7 @@ public:
else if (IsVoid())
{
*this = theRange;
return;
}
myFirst = (std::min)(myFirst, theRange.myFirst);
myLast = (std::max)(myLast, theRange.myLast);
@@ -146,6 +166,22 @@ public:
return true;
}
//! Returns the bounds of this range as a Bounds structure.
//! Returns std::nullopt if IsVoid().
//! Can be used with C++17 structured bindings:
//! @code
//! if (auto aBounds = aRange.Get())
//! {
//! auto [aMin, aMax] = *aBounds;
//! }
//! @endcode
[[nodiscard]] std::optional<Bounds> Get() const noexcept
{
if (IsVoid())
return std::nullopt;
return Bounds{myFirst, myLast};
}
//! Obtain theParameter satisfied to the equation
//! (theParameter-MIN)/(MAX-MIN) == theLambda.
//! * theLambda == 0 --> MIN boundary will be returned;
@@ -165,21 +201,30 @@ public:
return true;
}
//! Returns the center of this range ((Min + Max) / 2).
//! Returns std::nullopt if IsVoid().
[[nodiscard]] std::optional<double> Center() const noexcept
{
if (IsVoid())
return std::nullopt;
return 0.5 * (myFirst + myLast);
}
//! Returns range value (MAX-MIN). Returns negative value for VOID range.
double Delta() const { return (myLast - myFirst); }
[[nodiscard]] double Delta() const noexcept { return (myLast - myFirst); }
//! Is <this> initialized.
bool IsVoid() const { return (myLast < myFirst); }
[[nodiscard]] bool IsVoid() const noexcept { return (myLast < myFirst); }
//! Initializes <this> by default parameters. Makes <this> VOID.
void SetVoid()
void SetVoid() noexcept
{
myLast = -1.0;
myFirst = 0.0;
}
//! Extends this to the given value (in both side)
void Enlarge(const double theDelta)
void Enlarge(const double theDelta) noexcept
{
if (IsVoid())
{
@@ -191,13 +236,13 @@ public:
}
//! Returns the copy of <*this> shifted by theVal
Bnd_Range Shifted(const double theVal) const
[[nodiscard]] Bnd_Range Shifted(const double theVal) const
{
return !IsVoid() ? Bnd_Range(myFirst + theVal, myLast + theVal) : Bnd_Range();
}
//! Shifts <*this> by theVal
void Shift(const double theVal)
void Shift(const double theVal) noexcept
{
if (!IsVoid())
{
@@ -208,7 +253,7 @@ public:
//! Trims the First value in range by the given lower limit.
//! Marks range as Void if the given Lower value is greater than range Max.
void TrimFrom(const double theValLower)
void TrimFrom(const double theValLower) noexcept
{
if (!IsVoid())
{
@@ -218,7 +263,7 @@ public:
//! Trim the Last value in range by the given Upper limit.
//! Marks range as Void if the given Upper value is smaller than range Max.
void TrimTo(const double theValUpper)
void TrimTo(const double theValUpper) noexcept
{
if (!IsVoid())
{
@@ -227,16 +272,46 @@ public:
}
//! Returns True if the value is out of this range.
bool IsOut(double theValue) const { return IsVoid() || theValue < myFirst || theValue > myLast; }
[[nodiscard]] bool IsOut(double theValue) const noexcept
{
return IsVoid() || theValue < myFirst || theValue > myLast;
}
//! Returns True if the given range is out of this range.
bool IsOut(const Bnd_Range& theRange) const
[[nodiscard]] bool IsOut(const Bnd_Range& theRange) const noexcept
{
return IsVoid() || theRange.IsVoid() || theRange.myLast < myFirst || theRange.myFirst > myLast;
}
//! Returns True if the value is within this range.
[[nodiscard]] bool Contains(double theValue) const noexcept { return !IsOut(theValue); }
//! Returns True if the given range intersects (overlaps with) this range.
[[nodiscard]] bool Intersects(const Bnd_Range& theRange) const noexcept
{
return !IsOut(theRange);
}
//! Returns the MIN boundary of <this>.
//! Returns std::nullopt if IsVoid().
[[nodiscard]] std::optional<double> Min() const noexcept
{
if (IsVoid())
return std::nullopt;
return myFirst;
}
//! Returns the MAX boundary of <this>.
//! Returns std::nullopt if IsVoid().
[[nodiscard]] std::optional<double> Max() const noexcept
{
if (IsVoid())
return std::nullopt;
return myLast;
}
//! Returns TRUE if theOther is equal to <*this>
bool operator==(const Bnd_Range& theOther) const
[[nodiscard]] bool operator==(const Bnd_Range& theOther) const noexcept
{
return ((myFirst == theOther.myFirst) && (myLast == theOther.myLast));
}

View File

@@ -36,9 +36,10 @@ Bnd_Sphere::Bnd_Sphere(const gp_XYZ& theCenter,
void Bnd_Sphere::SquareDistances(const gp_XYZ& theXYZ, double& theMin, double& theMax) const
{
theMax = (theXYZ - myCenter).SquareModulus();
theMin = (theMax - myRadius < 0 ? 0.0 : theMax - myRadius * myRadius);
theMax += myRadius * myRadius;
theMax = (theXYZ - myCenter).SquareModulus();
const double aRadSq = myRadius * myRadius;
theMin = (theMax < aRadSq ? 0.0 : theMax - aRadSq);
theMax += aRadSq;
}
void Bnd_Sphere::Distances(const gp_XYZ& theXYZ, double& theMin, double& theMax) const

View File

@@ -21,8 +21,6 @@
#include <Standard_Handle.hxx>
#include <gp_XYZ.hxx>
#include <Standard_Real.hxx>
#include <Standard_Boolean.hxx>
//! This class represents a bounding sphere of a geometric entity
//! (triangle, segment of line or whatever else).
@@ -41,22 +39,22 @@ public:
const int theV);
//! Returns the U parameter on shape
int U() const;
int U() const noexcept { return myU; }
//! Returns the V parameter on shape
int V() const;
int V() const noexcept { return myV; }
//! Returns validity status, indicating that this
//! sphere corresponds to a real entity
bool IsValid() const;
bool IsValid() const noexcept { return myIsValid; }
void SetValid(const bool isValid);
void SetValid(const bool isValid) noexcept { myIsValid = isValid; }
//! Returns center of sphere object
const gp_XYZ& Center() const;
const gp_XYZ& Center() const noexcept { return myCenter; }
//! Returns the radius value
double Radius() const;
double Radius() const noexcept { return myRadius; }
//! Calculate and return minimal and maximal distance to sphere.
//! NOTE: This function is tightly optimized; any modifications
@@ -95,6 +93,4 @@ private:
int myV;
};
#include <Bnd_Sphere.lxx>
#endif // _Bnd_Sphere_HeaderFile

View File

@@ -1,42 +0,0 @@
// Copyright (c) 1999-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.
inline int Bnd_Sphere::U() const
{
return myU;
}
inline int Bnd_Sphere::V() const
{
return myV;
}
inline bool Bnd_Sphere::IsValid() const
{
return myIsValid;
}
inline void Bnd_Sphere::SetValid(const bool isValid)
{
myIsValid = isValid;
}
inline const gp_XYZ& Bnd_Sphere::Center() const
{
return myCenter;
}
inline double Bnd_Sphere::Radius() const
{
return myRadius;
}

View File

@@ -18,6 +18,5 @@ set(OCCT_Bnd_FILES
Bnd_Range.hxx
Bnd_Sphere.cxx
Bnd_Sphere.hxx
Bnd_Sphere.lxx
Bnd_Tools.hxx
)

View File

@@ -0,0 +1,443 @@
// Copyright (c) 2025 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 <Bnd_Box2d.hxx>
#include <gp_Dir2d.hxx>
#include <gp_Lin2d.hxx>
#include <gp_Pnt2d.hxx>
#include <gp_Trsf2d.hxx>
#include <gtest/gtest.h>
TEST(Bnd_Box2dTest, DefaultConstructor)
{
Bnd_Box2d aBox;
EXPECT_TRUE(aBox.IsVoid());
EXPECT_FALSE(aBox.IsWhole());
EXPECT_DOUBLE_EQ(aBox.GetGap(), 0.0);
}
TEST(Bnd_Box2dTest, SetVoid)
{
Bnd_Box2d aBox;
aBox.Update(0.0, 0.0, 10.0, 10.0);
EXPECT_FALSE(aBox.IsVoid());
aBox.SetVoid();
EXPECT_TRUE(aBox.IsVoid());
}
TEST(Bnd_Box2dTest, SetWhole)
{
Bnd_Box2d aBox;
aBox.SetWhole();
EXPECT_TRUE(aBox.IsWhole());
EXPECT_FALSE(aBox.IsVoid());
}
TEST(Bnd_Box2dTest, Update_Bounds)
{
Bnd_Box2d aBox;
aBox.Update(1.0, 2.0, 10.0, 20.0);
double aXmin = 0.0, aYmin = 0.0, aXmax = 0.0, aYmax = 0.0;
aBox.Get(aXmin, aYmin, aXmax, aYmax);
EXPECT_DOUBLE_EQ(aXmin, 1.0);
EXPECT_DOUBLE_EQ(aYmin, 2.0);
EXPECT_DOUBLE_EQ(aXmax, 10.0);
EXPECT_DOUBLE_EQ(aYmax, 20.0);
}
TEST(Bnd_Box2dTest, Update_SinglePoint)
{
Bnd_Box2d aBox;
aBox.Update(5.0, 7.0);
double aXmin = 0.0, aYmin = 0.0, aXmax = 0.0, aYmax = 0.0;
aBox.Get(aXmin, aYmin, aXmax, aYmax);
EXPECT_DOUBLE_EQ(aXmin, 5.0);
EXPECT_DOUBLE_EQ(aYmin, 7.0);
EXPECT_DOUBLE_EQ(aXmax, 5.0);
EXPECT_DOUBLE_EQ(aYmax, 7.0);
}
TEST(Bnd_Box2dTest, Update_Expansion)
{
Bnd_Box2d aBox;
aBox.Update(1.0, 2.0, 10.0, 20.0);
aBox.Update(0.0, 0.0, 15.0, 25.0);
double aXmin = 0.0, aYmin = 0.0, aXmax = 0.0, aYmax = 0.0;
aBox.Get(aXmin, aYmin, aXmax, aYmax);
EXPECT_DOUBLE_EQ(aXmin, 0.0);
EXPECT_DOUBLE_EQ(aYmin, 0.0);
EXPECT_DOUBLE_EQ(aXmax, 15.0);
EXPECT_DOUBLE_EQ(aYmax, 25.0);
}
TEST(Bnd_Box2dTest, GapOperations)
{
Bnd_Box2d aBox;
aBox.Update(0.0, 0.0, 10.0, 10.0);
EXPECT_DOUBLE_EQ(aBox.GetGap(), 0.0);
aBox.SetGap(2.0);
EXPECT_DOUBLE_EQ(aBox.GetGap(), 2.0);
aBox.SetGap(-3.0);
EXPECT_DOUBLE_EQ(aBox.GetGap(), 3.0);
}
TEST(Bnd_Box2dTest, Enlarge)
{
Bnd_Box2d aBox;
aBox.Update(0.0, 0.0, 10.0, 10.0);
aBox.Enlarge(5.0);
EXPECT_DOUBLE_EQ(aBox.GetGap(), 5.0);
aBox.Enlarge(3.0);
EXPECT_DOUBLE_EQ(aBox.GetGap(), 5.0); // max(5, 3) = 5
aBox.Enlarge(7.0);
EXPECT_DOUBLE_EQ(aBox.GetGap(), 7.0); // max(5, 7) = 7
}
TEST(Bnd_Box2dTest, Get_WithGap)
{
Bnd_Box2d aBox;
aBox.Update(1.0, 2.0, 10.0, 20.0);
aBox.SetGap(0.5);
double aXmin = 0.0, aYmin = 0.0, aXmax = 0.0, aYmax = 0.0;
aBox.Get(aXmin, aYmin, aXmax, aYmax);
EXPECT_DOUBLE_EQ(aXmin, 0.5);
EXPECT_DOUBLE_EQ(aYmin, 1.5);
EXPECT_DOUBLE_EQ(aXmax, 10.5);
EXPECT_DOUBLE_EQ(aYmax, 20.5);
}
TEST(Bnd_Box2dTest, Get_StructuredBindings)
{
Bnd_Box2d aBox;
aBox.Update(1.0, 2.0, 10.0, 20.0);
const auto [aXmin, aXmax, aYmin, aYmax] = aBox.Get();
EXPECT_DOUBLE_EQ(aXmin, 1.0);
EXPECT_DOUBLE_EQ(aXmax, 10.0);
EXPECT_DOUBLE_EQ(aYmin, 2.0);
EXPECT_DOUBLE_EQ(aYmax, 20.0);
}
TEST(Bnd_Box2dTest, Set_Point)
{
Bnd_Box2d aBox;
aBox.Set(gp_Pnt2d(5.0, 7.0));
double aXmin = 0.0, aYmin = 0.0, aXmax = 0.0, aYmax = 0.0;
aBox.Get(aXmin, aYmin, aXmax, aYmax);
EXPECT_DOUBLE_EQ(aXmin, 5.0);
EXPECT_DOUBLE_EQ(aYmin, 7.0);
EXPECT_DOUBLE_EQ(aXmax, 5.0);
EXPECT_DOUBLE_EQ(aYmax, 7.0);
}
TEST(Bnd_Box2dTest, Add_Point)
{
Bnd_Box2d aBox;
aBox.Add(gp_Pnt2d(1.0, 2.0));
aBox.Add(gp_Pnt2d(10.0, 20.0));
double aXmin = 0.0, aYmin = 0.0, aXmax = 0.0, aYmax = 0.0;
aBox.Get(aXmin, aYmin, aXmax, aYmax);
EXPECT_DOUBLE_EQ(aXmin, 1.0);
EXPECT_DOUBLE_EQ(aYmin, 2.0);
EXPECT_DOUBLE_EQ(aXmax, 10.0);
EXPECT_DOUBLE_EQ(aYmax, 20.0);
}
TEST(Bnd_Box2dTest, Add_Box)
{
Bnd_Box2d aBox1;
aBox1.Update(0.0, 0.0, 5.0, 5.0);
Bnd_Box2d aBox2;
aBox2.Update(3.0, 3.0, 10.0, 10.0);
aBox1.Add(aBox2);
double aXmin = 0.0, aYmin = 0.0, aXmax = 0.0, aYmax = 0.0;
aBox1.Get(aXmin, aYmin, aXmax, aYmax);
EXPECT_DOUBLE_EQ(aXmin, 0.0);
EXPECT_DOUBLE_EQ(aYmin, 0.0);
EXPECT_DOUBLE_EQ(aXmax, 10.0);
EXPECT_DOUBLE_EQ(aYmax, 10.0);
}
TEST(Bnd_Box2dTest, Add_VoidBox)
{
Bnd_Box2d aBox;
aBox.Update(1.0, 2.0, 5.0, 6.0);
Bnd_Box2d aVoidBox;
aBox.Add(aVoidBox);
double aXmin = 0.0, aYmin = 0.0, aXmax = 0.0, aYmax = 0.0;
aBox.Get(aXmin, aYmin, aXmax, aYmax);
EXPECT_DOUBLE_EQ(aXmin, 1.0);
EXPECT_DOUBLE_EQ(aYmin, 2.0);
}
TEST(Bnd_Box2dTest, Add_ToVoidBox)
{
Bnd_Box2d aVoidBox;
Bnd_Box2d aBox;
aBox.Update(1.0, 2.0, 5.0, 6.0);
aVoidBox.Add(aBox);
double aXmin = 0.0, aYmin = 0.0, aXmax = 0.0, aYmax = 0.0;
aVoidBox.Get(aXmin, aYmin, aXmax, aYmax);
EXPECT_DOUBLE_EQ(aXmin, 1.0);
EXPECT_DOUBLE_EQ(aYmin, 2.0);
EXPECT_DOUBLE_EQ(aXmax, 5.0);
EXPECT_DOUBLE_EQ(aYmax, 6.0);
}
TEST(Bnd_Box2dTest, OpenDirections)
{
Bnd_Box2d aBox;
aBox.Update(0.0, 0.0, 10.0, 10.0);
EXPECT_FALSE(aBox.IsOpenXmin());
EXPECT_FALSE(aBox.IsOpenXmax());
EXPECT_FALSE(aBox.IsOpenYmin());
EXPECT_FALSE(aBox.IsOpenYmax());
aBox.OpenXmin();
EXPECT_TRUE(aBox.IsOpenXmin());
aBox.OpenYmax();
EXPECT_TRUE(aBox.IsOpenYmax());
EXPECT_FALSE(aBox.IsWhole());
}
TEST(Bnd_Box2dTest, IsOut_Point)
{
Bnd_Box2d aBox;
aBox.Update(0.0, 0.0, 10.0, 10.0);
EXPECT_FALSE(aBox.IsOut(gp_Pnt2d(5.0, 5.0)));
EXPECT_TRUE(aBox.IsOut(gp_Pnt2d(15.0, 5.0)));
EXPECT_TRUE(aBox.IsOut(gp_Pnt2d(-1.0, 5.0)));
}
TEST(Bnd_Box2dTest, IsOut_Box_Overlapping)
{
Bnd_Box2d aBox1;
aBox1.Update(0.0, 0.0, 10.0, 10.0);
Bnd_Box2d aBox2;
aBox2.Update(5.0, 5.0, 15.0, 15.0);
EXPECT_FALSE(aBox1.IsOut(aBox2));
}
TEST(Bnd_Box2dTest, IsOut_Box_Separated)
{
Bnd_Box2d aBox1;
aBox1.Update(0.0, 0.0, 5.0, 5.0);
Bnd_Box2d aBox2;
aBox2.Update(10.0, 10.0, 15.0, 15.0);
EXPECT_TRUE(aBox1.IsOut(aBox2));
}
TEST(Bnd_Box2dTest, IsOut_Box_FastPath)
{
// Both non-void, non-whole, non-open -> fast path
Bnd_Box2d aBox1;
aBox1.Update(0.0, 0.0, 10.0, 10.0);
Bnd_Box2d aBox2;
aBox2.Update(5.0, 5.0, 15.0, 15.0);
EXPECT_FALSE(aBox1.IsOut(aBox2));
Bnd_Box2d aBox3;
aBox3.Update(20.0, 20.0, 30.0, 30.0);
EXPECT_TRUE(aBox1.IsOut(aBox3));
}
TEST(Bnd_Box2dTest, IsOut_Box_FastPath_WithGap)
{
// Boxes separated by 1.0, but gap bridges it
Bnd_Box2d aBox1;
aBox1.Update(0.0, 0.0, 5.0, 5.0);
aBox1.SetGap(1.0);
Bnd_Box2d aBox2;
aBox2.Update(6.0, 0.0, 10.0, 5.0);
// Gap from box1 = 1.0, gap from box2 = 0.0, total = 1.0
// Xmin1 - Xmax2 = 0 - 10 = -10 (not > 1.0) -> not out in X
// Xmin2 - Xmax1 = 6 - 5 = 1 (not > 1.0, equal) -> not out
EXPECT_FALSE(aBox1.IsOut(aBox2));
// Now increase separation
Bnd_Box2d aBox3;
aBox3.Update(7.0, 0.0, 10.0, 5.0);
// Xmin3 - Xmax1 = 7 - 5 = 2 > 1.0 -> out
EXPECT_TRUE(aBox1.IsOut(aBox3));
}
TEST(Bnd_Box2dTest, IsOut_VoidBox)
{
Bnd_Box2d aBox;
aBox.Update(0.0, 0.0, 10.0, 10.0);
Bnd_Box2d aVoid;
EXPECT_TRUE(aBox.IsOut(aVoid));
EXPECT_TRUE(aVoid.IsOut(aBox));
}
TEST(Bnd_Box2dTest, IsOut_WholeBox)
{
Bnd_Box2d aBox;
aBox.Update(0.0, 0.0, 10.0, 10.0);
Bnd_Box2d aWhole;
aWhole.SetWhole();
EXPECT_FALSE(aBox.IsOut(aWhole));
EXPECT_FALSE(aWhole.IsOut(aBox));
}
TEST(Bnd_Box2dTest, IsOut_OpenBox)
{
Bnd_Box2d aBox1;
aBox1.Update(0.0, 0.0, 10.0, 10.0);
aBox1.OpenXmin(); // extends to -infinity in X
Bnd_Box2d aBox2;
aBox2.Update(-100.0, 0.0, -50.0, 10.0);
// aBox1 is open in Xmin, so extends to -infinity: should overlap
EXPECT_FALSE(aBox1.IsOut(aBox2));
}
TEST(Bnd_Box2dTest, IsOut_Line)
{
Bnd_Box2d aBox;
aBox.Update(0.0, 0.0, 10.0, 10.0);
// Line that intersects
gp_Lin2d aLinInside(gp_Pnt2d(5.0, -5.0), gp_Dir2d(0.0, 1.0));
EXPECT_FALSE(aBox.IsOut(aLinInside));
// Line that doesn't intersect
gp_Lin2d aLinOutside(gp_Pnt2d(20.0, -5.0), gp_Dir2d(0.0, 1.0));
EXPECT_TRUE(aBox.IsOut(aLinOutside));
}
TEST(Bnd_Box2dTest, IsOut_Segment)
{
Bnd_Box2d aBox;
aBox.Update(0.0, 0.0, 10.0, 10.0);
// Segment that crosses
EXPECT_FALSE(aBox.IsOut(gp_Pnt2d(5.0, -5.0), gp_Pnt2d(5.0, 15.0)));
// Segment fully outside
EXPECT_TRUE(aBox.IsOut(gp_Pnt2d(20.0, 0.0), gp_Pnt2d(20.0, 10.0)));
}
TEST(Bnd_Box2dTest, Transformed)
{
Bnd_Box2d aBox;
aBox.Update(0.0, 0.0, 10.0, 10.0);
gp_Trsf2d aTrsf;
aTrsf.SetTranslation(gp_Pnt2d(0.0, 0.0), gp_Pnt2d(5.0, 5.0));
Bnd_Box2d aTransformed = aBox.Transformed(aTrsf);
double aXmin = 0.0, aYmin = 0.0, aXmax = 0.0, aYmax = 0.0;
aTransformed.Get(aXmin, aYmin, aXmax, aYmax);
EXPECT_DOUBLE_EQ(aXmin, 5.0);
EXPECT_DOUBLE_EQ(aYmin, 5.0);
EXPECT_DOUBLE_EQ(aXmax, 15.0);
EXPECT_DOUBLE_EQ(aYmax, 15.0);
}
TEST(Bnd_Box2dTest, SquareExtent)
{
Bnd_Box2d aBox;
aBox.Update(0.0, 0.0, 3.0, 4.0);
// Diagonal^2 = 3^2 + 4^2 = 25
EXPECT_DOUBLE_EQ(aBox.SquareExtent(), 25.0);
}
TEST(Bnd_Box2dTest, SquareExtent_Void)
{
Bnd_Box2d aBox;
EXPECT_DOUBLE_EQ(aBox.SquareExtent(), 0.0);
}
TEST(Bnd_Box2dTest, Add_Direction)
{
Bnd_Box2d aBox;
aBox.Set(gp_Pnt2d(5.0, 5.0));
aBox.Add(gp_Dir2d(1.0, 0.0));
EXPECT_TRUE(aBox.IsOpenXmax());
EXPECT_FALSE(aBox.IsOpenXmin());
}
TEST(Bnd_Box2dTest, Contains_Point)
{
Bnd_Box2d aBox;
aBox.Update(0.0, 0.0, 10.0, 10.0);
EXPECT_TRUE(aBox.Contains(gp_Pnt2d(5.0, 5.0)));
EXPECT_TRUE(aBox.Contains(gp_Pnt2d(0.0, 0.0)));
EXPECT_FALSE(aBox.Contains(gp_Pnt2d(-1.0, 5.0)));
EXPECT_FALSE(aBox.Contains(gp_Pnt2d(5.0, 11.0)));
}
TEST(Bnd_Box2dTest, Intersects_Box)
{
Bnd_Box2d aBox1;
aBox1.Update(0.0, 0.0, 10.0, 10.0);
Bnd_Box2d aBox2;
aBox2.Update(5.0, 5.0, 15.0, 15.0);
Bnd_Box2d aBox3;
aBox3.Update(20.0, 20.0, 30.0, 30.0);
EXPECT_TRUE(aBox1.Intersects(aBox2));
EXPECT_FALSE(aBox1.Intersects(aBox3));
}
TEST(Bnd_Box2dTest, Distance_Separated)
{
Bnd_Box2d aBox1;
aBox1.Update(0.0, 0.0, 1.0, 1.0);
Bnd_Box2d aBox2;
aBox2.Update(4.0, 0.0, 5.0, 1.0);
EXPECT_NEAR(aBox1.Distance(aBox2), 3.0, 1e-10);
}
TEST(Bnd_Box2dTest, Distance_Overlapping)
{
Bnd_Box2d aBox1;
aBox1.Update(0.0, 0.0, 10.0, 10.0);
Bnd_Box2d aBox2;
aBox2.Update(5.0, 5.0, 15.0, 15.0);
EXPECT_DOUBLE_EQ(aBox1.Distance(aBox2), 0.0);
}
TEST(Bnd_Box2dTest, Distance_Diagonal)
{
Bnd_Box2d aBox1;
aBox1.Update(0.0, 0.0, 1.0, 1.0);
Bnd_Box2d aBox2;
aBox2.Update(4.0, 4.0, 5.0, 5.0);
EXPECT_NEAR(aBox1.Distance(aBox2), std::sqrt(18.0), 1e-10);
}
TEST(Bnd_Box2dTest, Distance_Void)
{
Bnd_Box2d aBox1;
Bnd_Box2d aBox2;
aBox2.Update(0.0, 0.0, 1.0, 1.0);
EXPECT_DOUBLE_EQ(aBox1.Distance(aBox2), 0.0);
}
TEST(Bnd_Box2dTest, Center)
{
Bnd_Box2d aBox;
aBox.Update(0.0, 0.0, 10.0, 20.0);
auto aCenter = aBox.Center();
ASSERT_TRUE(aCenter.has_value());
EXPECT_DOUBLE_EQ(aCenter->X(), 5.0);
EXPECT_DOUBLE_EQ(aCenter->Y(), 10.0);
}
TEST(Bnd_Box2dTest, Center_VoidNullopt)
{
Bnd_Box2d aVoid;
EXPECT_FALSE(aVoid.Center().has_value());
}

View File

@@ -845,4 +845,38 @@ TEST(Bnd_BoxTest, OCC16485_CumulativeEnlargeTolerance)
// Verify that Xmin is approximately -tolerance (not growing with iterations)
EXPECT_NEAR(-aTol, aXmin, 1e-10) << "Xmin should be equal to -tolerance";
EXPECT_NEAR(aNbStep + aTol, aXmax, 1e-10) << "Xmax should be equal to nbstep + tolerance";
}
TEST(Bnd_BoxTest, Contains_Point)
{
Bnd_Box aBox(gp_Pnt(0, 0, 0), gp_Pnt(10, 10, 10));
EXPECT_TRUE(aBox.Contains(gp_Pnt(5, 5, 5)));
EXPECT_TRUE(aBox.Contains(gp_Pnt(0, 0, 0)));
EXPECT_FALSE(aBox.Contains(gp_Pnt(-1, 5, 5)));
EXPECT_FALSE(aBox.Contains(gp_Pnt(5, 5, 11)));
}
TEST(Bnd_BoxTest, Intersects_Box)
{
Bnd_Box aBox1(gp_Pnt(0, 0, 0), gp_Pnt(10, 10, 10));
Bnd_Box aBox2(gp_Pnt(5, 5, 5), gp_Pnt(15, 15, 15));
Bnd_Box aBox3(gp_Pnt(20, 20, 20), gp_Pnt(30, 30, 30));
EXPECT_TRUE(aBox1.Intersects(aBox2));
EXPECT_FALSE(aBox1.Intersects(aBox3));
}
TEST(Bnd_BoxTest, Center)
{
Bnd_Box aBox(gp_Pnt(0, 0, 0), gp_Pnt(10, 20, 30));
auto aCenter = aBox.Center();
ASSERT_TRUE(aCenter.has_value());
EXPECT_DOUBLE_EQ(aCenter->X(), 5.0);
EXPECT_DOUBLE_EQ(aCenter->Y(), 10.0);
EXPECT_DOUBLE_EQ(aCenter->Z(), 15.0);
}
TEST(Bnd_BoxTest, Center_VoidNullopt)
{
Bnd_Box aVoid;
EXPECT_FALSE(aVoid.Center().has_value());
}

View File

@@ -77,3 +77,30 @@ TEST(Bnd_OBB_Test, OCC30704_AddPointToVoidBox)
EXPECT_DOUBLE_EQ(aCenter.Y(), 200.0);
EXPECT_DOUBLE_EQ(aCenter.Z(), 300.0);
}
TEST(Bnd_OBB_Test, Contains_Point)
{
Bnd_OBB anOBB(gp_Pnt(0, 0, 0), gp_Dir(1, 0, 0), gp_Dir(0, 1, 0), gp_Dir(0, 0, 1), 5.0, 5.0, 5.0);
EXPECT_TRUE(anOBB.Contains(gp_Pnt(0, 0, 0)));
EXPECT_TRUE(anOBB.Contains(gp_Pnt(4, 4, 4)));
EXPECT_FALSE(anOBB.Contains(gp_Pnt(10, 0, 0)));
}
TEST(Bnd_OBB_Test, Intersects_OBB)
{
Bnd_OBB anOBB1(gp_Pnt(0, 0, 0), gp_Dir(1, 0, 0), gp_Dir(0, 1, 0), gp_Dir(0, 0, 1), 5.0, 5.0, 5.0);
Bnd_OBB anOBB2(gp_Pnt(8, 0, 0), gp_Dir(1, 0, 0), gp_Dir(0, 1, 0), gp_Dir(0, 0, 1), 5.0, 5.0, 5.0);
Bnd_OBB
anOBB3(gp_Pnt(20, 0, 0), gp_Dir(1, 0, 0), gp_Dir(0, 1, 0), gp_Dir(0, 0, 1), 5.0, 5.0, 5.0);
EXPECT_TRUE(anOBB1.Intersects(anOBB2));
EXPECT_FALSE(anOBB1.Intersects(anOBB3));
}
TEST(Bnd_OBB_Test, GetHalfSizes_StructuredBindings)
{
Bnd_OBB anOBB(gp_Pnt(0, 0, 0), gp_Dir(1, 0, 0), gp_Dir(0, 1, 0), gp_Dir(0, 0, 1), 3.0, 5.0, 7.0);
const auto [aHX, aHY, aHZ] = anOBB.GetHalfSizes();
EXPECT_DOUBLE_EQ(aHX, 3.0);
EXPECT_DOUBLE_EQ(aHY, 5.0);
EXPECT_DOUBLE_EQ(aHZ, 7.0);
}

View File

@@ -0,0 +1,511 @@
// Copyright (c) 2025 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 <Bnd_Range.hxx>
#include <NCollection_List.hxx>
#include <Standard_ConstructionError.hxx>
#include <gtest/gtest.h>
TEST(Bnd_RangeTest, DefaultConstructor_IsVoid)
{
Bnd_Range aRange;
EXPECT_TRUE(aRange.IsVoid());
EXPECT_LT(aRange.Delta(), 0.0);
}
TEST(Bnd_RangeTest, ParameterizedConstructor)
{
Bnd_Range aRange(3.0, 15.0);
EXPECT_FALSE(aRange.IsVoid());
EXPECT_DOUBLE_EQ(aRange.Delta(), 12.0);
}
TEST(Bnd_RangeTest, ParameterizedConstructor_InvalidRange)
{
EXPECT_THROW(Bnd_Range(10.0, 5.0), Standard_ConstructionError);
}
TEST(Bnd_RangeTest, ParameterizedConstructor_PointRange)
{
Bnd_Range aRange(5.0, 5.0);
EXPECT_FALSE(aRange.IsVoid());
EXPECT_DOUBLE_EQ(aRange.Delta(), 0.0);
}
TEST(Bnd_RangeTest, GetMin_GetMax_GetBounds)
{
Bnd_Range aRange(2.0, 8.0);
double aMin = 0.0, aMax = 0.0;
EXPECT_TRUE(aRange.GetMin(aMin));
EXPECT_DOUBLE_EQ(aMin, 2.0);
EXPECT_TRUE(aRange.GetMax(aMax));
EXPECT_DOUBLE_EQ(aMax, 8.0);
double aFirst = 0.0, aLast = 0.0;
EXPECT_TRUE(aRange.GetBounds(aFirst, aLast));
EXPECT_DOUBLE_EQ(aFirst, 2.0);
EXPECT_DOUBLE_EQ(aLast, 8.0);
}
TEST(Bnd_RangeTest, GetMin_GetMax_Void)
{
Bnd_Range aRange;
double aVal = 0.0;
EXPECT_FALSE(aRange.GetMin(aVal));
EXPECT_FALSE(aRange.GetMax(aVal));
double aFirst = 0.0, aLast = 0.0;
EXPECT_FALSE(aRange.GetBounds(aFirst, aLast));
}
TEST(Bnd_RangeTest, GetIntermediatePoint)
{
Bnd_Range aRange(10.0, 20.0);
double aPar = 0.0;
EXPECT_TRUE(aRange.GetIntermediatePoint(0.0, aPar));
EXPECT_DOUBLE_EQ(aPar, 10.0);
EXPECT_TRUE(aRange.GetIntermediatePoint(0.5, aPar));
EXPECT_DOUBLE_EQ(aPar, 15.0);
EXPECT_TRUE(aRange.GetIntermediatePoint(1.0, aPar));
EXPECT_DOUBLE_EQ(aPar, 20.0);
}
TEST(Bnd_RangeTest, GetIntermediatePoint_Void)
{
Bnd_Range aRange;
double aPar = 0.0;
EXPECT_FALSE(aRange.GetIntermediatePoint(0.5, aPar));
}
TEST(Bnd_RangeTest, SetVoid)
{
Bnd_Range aRange(1.0, 5.0);
EXPECT_FALSE(aRange.IsVoid());
aRange.SetVoid();
EXPECT_TRUE(aRange.IsVoid());
}
TEST(Bnd_RangeTest, Add_Double_ToVoid)
{
Bnd_Range aRange;
aRange.Add(5.0);
EXPECT_FALSE(aRange.IsVoid());
double aMin = 0.0, aMax = 0.0;
EXPECT_TRUE(aRange.GetBounds(aMin, aMax));
EXPECT_DOUBLE_EQ(aMin, 5.0);
EXPECT_DOUBLE_EQ(aMax, 5.0);
}
TEST(Bnd_RangeTest, Add_Double_Extends)
{
Bnd_Range aRange(3.0, 7.0);
aRange.Add(1.0);
aRange.Add(10.0);
double aMin = 0.0, aMax = 0.0;
EXPECT_TRUE(aRange.GetBounds(aMin, aMax));
EXPECT_DOUBLE_EQ(aMin, 1.0);
EXPECT_DOUBLE_EQ(aMax, 10.0);
}
TEST(Bnd_RangeTest, Add_Range_ToVoid)
{
Bnd_Range aRange;
Bnd_Range anOther(3.0, 7.0);
aRange.Add(anOther);
double aMin = 0.0, aMax = 0.0;
EXPECT_TRUE(aRange.GetBounds(aMin, aMax));
EXPECT_DOUBLE_EQ(aMin, 3.0);
EXPECT_DOUBLE_EQ(aMax, 7.0);
}
TEST(Bnd_RangeTest, Add_VoidRange)
{
Bnd_Range aRange(3.0, 7.0);
Bnd_Range aVoid;
aRange.Add(aVoid);
// Should remain unchanged
double aMin = 0.0, aMax = 0.0;
EXPECT_TRUE(aRange.GetBounds(aMin, aMax));
EXPECT_DOUBLE_EQ(aMin, 3.0);
EXPECT_DOUBLE_EQ(aMax, 7.0);
}
TEST(Bnd_RangeTest, Add_Range_VoidToVoid)
{
Bnd_Range aRange;
Bnd_Range aVoid;
aRange.Add(aVoid);
EXPECT_TRUE(aRange.IsVoid());
}
TEST(Bnd_RangeTest, Add_Range_Extends)
{
Bnd_Range aRange(3.0, 7.0);
Bnd_Range anOther(1.0, 10.0);
aRange.Add(anOther);
double aMin = 0.0, aMax = 0.0;
EXPECT_TRUE(aRange.GetBounds(aMin, aMax));
EXPECT_DOUBLE_EQ(aMin, 1.0);
EXPECT_DOUBLE_EQ(aMax, 10.0);
}
TEST(Bnd_RangeTest, Common_Overlapping)
{
Bnd_Range aRange(1.0, 10.0);
Bnd_Range anOther(5.0, 15.0);
aRange.Common(anOther);
double aMin = 0.0, aMax = 0.0;
EXPECT_TRUE(aRange.GetBounds(aMin, aMax));
EXPECT_DOUBLE_EQ(aMin, 5.0);
EXPECT_DOUBLE_EQ(aMax, 10.0);
}
TEST(Bnd_RangeTest, Common_NoOverlap)
{
Bnd_Range aRange(1.0, 5.0);
Bnd_Range anOther(7.0, 10.0);
aRange.Common(anOther);
EXPECT_TRUE(aRange.IsVoid());
}
TEST(Bnd_RangeTest, Common_WithVoid)
{
Bnd_Range aRange(1.0, 5.0);
Bnd_Range aVoid;
aRange.Common(aVoid);
EXPECT_TRUE(aRange.IsVoid());
}
TEST(Bnd_RangeTest, Union_Overlapping)
{
Bnd_Range aRange(1.0, 7.0);
Bnd_Range anOther(5.0, 15.0);
EXPECT_TRUE(aRange.Union(anOther));
double aMin = 0.0, aMax = 0.0;
EXPECT_TRUE(aRange.GetBounds(aMin, aMax));
EXPECT_DOUBLE_EQ(aMin, 1.0);
EXPECT_DOUBLE_EQ(aMax, 15.0);
}
TEST(Bnd_RangeTest, Union_Separated)
{
Bnd_Range aRange(1.0, 5.0);
Bnd_Range anOther(7.0, 10.0);
EXPECT_FALSE(aRange.Union(anOther));
// aRange should remain unchanged
double aMin = 0.0, aMax = 0.0;
EXPECT_TRUE(aRange.GetBounds(aMin, aMax));
EXPECT_DOUBLE_EQ(aMin, 1.0);
EXPECT_DOUBLE_EQ(aMax, 5.0);
}
TEST(Bnd_RangeTest, Union_WithVoid)
{
Bnd_Range aRange(1.0, 5.0);
Bnd_Range aVoid;
EXPECT_FALSE(aRange.Union(aVoid));
}
TEST(Bnd_RangeTest, IsIntersected_Void)
{
Bnd_Range aRange;
EXPECT_EQ(aRange.IsIntersected(5.0), Bnd_Range::IntersectStatus_Out);
}
TEST(Bnd_RangeTest, IsIntersected_Contains)
{
Bnd_Range aRange(3.0, 15.0);
EXPECT_EQ(aRange.IsIntersected(5.0), Bnd_Range::IntersectStatus_In);
}
TEST(Bnd_RangeTest, IsIntersected_Outside)
{
Bnd_Range aRange(3.0, 15.0);
EXPECT_EQ(aRange.IsIntersected(20.0), Bnd_Range::IntersectStatus_Out);
EXPECT_EQ(aRange.IsIntersected(1.0), Bnd_Range::IntersectStatus_Out);
}
TEST(Bnd_RangeTest, IsIntersected_OnBoundary)
{
Bnd_Range aRange(3.0, 15.0);
EXPECT_EQ(aRange.IsIntersected(3.0), Bnd_Range::IntersectStatus_Boundary);
EXPECT_EQ(aRange.IsIntersected(15.0), Bnd_Range::IntersectStatus_Boundary);
}
TEST(Bnd_RangeTest, IsIntersected_PointRange)
{
// When myFirst == myLast, only Out or Boundary possible
Bnd_Range aRange(5.0, 5.0);
EXPECT_EQ(aRange.IsIntersected(5.0), Bnd_Range::IntersectStatus_Boundary);
EXPECT_EQ(aRange.IsIntersected(3.0), Bnd_Range::IntersectStatus_Out);
}
TEST(Bnd_RangeTest, IsIntersected_Periodic_Contains)
{
// Range [3, 15], period 4, val 1
// Check: val + k*period in [3, 15]
// k=1: 1+4=5, inside [3,15] -> In
Bnd_Range aRange(3.0, 15.0);
EXPECT_EQ(aRange.IsIntersected(1.0, 4.0), Bnd_Range::IntersectStatus_In);
}
TEST(Bnd_RangeTest, IsIntersected_Periodic_OnBoundary)
{
// Range [3, 15], period 4, val 3 -> val + 0*4 = 3 exactly
Bnd_Range aRange(3.0, 15.0);
EXPECT_EQ(aRange.IsIntersected(3.0, 4.0), Bnd_Range::IntersectStatus_Boundary);
}
TEST(Bnd_RangeTest, IsIntersected_Periodic_Outside)
{
// Range [3.5, 3.9], period 4, val 0
// Check: 0 + k*4 in [3.5, 3.9] -> k=1 gives 4.0, not in range
Bnd_Range aRange(3.5, 3.9);
EXPECT_EQ(aRange.IsIntersected(0.0, 4.0), Bnd_Range::IntersectStatus_Out);
}
TEST(Bnd_RangeTest, Split_NoIntersection)
{
Bnd_Range aRange(3.0, 15.0);
NCollection_List<Bnd_Range> aList;
aRange.Split(20.0, aList);
EXPECT_EQ(aList.Size(), 1);
double aMin = 0.0, aMax = 0.0;
EXPECT_TRUE(aList.First().GetBounds(aMin, aMax));
EXPECT_DOUBLE_EQ(aMin, 3.0);
EXPECT_DOUBLE_EQ(aMax, 15.0);
}
TEST(Bnd_RangeTest, Split_AtInteriorPoint)
{
Bnd_Range aRange(3.0, 15.0);
NCollection_List<Bnd_Range> aList;
aRange.Split(5.0, aList);
EXPECT_EQ(aList.Size(), 2);
double aMin1 = 0.0, aMax1 = 0.0;
EXPECT_TRUE(aList.First().GetBounds(aMin1, aMax1));
EXPECT_DOUBLE_EQ(aMin1, 3.0);
EXPECT_DOUBLE_EQ(aMax1, 5.0);
double aMin2 = 0.0, aMax2 = 0.0;
EXPECT_TRUE(aList.Last().GetBounds(aMin2, aMax2));
EXPECT_DOUBLE_EQ(aMin2, 5.0);
EXPECT_DOUBLE_EQ(aMax2, 15.0);
}
TEST(Bnd_RangeTest, Split_Periodic)
{
// Range [3, 15], split at val=5, period=4
// Split points: 5, 9, 13 -> sub-ranges: [3,5], [5,9], [9,13], [13,15]
Bnd_Range aRange(3.0, 15.0);
NCollection_List<Bnd_Range> aList;
aRange.Split(5.0, aList, 4.0);
EXPECT_EQ(aList.Size(), 4);
}
TEST(Bnd_RangeTest, Enlarge)
{
Bnd_Range aRange(5.0, 10.0);
aRange.Enlarge(2.0);
double aMin = 0.0, aMax = 0.0;
EXPECT_TRUE(aRange.GetBounds(aMin, aMax));
EXPECT_DOUBLE_EQ(aMin, 3.0);
EXPECT_DOUBLE_EQ(aMax, 12.0);
}
TEST(Bnd_RangeTest, Enlarge_Void)
{
Bnd_Range aRange;
aRange.Enlarge(2.0);
EXPECT_TRUE(aRange.IsVoid());
}
TEST(Bnd_RangeTest, Shifted)
{
Bnd_Range aRange(3.0, 7.0);
Bnd_Range aShifted = aRange.Shifted(10.0);
double aMin = 0.0, aMax = 0.0;
EXPECT_TRUE(aShifted.GetBounds(aMin, aMax));
EXPECT_DOUBLE_EQ(aMin, 13.0);
EXPECT_DOUBLE_EQ(aMax, 17.0);
// Original unchanged
EXPECT_TRUE(aRange.GetBounds(aMin, aMax));
EXPECT_DOUBLE_EQ(aMin, 3.0);
EXPECT_DOUBLE_EQ(aMax, 7.0);
}
TEST(Bnd_RangeTest, Shifted_Void)
{
Bnd_Range aRange;
Bnd_Range aShifted = aRange.Shifted(10.0);
EXPECT_TRUE(aShifted.IsVoid());
}
TEST(Bnd_RangeTest, Shift)
{
Bnd_Range aRange(3.0, 7.0);
aRange.Shift(10.0);
double aMin = 0.0, aMax = 0.0;
EXPECT_TRUE(aRange.GetBounds(aMin, aMax));
EXPECT_DOUBLE_EQ(aMin, 13.0);
EXPECT_DOUBLE_EQ(aMax, 17.0);
}
TEST(Bnd_RangeTest, TrimFrom)
{
Bnd_Range aRange(3.0, 10.0);
aRange.TrimFrom(5.0);
double aMin = 0.0, aMax = 0.0;
EXPECT_TRUE(aRange.GetBounds(aMin, aMax));
EXPECT_DOUBLE_EQ(aMin, 5.0);
EXPECT_DOUBLE_EQ(aMax, 10.0);
}
TEST(Bnd_RangeTest, TrimFrom_MakesVoid)
{
Bnd_Range aRange(3.0, 10.0);
aRange.TrimFrom(15.0);
EXPECT_TRUE(aRange.IsVoid());
}
TEST(Bnd_RangeTest, TrimTo)
{
Bnd_Range aRange(3.0, 10.0);
aRange.TrimTo(7.0);
double aMin = 0.0, aMax = 0.0;
EXPECT_TRUE(aRange.GetBounds(aMin, aMax));
EXPECT_DOUBLE_EQ(aMin, 3.0);
EXPECT_DOUBLE_EQ(aMax, 7.0);
}
TEST(Bnd_RangeTest, TrimTo_MakesVoid)
{
Bnd_Range aRange(3.0, 10.0);
aRange.TrimTo(1.0);
EXPECT_TRUE(aRange.IsVoid());
}
TEST(Bnd_RangeTest, IsOut_Value)
{
Bnd_Range aRange(3.0, 10.0);
EXPECT_FALSE(aRange.IsOut(5.0));
EXPECT_FALSE(aRange.IsOut(3.0));
EXPECT_FALSE(aRange.IsOut(10.0));
EXPECT_TRUE(aRange.IsOut(1.0));
EXPECT_TRUE(aRange.IsOut(15.0));
}
TEST(Bnd_RangeTest, IsOut_Void)
{
Bnd_Range aRange;
EXPECT_TRUE(aRange.IsOut(5.0));
}
TEST(Bnd_RangeTest, IsOut_Range)
{
Bnd_Range aRange(3.0, 10.0);
EXPECT_FALSE(aRange.IsOut(Bnd_Range(5.0, 8.0)));
EXPECT_FALSE(aRange.IsOut(Bnd_Range(1.0, 5.0)));
EXPECT_TRUE(aRange.IsOut(Bnd_Range(11.0, 15.0)));
}
TEST(Bnd_RangeTest, IsOut_Range_Void)
{
Bnd_Range aRange(3.0, 10.0);
Bnd_Range aVoid;
EXPECT_TRUE(aRange.IsOut(aVoid));
EXPECT_TRUE(aVoid.IsOut(aRange));
}
TEST(Bnd_RangeTest, Equality)
{
Bnd_Range aRange1(3.0, 10.0);
Bnd_Range aRange2(3.0, 10.0);
Bnd_Range aRange3(3.0, 11.0);
EXPECT_TRUE(aRange1 == aRange2);
EXPECT_FALSE(aRange1 == aRange3);
}
TEST(Bnd_RangeTest, Contains_Value)
{
Bnd_Range aRange(3.0, 10.0);
EXPECT_TRUE(aRange.Contains(5.0));
EXPECT_TRUE(aRange.Contains(3.0));
EXPECT_TRUE(aRange.Contains(10.0));
EXPECT_FALSE(aRange.Contains(2.0));
EXPECT_FALSE(aRange.Contains(11.0));
Bnd_Range aVoid;
EXPECT_FALSE(aVoid.Contains(5.0));
}
TEST(Bnd_RangeTest, Intersects_Range)
{
Bnd_Range aRange1(3.0, 10.0);
Bnd_Range aRange2(5.0, 15.0);
Bnd_Range aRange3(11.0, 20.0);
EXPECT_TRUE(aRange1.Intersects(aRange2));
EXPECT_FALSE(aRange1.Intersects(aRange3));
Bnd_Range aVoid;
EXPECT_FALSE(aRange1.Intersects(aVoid));
}
TEST(Bnd_RangeTest, Min_Max_DirectAccess)
{
Bnd_Range aRange(3.0, 10.0);
ASSERT_TRUE(aRange.Min().has_value());
ASSERT_TRUE(aRange.Max().has_value());
EXPECT_DOUBLE_EQ(*aRange.Min(), 3.0);
EXPECT_DOUBLE_EQ(*aRange.Max(), 10.0);
}
TEST(Bnd_RangeTest, Min_Max_NulloptOnVoid)
{
Bnd_Range aVoid;
EXPECT_FALSE(aVoid.Min().has_value());
EXPECT_FALSE(aVoid.Max().has_value());
}
TEST(Bnd_RangeTest, Get_StructuredBindings)
{
Bnd_Range aRange(3.0, 10.0);
auto aBounds = aRange.Get();
ASSERT_TRUE(aBounds.has_value());
const auto [aMin, aMax] = *aBounds;
EXPECT_DOUBLE_EQ(aMin, 3.0);
EXPECT_DOUBLE_EQ(aMax, 10.0);
}
TEST(Bnd_RangeTest, Get_NulloptOnVoid)
{
Bnd_Range aVoid;
EXPECT_FALSE(aVoid.Get().has_value());
}
TEST(Bnd_RangeTest, Center)
{
Bnd_Range aRange(2.0, 8.0);
ASSERT_TRUE(aRange.Center().has_value());
EXPECT_DOUBLE_EQ(*aRange.Center(), 5.0);
Bnd_Range aPoint(3.0, 3.0);
ASSERT_TRUE(aPoint.Center().has_value());
EXPECT_DOUBLE_EQ(*aPoint.Center(), 3.0);
}
TEST(Bnd_RangeTest, Center_NulloptOnVoid)
{
Bnd_Range aVoid;
EXPECT_FALSE(aVoid.Center().has_value());
}

View File

@@ -0,0 +1,224 @@
// Copyright (c) 2025 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 <Bnd_Sphere.hxx>
#include <gp_XYZ.hxx>
#include <gtest/gtest.h>
TEST(Bnd_SphereTest, DefaultConstructor)
{
Bnd_Sphere aSphere;
EXPECT_DOUBLE_EQ(aSphere.Center().X(), 0.0);
EXPECT_DOUBLE_EQ(aSphere.Center().Y(), 0.0);
EXPECT_DOUBLE_EQ(aSphere.Center().Z(), 0.0);
EXPECT_DOUBLE_EQ(aSphere.Radius(), 0.0);
EXPECT_FALSE(aSphere.IsValid());
EXPECT_EQ(aSphere.U(), 0);
EXPECT_EQ(aSphere.V(), 0);
}
TEST(Bnd_SphereTest, ParameterizedConstructor)
{
const gp_XYZ aCenter(1.0, 2.0, 3.0);
Bnd_Sphere aSphere(aCenter, 5.0, 10, 20);
EXPECT_DOUBLE_EQ(aSphere.Center().X(), 1.0);
EXPECT_DOUBLE_EQ(aSphere.Center().Y(), 2.0);
EXPECT_DOUBLE_EQ(aSphere.Center().Z(), 3.0);
EXPECT_DOUBLE_EQ(aSphere.Radius(), 5.0);
EXPECT_EQ(aSphere.U(), 10);
EXPECT_EQ(aSphere.V(), 20);
}
TEST(Bnd_SphereTest, Validity)
{
Bnd_Sphere aSphere;
EXPECT_FALSE(aSphere.IsValid());
aSphere.SetValid(true);
EXPECT_TRUE(aSphere.IsValid());
aSphere.SetValid(false);
EXPECT_FALSE(aSphere.IsValid());
}
TEST(Bnd_SphereTest, Distance)
{
const gp_XYZ aCenter(0.0, 0.0, 0.0);
Bnd_Sphere aSphere(aCenter, 1.0, 0, 0);
const gp_XYZ aPoint(3.0, 4.0, 0.0);
const double aDist = aSphere.Distance(aPoint);
EXPECT_DOUBLE_EQ(aDist, 5.0);
}
TEST(Bnd_SphereTest, SquareDistance)
{
const gp_XYZ aCenter(0.0, 0.0, 0.0);
Bnd_Sphere aSphere(aCenter, 1.0, 0, 0);
const gp_XYZ aPoint(3.0, 4.0, 0.0);
EXPECT_DOUBLE_EQ(aSphere.SquareDistance(aPoint), 25.0);
}
TEST(Bnd_SphereTest, Distances_PointOutside)
{
const gp_XYZ aCenter(0.0, 0.0, 0.0);
Bnd_Sphere aSphere(aCenter, 2.0, 0, 0);
const gp_XYZ aPoint(5.0, 0.0, 0.0);
double aMin = 0.0, aMax = 0.0;
aSphere.Distances(aPoint, aMin, aMax);
EXPECT_DOUBLE_EQ(aMin, 3.0);
EXPECT_DOUBLE_EQ(aMax, 7.0);
}
TEST(Bnd_SphereTest, Distances_PointInside)
{
const gp_XYZ aCenter(0.0, 0.0, 0.0);
Bnd_Sphere aSphere(aCenter, 5.0, 0, 0);
const gp_XYZ aPoint(1.0, 0.0, 0.0);
double aMin = 0.0, aMax = 0.0;
aSphere.Distances(aPoint, aMin, aMax);
EXPECT_DOUBLE_EQ(aMin, 0.0);
EXPECT_DOUBLE_EQ(aMax, 6.0);
}
TEST(Bnd_SphereTest, SquareDistances_PointOutside)
{
const gp_XYZ aCenter(0.0, 0.0, 0.0);
Bnd_Sphere aSphere(aCenter, 2.0, 0, 0);
const gp_XYZ aPoint(5.0, 0.0, 0.0);
double aMin = 0.0, aMax = 0.0;
aSphere.SquareDistances(aPoint, aMin, aMax);
// d^2 = 25, r^2 = 4
// min = d^2 - r^2 = 21, max = d^2 + r^2 = 29
EXPECT_DOUBLE_EQ(aMin, 21.0);
EXPECT_DOUBLE_EQ(aMax, 29.0);
}
TEST(Bnd_SphereTest, SquareDistances_PointInside)
{
const gp_XYZ aCenter(0.0, 0.0, 0.0);
Bnd_Sphere aSphere(aCenter, 5.0, 0, 0);
const gp_XYZ aPoint(1.0, 0.0, 0.0);
double aMin = 0.0, aMax = 0.0;
aSphere.SquareDistances(aPoint, aMin, aMax);
// d^2 = 1, r^2 = 25
// d^2 < r^2 -> min = 0, max = d^2 + r^2 = 26
EXPECT_DOUBLE_EQ(aMin, 0.0);
EXPECT_DOUBLE_EQ(aMax, 26.0);
}
TEST(Bnd_SphereTest, SquareDistances_PointAtCenter)
{
const gp_XYZ aCenter(0.0, 0.0, 0.0);
Bnd_Sphere aSphere(aCenter, 3.0, 0, 0);
const gp_XYZ aPoint(0.0, 0.0, 0.0);
double aMin = 0.0, aMax = 0.0;
aSphere.SquareDistances(aPoint, aMin, aMax);
// d^2 = 0, r^2 = 9
// d^2 < r^2 -> min = 0, max = 0 + 9 = 9
EXPECT_DOUBLE_EQ(aMin, 0.0);
EXPECT_DOUBLE_EQ(aMax, 9.0);
}
TEST(Bnd_SphereTest, SquareDistances_PointOnSurface)
{
const gp_XYZ aCenter(0.0, 0.0, 0.0);
Bnd_Sphere aSphere(aCenter, 3.0, 0, 0);
const gp_XYZ aPoint(3.0, 0.0, 0.0);
double aMin = 0.0, aMax = 0.0;
aSphere.SquareDistances(aPoint, aMin, aMax);
// d^2 = 9, r^2 = 9
// d^2 < r^2 is false (equal), so min = d^2 - r^2 = 0, max = d^2 + r^2 = 18
EXPECT_DOUBLE_EQ(aMin, 0.0);
EXPECT_DOUBLE_EQ(aMax, 18.0);
}
TEST(Bnd_SphereTest, Project)
{
const gp_XYZ aCenter(1.0, 2.0, 3.0);
Bnd_Sphere aSphere(aCenter, 5.0, 0, 0);
const gp_XYZ aNode(10.0, 20.0, 30.0);
gp_XYZ aProjNode;
double aDist = 0.0;
bool anInside = false;
const bool isOk = aSphere.Project(aNode, aProjNode, aDist, anInside);
EXPECT_TRUE(isOk);
EXPECT_TRUE(anInside);
EXPECT_DOUBLE_EQ(aProjNode.X(), aCenter.X());
EXPECT_DOUBLE_EQ(aProjNode.Y(), aCenter.Y());
EXPECT_DOUBLE_EQ(aProjNode.Z(), aCenter.Z());
}
TEST(Bnd_SphereTest, Add_EnclosingSphere)
{
Bnd_Sphere aSphere1(gp_XYZ(0.0, 0.0, 0.0), 10.0, 0, 0);
Bnd_Sphere aSphere2(gp_XYZ(1.0, 0.0, 0.0), 2.0, 0, 0);
aSphere1.Add(aSphere2);
// aSphere1 already encloses aSphere2, should remain unchanged
EXPECT_DOUBLE_EQ(aSphere1.Radius(), 10.0);
}
TEST(Bnd_SphereTest, Add_EnclosedBySphere)
{
Bnd_Sphere aSphere1(gp_XYZ(0.0, 0.0, 0.0), 2.0, 0, 0);
Bnd_Sphere aSphere2(gp_XYZ(0.0, 0.0, 0.0), 10.0, 0, 0);
aSphere1.Add(aSphere2);
// aSphere2 encloses aSphere1, should take aSphere2
EXPECT_DOUBLE_EQ(aSphere1.Radius(), 10.0);
}
TEST(Bnd_SphereTest, Add_PartialOverlap)
{
Bnd_Sphere aSphere1(gp_XYZ(0.0, 0.0, 0.0), 3.0, 0, 0);
Bnd_Sphere aSphere2(gp_XYZ(5.0, 0.0, 0.0), 3.0, 0, 0);
aSphere1.Add(aSphere2);
// Combined radius should be (5 + 3 + 3) / 2 = 5.5
EXPECT_DOUBLE_EQ(aSphere1.Radius(), 5.5);
}
TEST(Bnd_SphereTest, IsOut_Separated)
{
Bnd_Sphere aSphere1(gp_XYZ(0.0, 0.0, 0.0), 1.0, 0, 0);
Bnd_Sphere aSphere2(gp_XYZ(10.0, 0.0, 0.0), 1.0, 0, 0);
EXPECT_TRUE(aSphere1.IsOut(aSphere2));
}
TEST(Bnd_SphereTest, IsOut_Overlapping)
{
Bnd_Sphere aSphere1(gp_XYZ(0.0, 0.0, 0.0), 3.0, 0, 0);
Bnd_Sphere aSphere2(gp_XYZ(4.0, 0.0, 0.0), 3.0, 0, 0);
EXPECT_FALSE(aSphere1.IsOut(aSphere2));
}
TEST(Bnd_SphereTest, IsOut_PointWithMaxDist)
{
Bnd_Sphere aSphere(gp_XYZ(0.0, 0.0, 0.0), 5.0, 0, 0);
const gp_XYZ aPoint(20.0, 0.0, 0.0);
double aMaxDist = 100.0;
aSphere.SetValid(true);
EXPECT_FALSE(aSphere.IsOut(aPoint, aMaxDist));
// aMaxDist should be updated to aCurMaxDist = 20 + 5 = 25
EXPECT_DOUBLE_EQ(aMaxDist, 25.0);
}
TEST(Bnd_SphereTest, IsOut_PointTooFar)
{
Bnd_Sphere aSphere(gp_XYZ(0.0, 0.0, 0.0), 1.0, 0, 0);
const gp_XYZ aPoint(20.0, 0.0, 0.0);
double aMaxDist = 10.0;
EXPECT_TRUE(aSphere.IsOut(aPoint, aMaxDist));
}
TEST(Bnd_SphereTest, SquareExtent)
{
Bnd_Sphere aSphere(gp_XYZ(0.0, 0.0, 0.0), 3.0, 0, 0);
EXPECT_DOUBLE_EQ(aSphere.SquareExtent(), 36.0);
}

View File

@@ -6,7 +6,10 @@ set(OCCT_TKMath_GTests_FILES
Bnd_B3_Test.cxx
Bnd_BoundSortBox_Test.cxx
Bnd_Box_Test.cxx
Bnd_Box2d_Test.cxx
Bnd_OBB_Test.cxx
Bnd_Range_Test.cxx
Bnd_Sphere_Test.cxx
BSplCLib_Cache_Test.cxx
BSplCLib_Test.cxx
BSplSLib_Cache_Test.cxx

View File

@@ -613,10 +613,10 @@ static bool IsSeamOrBound(const IntSurf_PntOn2S& thePtf,
aBndR[i].Add(aParF[i]);
aBndR[i].Add(aParL[i]);
if (aBndR[i].IsIntersected(theFBound[i], theArrPeriods[i]) == 1)
if (aBndR[i].IsIntersected(theFBound[i], theArrPeriods[i]) == Bnd_Range::IntersectStatus_In)
return true;
if (aBndR[i].IsIntersected(theLBound[i], theArrPeriods[i]) == 1)
if (aBndR[i].IsIntersected(theLBound[i], theArrPeriods[i]) == Bnd_Range::IntersectStatus_In)
return true;
}
@@ -635,7 +635,7 @@ static bool IsSeamOrBound(const IntSurf_PntOn2S& thePtf,
return true;
}
if (aBndR[i].IsIntersected(0.0, theArrPeriods[i]) == 1)
if (aBndR[i].IsIntersected(0.0, theArrPeriods[i]) == Bnd_Range::IntersectStatus_In)
return true;
}