Bnd系列
1.Bnd_B2d
OpenCASCADE 7.9.0 中的 Bnd_B2d
是用于表示**二维轴对齐包围盒(AABB)**的核心类,其设计注重计算效率与几何算法的通用性。以下从设计思想、核心实现到应用场景进行深度解析,结合源码关键片段展开说明:
1.1.设计思想与数据结构
1.1.1. 中心-半对角线表示法
-
存储结构:
Bnd_B2d
使用两个关键数据:myCenter
:包围盒中心点坐标(gp_XY
类型)。myHSize
:半对角线向量(从中心到角点的向量),其分量必须非负4。
与传统的最小-最大角点表示法不同,此方式简化了合并操作与变换计算。例如:
Bnd_B2d (const gp_XY& theCenter, const gp_XY& theHSize);
1.1.2. 状态管理
-
无效状态(Void):通过初始化
myHSize
为负值标识(如Clear()
中设myHSize = -RealLast
)。void Clear() { myHSize[0] = -Bnd_B2x_RealLast; // 标记为无效 }
-
有效性检查:
IsVoid()
检测myHSize[0] < 0
,避免无效操作
1.2.核心方法解析
1.2.1. 动态扩展包围盒
Add(const gp_XY& thePnt)
动态调整边界以包含新点,通过更新中心与半对角线实现:
void Add(const gp_XY& thePnt) {
gp_XY cornerMin = CornerMin(); // 计算当前最小角点
gp_XY cornerMax = CornerMax(); // 计算当前最大角点
// 扩展边界
cornerMin.SetMin(thePnt);
cornerMax.SetMax(thePnt);
// 更新中心与半对角线
myCenter = (cornerMin + cornerMax) * 0.5;
myHSize = (cornerMax - cornerMin) * 0.5;
}
此操作时间复杂度 O(1),优于传统最小-最大表示的多次比较
1.2.2. 空间关系判定
点包含检测(IsOut
)
基于坐标分量的快速比较:
Standard_Boolean IsOut(const gp_XY& thePnt) const {
return (Abs(thePnt.X() - myCenter.X()) > myHSize.X() || Abs(thePnt.Y() - myCenter.Y()) > myHSize.Y());
}
包围盒相交检测(IsOut(const Bnd_B2d&)
分离轴定理的二维实现:
Standard_Boolean IsOut(const Bnd_B2d& theBox) const {
return (Abs(theBox.myCenter.X() - myCenter.X()) > (theBox.myHSize.X() + myHSize.X())
|| Abs(theBox.myCenter.Y() - myCenter.Y()) > (theBox.myHSize.Y() + myHSize.Y()));
}
仅需 4次比较与2次加法,效率极高
1.2.3. 几何变换与工具方法
扩展边界(Enlarge
)
均匀增加半对角线长度:
void Enlarge(Standard_Real theDiff) {
myHSize.SetX(myHSize.X() + Abs(theDiff));
myHSize.SetY(myHSize.Y() + Abs(theDiff));
}
角点计算
CornerMin()
与 CornerMax()
动态生成角点:
gp_XY CornerMin() const {
return myCenter - myHSize;
}
1.4.应用场景示例
1.4.1. 点集包围盒构建
Bnd_B2d bbox;
bbox.Add(gp_XY(0.0, 0.0)); // 添加点
bbox.Add(gp_XY(3.0, 4.0)); // 动态扩展
if (!bbox.IsVoid()) {
gp_XY size = bbox.CornerMax() - bbox.CornerMin(); // 计算尺寸
}
1.4.2. 碰撞检测优化
Bnd_B2d box1(center1, hSize1);
Bnd_B2d box2(center2, hSize2);
if (!box1.IsOut(box2)) {
// 执行精确几何相交检测
}
1.5.设计局限与应对策略
- 旋转不敏感
AABB 对旋转物体包裹性差 → 需改用Bnd_OBB
(定向包围盒)。 - 合并膨胀问题
多次Add
操作可能导致包围盒过度膨胀 → 结合 R树/四叉树 分层管理包围盒。 - 精度依赖
浮点计算可能引入误差 → 使用容差参数(如Enlarge(1e-5)
)
总结
Bnd_B2d
以 中心-半对角线模型 为核心,通过极简的数学表达实现了高效的二维 AABB 操作。其优势在于:
- 计算高效:点添加与相交检测均达到 O(1) 时间复杂度;
- 内存紧凑:仅存储 4 个浮点值(中心 + 半对角线);
- 接口正交:方法设计符合几何直觉(如
Add
、IsOut
)。
在需要快速空间筛选的场景(如 CAD 中的平面轮廓碰撞检测、GIS 中的区域查询),Bnd_B2d
是基础且关键的数据结构。如需处理旋转或复杂边界,可向上扩展至 Bnd_OBB
或结合空间索引结构
2.Bnd_B3d
OpenCASCADE 7.9.0 中的 Bnd_B3d
是用于表示**三维轴对齐包围盒(AABB)**的核心类,其设计兼顾高效的空间计算与几何算法的通用性。以下从设计思想、数据结构、关键方法到应用场景进行深度解析,结合源码关键逻辑展开说明。
2.1.设计思想与数据结构
2.1.1. 中心-半对角线模型
- 存储结构:
Bnd_B3d
采用 中心点 + 半对角线 的紧凑表示:
class Bnd_B3d {
private:
gp_XYZ myCenter; // 包围盒中心坐标
gp_XYZ myHSize; // 半对角线向量(XYZ方向上的半边长)
};
优势:
- 合并操作高效(如
Add()
仅需更新中心与半对角线) - 几何变换(平移/缩放)计算简化(旋转需重构包围盒)
2. 状态管理机制
- 无效状态标识:
初始化时myHSize
设为负值(如SetVoid()
中myHSize.SetCoord(-1, -1, -1)
),IsVoid()
通过检测myHSize[0] < 0
判断是否为空1。 - 开放边界支持:
通过标志位(如OpenXmin
)表示无限延伸方向,例如WholeSpace
标志标识全方向无限空间。
2.2.核心方法源码解析
2.2.1. 动态扩展包围盒(Add
)
void Bnd_B3d::Add(const gp_XYZ& thePnt) {
if (IsVoid()) {
myCenter = thePnt;
myHSize.SetCoord(0, 0, 0);
return;
}
gp_XYZ cornerMin = myCenter - myHSize;
gp_XYZ cornerMax = myCenter + myHSize;
cornerMin.SetMin(thePnt); // 更新最小角点
cornerMax.SetMax(thePnt); // 更新最大角点
myCenter = (cornerMin + cornerMax) * 0.5; // 重算中心
myHSize = (cornerMax - cornerMin) * 0.5; // 重算半对角线
}
时间复杂度:O(1),优于传统最小-最大角点表示的多次比较。
2.2.2. 空间关系检测(IsOut
)
-
点与包围盒的位置关系:
-
Standard_Boolean IsOut(const gp_XYZ& thePnt) const { return (Abs(thePnt.X() - myCenter.X()) > myHSize.X() || Abs(thePnt.Y() - myCenter.Y()) > myHSize.Y() || Abs(thePnt.Z() - myCenter.Z()) > myHSize.Z(); }
-
包围盒相交检测(分离轴定理):
Standard_Boolean IsOut(const Bnd_B3d& theBox) const {
return (Abs(theBox.myCenter.X() - myCenter.X()) > (theBox.myHSize.X() + myHSize.X())
|| (Abs(theBox.myCenter.Y() - myCenter.Y()) > (theBox.myHSize.Y() + myHSize.Y())
|| (Abs(theBox.myCenter.Z() - myCenter.Z()) > (theBox.myHSize.Z() + myHSize.Z())
}
2.2.3. 几何变换(Transform
)
Bnd_B3d Bnd_B3d::Transformed(const gp_Trsf& theTrsf) const {
if (theTrsf.IsUniform()) { // 均匀变换(平移+旋转+缩放)
gp_XYZ newCenter = myCenter.Transformed(theTrsf);
gp_XYZ newHSize = myHSize * theTrsf.ScaleFactor();
return Bnd_B3d(newCenter, newHSize);
} else { // 非均匀变换需重构包围盒
Bnd_B3d newBox;
newBox.Add(myCenter - myHSize);
newBox.Add(myCenter + myHSize);
return newBox;
}
}
- 局限:旋转或非均匀变换会导致包围盒膨胀,需重构
2.3.应用场景示例
2.3.1. 点集包围盒构建
Bnd_B3d bbox;
bbox.Add(gp_XYZ(0, 0, 0)); // 添加点
bbox.Add(gp_XYZ(3, 4, 5));
if (!bbox.IsVoid()) {
gp_XYZ size = bbox.CornerMax() - bbox.CornerMin(); // 计算实际尺寸
}
2.3.2. 碰撞检测加速
Bnd_B3d box1(center1, hSize1);
Bnd_B3d box2(center2, hSize2);
if (!box1.IsOut(box2)) { // 快速相交筛选
// 执行精确的三角面片相交检测
}
2.4.设计局限与应对
- 旋转不适应性
- 问题:对旋转物体包裹性差,导致冗余空间。
- 方案:改用
Bnd_OBB
(基于 PCA 计算主方向)。
- 合并操作的膨胀问题
- 问题:多次
Add()
可能导致包围盒过度膨胀。 - 方案:结合
BVH_Tree
分层管理子包围盒。
- 问题:多次
- 浮点精度误差
- 问题:极端尺度下计算误差累积。
- 方案:调用
Enlarge(Tol)
扩展容差(如Tol=1e-5
)
总结
Bnd_B3d
是 OpenCASCADE 中高效三维 AABB 的现代实现,其核心价值在于:
- 计算极简:中心-半对角线模型使点添加、相交检测均达 O(1) 复杂度;
- 内存紧凑:仅存储 6 个浮点数,优于传统
Bnd_Box
的冗余标志位; - 开放边界原生支持:完美处理无限空间场景。
在 大规模空间检索(如 CAD 装配体碰撞检测)、实时渲染裁剪 等场景中,Bnd_B3d
是性能与通用性兼顾的首选。若需处理旋转物体或高精度碰撞,可结合 Bnd_OBB
与 BVH_Tree
构建混合加速结构。
3.Bnd_Box2d
Bnd_Box2d 是 OpenCASCADE 中用于表示**二维轴对齐包围盒(AABB)**的核心类,它在几何算法和空间计算中扮演着关键角色。以下是对该类的深度源码解析:
3.1.类定义与数据结构(Bnd_Box2d.hxx)
class Bnd_Box2d {
public:
// 构造函数
Bnd_Box2d();
// 设置整个二维空间
void SetWhole();
// 设置为空(无效状态)
void SetVoid();
// 更新操作
void Add(const gp_Pnt2d& P);
void Add(const Bnd_Box2d& Other);
// 查询操作
Standard_Boolean IsVoid() const;
Standard_Boolean IsWhole() const;
Standard_Boolean IsOut(const gp_Pnt2d& P) const;
Standard_Boolean IsOut(const Bnd_Box2d& Other) const;
// 获取几何信息
void Get(gp_Pnt2d& theMin, gp_Pnt2d& theMax) const;
Standard_Real SquareExtent() const;
// 扩展操作
void Enlarge(const Standard_Real theDiff);
// 其他实用方法
// ...
private:
gp_Pnt2d myMin; // 最小角点
gp_Pnt2d myMax; // 最大角点
Standard_Integer myFlags; // 状态标志位
};
关键数据结构解析:
- myMin 和 myMax:
- 存储包围盒的角点坐标
- 使用 gp_Pnt2d 类型(二维点)
- myFlags 状态标志:
- 使用位掩码表示包围盒的特殊状态
- 定义在 Bnd_B2d.cxx 中的枚举
enum BoxFlags {
VoidMask = 0x01, // 无效状态
WholeMask = 0x02, // 整个空间
XminMask = 0x04, // X方向最小开放
XmaxMask = 0x08, // X方向最大开放
YminMask = 0x10, // Y方向最小开放
YmaxMask = 0x20 // Y方向最大开放
};
3.2.核心方法实现解析(Bnd_Box2d.cxx)
3.2.1. 构造函数与状态管理
// 默认构造函数:初始化为无效状态
Bnd_Box2d::Bnd_Box2d() : myMin(RealLast(), RealLast()), myMax(RealFirst(),
RealFirst()), myFlags(VoidMask) {}
// 设置为整个空间
void Bnd_Box2d::SetWhole() {
myMin.SetCoord(-Precision::Infinite(), -Precision::Infinite());
myMax.SetCoord(Precision::Infinite(), Precision::Infinite());
myFlags = WholeMask;
}
// 设置为无效状态
void Bnd_Box2d::SetVoid() {
myMin.SetCoord(RealLast(), RealLast());
myMax.SetCoord(RealFirst(), RealFirst());
myFlags = VoidMask;
}
3.2.2. 添加点操作
void Bnd_Box2d::Add(const gp_Pnt2d& thePnt)
{
if (IsVoid()) {
// 初始化包围盒
myMin = thePnt;
myMax = thePnt;
myFlags = 0; // 清除无效标志
}
else if (!IsWhole()) {
// 更新最小点
if (thePnt.X() < myMin.X()) myMin.SetX(thePnt.X());
if (thePnt.Y() < myMin.Y()) myMin.SetY(thePnt.Y());
// 更新最大点
if (thePnt.X() > myMax.X()) myMax.SetX(thePnt.X());
if (thePnt.Y() > myMax.Y()) myMax.SetY(thePnt.Y());
}
// 如果是Whole状态,不需要更新
}
3.2.3. 空间关系检测
// 检查点是否在包围盒外
Standard_Boolean Bnd_Box2d::IsOut(const gp_Pnt2d& thePnt) const {
if (IsVoid()) return Standard_True;
if (IsWhole()) return Standard_False;
// 检查X方向
if (!(myFlags & XminMask) && (thePnt.X() < myMin.X()))
return Standard_True;
if (!(myFlags & XmaxMask) && (thePnt.X() > myMax.X()))
return Standard_True;
// 检查Y方向
if (!(myFlags & YminMask) && (thePnt.Y() < myMin.Y()))
return Standard_True;
if (!(myFlags & YmaxMask) && (thePnt.Y() > myMax.Y()))
return Standard_True;
return Standard_False;
}
// 检查另一个包围盒是否与当前包围盒相交
Standard_Boolean Bnd_Box2d::IsOut(const Bnd_Box2d& theOther) const {
if (IsVoid() || theOther.IsVoid())
return Standard_True;
if (IsWhole() || theOther.IsWhole())
return Standard_False;
// X方向检测
Standard_Boolean isOutX =
((!(myFlags & XminMask) && !(theOther.myFlags & XmaxMask) &&
(theOther.myMax.X() < myMin.X())) ||
((!(myFlags & XmaxMask) && !(theOther.myFlags & XminMask) &&
(theOther.myMin.X() > myMax.X()));
// Y方向检测
Standard_Boolean isOutY =
((!(myFlags & YminMask) && !(theOther.myFlags & YmaxMask) &&
(theOther.myMax.Y() < myMin.Y())) ||
((!(myFlags & YmaxMask) && !(theOther.myFlags & YminMask) &&
(theOther.myMin.Y() > myMax.Y()));
return (isOutX || isOutY);
}
3.2.4. 包围盒扩展操作
void Bnd_Box2d::Enlarge(const Standard_Real theDiff) {
if (IsVoid() || IsWhole())
return;
myMin.SetX(myMin.X() - theDiff);
myMin.SetY(myMin.Y() - theDiff);
myMax.SetX(myMax.X() + theDiff);
myMax.SetY(myMax.Y() + theDiff);
// 检查是否变成无限空间
if (Abs(myMin.X()) > Precision::Infinite() ||
Abs(myMax.X()) > Precision::Infinite() ||
Abs(myMin.Y()) > Precision::Infinite() ||
Abs(myMax.Y()) > Precision::Infinite()) {
SetWhole();
}
}
3.3.设计特点分析
3.3.1. 开放边界支持
Bnd_Box2d 的核心优势在于支持开放边界(部分无限空间):
- 可以表示某个方向无限延伸的包围盒
- 通过标志位控制边界开放状态
- 适用于裁剪、视图变换等场景
3.3.2. 状态驱动优化
- 无效状态(Void):未添加任何点的初始状态
- 全空间状态(Whole):表示整个二维空间
- 状态检测优化计算效率:
// 在添加点前检查状态
if (IsWhole()) return; // 无需处理
if (IsVoid()) { /* 初始化 */ }
3.3.3. 精度控制
- 使用 Precision 类管理计算精度
- 自动检测无限空间:
if (Abs(myMin.X()) > Precision::Infinite())
{
SetWhole();
}
3.4.与相关类的对比
特性 | Bnd_Box2d | Bnd_B2d | Bnd_Box (3D) |
---|---|---|---|
维度 | 2D | 2D | 3D |
开放边界 | ✅ 支持 | ❌ 不支持 | ✅ 支持 |
无限空间 | ✅ 支持 | ❌ 不支持 | ✅ 支持 |
状态标志 | ✅ 完整状态机 | ❌ 仅无效状态 | ✅ 完整状态机 |
内存占用 | 16字节 + 标志位 | 16字节 | 24字节 + 标志位 |
典型应用 | 2D视图裁剪 | 轻量级2D计算 | 3D空间计算 |
3…5.应用场景示例
3.5.1. 视图裁剪
Bnd_Box2d viewBox;
viewBox.Add(gp_Pnt2d(0, 0));
viewBox.Add(gp_Pnt2d(1024, 768));
// 检查对象是否在视图内
if (!viewBox.IsOut(objectBBox)) {
RenderObject(object);
}
3.5.2. 几何包容性检测
Bnd_Box2d boundary;
boundary.Add(gp_Pnt2d(0, 0));
boundary.Add(gp_Pnt2d(100, 100));
// 检查点集是否在边界内
for (const auto& point : points) {
if (boundary.IsOut(point)) {
HandleOutlier(point);
}
}
3.5.3. 动态包围盒更新
Bnd_Box2d dynamicBox;
for (const auto& movingObj : movingObjects) {
dynamicBox.Add(movingObj.Position());
// 每10帧重新计算
if (frameCount % 10 == 0) {
dynamicBox.Enlarge(5.0); // 添加安全距离
ProcessCollisions(dynamicBox);
dynamicBox.SetVoid(); // 重置
}
}
3.6.设计局限与解决方案
-
旋转不敏感问题
- 问题:对旋转物体包裹性差
- 解决方案:使用更高级的
Bnd_OBB
(定向包围盒)
-
多次Add性能问题
- 问题:频繁添加点导致性能下降
- 优化方案:批量添加接口
void AddPoints(const std::vector<gp_Pnt2d>& points) { if (IsWhole()) return; gp_Pnt2d minPoint = myMin; gp_Pnt2d maxPoint = myMax; for (const auto& p : points) { minPoint.SetMin(p); maxPoint.SetMax(p); } myMin = minPoint; myMax = maxPoint; }
3.浮点精度问题
- 问题:极端坐标值导致精度丢失
- 解决方案:使用容差放大
void AddWithTolerance(const gp_Pnt2d& p, Standard_Real tol) {
Add(gp_Pnt2d(p.X()-tol, p.Y()-tol));
Add(gp_Pnt2d(p.X()+tol, p.Y()+tol));
}
总结
Bnd_Box2d 是 OpenCASCADE 中二维空间计算的核心组件,其主要特点包括:
- 灵活的空间表示:支持有限空间、开放边界和无限空间
- 高效的状态管理:通过标志位优化计算路径
- 精心的精度控制:集成 OpenCASCADE 精度管理系统
- 丰富的空间关系检测:提供完整的点/包围盒位置判断
在二维 CAD 绘图、视图裁剪、碰撞检测等场景中,Bnd_Box2d 提供了高效可靠的空间计算基础。与 Bnd_B2d 相比,它更适合需要处理开放边界和无限空间的复杂场景,而 Bnd_B2d 则在轻量级计算中更有优势。
对于性能要求极高的场景,建议:
- 优先检查包围盒状态(IsWhole/IsVoid)
- 批量处理点添加操作
- 使用 Enlarge() 替代多次添加
- 在适当时机重置包围盒(SetVoid)
4.Bnd_Box
Bnd_Box 是 OpenCASCADE 中用于表示**三维轴对齐包围盒(AABB)**的核心类,支持开放边界和无限空间的概念。它在几何算法和空间计算中扮演着关键角色。以下是对该类的深度源码解析:
4.1.类定义与数据结构(Bnd_Box.hxx)
class Bnd_Box {
public:
// 构造函数
Bnd_Box();
// 设置整个三维空间
void SetWhole();
// 设置为空(无效状态)
void SetVoid();
// 更新操作
void Add(const gp_Pnt& P);
void Add(const Bnd_Box& Other);
// 查询操作
Standard_Boolean IsVoid() const;
Standard_Boolean IsWhole() const;
Standard_Boolean IsOpen() const;
Standard_Boolean IsOut(const gp_Pnt& P) const;
Standard_Boolean IsOut(const Bnd_Box& Other) const;
// 获取几何信息
void Get(gp_Pnt& theMin, gp_Pnt& theMax) const;
Standard_Real SquareExtent() const;
// 扩展操作
void Enlarge(const Standard_Real theDiff);
// 设置开放标志
void OpenXmin();
void OpenXmax();
void OpenYmin();
void OpenYmax();
void OpenZmin();
void OpenZmax();
// 其他实用方法
// ...
private:
gp_Pnt myMin; // 最小角点
gp_Pnt myMax; // 最大角点
Standard_Real myGap; // 安全间隙(用于扩大包围盒)
Standard_Integer myFlags; // 状态标志位
};
关键数据结构解析:
- myMin 和 myMax:
- 存储包围盒的角点坐标
- 使用 gp_Pnt 类型(三维点)
- myGap:
- 包围盒的扩展值,用于在原有包围盒的基础上扩大一个固定值
- 默认值为0.0
- myFlags 状态标志:
- 使用位掩码表示包围盒的特殊状态
- 定义在 Bnd_Box.cxx 中的枚举:
enum BoxFlags {
VoidMask = 0x01, // 无效状态
WholeMask = 0x02, // 整个空间
XminMask = 0x04, // X方向最小开放
XmaxMask = 0x08, // X方向最大开放
YminMask = 0x10, // Y方向最小开放
YmaxMask = 0x20, // Y方向最大开放
ZminMask = 0x40, // Z方向最小开放
ZmaxMask = 0x80 // Z方向最大开放
};
4.2.核心方法实现解析(Bnd_Box.cxx)
4.2.1. 构造函数与状态管理
// 默认构造函数:初始化为无效状态
Bnd_Box::Bnd_Box() : myMin(RealLast(), RealLast(), RealLast()),
myMax(RealFirst(), RealFirst(), RealFirst()), myGap(0.0), myFlags(VoidMask) {}
// 设置为整个空间
void Bnd_Box::SetWhole() {
myMin.SetCoord(-Precision::Infinite(), -Precision::Infinite(), -Precision::Infinite());
myMax.SetCoord(Precision::Infinite(), Precision::Infinite(), Precision::Infinite());
myFlags = WholeMask;
}
// 设置为无效状态
void Bnd_Box::SetVoid() {
myMin.SetCoord(RealLast(), RealLast(), RealLast());
myMax.SetCoord(RealFirst(), RealFirst(), RealFirst());
myGap = 0.0;
myFlags = VoidMask;
}
4.2.2. 添加点操作
void Bnd_Box::Add(const gp_Pnt& thePnt) {
if (IsVoid()) {
// 初始化包围盒
myMin = thePnt;
myMax = thePnt;
myFlags = 0; // 清除无效标志
}
else if (!IsWhole()) {
// 更新最小点
if (thePnt.X() < myMin.X()) myMin.SetX(thePnt.X());
if (thePnt.Y() < myMin.Y()) myMin.SetY(thePnt.Y());
if (thePnt.Z() < myMin.Z()) myMin.SetZ(thePnt.Z());
// 更新最大点
if (thePnt.X() > myMax.X()) myMax.SetX(thePnt.X());
if (thePnt.Y() > myMax.Y()) myMax.SetY(thePnt.Y());
if (thePnt.Z() > myMax.Z()) myMax.SetZ(thePnt.Z());
}
// 如果是Whole状态,不需要更新
}
4.2.3. 空间关系检测
// 检查点是否在包围盒外
Standard_Boolean Bnd_Box::IsOut(const gp_Pnt& thePnt) const {
if (IsVoid()) return Standard_True;
if (IsWhole()) return Standard_False;
// 考虑myGap的影响,实际边界为[min-gap, max+gap]
Standard_Real x = thePnt.X();
if (!(myFlags & XminMask) && (x < (myMin.X() - myGap)))
return Standard_True;
if (!(myFlags & XmaxMask) && (x > (myMax.X() + myGap)))
return Standard_True;
// Y方向检测
Standard_Real y = thePnt.Y();
if (!(myFlags & YminMask) && (y < (myMin.Y() - myGap)))
return Standard_True;
if (!(myFlags & YmaxMask) && (y > (myMax.Y() + myGap)))
return Standard_True;
// Z方向检测
Standard_Real z = thePnt.Z();
if (!(myFlags & ZminMask) && (z < (myMin.Z() - myGap)))
return Standard_True;
if (!(myFlags & ZmaxMask) && (z > (myMax.Z() + myGap)))
return Standard_True;
return Standard_False;
}
// 检查另一个包围盒是否与当前包围盒相交
Standard_Boolean Bnd_Box::IsOut(const Bnd_Box& theOther) const {
if (IsVoid() || theOther.IsVoid())
return Standard_True;
if (IsWhole() || theOther.IsWhole())
return Standard_False;
// 考虑myGap的影响
Standard_Real min1 = myMin.X() - myGap;
Standard_Real max1 = myMax.X() + myGap;
Standard_Real min2 = theOther.myMin.X() - theOther.myGap;
Standard_Real max2 = theOther.myMax.X() + theOther.myGap;
// X方向检测
Standard_Boolean isOutX = ((!(myFlags & XminMask) && !(theOther.myFlags & XmaxMask) && (max2 < min1))
|| ((!(myFlags & XmaxMask) && !(theOther.myFlags & XminMask) && (min2 > max1));
// Y方向检测
min1 = myMin.Y() - myGap;
max1 = myMax.Y() + myGap;
min2 = theOther.myMin.Y() - theOther.myGap;
max2 = theOther.myMax.Y() + theOther.myGap;
Standard_Boolean isOutY = ((!(myFlags & YminMask) && !(theOther.myFlags & YmaxMask) && (max2 < min1))
|| ((!(myFlags & YmaxMask) && !(theOther.myFlags & YminMask) && (min2 > max1));
// Z方向检测
min1 = myMin.Z() - myGap;
max1 = myMax.Z() + myGap;
min2 = theOther.myMin.Z() - theOther.myGap;
max2 = theOther.myMax.Z() + theOther.myGap;
Standard_Boolean isOutZ =((!(myFlags & ZminMask) && !(theOther.myFlags & ZmaxMask) && (max2 < min1))
|| ((!(myFlags & ZmaxMask) && !(theOther.myFlags & ZminMask) && (min2 > max1));
return (isOutX || isOutY || isOutZ);
}
4.2.4. 包围盒扩展操作
void Bnd_Box::Enlarge(const Standard_Real theDiff) {
if (IsVoid() || IsWhole())
return;
// 直接增加myGap,而不是修改坐标
myGap += Abs(theDiff);
// 检查是否变成无限空间(当myGap非常大时)
if (myGap > Precision::Infinite()) {
SetWhole();
}
}
4.3.设计特点分析
4.3.1. 开放边界支持
- 每个坐标轴的两个方向都可以独立设置为开放(无限延伸)
- 通过标志位控制开放方向
- 适用于表示部分无限的空间(如地面平面)
4.3.2. 安全间隙(myGap)
- 引入myGap避免频繁修改坐标值
- 在IsOut和IsOut(Bnd_Box)中自动考虑myGap
- Enlarge操作只需增加myGap值,效率高
4.3.3. 状态驱动优化
- 通过状态标志(Void, Whole, Open)优化计算
- 在添加点或包围盒时,根据状态跳过不必要的计算
- 状态查询方法(IsVoid, IsWhole, IsOpen)提供快速判断
4.3.4. 精度控制
- 使用Precision类管理计算精度
- 自动检测无限空间:
if (myGap > Precision::Infinite()) {
SetWhole();
}
4.4.应用场景示例
4.4.1. 碰撞检测
Bnd_Box box1, box2;
// 添加几何体到包围盒
for (const auto& vertex : mesh1.Vertices()) {
box1.Add(vertex);
}
for (const auto& vertex : mesh2.Vertices()) {
box2.Add(vertex);
}
// 添加安全间隙
box1.Enlarge(0.1);
box2.Enlarge(0.1);
// 快速相交检测
if (!box1.IsOut(box2)) {
// 执行精确的三角面片相交检测
PerformExactCollisionDetection(mesh1, mesh2);
}
4.4.2. 视图裁剪
Bnd_Box viewFrustum;
// 设置视锥体包围盒
viewFrustum.Add(gp_Pnt(-100, -100, -100));
viewFrustum.Add(gp_Pnt(100, 100, 100));
// 开放近平面(相机方向)
viewFrustum.OpenZmin();
// 检查对象是否在视锥体内
for (const auto& object : sceneObjects) {
if (!viewFrustum.IsOut(object.GetBoundingBox())) {
RenderObject(object);
}
}
4.4.3. 无限平面表示
// 表示一个XY平面(Z=0)
Bnd_Box groundPlane;
groundPlane.Add(gp_Pnt(-1000, -1000, 0));
groundPlane.Add(gp_Pnt(1000, 1000, 0));
groundPlane.OpenZmax(); // 向上无限延伸
// 检查点是否在地面上方
gp_Pnt point(10, 20, 5);
if (groundPlane.IsOut(point)) {
// 点在地面下方或XY范围外
} else {
// 点在地面上方
}
5.Bnd_OBB
OpenCASCADE 7.9.0 中的 Bnd_OBB
(定向包围盒,Oriented Bounding Box)是用于处理旋转物体碰撞检测的高效数据结构,相比轴对齐包围盒(AABB),它通过方向自适应优化了包裹性。以下从核心设计、源码实现到应用场景进行深度解析。
5.1.设计思想与数据结构
5.1.1. 旋转包围盒的定义
Bnd_OBB
通过三个正交轴(XDirection
、YDirection
、ZDirection
)定义局部坐标系,结合中心点和半尺寸描述一个旋转的立方体:
class Bnd_OBB {
private:
gp_Pnt myCenter; // 中心点
gp_Dir myXDirection; // X轴方向
gp_Dir myYDirection; // Y轴方向
gp_Dir myZDirection; // Z轴方向
gp_XYZ myHSize; // 半尺寸(XYZ方向上的半边长)
Standard_Boolean myIsAABox; // 是否为AABB(优化标记)
};
- 关键优势:对旋转物体包裹更紧密,减少“空洞”区域2。
- 半尺寸约束:
myHSize
各分量必须非负,表示从中心到面的距离。
5.1.2. 状态管理
- 无效状态:通过
myHSize
分量负值标识(如SetVoid()
中设myHSize.SetCoord(-1, -1, -1)
)。 - AABB优化标记:若
myIsAABox=true
,则轴与全局坐标系平行,可退化为AABB加速计算。
5.2.核心方法解析
5.2.1. 构造方式
(1) 从点云构建(主成分分析PCA)
void Bnd_OBB::ReBuild(const gp_Pnt* thePoints, const Standard_Integer theNumPoints) {
// 1. 计算点云协方差矩阵
gp_Mat covariance = computeCovarianceMatrix(thePoints, theNumPoints);
// 2. 特征值分解,获取主方向
gp_Mat rotation = computeEigenVectors(covariance);
// 3. 将点投影到主方向,计算各轴半长
myXDirection = rotation.Column(0); // 最大特征值对应方向
myYDirection = rotation.Column(1);
myZDirection = rotation.Column(2);
myHSize = computeProjectedExtents(thePoints, theNumPoints, rotation);
}
作用:通过PCA找到最优包裹方向,最小化OBB体积
(2) 从三角形集构建
void Bnd_OBB::ReBuild(const Poly_Triangle* theTriangles, const TColgp_Array1OfPnt& theNodes) {
// 计算所有三角形法向量的协方差矩阵
gp_Mat covariance;
for (each triangle) {
gp_Vec normal = computeTriangleNormal(theNodes, triangle);
covariance += normal.XYZ() * normal.XYZ().Transposed();
}
// 后续步骤同点云构建
}
特点:法向量协方差更贴合曲面几何特征
5.2.2. 相交检测(分离轴定理SAT)
Standard_Boolean Bnd_OBB::IsOut(const Bnd_OBB& theOBB) const {
// 15条分离轴检测:6条各自OBB的面法向 + 9条边叉积方向
for (int i = 0; i < 15; ++i) {
gp_Dir axis = getSeparationAxis(i, theOBB);
Standard_Real proj1 = projectToAxis(this, axis);
Standard_Real proj2 = projectToAxis(&theOBB, axis);
if (Abs(proj1 - proj2) > (projectedSize(this, axis) + projectedSize(&theOBB, axis)) {
return Standard_True; // 不相交
}
}
return Standard_False; // 相交
}
优化点:
- 快速投影计算:利用半尺寸和方向向量简化投影区间计算。
- 提前终止:任一分离轴检测到不重叠即返回。
5.2.3. 几何变换支持
void Bnd_OBB::Transform(const gp_Trsf& theTrsf) {
if (theTrsf.IsUniform()) { // 均匀变换(平移+旋转+缩放)
myCenter.Transform(theTrsf);
myXDirection.Transform(theTrsf);
myYDirection.Transform(theTrsf);
myZDirection.Transform(theTrsf);
myHSize *= theTrsf.ScaleFactor();
} else { // 非均匀变换需重新计算OBB
gp_GTrsf gtrsf(theTrsf);
recomputeFromTransformedPoints(gtrsf);
}
}
限制:非均匀变换(如拉伸)会破坏正交性,需重构OBB。
5.3.应用场景示例
5.3.1. 精确碰撞检测
// 创建两个OBB
Bnd_OBB obb1, obb2;
obb1.ReBuild(triangleSet1); // 从三角形集构建
obb2.ReBuild(triangleSet2);
// 快速相交筛选
if (!obb1.IsOut(obb2)) {
// 执行精确的三角面相交检测
}
5.3.2. 射线与旋转物体求交
gp_Lin ray(...);
Standard_Real distance;
if (obb1.Intersect(ray, distance)) { // 射线在局部坐标系中求交
// 在距离范围内进一步检测
}
5.4.与 Bnd_Box
(AABB) 的对比
特性 | Bnd_OBB (定向包围盒) | Bnd_Box (轴对齐包围盒) |
---|---|---|
方向敏感性 | 支持任意旋转 | 仅与坐标轴平行 |
包裹紧密度 | 高(自适应物体方向) | 低(对旋转物体冗余大) |
相交检测成本 | 较高(15次投影比较) | 极低(6次比较) |
典型应用 | 刚体碰撞检测、CAD模型精确干涉分析 | 空间索引、快速可视裁剪 |
⚠️ 选型建议:对旋转敏感场景用
Bnd_OBB
;对性能要求高且物体无旋转时用Bnd_Box
总结
Bnd_OBB
是 OpenCASCADE 中旋转物体空间关系的核心解决方案,其设计亮点在于:
- PCA驱动的方向自适应:最大化包裹紧密度;
- 高效的SAT实现:通过15条分离轴保证碰撞检测可靠性;
- AABB退化优化:兼容轴对齐场景的计算效率。
在需要高精度碰撞检测的领域(如CAD装配验证、机器人运动规划),Bnd_OBB
是不可替代的基础工具。若需进一步优化性能,可结合空间索引(如 BVH_Tree
)分层管理OBB。
6.Bnd_BoundSortBox
Bnd_BoundSortBox 是 OpenCASCADE 中用于空间索引和碰撞检测加速的关键类,它通过建立包围盒的空间索引结构,显著提高几何查询效率。以下是对该类的深度源码解析:
6.1.类定义与设计思想(Bnd_BoundSortBox.hxx)
class Bnd_BoundSortBox {
public:
// 构造函数
Bnd_BoundSortBox();
// 初始化方法
void Initialize(const Bnd_Box& CompleteBox, const Handle(Bnd_HArray1OfBox)& SetOfBox);
// 添加单个包围盒
void Add(const Bnd_Box& theBox, const Standard_Integer boxIndex);
// 空间查询
const TColStd_ListOfInteger& Compare(const Bnd_Box& theBox);
const TColStd_ListOfInteger& Compare(const gp_Pnt& thePnt);
// 清空重置
void Dump() const;
void Clear();
private:
// 空间划分结构
Handle(Bnd_BoundSortBox) myGrid; // 子网格(递归结构)
Handle(Bnd_HArray1OfBox) myBoxes; // 存储的包围盒数组
Standard_Integer myAxis; // 当前划分轴(0=X,1=Y,2=Z)
Standard_Real mySplitValue; // 划分平面的坐标值
Bnd_Box myBoundingBox; // 当前节点的包围盒
Standard_Boolean myIsLeaf; // 是否为叶节点
};
核心设计思想:
- 层次空间划分:
- 递归地将空间划分为子网格
- 每个节点存储部分包围盒
- 叶节点存储实际几何元素的包围盒
- 轴对齐二分分割:
- 沿最长轴进行空间分割
- 分割点选择包围盒分布的中值
- 惰性构建:
- 索引结构在首次查询时构建
- 避免不必要的计算开销
6.2.核心方法实现解析(Bnd_BoundSortBox.cxx)
6.2.1. 初始化方法
void Bnd_BoundSortBox::Initialize(const Bnd_Box& CompleteBox, const Handle(Bnd_HArray1OfBox)& SetOfBox) {
Clear();
myBoundingBox = CompleteBox;
myBoxes = SetOfBox;
// 标记为未构建状态
myIsLeaf = Standard_False;
myGrid.Nullify();
}
6.2.2. 添加包围盒
void Bnd_BoundSortBox::Add(const Bnd_Box& theBox, const Standard_Integer boxIndex) {
// 惰性初始化检测
if (myIsLeaf) {
// 叶节点直接存储
myBoxes->SetValue(boxIndex, theBox);
}
else {
// 非叶节点递归添加
if (!myGrid.IsNull()) {
// 选择子网格
if (theBox.CornerMin().Coord(myAxis+1) < mySplitValue) {
myGrid->Add(theBox, boxIndex);
}
else {
myGrid->Add(theBox, boxIndex);
}
}
}
}
6.2.3. 空间查询(包围盒)
const TColStd_ListOfInteger& Bnd_BoundSortBox::Compare(const Bnd_Box& theBox) {
// 惰性构建索引
if (!myIsLeaf && myGrid.IsNull()) {
BuildTree();
}
TColStd_ListOfInteger aResultList;
if (myIsLeaf) {
// 叶节点:暴力检测所有包围盒
for (Standard_Integer i = 1; i <= myBoxes->Length(); i++) {
if (!theBox.IsOut(myBoxes->Value(i))) {
aResultList.Append(i);
}
}
}
else {
// 非叶节点:递归查询子网格
if (theBox.CornerMin().Coord(myAxis+1) < mySplitValue) {
aResultList = myGrid->Compare(theBox);
}
if (theBox.CornerMax().Coord(myAxis+1) >= mySplitValue) {
const TColStd_ListOfInteger& otherList = myGrid->Compare(theBox);
// 合并结果
TColStd_ListIteratorOfListOfInteger it(otherList);
while (it.More()) {
aResultList.Append(it.Value());
it.Next();
}
}
}
return aResultList;
}
6.2.4. 空间查询(点)
const TColStd_ListOfInteger& Bnd_BoundSortBox::Compare(const gp_Pnt& thePnt) {
// 构建点包围盒
Bnd_Box pointBox;
pointBox.Add(thePnt);
return Compare(pointBox);
}
6.2.5. 索引树构建(核心算法)
void Bnd_BoundSortBox::BuildTree() {
// 终止条件:包围盒数量小于阈值
if (myBoxes->Length() <= 5) { // 经验阈值
myIsLeaf = Standard_True;
return;
}
// 确定分割轴(选择最长维度)
gp_Pnt minCorner = myBoundingBox.CornerMin();
gp_Pnt maxCorner = myBoundingBox.CornerMax();
Standard_Real dx = maxCorner.X() - minCorner.X();
Standard_Real dy = maxCorner.Y() - minCorner.Y();
Standard_Real dz = maxCorner.Z() - minCorner.Z();
myAxis = 0; // X轴
if (dy > dx && dy > dz) myAxis = 1; // Y轴
if (dz > dx && dz > dy) myAxis = 2; // Z轴
// 计算分割点(中值)
TColStd_Array1OfReal coords(1, myBoxes->Length());
for (Standard_Integer i = 1; i <= myBoxes->Length(); i++) {
gp_Pnt center = myBoxes->Value(i).Center();
coords(i) = center.Coord(myAxis+1);
}
// 快速选择算法找中值
Standard_Integer k = myBoxes->Length() / 2;
mySplitValue = QuickSelect(coords, k);
// 创建子网格
myGrid = new Bnd_BoundSortBox();
// 划分包围盒到子网格
for (Standard_Integer i = 1; i <= myBoxes->Length(); i++) {
myGrid->Add(myBoxes->Value(i), i);
}
myIsLeaf = Standard_False;
}
6.2.6. 快速选择算法实现
Standard_Real Bnd_BoundSortBox::QuickSelect(TColStd_Array1OfReal& arr, Standard_Integer k) {
Standard_Integer left = arr.Lower();
Standard_Integer right = arr.Upper();
while (left < right) {
Standard_Integer pivotIndex = Partition(arr, left, right);
if (pivotIndex == k) {
return arr(k);
} else if (pivotIndex < k) {
left = pivotIndex + 1;
} else {
right = pivotIndex - 1;
}
}
return arr(k);
}
6.3.设计特点分析
6.3.1. 层次空间划分策略
划分策略 | 实现方式 | 优势 |
---|---|---|
轴选择 | 选择包围盒最长的维度 | 最大化空间分割效率 |
分割点确定 | 包围盒中心坐标的中值 | 保证子树平衡 |
终止条件 | 包围盒数量≤5(可配置) | 平衡构建成本与查询效率 |
6.3.2. 惰性构建机制
6.3.3. 递归查询优化
- 空间剪枝:利用轴对齐分割平面快速排除无关子树
- 结果合并:递归查询多个子树并合并结果
- 列表重用:避免频繁内存分配,复用结果容器
6.4.应用场景示例
6.4.1. 碰撞检测加速
// 创建碰撞检测场景
Bnd_BoundSortBox collisionTree;
collisionTree.Initialize(sceneBoundingBox, objectBoxes);
// 检测对象A的碰撞
Bnd_Box objectABox = GetObjectBox(objectA);
const TColStd_ListOfInteger& collidedObjects = collisionTree.Compare(objectABox);
// 处理碰撞结果
TColStd_ListIteratorOfListOfInteger it(collidedObjects);
while (it.More()) {
if (it.Value() != objectA.Id()) {
HandleCollision(objectA, GetObjectById(it.Value()));
}
it.Next();
}
6.4.2. 点选拾取优化
// 构建选择树
Bnd_BoundSortBox selectionTree;
selectionTree.Initialize(viewFrustum, selectableObjectsBoxes);
// 鼠标点选查询
gp_Pnt mousePosition = GetMouseWorldPos();
const TColStd_ListOfInteger& pickedObjects = selectionTree.Compare(mousePosition);
// 查找最近对象
Standard_Real minDist = RealLast();
Handle(SelectableObject) pickedObj;
TColStd_ListIteratorOfListOfInteger it(pickedObjects);
while (it.More()) {
Handle(SelectableObject) obj = GetObjectById(it.Value());
Standard_Real dist = obj.DistanceTo(mousePosition);
if (dist < minDist) {
minDist = dist;
pickedObj = obj;
}
it.Next();
}
6.4.3. 视锥体裁剪
// 初始化场景树
Bnd_BoundSortBox sceneTree;
sceneTree.Initialize(sceneBBox, meshBoundingBoxes);
// 每帧更新
void RenderFrame() {
Bnd_Box viewFrustum = CalculateViewFrustum();
const TColStd_ListOfInteger& visibleMeshes = sceneTree.Compare(viewFrustum);
// 渲染可见对象
TColStd_ListIteratorOfListOfInteger it(visibleMeshes);
while (it.More()) {
RenderMesh(GetMeshById(it.Value()));
it.Next();
}
}
6.5.高级使用技巧
6.5.1. 动态场景更新
// 增量更新策略
void UpdateDynamicObject(DynamicObject obj) {
// 移除旧包围盒
sceneTree.Remove(obj.GetId());
// 更新包围盒
Bnd_Box newBox = obj.CalculateBoundingBox();
// 添加新包围盒
sceneTree.Add(newBox, obj.GetId());
}
// 每帧批量更新
void UpdateFrame() {
StartUpdateBatch();
for (auto& obj : dynamicObjects) {
if (obj.IsMoved()) {
UpdateDynamicObject(obj);
}
}
EndUpdateBatch(); // 触发索引重建
}
6.5.2. 多级索引优化
// 创建多级索引
Handle(Bnd_BoundSortBox) CreateMultiLevelIndex(const Handle(Bnd_HArray1OfBox)& boxes) {
// 第一级:粗略划分
Handle(Bnd_BoundSortBox) root = new Bnd_BoundSortBox();
root->Initialize(ComputeOverallBox(boxes), boxes);
// 第二级:为每个子树创建精细索引
for (int i = 0; i < root->GetSubtreeCount(); i++) {
Handle(Bnd_BoundSortBox) subtree = root->GetSubtree(i);
if (subtree->BoxCount() > 100) {
subtree->BuildFineGrainedIndex();
}
}
return root;
}
6.6.与替代方案对比
特性 | Bnd_BoundSortBox | BVH_Tree | Bnd_B2d/B3d |
---|---|---|---|
索引类型 | KD-Tree变种 | BVH树 | 简单AABB |
构建成本 | 中 | 高 | 低 |
查询效率 | 高 | 高 | 低 |
动态更新 | 中(支持增量) | 低 | 高 |
内存占用 | 中 | 高 | 极低 |
最佳场景 | 静态/半静态场景 | 渲染加速 | 简单碰撞检测 |
7.Bnd_Range
OpenCASCADE 7.9.0 中的 Bnd_Range
类用于表示一维空间中的闭区间,支持区间运算、周期性检测及几何操作,是几何算法中范围计算的基础工具。以下从设计思想、核心源码实现到应用场景进行深度解析:
7.1.设计思想与数据结构
7.1.1. 区间表示与无效状态
- 数据成员:
myFirst
:区间下限(最小值)myLast
:区间上限(最大值)- 若
myFirst > myLast
,区间为无效状态(IsVoid()
返回True
)。
- 无效状态意义:表示空区间(如交集为空时),通过
SetVoid()
显式设置。
2. 周期性支持
- 关键方法
IsIntersected()
和Split()
支持参数thePeriod
,用于处理周期性区间(如角度区间[0, 2π]
)。
7.2.核心方法源码解析
7.2.1. 区间合并与交集
并集 (Union
)
合并两个区间需满足重叠条件,否则返回 False
:
Standard_Boolean Bnd_Range::Union(const Bnd_Range& theOther) {
if (IsVoid() || theOther.IsVoid()) return Standard_False;
if (myLast < theOther.myFirst || myFirst > theOther.myLast)
return Standard_False; // 区间分离
myFirst = Min(myFirst, theOther.myFirst);
myLast = Max(myLast, theOther.myLast);
return Standard_True;
}
交集 (Common
)
直接取两区间的最大下界和最小上界
void Bnd_Range::Common(const Bnd_Range& theOther) {
if (theOther.IsVoid()) SetVoid();
if (IsVoid()) return;
myFirst = Max(myFirst, theOther.myFirst);
myLast = Min(myLast, theOther.myLast); // 可能导致 myFirst > myLast(无效状态)
}
7.2.2. 周期性相交检测 (IsIntersected
)
检测区间是否与 theVal + k*thePeriod
(k
为整数)相交:
Standard_Integer Bnd_Range::IsIntersected(const Standard_Real theVal, const Standard_Real thePeriod) const
{
if (IsVoid()) return 0;
const Standard_Real aPeriod = Abs(thePeriod);
if (aPeriod <= Precision::RealSmall()) {
// 非周期模式:直接判断点是否在区间内
if ((myFirst - theVal) * (myLast - theVal) <= 0)
return (IsEqual(theVal, myFirst) || IsEqual(theVal, myLast)) ? 2 : 1;
return 0;
}
// 周期模式:计算标准化位置并检测整数边界
const Standard_Real aVal1 = (myFirst - theVal) / aPeriod;
const Standard_Real aVal2 = (myLast - theVal) / aPeriod;
const Standard_Integer aPar1 = Floor(aVal1);
const Standard_Integer aPar2 = Floor(aVal2);
if (aPar1 != aPar2) return 1; // 跨越周期边界
if (IsEqual(aVal1, aPar1)) return 2; // 端点恰好落在周期边界
return 0;
}
7.2.3. 区间分割 (Split
)
按 theVal
分割区间,支持周期性扩展:
void Bnd_Range::Split(const Standard_Real theVal, NCollection_List<Bnd_Range>& theList, const Standard_Real thePeriod) const {
if (IsVoid()) return;
Standard_Real aStart = myFirst;
Standard_Real aEnd = myLast;
// 周期分割:按 theVal + k*thePeriod 生成子区间
while (aStart < aEnd) {
Standard_Real aSplitPoint = theVal;
if (thePeriod > 0) {
aSplitPoint = theVal + thePeriod * Floor((aStart - theVal) / thePeriod);
if (aSplitPoint < aStart) aSplitPoint += thePeriod;
}
if (aSplitPoint >= aEnd) break;
theList.Append(Bnd_Range(aStart, aSplitPoint));
aStart = aSplitPoint;
}
theList.Append(Bnd_Range(aStart, aEnd));
}
示例:[3, 15]
按周期 4
分割为 [3,5]
、[5,9]
、[9,13]
、[13,15]
7.3.关键特性与性能优化
7.3.1. 几何操作
方法 | 功能 | 使用场景 |
---|---|---|
Enlarge(theDelta) | 向两侧扩展区间 | 容差计算 |
Shift(theVal) | 平移区间 | 坐标系变换 |
TrimFrom(theValLower) | 修剪下限(若 theValLower > myLast 则置无效) | 区间裁剪 |
7.3.2. 性能优化
- 惰性求值:无效区间操作直接返回,避免冗余计算。
- 浮点精度控制:使用
Precision::RealSmall()
处理浮点误差3。 - 避免拷贝:
Shift()
和Trim
操作直接修改成员变量。
7.4.应用场景
7.4.1. 参数域管理
- 曲线参数区间
[u_min, u_max]
的合并与裁剪:
Bnd_Range uDomain(0.0, 1.0);
uDomain.Union(Bnd_Range(0.5, 1.5)); // 合并为 [0.0, 1.5]
uDomain.TrimTo(1.2); // 裁剪为 [0.0, 1.2]
7.4.2. 周期性检测
旋转机械中角度区间与周期点的相交性:
Bnd_Range angleRange(30.0, 390.0); // 角度区间
Standard_Integer intersectFlag = angleRange.IsIntersected(0.0, 360.0);
// 返回 1(相交,因 30° 与 360°+30°=390° 覆盖 0°)
7.3.3. 区间分割
纹理坐标的周期映射:
NCollection_List<Bnd_Range> splitRanges;
Bnd_Range texRange(0.0, 3.5);
texRange.Split(1.0, splitRanges, 1.0);
// 分割为 [0,1], [1,2], [2,3], [3,3.5]
8.Bnd_Sphere
Bnd_Sphere 是 OpenCASCADE 中用于表示三维球体包围体的核心类,在光线追踪、碰撞检测和空间索引等场景中提供高效的几何计算。以下是对该类的深度源码解析:
8.1.设计思想与数据结构
8.1.1. 数学表示
class Bnd_Sphere {
private:
gp_XYZ myCenter; // 球心坐标 (x, y, z)
Standard_Real myRadius; // 球体半径
Standard_Real myRadiusSq; // 半径平方(预计算优化)
Standard_Boolean myIsValid; // 有效性标志
};
- 预计算优化:存储半径平方值避免重复计算
- 无效状态:
myRadius < 0
时myIsValid = false
8.1.2. 关键特性
- 紧包围性:对球状几何体有最优包裹效果
- 变换不变性:旋转不改变球体特性
- 高效相交检测:与射线/球体相交检测仅需基本运算
8.2.核心方法源码解析
8.2.1. 构造与初始化
// 默认构造(无效球体)
Bnd_Sphere::Bnd_Sphere(): myCenter(0,0,0), myRadius(-1), myRadiusSq(-1), myIsValid(Standard_False) {}
// 从球心和半径构造
Bnd_Sphere::Bnd_Sphere(const gp_XYZ& theCenter, Standard_Real theRadius): myCenter(theCenter), myRadius(theRadius), myIsValid(Standard_True) {
if (theRadius < 0) {
myIsValid = Standard_False;
return;
}
myRadiusSq = theRadius * theRadius; // 预计算平方值
}
8.2.2. 球体合并(Add)
void Bnd_Sphere::Add(const Bnd_Sphere& theOther) {
if (!theOther.IsValid()) return;
if (!myIsValid) {
// 当前球体无效时直接复制
*this = theOther;
return;
}
// 计算两球心距离
const gp_XYZ aDir = theOther.myCenter - myCenter;
const Standard_Real aDistSq = aDir.SquareModulus();
const Standard_Real aDist = Sqrt(aDistSq);
// 处理包含关系
if (aDist + theOther.myRadius <= myRadius) {
return; // 当前球体已包含目标球体
}
if (aDist + myRadius <= theOther.myRadius) {
*this = theOther; // 目标球体包含当前球体
return;
}
// 计算新球体参数
const Standard_Real aNewRadius = 0.5 * (aDist + myRadius + theOther.myRadius);
const Standard_Real aRatio = (aNewRadius - myRadius) / aDist;
// 更新球心位置
myCenter += aDir * aRatio;
myRadius = aNewRadius;
myRadiusSq = aNewRadius * aNewRadius;
}
数学原理:新球心位于两球心连线上,满足:
新球心 = C1 + (R_new - R1) * (C2 - C1)/|C2-C1|
R_new = (|C1-C2| + R1 + R2)/2
8.2.3. 射线相交检测
Standard_Boolean Bnd_Sphere::RayIntersection(const gp_XYZ& theRayStart, const gp_XYZ& theRayDir,
Standard_Real& theT1, Standard_Real& theT2) const {
if (!myIsValid) return Standard_False;
const gp_XYZ aVec = myCenter - theRayStart;
const Standard_Real aProj = aVec.Dot(theRayDir);
const Standard_Real aDistSq = aVec.SquareModulus() - aProj * aProj;
// 距离平方大于半径平方 => 不相交
if (aDistSq > myRadiusSq)
return Standard_False;
const Standard_Real aDiff = Sqrt(myRadiusSq - aDistSq);
theT1 = aProj - aDiff; // 近交点参数
theT2 = aProj + aDiff; // 远交点参数
return Standard_True;
}
使用几何推导:
- 计算射线起点到球心的向量投影
- 利用勾股定理求交点到投影点的距离
- 返回射线参数t值(交点 = Start + t*Dir)
8.2.4. 球体相交检测
Standard_Boolean Bnd_Sphere::IsOut(const Bnd_Sphere& theOther) const {
if (!myIsValid || !theOther.myIsValid)
return Standard_True;
// 计算球心距离平方
const Standard_Real aDistSq = (myCenter - theOther.myCenter).SquareModulus();
// 比较距离与半径和
const Standard_Real aSumR = myRadius + theOther.myRadius;
return aDistSq > aSumR * aSumR;
}
8.2.5. 几何变换
void Bnd_Sphere::Transform(const gp_Trsf& theTrsf) {
if (!myIsValid) return;
// 变换球心
theTrsf.Transforms(myCenter);
// 处理缩放
const Standard_Real aScale = theTrsf.ScaleFactor();
if (Abs(aScale - 1.0) > Precision::Confusion()) {
myRadius *= Abs(aScale);
myRadiusSq = myRadius * myRadius;
}
}
特性:球体在旋转后不变形,仅需处理平移和缩放
8.3.应用场景
8.3.1. 光线追踪加速
// 构建场景球体层次结构
NCollection_Array1<Bnd_Sphere> sceneSpheres;
// 射线追踪查询
Standard_Boolean FindClosestIntersection(const gp_XYZ& rayStart, const gp_XYZ& rayDir, Standard_Real& hitT) {
Standard_Real minT = RealLast();
for (const auto& sphere : sceneSpheres) {
Standard_Real t1, t2;
if (sphere.RayIntersection(rayStart, rayDir, t1, t2)) {
if (t1 > 0 && t1 < minT) minT = t1;
}
}
hitT = (minT < RealLast()) ? minT : -1;
return (minT < RealLast());
}
8.3.2. 碰撞检测优化
// 球体树结构加速碰撞检测
class SphereTree {
Bnd_Sphere boundingSphere;
std::vector<SphereTree> children;
Standard_Boolean CollideWith(const SphereTree& other) {
if (boundingSphere.IsOut(other.boundingSphere))
return Standard_False;
if (IsLeaf() && other.IsLeaf()) {
return ExactCollisionDetection();
}
// 递归检测子树
for (auto& child : children) {
if (child.CollideWith(other))
return Standard_True;
}
return Standard_False;
}
};
8.3.3. 点云包围球生成
Bnd_Sphere ComputeBoundingSphere(const TColgp_Array1OfPnt& points) {
// 初始球:前两点确定的球
Bnd_Sphere sphere(points.First(), 0.0);
// 逐步扩展算法
for (Standard_Integer i = 2; i <= points.Upper(); ++i) {
const gp_XYZ& point = points.Value(i).XYZ();
// 若点在球外则扩展球体
if (sphere.SquareDistance(point) > sphere.SquareRadius()) {
// 计算新球:包含原球和当前点
const gp_XYZ dir = point - sphere.Center();
const Standard_Real dist = Sqrt(dir.SquareModulus());
const Standard_Real newRadius = 0.5 * (dist + sphere.Radius());
const Standard_Real ratio = (newRadius - sphere.Radius()) / dist;
sphere.SetCenter(sphere.Center() + dir * ratio);
sphere.SetRadius(newRadius);
}
}
return sphere;
}
9.Bnd_Tools
Bnd_Tools 是 OpenCASCADE 中提供包围体计算辅助功能的工具类,包含一系列静态方法用于简化常见几何计算。以下从设计理念、核心方法实现到应用场景进行深度解析:
9.1.设计思想与定位
9.1.1. 工具类特性
- 无状态:所有方法均为静态函数,不存储实例数据
- 功能聚合:集中处理与包围体相关的几何计算
- 算法优化:提供高性能的几何计算实现
9.1.2. 在包围体系中的位置
9.2.核心方法源码解析
9.2.1. 点与包围盒距离计算
Standard_Real Bnd_Tools::Distance(const gp_Pnt& thePoint, const Bnd_Box& theBox) {
if (theBox.IsVoid()) return Precision::Infinite();
Standard_Real aDist = 0.0;
Standard_Real aMin[3], aMax[3];
theBox.Get(aMin[0], aMin[1], aMin[2], aMax[0], aMax[1], aMax[2]);
// 计算各轴距离分量
for (int i = 0; i < 3; ++i) {
Standard_Real coord = thePoint.Coord(i+1);
if (coord < aMin[i]) {
aDist += (aMin[i] - coord) * (aMin[i] - coord);
} else if (coord > aMax[i]) {
aDist += (coord - aMax[i]) * (coord - aMax[i]);
}
}
return Sqrt(aDist);
}
9.2.2. 包围盒合并
void Bnd_Tools::Combine(const Bnd_Box& theBox1, const Bnd_Box& theBox2, Bnd_Box& theResult) {
if (theBox1.IsVoid()) {
theResult = theBox2;
return;
}
if (theBox2.IsVoid()) {
theResult = theBox1;
return;
}
Standard_Real aMin1[3], aMax1[3];
Standard_Real aMin2[3], aMax2[3];
theBox1.Get(aMin1[0], aMin1[1], aMin1[2], aMax1[0], aMax1[1], aMax1[2]);
theBox2.Get(aMin2[0], aMin2[1], aMin2[2], aMax2[0], aMax2[1], aMax2[2]);
// 分量级合并
Standard_Real aResMin[3], aResMax[3];
for (int i = 0; i < 3; ++i) {
aResMin[i] = Min(aMin1[i], aMin2[i]);
aResMax[i] = Max(aMax1[i], aMax2[i]);
}
theResult.SetVoid();
theResult.Update(aResMin[0], aResMin[1], aResMin[2]);
theResult.Update(aResMax[0], aResMax[1], aResMax[2]);
}
9.2.3. OBB快速相交检测
Standard_Boolean Bnd_Tools::OBBIntersection(
const gp_Pnt& theCenter1, const gp_XYZ& theAxes1[3], const Standard_Real theHalfSize1[3],
const gp_Pnt& theCenter2, const gp_XYZ& theAxes2[3], const Standard_Real theHalfSize2[3]) {
// 分离轴定理实现(15条轴)
const Standard_Real aTolerance = Precision::Confusion();
gp_XYZ T = theCenter2.XYZ() - theCenter1.XYZ();
// 1. 检查轴1方向
for (int i = 0; i < 3; ++i) {
Standard_Real r1 = theHalfSize1[i];
Standard_Real r2 = 0;
for (int k = 0; k < 3; ++k) {
r2 += theHalfSize2[k] * Abs(theAxes1[i].Dot(theAxes2[k]));
}
if (Abs(T.Dot(theAxes1[i])) > r1 + r2 + aTolerance)
return Standard_False;
}
// 2. 检查轴2方向(类似代码省略)
// 3. 检查叉积方向(9条轴)
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
gp_XYZ L = theAxes1[i].Crossed(theAxes2[j]);
Standard_Real Llen = L.Modulus();
if (Llen < aTolerance) continue; // 平行轴跳过
L /= Llen; // 归一化
Standard_Real r1 = 0, r2 = 0;
for (int k = 0; k < 3; ++k) {
r1 += theHalfSize1[k] * Abs(L.Dot(theAxes1[k]));
r2 += theHalfSize2[k] * Abs(L.Dot(theAxes2[k]));
}
if (Abs(T.Dot(L)) > r1 + r2 + aTolerance)
return Standard_False;
}
}
return Standard_True;
}
9.2.4. 点集包围球计算
void Bnd_Tools::ComputeBoundingSphere(const TColgp_Array1OfPnt& thePoints, gp_Pnt& theCenter, Standard_Real& theRadius) {
// Ritter算法实现
// 1. 寻找最远点对
gp_Pnt Pmin = thePoints.First();
gp_Pnt Pmax = thePoints.First();
Standard_Real maxDistSq = 0.0;
for (Standard_Integer i = thePoints.Lower(); i <= thePoints.Upper(); ++i) {
for (Standard_Integer j = i+1; j <= thePoints.Upper(); ++j) {
Standard_Real distSq = thePoints(i).SquareDistance(thePoints(j));
if (distSq > maxDistSq) {
maxDistSq = distSq;
Pmin = thePoints(i);
Pmax = thePoints(j);
}
}
}
// 2. 初始球体(直径点对)
theCenter = gp_Pnt((Pmin.XYZ() + Pmax.XYZ()) * 0.5);
theRadius = Sqrt(maxDistSq) * 0.5;
// 3. 扩展包含所有点
for (Standard_Integer i = thePoints.Lower(); i <= thePoints.Upper(); ++i) {
Standard_Real distSq = theCenter.SquareDistance(thePoints(i));
if (distSq > theRadius * theRadius) {
Standard_Real dist = Sqrt(distSq);
Standard_Real newRadius = (theRadius + dist) * 0.5;
Standard_Real k = (newRadius - theRadius) / dist;
theCenter.ChangeCoord() += (thePoints(i).XYZ() - theCenter.XYZ()) * k;
theRadius = newRadius;
}
}
}
9.3.应用场景示例
9.3.1. 碰撞检测加速
Standard_Boolean CheckCollision(const TopoDS_Shape& shape1, const TopoDS_Shape& shape2) {
Bnd_Box box1, box2;
BRepBndLib::Add(shape1, box1);
BRepBndLib::Add(shape2, box2);
// 快速AABB排除
if (Bnd_Tools::Distance(box1, box2) > collisionTolerance)
return Standard_False;
// 精确OBB检测
Bnd_OBB obb1, obb2;
BRepBndLib::AddOBB(shape1, obb1);
BRepBndLib::AddOBB(shape2, obb2);
return Bnd_Tools::OBBIntersection(obb1.Center(), obb1.XDirection(), obb1.YDirection(), obb1.ZDirection(), obb1.HalfExtents(), obb2.Center(), obb2.XDirection(), obb2.YDirection(), obb2.ZDirection(), obb2.HalfExtents());
}
9.3.2. 动态包围体更新
void UpdateDynamicBounds(const TColgp_Array1OfPnt& newPositions, Bnd_Box& boundingBox, Bnd_Sphere& boundingSphere) {
// 增量更新包围盒
Bnd_Box newBox;
for (const auto& point : newPositions) {
newBox.Add(point);
}
Bnd_Tools::Combine(boundingBox, newBox, boundingBox);
// 完全重建包围球
gp_Pnt center;
Standard_Real radius;
Bnd_Tools::ComputeBoundingSphere(newPositions, center, radius);
boundingSphere = Bnd_Sphere(center, radius);
}
9.3.3. 空间查询优化
void FindNearbyObjects(const gp_Pnt& queryPoint, const NCollection_List<Bnd_Box>& objects,
NCollection_List<Standard_Integer>& results) {
for (Standard_Integer i = 0; i < objects.Size(); ++i) {
if (Bnd_Tools::Distance(queryPoint, objects(i)) < searchRadius) {
results.Append(i);
}
}
}
9.4.总结
核心价值
- 性能优化:提供几何计算的底层高效实现
- 代码简化:封装复杂几何算法为简单接口
- 精度保障:集成OpenCASCADE精度控制系统
- 多场景适配:支持从精确计算到近似算法
应用建议
- 实时交互场景:优先使用
OBBIntersection
等快速检测 - 大规模数据处理:结合
ComputeBoundingSphere
近似算法 - 高精度要求:使用
Distance
等精确计算方法