TMath模块:Bnd系列(包围盒)

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.设计局限与应对策略

  1. 旋转不敏感
    AABB 对旋转物体包裹性差 → 需改用 Bnd_OBB(定向包围盒)。
  2. 合并膨胀问题
    多次 Add 操作可能导致包围盒过度膨胀 → 结合 R树/四叉树 分层管理包围盒。
  3. 精度依赖
    浮点计算可能引入误差 → 使用容差参数(如 Enlarge(1e-5)

总结

Bnd_B2d中心-半对角线模型 为核心,通过极简的数学表达实现了高效的二维 AABB 操作。其优势在于:

  1. 计算高效:点添加与相交检测均达到 O(1) 时间复杂度;
  2. 内存紧凑:仅存储 4 个浮点值(中心 + 半对角线);
  3. 接口正交:方法设计符合几何直觉(如 AddIsOut)。

在需要快速空间筛选的场景(如 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.设计局限与应对

  1. 旋转不适应性
    • 问题:对旋转物体包裹性差,导致冗余空间。
    • 方案:改用 Bnd_OBB(基于 PCA 计算主方向)。
  2. 合并操作的膨胀问题
    • 问题:多次 Add() 可能导致包围盒过度膨胀。
    • 方案:结合 BVH_Tree 分层管理子包围盒。
  3. 浮点精度误差
    • 问题:极端尺度下计算误差累积。
    • 方案:调用 Enlarge(Tol) 扩展容差(如 Tol=1e-5

总结

Bnd_B3d 是 OpenCASCADE 中高效三维 AABB 的现代实现,其核心价值在于:

  1. 计算极简:中心-半对角线模型使点添加、相交检测均达 O(1) 复杂度;
  2. 内存紧凑:仅存储 6 个浮点数,优于传统 Bnd_Box 的冗余标志位;
  3. 开放边界原生支持:完美处理无限空间场景。

大规模空间检索(如 CAD 装配体碰撞检测)、实时渲染裁剪 等场景中,Bnd_B3d 是性能与通用性兼顾的首选。若需处理旋转物体或高精度碰撞,可结合 Bnd_OBBBVH_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; // 状态标志位
};

关键数据结构解析:

  1. myMin 和 myMax
    • 存储包围盒的角点坐标
    • 使用 gp_Pnt2d 类型(二维点)
  2. 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_Box2dBnd_B2dBnd_Box (3D)
维度2D2D3D
开放边界✅ 支持❌ 不支持✅ 支持
无限空间✅ 支持❌ 不支持✅ 支持
状态标志✅ 完整状态机❌ 仅无效状态✅ 完整状态机
内存占用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.设计局限与解决方案

  1. 旋转不敏感问题

    • 问题:对旋转物体包裹性差
    • 解决方案:使用更高级的 Bnd_OBB(定向包围盒)
  2. 多次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 中二维空间计算的核心组件,其主要特点包括:

  1. 灵活的空间表示:支持有限空间、开放边界和无限空间
  2. 高效的状态管理:通过标志位优化计算路径
  3. 精心的精度控制:集成 OpenCASCADE 精度管理系统
  4. 丰富的空间关系检测:提供完整的点/包围盒位置判断

在二维 CAD 绘图、视图裁剪、碰撞检测等场景中,Bnd_Box2d 提供了高效可靠的空间计算基础。与 Bnd_B2d 相比,它更适合需要处理开放边界和无限空间的复杂场景,而 Bnd_B2d 则在轻量级计算中更有优势。

对于性能要求极高的场景,建议:

  1. 优先检查包围盒状态(IsWhole/IsVoid)
  2. 批量处理点添加操作
  3. 使用 Enlarge() 替代多次添加
  4. 在适当时机重置包围盒(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; // 状态标志位
};

关键数据结构解析:

  1. myMin 和 myMax
    • 存储包围盒的角点坐标
    • 使用 gp_Pnt 类型(三维点)
  2. myGap
    • 包围盒的扩展值,用于在原有包围盒的基础上扩大一个固定值
    • 默认值为0.0
  3. 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 通过三个正交轴(XDirectionYDirectionZDirection)定义局部坐标系,结合中心点和半尺寸描述一个旋转的立方体:

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 中旋转物体空间关系的核心解决方案,其设计亮点在于:

  1. PCA驱动的方向自适应:最大化包裹紧密度;
  2. 高效的SAT实现:通过15条分离轴保证碰撞检测可靠性;
  3. 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;        // 是否为叶节点
};
核心设计思想:
  1. 层次空间划分
    • 递归地将空间划分为子网格
    • 每个节点存储部分包围盒
    • 叶节点存储实际几何元素的包围盒
  2. 轴对齐二分分割
    • 沿最长轴进行空间分割
    • 分割点选择包围盒分布的中值
  3. 惰性构建
    • 索引结构在首次查询时构建
    • 避免不必要的计算开销

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_BoundSortBoxBVH_TreeBnd_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*thePeriodk 为整数)相交:

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 < 0myIsValid = 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.总结

核心价值

  1. 性能优化:提供几何计算的底层高效实现
  2. 代码简化:封装复杂几何算法为简单接口
  3. 精度保障:集成OpenCASCADE精度控制系统
  4. 多场景适配:支持从精确计算到近似算法

应用建议

  • 实时交互场景:优先使用OBBIntersection等快速检测
  • 大规模数据处理:结合ComputeBoundingSphere近似算法
  • 高精度要求:使用Distance等精确计算方法
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

raindayinrain

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值