第18章:二次开发实战与综合案例
前 17 章把 OCCT 的“技术栈”一一拆解。本章以一个综合案例把它们串起来:实现一个简化版的“参数化机械零件查看器”,覆盖建模、可视化、数据交换、OCAF、Draw 测试与部署。读者可以把它当作动手项目,逐步扩展为自己的 CAD 应用。
1. 案例需求
- 用 OCCT 构造一个参数化“法兰盘”模型;
- 提供 Qt 视图,可旋转/缩放,支持面拾取;
- 参数(外径、内径、孔数、孔径、厚度)可在 UI 修改;
- 模型可导出 STEP、glTF、STL;
- 用 OCAF 维护参数与撤销/重做;
- 用 Draw 脚本编写自动化回归测试。
2. 工程结构
flange-app/
CMakeLists.txt
src/
main.cpp
MainWindow.cpp/.hpp
OcctView.cpp/.hpp
FlangeBuilder.cpp/.hpp
FlangeDocument.cpp/.hpp
tests/
flange.tcl
resources/
依赖:Qt6、OpenCASCADE 7.8+、CMake 3.22+。
3. 参数化建模(FlangeBuilder)
struct FlangeParams {
double outerDiameter = 120.0;
double innerDiameter = 40.0;
double thickness = 20.0;
int boltCount = 6;
double boltDiameter = 10.0;
double boltCircle = 80.0;
};
TopoDS_Shape FlangeBuilder::Build(const FlangeParams& p) {
gp_Ax2 ax(gp_Pnt(0,0,0), gp_Dir(0,0,1));
TopoDS_Shape body = BRepPrimAPI_MakeCylinder(ax, p.outerDiameter/2, p.thickness).Shape();
TopoDS_Shape hole = BRepPrimAPI_MakeCylinder(ax, p.innerDiameter/2, p.thickness + 2).Shape();
TopoDS_Shape r1 = BRepAlgoAPI_Cut(body, hole);
BRep_Builder bb; TopoDS_Compound bolts; bb.MakeCompound(bolts);
for (int i = 0; i < p.boltCount; ++i) {
double a = 2 * M_PI * i / p.boltCount;
gp_Ax2 axb(gp_Pnt(p.boltCircle/2 * std::cos(a),
p.boltCircle/2 * std::sin(a), -1),
gp_Dir(0,0,1));
bb.Add(bolts, BRepPrimAPI_MakeCylinder(axb, p.boltDiameter/2, p.thickness + 2).Shape());
}
TopoDS_Shape r2 = BRepAlgoAPI_Cut(r1, bolts);
BRepFilletAPI_MakeFillet f(r2);
for (TopExp_Explorer ex(r2, TopAbs_EDGE); ex.More(); ex.Next()) {
gp_Pnt mid; double u1, u2;
BRepAdaptor_Curve c(TopoDS::Edge(ex.Current()));
mid = c.Value(0.5*(c.FirstParameter()+c.LastParameter()));
if (std::abs(mid.Z() - p.thickness) < 1e-3 || std::abs(mid.Z()) < 1e-3) {
f.Add(1.0, TopoDS::Edge(ex.Current()));
}
}
f.Build();
return f.IsDone() ? f.Shape() : r2;
}
要点:
- 解析体优先(
MakeCylinder); - 用 Compound 收集所有螺栓孔,一次
Cut; - 圆角通过几何位置筛选边;
IsDone()检查后退回上一步。
4. OCAF 文档(FlangeDocument)
class FlangeDocument {
public:
void New(); // 创建空文档
void SetParams(const FlangeParams&);
FlangeParams GetParams() const;
void Recompute(); // 重算并写入 NamedShape
void Save(const TCollection_AsciiString& path);
void Open(const TCollection_AsciiString& path);
void Undo(); void Redo();
Handle(TDocStd_Document) Doc() const { return myDoc; }
TDF_Label ResultLabel() const { return myDoc->Main().FindChild(1, true); }
private:
Handle(TDocStd_Document) myDoc;
};
实现要点:
- 用
TDataStd_Real保存每个参数到独立子 label; Recompute中调用BRepAlgoAPI等并TNaming_Builder::Generated(shape)写结果;NewCommand/CommitCommand包裹每次修改;BinXCAF格式保存,便于以后扩展 XCAF 颜色/层。
5. 视图(OcctView)
继承 QWidget:
class OcctView : public QWidget {
public:
OcctView(const Handle(AIS_InteractiveContext)& ctx, QWidget* parent = nullptr);
protected:
QPaintEngine* paintEngine() const override { return nullptr; }
void paintEvent(QPaintEvent*) override { myView->Redraw(); }
void resizeEvent(QResizeEvent*) override { myView->MustBeResized(); }
void mousePressEvent(QMouseEvent*) override;
// ... 鼠标事件
private:
Handle(V3d_View) myView;
Handle(AIS_InteractiveContext) myContext;
};
复用 OCCT 7.5+ 的 AIS_ViewController 来处理鼠标交互(节省大量代码)。
6. 主窗口(MainWindow)
- 左侧
QFormLayout展示参数控件(QDoubleSpinBox 等); - 中间
OcctView; - 顶部菜单:新建、打开、保存、导出 STEP/glTF/STL、撤销、重做;
- 状态栏:拾取面信息、几何统计;
参数变化 → FlangeDocument::SetParams → Recompute → AIS Refresh。
7. 数据交换菜单
void MainWindow::exportStep() {
STEPControl_Writer w;
w.Transfer(myDoc->Result(), STEPControl_AsIs);
w.Write("flange.step");
}
void MainWindow::exportGltf() {
BRepMesh_IncrementalMesh(myDoc->Result(), 0.05, false, 0.3, true);
Handle(TDocStd_Document) tmp;
XCAFApp_Application::GetApplication()->NewDocument("BinXCAF", tmp);
auto st = XCAFDoc_DocumentTool::ShapeTool(tmp->Main());
st->AddShape(myDoc->Result(), false);
RWGltf_CafWriter w("flange.glb", true);
TColStd_IndexedDataMapOfStringString info;
info.Add("Generator", "FlangeApp");
w.Perform(tmp, info, Message_ProgressRange());
}
void MainWindow::exportStl() {
BRepMesh_IncrementalMesh(myDoc->Result(), 0.05);
StlAPI_Writer w; w.ASCIIMode() = false;
w.Write(myDoc->Result(), "flange.stl");
}
8. 拾取与高亮
ctx->Activate(ais, /*FACE=*/4);
// MouseRelease:
ctx->MoveTo(pos.x(), pos.y(), view, true);
ctx->Select(false);
for (ctx->InitSelected(); ctx->MoreSelected(); ctx->NextSelected()) {
TopoDS_Shape s = ctx->SelectedShape();
if (!s.IsNull() && s.ShapeType() == TopAbs_FACE) {
// 显示属性、面积...
GProp_GProps g;
BRepGProp::SurfaceProperties(s, g);
statusBar()->showMessage(QString("Area=%1").arg(g.Mass()));
}
}
9. Draw 回归测试
tests/flange.tcl:
pload ALL
source flange_lib.tcl
flange f 120 40 20 6 10 80
checkprops f -v 169646
checkprops f -s 38800
checkmaxtol f 1e-3
flange_lib.tcl 通过自定义 plugin 暴露 C++ FlangeBuilder::Build,或直接用 Tcl 拼装算法命令。运行:
DRAWEXE -batch -script flange.tcl
CI 中执行返回码即可作为测试通过/失败标识。
10. 性能与优化
- 大量螺栓孔时改用一次 BOP,并
SetRunParallel(true); - 参数变更但拓扑不变时,只更新
Location; - AIS 显示模式按用户视图分级:远视 wireframe,近视 shaded;
- 启用
BRepMesh缓存:参数变化才BRepTools::Clean。
11. 部署
- Linux:CMake
install;用cqtdeployer或自制脚本拷贝 OCCT、Qt、resources/; - Windows:使用
windeployqt+ 手动拷贝TK*.dll; - macOS:
macdeployqt+install_name_tool; - Docker:基于
ubuntu:24.04构建,开放 X 转发或 noVNC 访问; - Web:可把核心算法编译为 WASM,前端用 Three.js 显示导出的 glTF。
12. 扩展方向
- 增加更多零件库(轴承、齿轮、螺栓),每个零件单独的
IBuilder; - 用
TFunction把零件和装配的依赖关系自动求解; - 与 ERP/PLM 集成:把 OCAF
XCAF中的元数据映射到企业系统; - 加入仿真前处理:基于 OCCT 网格生成 SMESH 或 CalculiX 输入;
- 用 LLM 自动从自然语言生成
FlangeParams,再构造模型; - 支持插件机制:用
Plugin_Map加载第三方零件库。
13. 学习反馈循环
完成本案例后,建议按以下循环深耕:
- 在仓库
tests/中找到与你算法相关的 grid,跑一遍熟悉行为; - 在
dox/user_guides/中找到对应章节,对照源码复习; - 在 OCCT bugtracker / 论坛搜索类似问题;
- 写自己的 Draw 命令,做参数扫描;
- 阅读关键算法源码(
BOPAlgo_PaveFiller、ChFi3d_Builder、BRepFill_Pipe); - 把改进/修复贡献回 OCCT。
14. 结语
OCCT 是开源 CAD 内核中最完整的一个:从最底层的数学到最上层的应用框架,几乎所有 CAD 软件需要的能力它都有。本教程带你走过 18 章,从全景到底层,从底层回到综合实战。真正掌握 OCCT 需要在每个章节涉及的源码、文档、测试中反复实践——而它给你的回报是:你具备了构建任何 CAD 应用所需的几何与拓扑基础设施。
愿你在 OCCT 的世界里既能解决工程问题,也能享受几何之美。