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:

  1. 我生成一个方块,长宽高参数是w=10,h=20,d=30;TopoDS_Shape只存最终BRep模型,不会记住w/h/d这几个原始参数。模型做完,参数就丢了,想要修改尺寸只能自己搞std::map存参数,自己维护映射。
  2. 想给方块起名字叫底座、设置红色;BRep底层没有“名字”“颜色”字段,你要自己写容器std::map<TopoDS_Shape, MetaInfo>。
  3. 想要撤销(Undo):用户点撤销回到改尺寸之前;你要自己做快照,保存每一步全部模型数据,逻辑非常复杂。
  4. 保存工程文件:既要存BRep几何,又要存名字、参数、颜色;自己设计文件格式,自己写序列化解析。
  5. 做装配:零件层级、实例引用,自己手写树结构。

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),一套树形数据容器,四个核心类:

  1. TDocStd_Application 应用实例(全局单例)
  2. TDocStd_Document 文档对象(一个.caf工程文件对应一个Document)
  3. TDF_Label 标签:树上面的节点/锚点,相当于文件夹,标签本身不存真实数据
  4. 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代表“新生成的形体”

解读上面代码关键点:

  1. 建模还是BRepPrimAPI_MakeBox,OCAF不代替建模;
  2. boxLab只是标签(文件夹);名字、参数、几何,全部是附加到它上面的不同Attribute;
  3. 几何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(); 全部操作丢弃

规则:

  1. 只要修改Document内部Label/Attribute,必须包NewCommand~CommitCommand;
  2. 提交之后,doc->Undo()就可以撤销这一整批操作;doc->Redo()重做;
  3. 一个事务可以包含很多次标签、属性修改;事务粒度建议对应用户的一次交互操作(比如用户点击“创建立方体按钮”作为一个事务)。

如果你忘记写事务,修改的数据不会进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等属性

⚠️注意:

  1. .caf是OCAF私有格式,其他CAD软件打不开;用于自己程序的工程保存;导出给外部软件要用STEP。
  2. 保存的时候会序列化全部BRep底层数据,不是只存句柄;关闭Application后内存释放,再次Open可以完整恢复所有几何和元数据。
  3. 外部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树两层概念:

  1. Part(零件定义):存放原始几何,只定义一次;
  2. 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标签

需求:

  1. OCAF标签存储方块参数 w=10,h=20;
  2. 用户修改w=30;
  3. 根据新参数重新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高频踩坑清单

  1. 忘记事务 NewCommand / CommitCommand 标签、属性修改不包事务:数据可以改,但是Undo栈不记录,撤销无效。异常要调用AbortCommand()放弃本次事务全部修改。
  2. 混淆普通TDocStd_Application和XCAFApp_Application 要装配、STEP装配读写,必须使用XCAFApp,文档类型是XCAF‑DOC。普通OCAF文档无法存储装配实例。
  3. 直接把TopoDS_Shape绑定Label,忘记TNaming_NamedShape Label不能直接存shape,shape必须放在TNaming_NamedShape属性。
  4. 修改TDataStd_Real参数,期望模型自动更新 OCAF只是存储参数,没有参数化求解引擎,需要业务代码重生成BRep,Replace替换形体。
  5. 混淆Part定义与装配Instance 不要把几何直接创建在Assembly根标签,几何放在Part标签;装配只是引用实例+变换。
  6. 不要长期持有外部裸指针指向TDF_Label、Attribute Document执行Undo/Redo后部分属性对象会失效;需要时从标签FindAttribute重新取。
  7. .caf文件只给自己程序使用,不能交付第三方CAD;对外交换输出STEP。

OCAF/XCAF整体总结

  1. TNaming_NamedShape记录形体,提供Generated / Modified / Delete / Replace,跟踪形体演变,支撑Undo。
  2. OCAF文档可以保存为.caf/.xml,完整保存树形数据+BRep+元数据。
  3. XCAF是OCAF扩展,专门处理装配树、零件实例、颜色、STEP装配读写,工程开发优先选XCAF。
  4. OCAF只负责数据存储;几何建模依旧全部依靠BRepPrimAPI/BRepAlgo;参数变更必须业务代码重生成模型再替换。
  5. 事务是撤销重做的基石,所有修改Document的操作必须包裹事务。

OCAF部分到此结束。 回到原始大纲,下一部分是第九部分工程实战项目,包含:简易零件建模器、Qt+OCCT、批量修复工具。 如果你需要,我继续讲第九部分;如果你OCAF还有概念疑问,可以直接提问。

posted @ 2026-08-24 16:18  Drunker•L  阅读(37)  评论(1)    收藏  举报