当前位置: 首页 > news >正文

【OCCT+ImGUI系列】013-碰撞检测-包围盒Bnd_Box

请添加图片描述

一、引言

接下来增加一个碰撞检测相关的专题,主要讲OpenCASCADE与碰撞检测相关的概念。首先开始的概念是包围盒(Bounding Box):在三维建模、碰撞检测、几何布尔运算等场景中,包围盒是高效处理几何体空间关系的关键数据结构。OpenCASCADE 提供的 Bnd_Box就是这样一个强大的工具,它支持构建、扩展、查询和变换三维空间中的包围盒。

学习完下面内容可以实现:

  • 生成TopoDS_Shape对应的包围盒
  • 使用Gap来调整包围盒
  • 生成空盒子(Void)或整个空间(WholeSpace)
  • 判断两个包围盒是否相交

请添加图片描述
请添加图片描述


二、Bnd_Box 的作用与基本概念

Bnd_Box 表示一个三维轴对齐的包围盒(AABB),即其面总是与坐标轴平行。它可以:

  • 包围几何对象(如点、线、面、体)
  • 判断空间相交或包含关系
  • 用作快速粗略碰撞检测的加速结构

特点包括:

  • 可以是有限盒子
  • 可以在某些方向上是无限延伸的
  • 可以表示空盒子(Void)或整个空间(WholeSpace)
  • 支持**间隙(Gap)**来考虑数值精度误差

三、Bnd_Box 的创建方式

1. 创建空盒子(默认构造)

Bnd_Box box;

创建后默认是 Void 状态,即为空。

2. 通过两点定义最小包围盒

gp_Pnt pmin(0, 0, 0), pmax(10, 10, 10);
Bnd_Box box(pmin, pmax);

用于表示有限区域。


四、设置特殊状态

1. 空盒子(Void)

box.SetVoid();

表示盒子中不包含任何点。

2. 整个空间(WholeSpace)

box.SetWhole();

表示整个空间的无限盒子,任何点都在其内。

3. 单方向开口

box.OpenXmin();
box.OpenZmax();

表示该方向是无限的,可以与 IsOpenXmin() 等方法配合使用。


五、更新与扩展 Bnd_Box

1. 添加点

box.Add(gp_Pnt(1, 2, 3));

将点纳入包围盒,自动扩展边界。

2. 添加方向(射线)

box.Add(gp_Pnt(0,0,0), gp_Dir(1,0,0));  // 添加从原点出发的正X方向射线

将整个方向扩展为无限。

3. 添加其他盒子

box.Add(otherBox);

合并两个盒子。

4. 设置间隙

box.SetGap(0.1);  // 增加 0.1 的容差

所有边界都扩展该值,常用于容错计算。


六、查询与判断

1. 获取边界

Standard_Real xmin, ymin, zmin, xmax, ymax, zmax;
box.Get(xmin, ymin, zmin, xmax, ymax, zmax);

若盒子为空(IsVoid),将抛出异常。

2. 获取角点

gp_Pnt minCorner = box.CornerMin();
gp_Pnt maxCorner = box.CornerMax();

3. 判断状态

  • box.IsVoid()
  • box.IsWhole()
  • box.IsOpen()
  • box.IsXThin(tol), IsThin(tol):判断盒子是否厚度很小(用于简化处理)

4. 距离计算

Standard_Real dist = box.Distance(otherBox);

计算两个盒子的最小间距(非负)。

5. 是否相交(是否在外)

if (box.IsOut(gp_Pnt(5, 5, 5))) { ... }
if (!box.IsOut(otherBox)) { ... } // 有交集

七、几何变换与有限部分提取

1. 应用变换

gp_Trsf trsf;
trsf.SetRotation(gp::OX(), M_PI/4);
Bnd_Box rotatedBox = box.Transformed(trsf);

注意:旋转后会包围整个旋转后的对象,可能导致边界变大。

2. 提取有限部分

Bnd_Box finite = box.FinitePart();

用于从无限盒中提取有限部分,仅当存在有限部分时有效。


八、使用建议与注意事项

  1. Void 状态下不能 Get() 或 CornerMin(),需先判断 IsVoid()
  2. Gap 设定应适度,避免误判碰撞。
  3. 添加射线或方向后必须明确是否仍保持有限,尤其用于 FCL 或距离判断时。
  4. Transformed() 不保持最小体积,谨慎用于碰撞预处理。

九、总结

Bnd_Box 是 OpenCASCADE 中简单而高效的空间管理工具,适用于几乎所有涉及空间范围的模块。从基础的点添加到复杂的变换包围,都可使用统一接口处理,为 CAD 几何计算提供良好支撑。


相关概念拓展阅读

  • AABB-box碰撞检测
  • Bnd_Box官方文档

演示代码

