CATIA V5 CAA 特征选择代理机 使用指南
作者:CATIA开发者指南
发布时间:2026年6月8日 15:21
原文链接:https://mp.weixin.qq.com/s?__biz=MzkxNTQ0MTY0NQ==&mid=2247486514&idx=1&sn=112d465d1cec62ae5d012733c8aaed80&chksm=c15e5eb0f629d7a6b7848cf5e89c0b58ad132cdab27c83c1b36562e6f99e8ea6d1bc384208af&cur_album_id=2800898825187262468&scene=189#wechat_redirect
前面几篇文章已经讲过 CATStateCommand、常用 dialog agent、自定义特征和 CATOtherDocumentAgent。本文单独展开机械建模中最容易混淆的一组选择代理机:CATFeatureAgent 和 CATFeatureImportAgent。
它们都属于 MechanicalModelerUI framework,都用于机械上下文中的选择,也都继承自 CATPathElementAgent。但二者解决的问题并不一样:
CATFeatureAgent 负责“在机械上下文里选择并解释对象”。
CATFeatureImportAgent 负责“选择外部 Part 几何,并把它导入目标 Part 形成外部引用”。
如果只把它们看成“更高级的 CATPathElementAgent”,很容易在装配上下文、BRep featurization 和外部引用持久化上踩坑。
1. 先看继承和位置
在 dialog agent 体系里,它们的位置可以简化成:
CATCommand
-> CATDialogAgent
-> CATAcquisitionAgent
-> CATPathElementAgent
-> CATFeatureAgent
-> CATFeatureImportAgent
这意味着它们首先仍然是选择 agent:
- 可以添加到
CATDialogState; - 可以用
IsOutputSetCondition触发 transition; - 单选结果仍然通过
GetValue()读取CATPathElement; - 多选结果仍然通过
GetListOfValues()读取CATSO; - 类型过滤仍然基于接口,例如
AddElementType(IID_xxx)。
但它们比普通 CATPathElementAgent 多了一层机械建模语义:
- 能处理 mechanical feature、face、edge、BRep 等选择;
- 能控制是否把选择对象 featurize 成 BRep feature;
- 能理解 Product 装配上下文中的当前 component;
- 能把 Visu mode 文档在选择时自动切换到 Design mode;
- 默认 tooltip 和用户选择过滤行为更贴近机械建模命令。
官方文档还明确说明:这两个类应该直接使用,不建议派生。需要扩展过滤逻辑时,优先使用类型过滤、ordered type list、SetFilter 或状态机 action 中的业务检查。
2. 快速差异表
| 对比点 | CATFeatureAgent | CATFeatureImportAgent |
|---|---|---|
| 继承关系 | 继承 CATPathElementAgent | 继承 CATFeatureAgent |
| 核心用途 | 机械上下文选择 feature、BRep、拓扑子元素 | 选择其它 Part 几何并导入目标 Part |
| 是否导入外部几何 | 不导入,只返回选择路径或 featurized 对象 | 会导入,导入对象聚合在目标 Part 的 External References 下 |
| 装配外部选择 | 默认禁止,可用 CATFAgAllowSelectionInContext 放开 | 不能像 CATFeatureAgent 那样禁止外部 component 选择 |
| SetActiveObjectPath 含义 | 指定当前/目标 component,默认行为下只允许在该路径对应文档内选择 | 指定要把外部选择导入到哪个 Part |
| GetValue() 语义 | 返回选择路径 | 返回选择路径;发生导入时返回导入后对象的路径 |
| 类型检查 | 选择时按接口过滤 | 可选择是否在导入后再按接口检查 |
| 典型风险 | 临时 BRep feature 没有保存或删除 | 把外部引用导入到错误 Part,或忘记处理 Keep Link 设置 |
一句话选型:
需要机械上下文选择,用 CATFeatureAgent。
需要把外部 Part 几何导入目标 Part,用 CATFeatureImportAgent。
3. CATFeatureAgent 解决什么问题
普通 CATPathElementAgent 能选对象,但它并不主动处理机械建模里这些问题:
- 用户点到的是 face、edge、vertex 还是某个 mechanical feature;
- 这个拓扑选择是否需要转换成稳定可引用的 BRep feature;
- 当前命令运行在 Product 中时,什么叫“当前 Part”;
- 用户是否允许选到当前 component 以外的几何;
- 选择路径的 leaf 是否已经变成 featurized 的 BRep feature。
CATFeatureAgent 的职责就是在 CATPathElementAgent 之上补上这些机械语义。
典型场景:
| 场景 | 推荐用法 |
|---|---|
| Part Design 命令中选择一个支撑面 | CATFeatureAgent + 面/BRep 接口过滤 |
| GSD 命令中选择 wire、edge、surface | CATFeatureAgent,按需要设置 featurization mode |
| 自定义特征需要保存一个拓扑输入 | 只有需要稳定 BRep 引用时才启用 featurization |
| Product 环境中只允许选当前 Part | 默认行为 + SetActiveObjectPath |
| Product 环境中允许选其它 component | 设置 CATFAgAllowSelectionInContext,再用 ElementInContext 判断来源 |
构造函数的主要参数如下:
CATFeatureAgent(
const CATString& iId,
CATClassId iType = NULL,
CATDlgEngBehavior iBehavior = NULL,
CATMfFeaturizeMode iMode = 0,
const CATBaseUnknown_var& iSupport = NULL_var,
CATFeatureAgentBehavior iAgBehavior = NULL);
参数可以这样记:
| 参数 | 含义 |
|---|---|
| iId | agent 标识,也用于资源文件中的提示、undo/redo 标题等 |
| iType | 期望选择对象实现的接口;为 NULL 时不在构造阶段限制 |
| iBehavior | dialog engine 行为位,例如 CATDlgEngWithPSOHSO、CATDlgEngWithPrevaluation |
| iMode | BRep featurization 模式,默认 MfNoFeaturization |
| iSupport | featurized 对象的支撑 feature;某些 mode 需要提供 |
| iAgBehavior | CATFeatureAgent 自己的行为位 |
如果不显式传 iBehavior,它有两个默认行为和普通 CATPathElementAgent 相反:默认带 tooltip,默认启用用户选择过滤。
4. Featurization 不要默认打开
CATFeatureAgent 最重要也最危险的能力是 featurization。
在 CAA 机械建模里,用户点到的拓扑对象有时不能直接作为特征输入长期保存。为了让后续更新、命名和上下文关系更稳定,系统可以把选中的几何转换成一个 BRep feature。这个 BRep feature 至少实现 CATIMfBRep。
常见 mode 来自 CATMfBRepDefs.h:
| mode | 含义 |
|---|---|
| MfNoFeaturization | 不创建 BRep feature |
| MfFunctionalFeaturization | 创建 functional BRep feature |
| MfRelimitedFeaturization | 创建 relimited BRep feature |
| MfBorderFeaturization | 创建 border BRep feature |
| MfWireFeaturization | 创建 wire BRep feature |
| MfPermanentBody | BRep feature 使用 permanent body |
| MfTemporaryBody | BRep feature 使用 temporary body |
| MfSelectingFeatureSupport | 以 selecting feature 作为 support |
| MfLastFeatureSupport | 以 last feature 作为 support |
文档里有一句非常关键:featurization 只在确实需要时使用。原因是,一旦创建了 BRep feature,你只有两个正确选择:
- 保留它:用
CATIDescendants等机制把它聚合到合适的 feature 下,让系统管理自动删除。 - 删除它:用
CATICutAndPastable删除,不要把临时对象留在模型里。
判断当前选择是否被 featurize:
CATPathElement *path = _featureAgent->GetValue();
if (NULL != path && TRUE == _featureAgent->FeaturizedElement(path))
{
CATBaseUnknown *brepFeature = _featureAgent->GetElementValue(path);
// brepFeature should implement CATIMfBRep.
}
选择建议:
只需要知道用户选了哪个 feature,不要 featurize。
需要把 face/edge 等拓扑输入作为稳定机械引用保存,再考虑 featurize。
5. 装配上下文和 SetActiveObjectPath
CATFeatureAgent 在 Product 装配上下文中最容易让人误会。
默认行为下,当当前文档是 Product 时,CATFeatureAgent 禁止用户选择当前 component 外部的几何。这里的“当前 component”默认来自当前 UI active object,也可以用 SetActiveObjectPath 改成命令指定的路径:
_featureAgent->SetActiveObjectPath(targetPartPath);
默认行为下,这表示:
只允许用户在 targetPartPath 对应的 Part 文档中选择。
如果设置了 CATFAgAllowSelectionInContext:
_featureAgent->SetAgentBehavior(
MfNoFeaturization,
NULL_var,
CATFAgAllowSelectionInContext);
此时含义就变了:
允许用户在装配上下文中选择其它 component 的几何。
SetActiveObjectPath 不再起“限制选择范围”的作用。
可以用 ElementInContext 判断选择是否发生在当前 Part 外部:
CATPathElement *path = _featureAgent->GetValue();
int inContext = _featureAgent->ElementInContext(path);
if (0 == inContext)
{
// Selected element is in the current Part.
}
elseif (1 == inContext)
{
// Selected element is in another Part.
}
这个判断只告诉你“选在哪里”,不等于已经建立外部引用。如果要把外部 Part 的几何保存为当前 Part 的引用,应该看 CATFeatureImportAgent。
6. CATFeatureImportAgent 解决什么问题
CATFeatureImportAgent 继承自 CATFeatureAgent,但它的关键词不是“选择”,而是“导入”。
在 Product 环境中,用户可能需要在另一个 Part 上选择几何,然后当前命令把这个几何作为目标 Part 的输入。普通 CATFeatureAgent 即使允许外部选择,也只是让你拿到选择路径;它不负责把外部对象变成目标 Part 里的 External Reference。
CATFeatureImportAgent 会做这件事:
用户在其它 Part 中选择几何
-> agent 把选择导入 SetActiveObjectPath 指定的目标 Part
-> 导入对象聚合到目标 Part 的 External References feature 下
-> GetValue 返回导入后对象的路径
构造函数如下:
CATFeatureImportAgent(
const CATString& iId,
CATClassId iType = NULL,
CATDlgEngBehavior iBehavior = NULL,
CATMfFeaturizeMode iMode = NULL,
const CATBaseUnknown_var& iSupport = NULL_var,
CATImportAgentBehavior iAgBehavior = NULL);
和 CATFeatureAgent 很像,但 iAgBehavior 类型换成了 CATImportAgentBehavior。最常用的 import 行为位是:
| 行为位 | 作用 |
|---|---|
| CATFIAgCheckTypesAfterImport | 在装配上下文中,导入完成后再按 AddElementType 或 SetOrderedTypeList 的接口类型检查导入结果 |
SetActiveObjectPath 对它的含义非常具体:
这个 path 指向要接收导入对象的 Part。
最小示意:
_importAgent = newCATFeatureImportAgent(
"ExternalSupportId",
IID_CATIMfBRep,
CATDlgEngWithPSOHSO | CATDlgEngWithPrevaluation,
MfNoFeaturization);
_importAgent->SetActiveObjectPath(targetPartPath);
导入发生后:
CATPathElement *importedPath = _importAgent->GetValue();
CATBaseUnknown *importedObject = _importAgent->GetElementValue(importedPath);
如果需要追踪后续机械导入信息,可以设置 applicative id:
GUID importId = /* generated by uuidgen -s or application code */;
HRESULT hr = _importAgent->SetImportApplicativeId(importId);
文档里特别说明:这个 id 只在 Part Infrastructure 选项中启用 Keep link 时适用。后续可以通过 CATIMmiMechanicalImportApplicative 检索相关机械导入信息。
7. 典型场景怎么选
可以按下面几种场景直接套:
| 场景 | 选择 |
|---|---|
| 在当前 Part 中选择一个 sketch、body、plane 或 feature | CATPathElementAgent 或 CATFeatureAgent,看是否需要机械上下文处理 |
| 在当前 Part 中选择 face/edge,并作为机械特征输入 | CATFeatureAgent |
| 在 Product 中只允许选当前编辑 Part 内部几何 | CATFeatureAgent 默认行为 + SetActiveObjectPath |
| 在 Product 中允许选其它 component,但只做临时参考 | CATFeatureAgent + CATFAgAllowSelectionInContext |
| 在 Product 中选其它 Part 几何,并保存为当前 Part 外部引用 | CATFeatureImportAgent |
| 在另一个已打开文档窗口中交互选择,但不负责导入 | CATOtherDocumentAgent |
| 只是选普通对象,不涉及机械 feature/BRep/装配上下文 | CATPathElementAgent |
这里最容易混的是 CATOtherDocumentAgent 和 CATFeatureImportAgent:
CATOtherDocumentAgent 解决“到其它已打开文档窗口里怎么点对象”。
CATFeatureImportAgent 解决“装配上下文中外部 Part 几何怎么导入当前 Part”。
它们不是替代关系。一个偏交互窗口代理,一个偏机械外部引用导入。
8. 最小 CATFeatureAgent 模板
下面是一个状态命令中选择机械对象的最小片段。真实项目中 IID_CATIExpectedInterface 应替换成你实际期望的接口。
头文件保存 agent 指针:
#include"CATStateCommand.h"
classCATFeatureAgent;
classMySelectFeatureCmd : public CATStateCommand
{
CmdDeclareResource(MySelectFeatureCmd, CATStateCommand);
public:
MySelectFeatureCmd();
virtual ~MySelectFeatureCmd();
virtualvoidBuildGraph();
private:
CATBoolean OnFeatureSelected(void *iData);
private:
CATFeatureAgent *_featureAgent;
};
构造、析构和状态图:
#include"CATFeatureAgent.h"
#include"CATDialogState.h"
#include"CATDialogTransition.h"
#include"CATDlgEngUtility.h"
#include"CATMfBRepDefs.h"
MySelectFeatureCmd::MySelectFeatureCmd()
: CATStateCommand("MySelectFeatureCmd"),
_featureAgent(NULL)
{
}
MySelectFeatureCmd::~MySelectFeatureCmd()
{
if (NULL != _featureAgent)
{
_featureAgent->RequestDelayedDestruction();
_featureAgent = NULL;
}
}
voidMySelectFeatureCmd::BuildGraph()
{
_featureAgent = newCATFeatureAgent(
"FeatureSelectionId",
IID_CATIExpectedInterface,
CATDlgEngWithPSOHSO | CATDlgEngWithPrevaluation | CATDlgEngRepeat,
MfNoFeaturization);
CATDialogState *stSelect = GetInitialState("SelectFeatureStateId");
stSelect->AddDialogAgent(_featureAgent);
AddTransition(
stSelect,
stSelect,
IsOutputSetCondition(_featureAgent),
Action((ActionMethod)&MySelectFeatureCmd::OnFeatureSelected));
}
读取选择结果:
CATBoolean MySelectFeatureCmd::OnFeatureSelected(void *iData)
{
CATPathElement *path = _featureAgent->GetValue();
if (NULL == path)
{
return FALSE;
}
CATBaseUnknown *selected = _featureAgent->GetElementValue(path);
if (NULL == selected)
{
return FALSE;
}
int outsideCurrentPart = _featureAgent->ElementInContext(path);
CATBoolean wasFeaturized = _featureAgent->FeaturizedElement(path);
// Query expected interfaces and store only what your feature really owns.
_featureAgent->InitializeAcquisition();
return TRUE;
}
如果命令只想选择一次,把 transition 的目标状态改成 NULL 或后续状态;如果需要连续选择,则像上面一样回到同一状态,并在 action 末尾 InitializeAcquisition()。
9. 最小 CATFeatureImportAgent 模板
下面的模板表示:命令在 Product/Part 机械上下文中运行,用户可以选择外部 Part 的几何,agent 把它导入 targetPartPath 指向的 Part。
#include"CATFeatureImportAgent.h"
#include"CATDialogState.h"
#include"CATDialogTransition.h"
#include"CATDlgEngUtility.h"
#include"CATMfBRepDefs.h"
voidMyImportSupportCmd::BuildGraph()
{
_importAgent = newCATFeatureImportAgent(
"ExternalSupportSelectionId",
IID_CATIExpectedInterface,
CATDlgEngWithPSOHSO | CATDlgEngWithPrevaluation,
MfNoFeaturization,
NULL_var,
CATFIAgCheckTypesAfterImport);
_importAgent->SetActiveObjectPath(_targetPartPath);
CATDialogState *stSelect = GetInitialState("SelectExternalSupportStateId");
stSelect->AddDialogAgent(_importAgent);
AddTransition(
stSelect,
NULL,
IsOutputSetCondition(_importAgent),
Action((ActionMethod)&MyImportSupportCmd::OnExternalSupportSelected));
}
读取导入后的路径:
CATBoolean MyImportSupportCmd::OnExternalSupportSelected(void *iData)
{
CATPathElement *importedPath = _importAgent->GetValue();
if (NULL == importedPath)
{
return FALSE;
}
CATBaseUnknown *importedObject = _importAgent->GetElementValue(importedPath);
if (NULL == importedObject)
{
return FALSE;
}
int createdCount = _importAgent->HowManyElementsCreated(importedPath);
// Store the imported object/path according to your feature model.
// The object is now in the target Part external references context.
return TRUE;
}
如果业务需要区分导入批次或后续追踪导入来源,可以在开始选择前设置 GUID:
GUID importId = /* generated once for this import operation */;
_importAgent->SetImportApplicativeId(importId);
注意这里的 _targetPartPath 必须是目标 Part 的路径,不是随便一个选择对象的路径。这个路径错了,导入对象就会进入错误的 Part 上下文。
如果导入结果还需要 BRep featurization,再把 MfNoFeaturization 换成合适的 MfxxxFeaturization 组合,并按 mode 要求传入 support。不要在还没想清楚生命周期和 support 的时候,把 featurization 放进最小模板里。
10. 程序化设置选择值
CATFeatureAgent 还提供了两个方法,可以把已有路径当成用户选择那样交给 agent 处理:
intSetValueToSelect(CATPathElement *iValue);
intSetListOfValuesToSelect(CATSO *iList);
这适合“命令启动时已有预输入”或“从其它逻辑转交选择结果”的场景。关键坑是:这两个方法只是设置待处理值,不会自动让 agent 进入 accepted valuation。
正确流程是:
if (1 == _featureAgent->SetValueToSelect(path))
{
_featureAgent->SetValuation();
}
多选时才用 SetListOfValuesToSelect,并且 agent 行为要支持 CATDlgEngMultiAcquisition。否则应该使用 SetValueToSelect。
11. 常见坑
1. 把 CATFeatureImportAgent 当成 CATFeatureAgent 的“升级版”
CATFeatureImportAgent 会导入外部对象,副作用比普通选择大得多。只是想在当前 Part 里选 face/edge,不要用它。
2. 允许外部选择后直接保存原对象指针
CATFAgAllowSelectionInContext 只表示允许用户在其它 component 上选对象,不表示外部引用已经建立。要持久化到当前 Part,考虑 CATFeatureImportAgent 或专门的 external reference 机制。
3. Featurization 打开后不管理 BRep feature 生命周期
featurized 对象不是普通临时指针。保留就要聚合到合适位置,删除就要用正确接口删除。最糟糕的写法是拿到指针、存一半、既不聚合也不删除。
4. SetActiveObjectPath 的含义混用
对 CATFeatureAgent,默认行为下它限制当前可选 component;对 CATFeatureImportAgent,它指定外部对象要导入的目标 Part。两者同名但语义不同。
5. 忘记导入后再检查类型
外部选择导入前后对象路径可能变化。对 CATFeatureImportAgent,如果业务依赖导入后的对象接口,建议使用 CATFIAgCheckTypesAfterImport,并在 action 中再次 QueryInterface。
6. 多选仍然只读 GetValue()
启用 CATDlgEngMultiAcquisition 后,要用 GetListOfValues()。GetElementValue(path) 适合逐个 path 处理,不要假设单选 API 会自动代表整个列表。
7. 忘记清空 agent 输出
重复采集时,如果 action 结束没有 InitializeAcquisition(),同一状态可能立刻再次满足 IsOutputSetCondition。
8. 把 prevaluation 当最终选择
启用 CATDlgEngWithPrevaluation 后,agent 可能在预选阶段产生 prevalue。真正创建模型对象时,要确认 transition 是正式选择触发,而不是 hover 预选逻辑。
9. 忽略 Visu 到 Design 的切换成本
CATFeatureAgent 选择 Visu mode 文档元素时会自动转 Design mode。这很方便,但在大装配里可能带来明显性能和内存影响。
10. 只 include 头文件,忘记模块依赖
CATFeatureAgent.h 和 CATFeatureImportAgent.h 位于 MechanicalModelerUI。使用 featurization mode 时还会接触 MecModInterfaces/PublicInterfaces/CATMfBRepDefs.h。Imakefile 里要确认相关模块依赖。
12. 推荐使用流程
写新命令时,可以按下面流程判断:
1. 用户是不是要选择机械模型里的 feature / face / edge / BRep?
否 -> 先看 CATPathElementAgent
是 -> 继续
2. 选择结果是否需要作为当前 Part 的外部引用保存?
是 -> CATFeatureImportAgent
否 -> CATFeatureAgent
3. 是否只是当前 Part 内部选择?
是 -> CATFeatureAgent 默认行为即可
否 -> 继续
4. 是否只是允许参考其它 component,不导入?
是 -> CATFeatureAgent + CATFAgAllowSelectionInContext
5. 是否需要把外部 Part 几何导入目标 Part?
是 -> CATFeatureImportAgent + SetActiveObjectPath(targetPartPath)
6. 是否需要稳定 BRep 引用?
是 -> 选择合适 MfxxxFeaturization,并管理创建出的 BRep feature
否 -> MfNoFeaturization
13. 总结
CATFeatureAgent 和 CATFeatureImportAgent 都服务于机械建模选择,但边界要分清:
| 要做什么 | 用什么 |
|---|---|
| 普通对象选择 | CATPathElementAgent |
| 当前 Part 或机械上下文中的 feature/BRep 选择 | CATFeatureAgent |
| Product 中允许选择其它 component,但不导入 | CATFeatureAgent + CATFAgAllowSelectionInContext |
| 选择其它 Part 几何并导入当前/目标 Part | CATFeatureImportAgent |
| 只是在其它已打开文档窗口中代理一次选择 | CATOtherDocumentAgent |
实际开发里建议保守一点:先用 MfNoFeaturization 把状态机和类型过滤跑通,确认业务确实需要稳定 BRep 引用时,再启用 featurization。外部几何也是一样:只是临时参考就不要导入;需要成为当前 Part 的持久输入,再使用 CATFeatureImportAgent。
这两个 agent 的难点不在“怎么 new”,而在“选择结果属于哪个机械上下文,以及后续是否要被模型长期拥有”。只要这个问题想清楚,代码通常会自然很多。
评论