6.OCAF
第六部分 OCAF(Open CASCADE Application Framework)第一讲
前置背景:前面我们一直直接操作
TopoDS_Shape,只有纯几何BRep数据。 OCAF是OCCT的应用数据管理框架,不是几何建模器,它不生成模型,专门用来管:几何+参数+名字+颜色+装配+撤销重做+文件持久化保存。 小白极易混淆:OCAF ≠ 建模;建模还是BRepPrimAPI/BRepAlgo那一套,OCAF负责把建模出来的Shape“装起来、管起来、存起来”。
本次第一讲内容:为什么要用OCAF、核心四大概念(Application / Document / Label / Attribute)、简单可运行最小Demo、事务机制Undo/Redo;第二讲再讲TNaming命名形状、XCAF装配、保存加载文件。
1.1 不用OCAF会遇到什么痛点(重点理解)
假设我们写一个小CAD程序,只用TopoDS_Shape:
- 我生成一个方块,长宽高参数是
w=10,h=20,d=30;TopoDS_Shape只存最终BRep模型,不会记住w/h/d这几个原始参数。模型做完,参数就丢了,想要修改尺寸只能自己搞std::map存参数,自己维护映射。 - 想给方块起名字叫
底座、设置红色;BRep底层没有“名字”“颜色”字段,你要自己写容器std::map<TopoDS_Shape, MetaInfo>。 - 想要撤销(Undo):用户点撤销回到改尺寸之前;你要自己做快照,保存每一步全部模型数据,逻辑非常复杂。
- 保存工程文件:既要存BRep几何,又要存名字、参数、颜色;自己设计文件格式,自己写序列化解析。
- 做装配:零件层级、实例引用,自己手写树结构。
OCAF就是OCCT官方把上面这套通用业务全部封装好了,不用你从零造轮子。
| 功能 | 裸写TopoDS(无OCAF) | 使用OCAF |
|---|---|---|
| 几何生成 | 自己调用BRepAPI | 仍然自己调用BRepAPI |
| 参数、名字、颜色管理 | 自己写map容器 | 内置各类Attribute属性挂载到标签 |
| Undo/Redo撤销重做 | 自己实现快照 | 内置事务,开箱即用 |
| 工程文件保存打开 | 自己写序列化 | 二进制/xml直接保存整个Document文档 |
| 装配层级结构 | 手写树结构 | Label树形结构+XCAF工具 |
重要提醒:小工具、一次性脚本,完全可以不用OCAF;只要做交互式CAD软件、需要保存工程、撤销、装配,强烈建议上OCAF。
1.2 OCAF四大核心概念(必须吃透)
OCAF底层模块叫 TDF(The Data Framework),一套树形数据容器,四个核心类:
TDocStd_Application应用实例(全局单例)TDocStd_Document文档对象(一个.caf工程文件对应一个Document)TDF_Label标签:树上面的节点/锚点,相当于文件夹,标签本身不存真实数据TDF_Attribute属性:真正存数据的对象,挂载到Label上面;一个Label可以挂多个不同属性。
通俗比喻
TDocStd_Application:整个软件的大管家,可以打开多个文档。TDocStd_Document:一个完整工程文件,相当于一个磁盘根目录。TDF_Label:目录树里面的文件夹,有唯一路径编号,例如0:1、0:1:2;文件夹本身不存文件。TDF_Attribute:真正的文件,可以是:整数、浮点数、字符串名字、形状引用、颜色。一个文件夹可以放多个文件。
树形结构示意:
Document(文档)
└── Root根标签(0:0)
└── Main主标签(0:1) 【我们业务数据都放在这下面】
├─ Label(0:1:1) 标签:代表零件“底座方块”
│ ├─ TDataStd_Name("底座") //属性:零件名字字符串
│ ├─ TDataStd_Real(w=10) //属性:宽度参数
│ ├─ TDataStd_Real(h=20) //属性:高度参数
│ └─ TNaming_NamedShape(方块TopoDS_Shape) //属性:绑定几何模型
└─ Label(0:1:2) 标签:代表零件“圆柱”
├─ TDataStd_Name("立柱")
└─ TNaming_NamedShape(圆柱shape)
Label只是文件夹,数据全部在Attribute属性。同一个Label可以挂很多种属性。
常用内置标准属性(TDataStd 系列)
| 属性类 | 存储内容 |
|---|---|
TDataStd_Integer |
整数 |
TDataStd_Real |
浮点double |
TDataStd_Name |
字符串名字,零件名称 |
TDataStd_Comment |
备注说明 |
TNaming_NamedShape |
最重要!绑定TopoDS_Shape几何形体,几何就存在这个属性里面 |
TPrsStd_AISPresentation |
显示属性:颜色、透明度、显示/隐藏 |
重点:
TNaming_NamedShape,几何Shape不是直接塞Label,是放到这个Attribute属性上。
1.3 最小可理解Demo(伪+关键代码,看懂逻辑)
功能:创建OCAF文档,新建一个标签,保存方块参数、零件名字、绑定方块Shape。 头文件需要包含:
TDocStd_Application.hxx、TDocStd_Document.hxx、TDataStd_Name.hxx、TDataStd_Real.hxx、TNaming_NamedShape.hxx、BRepPrimAPI_MakeBox.hxx
//1. 全局应用实例,一般单例
Handle(TDocStd_Application) myApp = new TDocStd_Application();
//2. 新建OCAF文档;格式用 "OCC-CAF"
Handle(TDocStd_Document) doc;
myApp->NewDocument("OCC-CAF", doc);
//拿到Main主标签 路径编号0:1,业务数据全部放在Main下面
TDF_Label mainLab = doc->Main();
//新建子标签,mainLab下面,tag=1,路径就是0:1:1,代表我们的方块零件
TDF_Label boxLab = mainLab.NewChild(1);
//===== 在boxLab标签上挂载多个属性 =====
//① 设置零件名字 "底座方块"
TDataStd_Name::Set(boxLab, TCollection_ExtendedString("底座方块"));
//② 挂载尺寸参数 w h d
TDataStd_Real::Set(boxLab, 10.0, TDataStd_Real::GetID());//宽
TDataStd_Real::Set(boxLab, 20.0, TDataStd_Real::GetID());//高
//③ 先生成普通BRep方块(建模依旧用老的BRepPrimAPI)
TopoDS_Shape boxShape = BRepPrimAPI_MakeBox(10,20,30).Shape();
//④ 把生成好的TopoDS_Shape,挂载到标签的TNaming_NamedShape属性
TNaming_Builder builder(boxLab);
builder.Generated(boxShape); //Generated代表“新生成的形体”
解读上面代码关键点:
- 建模还是
BRepPrimAPI_MakeBox,OCAF不代替建模; boxLab只是标签(文件夹);名字、参数、几何,全部是附加到它上面的不同Attribute;- 几何Shape不能直接赋值给Label,要用
TNaming_Builder构建TNaming_NamedShape属性挂载上去。
❌错误认知:
boxLab = boxShape;Label不能直接等于Shape。Shape是放在属性里面。
如何读回标签上的数据
//读取零件名字
Handle(TDataStd_Name) nameAttr;
if(boxLab.FindAttribute(TDataStd_Name::GetID(), nameAttr))
{
TCollection_ExtendedString nameStr = nameAttr->Get();
std::cout<<"零件名称:"<<nameStr.ToUTF8()<<std::endl;
}
//读取挂载的几何Shape
Handle(TNaming_NamedShape) shapeAttr;
if(boxLab.FindAttribute(TNaming_NamedShape::GetID(), shapeAttr))
{
TopoDS_Shape getShape = shapeAttr->Get();
if(!getShape.IsNull())
{
//拿到方块形体,可以拿来布尔、显示
}
}
FindAttribute(GUID, handle):通过属性唯一GUID,从标签上面取出对应的属性对象。- 每一类Attribute都有一个唯一静态
GetID()返回GUID。
1.4 OCAF事务机制(Command / Commit / Abort,Undo/Redo核心)
OCAF所有修改文档数据(新增标签、添加属性、修改属性值),必须包裹在事务里面。 事务:
doc->NewCommand()开启;doc->CommitCommand()提交;出错调用doc->AbortCommand()回滚。 只有Commit之后,这一步操作才进入Undo栈,支持撤销。
上面完整代码加上事务之后:
Handle(TDocStd_Document) doc;
myApp->NewDocument("OCC-CAF", doc);
doc->NewCommand(); //====开启事务====
TDF_Label mainLab = doc->Main();
TDF_Label boxLab = mainLab.NewChild(1);
TDataStd_Name::Set(boxLab, TCollection_ExtendedString("底座方块"));
TopoDS_Shape boxShape = BRepPrimAPI_MakeBox(10,20,30).Shape();
TNaming_Builder builder(boxLab);
builder.Generated(boxShape);
doc->CommitCommand(); //====提交事务,加入撤销栈====
// 如果中间发生异常,不Commit,调用 doc->AbortCommand(); 全部操作丢弃
规则:
- 只要修改Document内部Label/Attribute,必须包
NewCommand~CommitCommand; - 提交之后,
doc->Undo()就可以撤销这一整批操作;doc->Redo()重做; - 一个事务可以包含很多次标签、属性修改;事务粒度建议对应用户的一次交互操作(比如用户点击“创建立方体按钮”作为一个事务)。
如果你忘记写事务,修改的数据不会进Undo栈,撤销功能完全无效,这是新手高频坑。
OCAF 第二讲:TNaming、文件持久化、XCAF装配、更新形体
承接第一讲:Application‑Document‑Label‑Attribute、事务、最小Demo。 本讲重点:TNaming_NamedShape 的四种状态、形体版本跟踪;caf文件读写;XCAF扩展框架;参数修改重生成模型案例;常见坑。
2.1 TNaming_NamedShape 深度理解
TNaming_NamedShape是OCAF里面存放TopoDS_Shape的专属属性。 之前示例写了:
TNaming_Builder builder(boxLab);
builder.Generated(boxShape);
TNaming_Builder用来生成/修改这个属性。它有4种核心动作,代表形体的生命周期:
| 接口 | 含义 | 业务场景 |
|---|---|---|
builder.Generated(shape) |
生成:本事务新创建出来的形体 | 创建立方体、圆柱,新建几何 |
builder.Modified(oldShape, newShape) |
修改:旧形体被修改,输出新形体 | 布尔、圆角,输入旧solid,运算输出新solid |
builder.Delete(oldShape) |
删除:旧形体被移除 | 模型删除某个零件 |
builder.Replace(oldShape,newShape) |
替换:直接覆盖旧形体 | 参数变更,重新生成模型替换旧的 |
OCAF强大之处:TNaming会记录形体演变历史。每一次事务提交,会记录:哪个Shape变成了哪个Shape,Undo的时候可以恢复到旧的几何。
⚠️关键点:
TNaming_NamedShape内部存储的依然是TopoDS_Shape句柄,底层还是BRep_TXXX。 OCAF并不会复制BRep,只是持有句柄引用;Document保存为caf文件的时候才会序列化完整BRep数据。
示例:布尔运算后记录Modified
原始方块boxA,用圆柱做Cut差集,原来的boxA被修改。
doc->NewCommand();
// 旧形体,来自某个label的NamedShape
TopoDS_Shape boxA = ...;
TopoDS_Shape cylinder = ...;
BRepAlgoAPI_Cut cutOp(boxA, cylinder);
cutOp.Build();
TopoDS_Shape result = cutOp.Shape();
// 当前标签记录:旧形体被修改为result
TNaming_Builder builder(targetLabel);
builder.Modified(boxA, result);
doc->CommitCommand();
当执行
doc->Undo(),TNaming可以恢复回boxA。
读取NamedShape历史
Handle(TNaming_NamedShape) nsAttr;
boxLab.FindAttribute(TNaming_NamedShape::GetID(), nsAttr);
TopoDS_Shape currentShape = nsAttr->Get(); // 获取当前最新shape
// nsAttr->GetPrevious() 可以拿到上一版本的shape(回滚历史)
误区:很多人以为Label直接存Shape;记住:Shape永远放在
TNaming_NamedShape属性。
2.2 OCAF 文档持久化:保存与打开 .caf 文件
.caf是OCAF原生工程文件,可以完整保存:树形Label、全部Attribute(名字、参数、颜色、BRep几何、TNaming历史),支持Undo栈一并保存。
保存文档
#include <TDocStd_Document.hxx>
Handle(TDocStd_Document) doc;
// ...前面完成数据构建...
//保存为二进制caf文件
myApp->SaveAs(doc, "model.caf");
//也可以保存xml格式(可读性好,体积更大)
// myApp->SaveAs(doc, "model.xml");
打开已有caf文档
Handle(TDocStd_Document) docLoad;
myApp->Open("model.caf", docLoad);
TDF_Label mainLab = docLoad->Main();
// 之后遍历mainLab子Label,读取Name、NamedShape等属性
⚠️注意:
.caf是OCAF私有格式,其他CAD软件打不开;用于自己程序的工程保存;导出给外部软件要用STEP。- 保存的时候会序列化全部BRep底层数据,不是只存句柄;关闭Application后内存释放,再次Open可以完整恢复所有几何和元数据。
- 外部STEP不能直接读入普通OCAF Document,需要 XCAF。
2.3 XCAF框架(OCAF扩展,装配+STEP/IGS读写)
普通TDocStd_Document只有基础TDF属性,不支持装配树、部件实例、材质,不能直接导入导出STEP带装配信息。 XCAF = Extended CAF,OCAF的扩展模块。
XCAFApp_Application:继承TDocStd_Application,替代原来的myApp;XCAFDoc_DocumentTool:核心工具类,用来处理零件、装配实例、颜色、图层、STEP导入导出。
业务区分:
- TDF/OCAF:单零件简单数据;
- XCAF:装配工程、多零件、实例引用、颜色、STEP互操作,实际项目几乎都用XCAF。
XCAF最小初始化示例
#include <XCAFApp_Application.hxx>
#include <XCAFDoc_DocumentTool.hxx>
// 替换原来的TDocStd_Application,使用XCAF应用
Handle(XCAFApp_Application) xcafApp = XCAFApp_Application::GetApplication();
Handle(TDocStd_Document) xcafDoc;
//文档类型必须是 "XCAF-DOC"
xcafApp->NewDocument("XCAF-DOC", xcafDoc);
//拿到XCAF文档工具句柄
Handle(XCAFDoc_DocumentTool) docTool = XCAFDoc_DocumentTool::DocumentTool(xcafDoc);
// 获取零件根标签、装配根标签
TDF_Label partRoot = docTool->PartTool()->Root();
TDF_Label assemblyRoot = docTool->AssemblyTool()->Root();
XCAF树两层概念:
- Part(零件定义):存放原始几何,只定义一次;
- Assembly Instance(装配实例):引用Part,可以做变换,同一个零件多次实例化(阵列装配)。
XCAF‑Document
├─ Parts(零件库根标签)
│ ├─Part‑Label01:方块定义(TNaming_NamedShape存方块BRep)
│ └─Part‑Label02:圆柱定义
└─Assembly(装配根标签)
└─Assembly‑Label01
├─ Instance1:引用Part‑Label01,带平移变换(20,0,0)
└─ Instance2:引用Part‑Label01,平移(40,0,0) 【同一个零件两次实例】
优势:多个装配实例引用同一份Part几何,BRep只存一份,节省内存,修改Part所有实例同步更新。
XCAF 读取STEP装配文件
#include <STEPCAFControl_Reader.hxx>
STEPCAFControl_Reader stepReader;
stepReader.ReadFile("asm.stp");
//直接读入XCAF文档,保留装配层级、颜色
stepReader.Transfer(xcafDoc);
对比老的
STEPControl_Reader:老接口只会输出一个Compound,丢失装配树;STEPCAFControl_Reader把STEP装配完整导入XCAF的Part‑Instance树形结构。
XCAF导出STEP
#include <STEPCAFControl_Writer.hxx>
STEPCAFControl_Writer stepWriter;
stepWriter.Transfer(xcafDoc, STEPControl_AsIs);
stepWriter.Write("output_asm.stp");
XCAF设置颜色
颜色属性不再是普通TPrsStd,使用XCAF材质工具,可以保存到STEP文件。
Handle(XCAFDoc_ColorTool) colorTool = docTool->ColorTool();
colorTool->SetColor(partLabel, Quantity_NOC_RED, XCAFDoc_ColorGen);
2.4 完整小案例:参数修改,重生成形体更新OCAF标签
需求:
- OCAF标签存储方块参数 w=10,h=20;
- 用户修改w=30;
- 根据新参数重新MakeBox生成新shape,替换标签内旧的NamedShape,Undo可以回退旧方块。
//=== 1.读取旧标签上保存的参数 ===
TDF_Label boxLab; // 已经存在的方块标签
Handle(TDataStd_Real) wAttr, hAttr;
boxLab.FindAttribute(TDataStd_Real::GetID(), wAttr);
boxLab.FindAttribute(TDataStd_Real::GetID(), hAttr);
double oldW = wAttr->Get();
double h = hAttr->Get();
double newW = 30.0; //用户修改后的宽度
//=== 2.开启事务,修改参数+重新生成几何,替换形体 ===
xcafDoc->NewCommand();
//更新标签上存储的参数值
wAttr->Set(newW);
//读取旧的shape
Handle(TNaming_NamedShape) nsOld;
boxLab.FindAttribute(TNaming_NamedShape::GetID(), nsOld);
TopoDS_Shape oldShape = nsOld->Get();
//根据新参数重生成方块(重新调用建模API!)
TopoDS_Shape newShape = BRepPrimAPI_MakeBox(newW, h, 30).Shape();
//替换标签内部几何形体
TNaming_Builder builder(boxLab);
builder.Replace(oldShape, newShape);
xcafDoc->CommitCommand();
//此时 doc->Undo() 可以回到w=10旧方块
⚠️OCAF不会自动根据参数更新几何!! OCAF只是存储参数,修改TDataStd_Real参数本身不会自动重新生成BRep。 业务代码必须自己调用建模API重新生成Shape,再通过
TNaming_Builder替换,这是OCCT/OCAF非常核心的设计。
很多新手幻想:改一下Label上Real属性,模型自动刷新。OCAF做不到,必须业务层重跑建模逻辑。
2.5 OCAF/XCAF高频踩坑清单
- 忘记事务 NewCommand / CommitCommand 标签、属性修改不包事务:数据可以改,但是Undo栈不记录,撤销无效。异常要调用
AbortCommand()放弃本次事务全部修改。 - 混淆普通
TDocStd_Application和XCAFApp_Application要装配、STEP装配读写,必须使用XCAFApp,文档类型是XCAF‑DOC。普通OCAF文档无法存储装配实例。 - 直接把TopoDS_Shape绑定Label,忘记
TNaming_NamedShapeLabel不能直接存shape,shape必须放在TNaming_NamedShape属性。 - 修改TDataStd_Real参数,期望模型自动更新 OCAF只是存储参数,没有参数化求解引擎,需要业务代码重生成BRep,Replace替换形体。
- 混淆Part定义与装配Instance 不要把几何直接创建在Assembly根标签,几何放在Part标签;装配只是引用实例+变换。
- 不要长期持有外部裸指针指向TDF_Label、Attribute Document执行Undo/Redo后部分属性对象会失效;需要时从标签FindAttribute重新取。
.caf文件只给自己程序使用,不能交付第三方CAD;对外交换输出STEP。
OCAF/XCAF整体总结
TNaming_NamedShape记录形体,提供Generated / Modified / Delete / Replace,跟踪形体演变,支撑Undo。- OCAF文档可以保存为
.caf/.xml,完整保存树形数据+BRep+元数据。 - XCAF是OCAF扩展,专门处理装配树、零件实例、颜色、STEP装配读写,工程开发优先选XCAF。
- OCAF只负责数据存储;几何建模依旧全部依靠BRepPrimAPI/BRepAlgo;参数变更必须业务代码重生成模型再替换。
- 事务是撤销重做的基石,所有修改Document的操作必须包裹事务。
OCAF部分到此结束。 回到原始大纲,下一部分是第九部分工程实战项目,包含:简易零件建模器、Qt+OCCT、批量修复工具。 如果你需要,我继续讲第九部分;如果你OCAF还有概念疑问,可以直接提问。

浙公网安备 33010602011771号