#pragma once#include "pch.h"// OpenCASCADE 几何建模与可视化相关头文件
#include <BRepPrimAPI_MakeBox.hxx>    // 创建立方体几何体
#include <BRepBndLib.hxx>             // 用于构建 Bnd_Box(包围盒)
#include <Bnd_Box.hxx>                // OpenCASCADE 中的轴对齐包围盒类
#include <AIS_Shape.hxx>              // 用于可视化 TopoDS_Shape
#include <AIS_TextLabel.hxx>          // 文本标签,可选
#include <gp_Trsf.hxx>                // 通用几何变换(平移/旋转/缩放等)
#include <gp_Pnt.hxx>                 // 三维点
#include <gp_Ax1.hxx>                 // 三维坐标轴(用于旋转)
#include <Precision.hxx>              // 精度控制常量// 本地模块
#include "BaseScene.h"
#include "VisSceneComponents.h"
#include "TutorialWindow.h"// 示例场景类:演示如何使用 Bnd_Box 构造包围盒并进行交互
class BndBox013 : public BaseScene, public VisSceneComponents, public TutorialWindow {
public:BndBox013() {// 构造函数:打开教程窗口(ImGui)openTutorialWindow();}// 渲染场景主入口函数void displayScene(const Handle(V3d_View)& view, const Handle(AIS_InteractiveContext)& context) override {// 如果场景尚未初始化,则进行初始化if (!bIsSceneInit) {sceneInit(view, context);bIsSceneInit = true;}// 渲染右侧 ImGui 教程交互窗口renderTutorialWindow(context);}// 教程窗口初始化(无内容)void customInitTutorialWindow(const Handle(AIS_InteractiveContext)& context) override {}// 场景初始化函数:构造两个立方体及其可视化对象,计算包围盒并可视化void sceneInit(const Handle(V3d_View)&, const Handle(AIS_InteractiveContext)& context) override {// 创建两个立方体几何体box1 = BRepPrimAPI_MakeBox(100, 100, 100).Shape();box2 = BRepPrimAPI_MakeBox(100, 100, 100).Shape();// 对 box2 施加平移变换(初始为沿 X 正方向移动 150)trsf2.SetTranslation(gp_Vec(150, 0, 0));box2.Move(TopLoc_Location(trsf2));  // 注意:这会修改原始 box2// 创建并设置可视化对象 aisBox1aisBox1 = new AIS_Shape(box1);aisBox1->SetColor(Quantity_NOC_BLUE1);context->Display(aisBox1, Standard_False);// 创建并设置可视化对象 aisBox2aisBox2 = new AIS_Shape(box2);aisBox2->SetColor(Quantity_NOC_GREEN);context->Display(aisBox2, Standard_False);// 初始化包围盒updateBndBoxes();// 显示包围盒(以彩色线框表示)displayBndBox(context, bndBox1, Quantity_NOC_CYAN1, box1BBox);displayBndBox(context, bndBox2, Quantity_NOC_ORANGE1, box2BBox);}// 教程窗口内容:交互式操作 ImGui 面板void renderTutorialContent(const Handle(AIS_InteractiveContext)& context) override {ImGui::Text("Bnd_Box");// 控制 box2 的平移操作if (ImGui::SliderFloat3("Move Box2 (XYZ)", box2Offset, -200.0f, 200.0f)) {trsf2.SetTranslation(gp_Vec(box2Offset[0], box2Offset[1], box2Offset[2]));box2Moved = box2.Located(TopLoc_Location(trsf2));  // 使用 Located 不修改原始几何体aisBox2->SetShape(box2Moved);context->Redisplay(aisBox2, Standard_False);updateBndBoxes();        // 更新包围盒数据updateBndAIS(context);   // 重新可视化包围盒}ImGui::Separator();// 将 bndBox1 扩大 10 个单位if (ImGui::Button("Enlarge Box1 by 10")) {bndBox1.Enlarge(10.0);updateBndAIS(context);}// 设置 Box1 的 Gap 值(间隙),用于逻辑扩展static float gapVal = 0.0f;if (ImGui::SliderFloat("Set Gap for Box1", &gapVal, 0.0f, 20.0f)) {bndBox1.SetGap(gapVal);updateBndAIS(context);}// 设置 Box1 为 Void 状态(空包围盒)if (ImGui::Button("Set Box1 as Void")) {bndBox1.SetVoid();updateBndAIS(context);}ImGui::SameLine();// 设置 Box1 为 WholeSpace(最大包围空间)if (ImGui::Button("Set Box1 as WholeSpace")) {bndBox1.SetWhole();updateBndAIS(context);}ImGui::Separator();// 实时显示 bndBox1 的状态信息ImGui::Text("Box1 Gap: %.2f", bndBox1.GetGap());ImGui::Text("IsVoid: %s | IsWhole: %s", bndBox1.IsVoid() ? "True" : "False", bndBox1.IsWhole() ? "True" : "False");ImGui::Text("OpenXmin: %s | OpenXmax: %s", bndBox1.IsOpenXmin() ? "True" : "False", bndBox1.IsOpenXmax() ? "True" : "False");// 检测两个包围盒是否发生交集static bool showCollisionWindow = false;bool intersect = !bndBox1.IsOut(bndBox2);ImGui::Separator();ImGui::Text("Intersection Result: %s", intersect ? "Intersecting" : "No Intersection");// 如果发生交集,显示碰撞窗口if (intersect && !showCollisionWindow) {showCollisionWindow = true;}// 漂浮的非模态窗口,提示碰撞if (showCollisionWindow) {ImGui::SetNextWindowSize(ImVec2(300, 100), ImGuiCond_FirstUseEver);ImGui::SetNextWindowPos(ImVec2(100, 100), ImGuiCond_FirstUseEver);ImGui::Begin("Collision Warning", &showCollisionWindow, ImGuiWindowFlags_AlwaysAutoResize);ImGui::Text("Collision detected!");if (ImGui::Button("Close") || !intersect) {showCollisionWindow = false;}ImGui::End();}}private:// 几何体与可视化对象TopoDS_Shape box1, box2, box2Moved;Handle(AIS_Shape) aisBox1, aisBox2;       // 原始几何的可视化Handle(AIS_Shape) box1BBox, box2BBox;     // 包围盒的可视化Handle(AIS_Shape) rotatedBBox;            // 预留旋转用包围盒(暂未用)gp_Trsf trsf2;        // box2 的几何变换float box2Offset[3] = { 150.0f, 0.0f, 0.0f };  // Box2 的初始偏移量Bnd_Box bndBox1, bndBox2;  // 两个几何体的包围盒(用于交集判断等)// 更新两个包围盒:将 box1 和 box2(已变换)添加到 bndBox 中void updateBndBoxes() {bndBox1.SetVoid();  // 重置包围盒bndBox2.SetVoid();BRepBndLib::Add(box1, bndBox1);BRepBndLib::Add(box2Moved.IsNull() ? box2 : box2Moved, bndBox2);}// 可视化包围盒:使用透明线框盒子表示 Bnd_Boxvoid displayBndBox(const Handle(AIS_InteractiveContext)& context, const Bnd_Box& bbox,Quantity_NameOfColor color, Handle(AIS_Shape)& bboxShapeOut) {// 如果为空或无穷大,则不显示if (bbox.IsVoid() || !bbox.HasFinitePart()) return;// 获取包围盒的最小/最大角点gp_Pnt pmin = bbox.CornerMin();gp_Pnt pmax = bbox.CornerMax();// 构造包围盒的形状TopoDS_Shape shape = BRepPrimAPI_MakeBox(pmin, pmax).Shape();// 构造 AIS 可视化对象bboxShapeOut = new AIS_Shape(shape);bboxShapeOut->SetColor(color);bboxShapeOut->SetDisplayMode(AIS_WireFrame);  // 显示为线框context->Display(bboxShapeOut, Standard_False);}// 更新包围盒的 AIS 显示(用于动态变化后刷新显示)void updateBndAIS(const Handle(AIS_InteractiveContext)& context) {context->Remove(box1BBox, Standard_False);context->Remove(box2BBox, Standard_False);context->Remove(rotatedBBox, Standard_False);displayBndBox(context, bndBox1, Quantity_NOC_CYAN1, box1BBox);displayBndBox(context, bndBox2, Quantity_NOC_ORANGE1, box2BBox);}
};
http://www.dtcms.com/a/320224.html

相关文章:

  • 【入门级-C++程序设计:9、函数与递归-函数定义与调用、形参与实参】
  • RESTful 服务概述:从理念到实践的全面解析
  • Coze开放平台综合文档指南
  • 达梦包含OR条件的SQL特定优化----INJECT-HINT优化方法
  • 最新完整内、外期货量化交易系统C#源码可售
  • 【C#补全计划:类和对象(九)】接口
  • redis--黑马点评--用户签到模块详解
  • dubbo源码之编解码逻辑
  • 一场 Dark Theme A/B 测试的复盘与提效实践
  • 聚集索引VS非聚集索引:核心差异详解
  • rebase 和pull的通俗区别是什么
  • 一个基于固定 IP地址查询天气的 C 语言程序,通过调用第三方天气 API:
  • React 多语言(i18n)方案全面指南
  • 计算机英语详细总结
  • 本地化密码恢复工具的技术实现与应用边界
  • RabbitMQ面试精讲 Day 13:HAProxy与负载均衡配置
  • Git `cherry-pick` 工具汇总
  • Docker 加载镜像时出现 “no space left on device” 错误的解决方法
  • Java Lambda表达式:简洁高效的函数式编程
  • 关于光猫研究
  • 【代码随想录day 14】 力扣 101. 对称二叉树
  • 技法笔记3 | 验证交互式shell连接
  • LocalSqueeze(图片压缩工具) v1.0.4 压缩
  • 美图复现|Science:添加显著性的GO富集分析美图
  • Nuxt 4.0 完全指南:Nitro 引擎升级与 Composable API 深度解析
  • 关于Android studio调试功能使用
  • 如何选择适合中小企业的OA系统?XKOA低成本高定制化方案详解
  • 数据可视化Matplotlib
  • 【AI智能编程】Cursor IDE工具学习
  • P1037 [NOIP 2002 普及组] 产生数