简介:这是一份面向CATIA二次开发工程师与三维曲面设计人员的CAA Nurbs插件工程源码包,围绕CATIA的CAA开放架构扩展Nurbs建模能力,可用于自由曲面设计、曲线控制点与权重编辑等场景,适合具备一定CAA开发基础的中高级用户研究或二次开发。压缩包共929个文件,约11.16MB,以535个h头文件、72个cpp源文件及56个mk编译脚本为主体,构成完整的CAA工程骨架;另有obj、o、pdb等编译中间产物,catnls、catrsc、catdlg等CATIA资源与对话框定义文件,以及bmp图标、data数据与tsrc测试脚本,覆盖从源码到界面资源的完整结构。内容预览可见控制点编辑、Nurbs创建、权重调整等对话框与命令头文件,说明插件围绕Nurbs曲线曲面的创建与编辑展开。目前已有325人学习下载,可作为理解CATIA CAA插件工程组织与Nurbs功能实现的参考素材。
1. CAA 与 CATIA NURBS 插件:从标题拆出的一条真实开发链路
如果你在汽车、航空或模具行业做过 CATIA 二次开发,大概率见过类似CAAKcNurbsPluginFrm_20200430_CAANurbs_CATIACAA这样的工程命名。它不是一个产品名,而是一条线索:CAA 是 CATIA 的二次开发框架,NURBS 是底层几何表达,Plugin 说明它挂在 CATIA 进程里,Frm 暗示带界面,日期戳说明这是一次迭代快照。把这几个词拼起来,指向的就是「用 CAA 在 CATIA 里做一个 NURBS 曲面/曲线处理插件」这件事。
这类插件解决什么问题?最常见的是:CATIA 原生功能对批量 NURBS 控制点编辑、跨版本曲面重建、自定义光顺算法支持不够,团队需要把自研算法塞进 CATIA 的操作流程里,让工程师不离开 CATIA 就能调用。适合谁?有 C++ 基础、装过 CAA RADE、能拿到 CATIA 安装目录和 API 文档的 CAD 开发人员。如果你只是会用 CATIA 建模,这篇文章会有点硬,但能让你知道插件是怎么长出来的。
2. CAA NURBS 插件的工程骨架:从工作空间到可加载模块
2.1 为什么 CAA 工程不能像普通 C++ 项目那样建
CAA 是 Dassault 的一套组件化框架,它要求代码按「框架(Framework)—模块(Module)—工作空间(Workspace)」三层组织。普通 C++ 项目一个 CMakeLists 就能编译,CAA 不行,它依赖一套叫 mkmk 的构建系统,以及 RADE(Rapid Application Development Environment)提供的工具链。你如果拿 Visual Studio 直接新建空项目,最后连 CATIA 的进程都进不去。
常见做法是:在 RADE 里用「New Workspace」创建空工作空间,再往里加 Framework,Framework 下再分 Module。Module 的类型决定了它能不能被 CATIA 加载。做插件界面,通常需要至少两个 Module:一个*Frm负责对话框和命令,一个*Mdl负责 NURBS 算法和 CATIA 数据交互。标题里的Frm就是前者。
这里有个血泪经验:Module 的Imakefile.mk里LINK_WITH顺序错了,编译能过,加载时报「无法解析符号」,而且报错信息不会告诉你缺哪个库。我一般会把 CATIA 安装目录下intel_a\code\bin和intel_a\code\product里的库按依赖顺序排,先放CATMath、CATGeoFactory,再放CATTopologicalObjects,最后放自己的算法库。
2.2 最小可加载插件的目录结构与关键文件
下面是一个能跑通的最小骨架,目录名用虚构代称,你按自己项目替换:
MyNurbsWorkspace/ ├── MyNurbsFramework/ │ ├── IdentityCard/ │ │ └── IdentityCard.h │ ├── MyNurbsFrm.m/ │ │ ├── src/ │ │ │ ├── MyNurbsCommand.cpp │ │ │ └── MyNurbsDialog.cpp │ │ ├── LocalInterfaces/ │ │ │ └── MyNurbsCommand.h │ │ └── Imakefile.mk │ └── MyNurbsMdl.m/ │ ├── src/ │ │ └── NurbsSmoothing.cpp │ ├── LocalInterfaces/ │ │ └── NurbsSmoothing.h │ └── Imakefile.mk └── CNext/ └── resources/ └── MyNurbsFramework/ └── CATRsc/ └── MyNurbs.CATRscIdentityCard.h是框架的身份证,里面用AddPrereqComponent声明依赖。CATRsc目录放资源文件,命令的图标、菜单文本、对话框布局都在这里。Imakefile.mk是 mkmk 的构建脚本,不是 Makefile,但语法类似。
一个典型的Imakefile.mk片段:
# MyNurbsFrm.m 的构建脚本 BUILT_OBJECT_TYPE = SHARED_LIBRARY LINK_WITH = CATApplicationFrame \ CATDialogEngine \ CATVisualization \ MyNurbsMdl LOCAL_CCFLAGS = -DCATIARefresh逻辑说明:BUILT_OBJECT_TYPE必须是SHARED_LIBRARY,否则 CATIA 加载不了。LINK_WITH里MyNurbsMdl放在最后,因为 Frm 依赖 Mdl 的算法接口。LOCAL_CCFLAGS里的宏控制是否刷新视图,不加的话改完 NURBS 控制点,模型不动,你会以为代码没生效。
参数说明:CATApplicationFrame提供命令和对话框基类;CATDialogEngine是对话框引擎;CATVisualization负责在 3D 窗口里画临时几何。这三个是 Frm 模块的标配,少一个都会在CreateCommand时返回空指针。
2.3 命令与对话框的绑定:让按钮真的能点
CAA 的命令不是直接new出来的,它走的是CATCommand体系。你需要继承CATStateCommand,在BuildGraph里定义状态机。下面是一个最小命令类:
// MyNurbsCommand.h #ifndef MyNurbsCommand_H #define MyNurbsCommand_H #include "CATStateCommand.h" #include "CATDialogAgent.h" class MyNurbsCommand : public CATStateCommand { CATDeclareClass; public: MyNurbsCommand(); virtual ~MyNurbsCommand(); virtual void BuildGraph(); virtual CATBoolean Execute(void *data); private: CATDialogAgent *_pSelectAgent; CATDialogAgent *_pOkAgent; }; #endif// MyNurbsCommand.cpp #include "MyNurbsCommand.h" #include "CATIndicationAgent.h" #include "CATPathElementAgent.h" #include "MyNurbsDialog.h" CATImplementClass(MyNurbsCommand, Implementation, CATStateCommand, CATNull); MyNurbsCommand::MyNurbsCommand() : CATStateCommand("MyNurbsCommand", CATCommandModeExclusive) { _pSelectAgent = NULL; _pOkAgent = NULL; } void MyNurbsCommand::BuildGraph() { // 第一步:选择一条 NURBS 曲线或曲面 _pSelectAgent = new CATPathElementAgent("SelectNurbs"); _pSelectAgent->AddElementType("CATINurbsCurve"); _pSelectAgent->SetBehavior(CATDlgEngWithUndo); AddTransition(NULL, _pSelectAgent, (CATCommandMethod)&MyNurbsCommand::Execute, NULL); // 第二步:弹出对话框,确认参数 _pOkAgent = new CATDialogAgent("OkAgent"); _pOkAgent->AcceptState("Ok"); AddTransition(_pSelectAgent, _pOkAgent, (CATCommandMethod)&MyNurbsCommand::Execute, NULL); } CATBoolean MyNurbsCommand::Execute(void *data) { MyNurbsDialog dlg; if (dlg.DoModal() == CATDlgOK) { // 调用 Mdl 模块的算法 NurbsSmoothing smoother; smoother.Run(_pSelectAgent->GetValue()); } return TRUE; }逻辑说明:BuildGraph是状态机的入口,AddTransition定义「从哪个状态、经过哪个 agent、触发哪个方法、到哪个状态」。CATPathElementAgent负责拾取几何元素,AddElementType限定只能选 NURBS 曲线。Execute里弹对话框,确认后调算法。
参数说明:CATCommandModeExclusive表示命令独占,执行时不能启动其他命令,避免多线程改同一个几何体。CATDlgEngWithUndo让选择操作可以撤销,不加的话用户误选只能重启命令。DoModal返回CATDlgOK才继续,否则直接退出。
这里有个坑:CATImplementClass的第四个参数必须是CATNull,不能写别的,否则 CATIA 启动时加载类失败,日志里只报「class not found」,不告诉你为什么。
3. NURBS 几何在 CAA 里的读写:控制点、节点矢量与权重
3.1 CATINurbsCurve 和 CATINurbsSurface 的接口边界
CAA 对 NURBS 的抽象在CATINurbsCurve和CATINurbsSurface两个接口里。曲线接口能拿到控制点数量、阶数、节点矢量、权重;曲面接口多两个方向。但注意:这些接口是只读的,想改控制点,得用CATNurbsCurveEditor或CATNurbsSurfaceEditor,或者走CATGeoFactory重建。
常见做法是:先用CATINurbsCurve读原始数据,在内存里算完新控制点,再用CATGeoFactory::CreateNurbsCurve建一条新曲线,最后用CATICGMContainer替换旧曲线。不要试图直接改接口返回的数组,那是 const 指针,改了会崩。
下面是一段读控制点的代码:
// 读取 NURBS 曲线的控制点和权重 #include "CATINurbsCurve.h" #include "CATMathPoint.h" #include "CATMathVector.h" void ReadNurbsCurve(CATINurbsCurve *pNurbs) { if (NULL == pNurbs) return; int nbCtrlPts = 0; pNurbs->GetNumberOfControlPoints(nbCtrlPts); int degree = 0; pNurbs->GetDegree(degree); // 控制点数组,CATMathPoint 是三维点 CATMathPoint *pCtrlPts = new CATMathPoint[nbCtrlPts]; double *pWeights = new double[nbCtrlPts]; for (int i = 0; i < nbCtrlPts; ++i) { pNurbs->GetControlPoint(i, pCtrlPts[i]); pNurbs->GetWeight(i, pWeights[i]); } // 节点矢量长度 = 控制点数 + 阶数 + 1 int nbKnots = nbCtrlPts + degree + 1; double *pKnots = new double[nbKnots]; pNurbs->GetKnotVector(pKnots); // 用完记得释放 delete[] pCtrlPts; delete[] pWeights; delete[] pKnots; }逻辑说明:GetNumberOfControlPoints和GetDegree先拿规模,再分配数组。GetControlPoint按索引取点,GetWeight取权重。节点矢量长度是控制点数 + 阶数 + 1,这是 NURBS 的标准定义,不是 CAA 特有的。
参数说明:CATMathPoint是 CAA 的三维点类,和CATMathVector不同,前者是位置,后者是方向。GetKnotVector要求传入的数组足够长,长度算错会写越界,但 CAA 不检查,直接内存踩踏。
3.2 用 CATGeoFactory 重建 NURBS 曲线的完整流程
改完控制点后,重建曲线是必须的。下面是一个完整函数:
// 用新控制点重建 NURBS 曲线 #include "CATGeoFactory.h" #include "CATICGMContainer.h" #include "CATNurbsCurveEditor.h" CATNurbsCurve *RebuildNurbsCurve(CATGeoFactory *pFactory, CATINurbsCurve *pOldNurbs, CATMathPoint *pNewCtrlPts, double *pNewWeights) { if (NULL == pFactory || NULL == pOldNurbs) return NULL; int nbCtrlPts = 0; pOldNurbs->GetNumberOfControlPoints(nbCtrlPts); int degree = 0; pOldNurbs->GetDegree(degree); int nbKnots = nbCtrlPts + degree + 1; double *pKnots = new double[nbKnots]; pOldNurbs->GetKnotVector(pKnots); // 创建编辑器,传入工厂和旧曲线 CATNurbsCurveEditor editor(pFactory, pOldNurbs); editor.SetControlPoints(pNewCtrlPts, nbCtrlPts); editor.SetWeights(pNewWeights, nbCtrlPts); editor.SetKnotVector(pKnots, nbKnots); editor.SetDegree(degree); CATNurbsCurve *pNewNurbs = editor.GetNurbsCurve(); delete[] pKnots; return pNewNurbs; }逻辑说明:CATNurbsCurveEditor是 CAA 提供的编辑工具,它内部会做合法性检查,比如节点矢量是否单调、权重是否为正。SetControlPoints和SetWeights必须同时设,只设一个会导致权重默认全 1,曲面形状突变。GetNurbsCurve返回新曲线,旧曲线还在,需要手动从容器里移除。
参数说明:pFactory是几何工厂,通常从当前文档的CATICGMContainer拿。pNewCtrlPts数组长度必须和nbCtrlPts一致,不能多也不能少。pKnots如果改了,必须保证首尾节点重复度等于阶数+1,否则曲线不闭合。
3.3 权重和节点矢量的常见误用
权重全设为 1,NURBS 退化成 B 样条,这是最常见的误用。很多团队做光顺算法时,只改控制点,不动权重,结果曲面局部变形不够,因为权重才是控制「吸引力」的参数。权重越大,曲线越靠近该控制点;权重为 0,该点无影响。
节点矢量的误用更隐蔽。有人为了「让曲线更光滑」,手动把节点矢量改成均匀分布,结果曲线首尾不通过端点。NURBS 的端点通过性由首尾节点重复度保证,不是由均匀性保证。改节点矢量前,先用GetKnotVector打印出来,确认首尾重复度。
提示:在 CATIA 里用「NURBS 曲线编辑器」手动改一个控制点,再用 CAA 读出来对比,能快速验证你的读写逻辑对不对。这是最省事的调试方法。
4. 插件加载与调试:为什么你的命令没出现在菜单里
4.1 CATRsc 资源文件与命令注册
命令写完了,CATIA 里看不到,九成是资源文件没配对。CATRsc目录下的.CATRsc文件定义命令的菜单路径、图标、快捷键。下面是一个最小资源文件:
// MyNurbs.CATRsc MyNurbsCommand.MyNurbsCommand = "NURBS 光顺"; MyNurbsCommand.MyNurbsCommand.Category = "MyNurbs"; MyNurbsCommand.MyNurbsCommand.Icon = "MyNurbsIcon"; MyNurbsCommand.MyNurbsCommand.ShortHelp = "对选中的 NURBS 曲线进行光顺处理"; MyNurbsCommand.MyNurbsCommand.LongHelp = "选择一条 NURBS 曲线,弹出对话框设置光顺参数,确认后重建曲线。";逻辑说明:第一行是命令的显示名,Category决定它出现在哪个工具栏,Icon对应CNext/resources/graphic下的图标文件。ShortHelp是鼠标悬停提示,LongHelp是状态栏提示。
参数说明:Category如果写了一个不存在的分类,命令不会报错,但也不会显示。常见做法是先用CATIA自带的分类名,比如Analyze或Shape,确认能显示后再改成自定义分类。
4.2 用 CATIA 日志定位加载失败
CATIA 启动时会在%APPDATA%\DassaultSystemes\CATSettings下写日志,但默认不详细。要开详细日志,在 CATIA 快捷方式的目标后面加-log参数,日志会写到CNext\resources\Log下。
加载失败最常见的三种日志:
| 日志内容 | 原因 | 解决 |
|---|---|---|
Cannot load library MyNurbsFrm | 依赖库缺失或路径不对 | 检查LINK_WITH里的库是否都在intel_a\code\bin下 |
Class MyNurbsCommand not found | CATImplementClass参数错或资源文件没注册 | 确认第四个参数是CATNull,资源文件在CATRsc目录 |
Command not authorized | 命令没有在IdentityCard里声明 | 在IdentityCard.h里加AddPrereqComponent("MyNurbsFramework", "MyNurbsFrm") |
4.3 调试时不要用断点,用 CATIA 的 Trace 机制
CAA 插件跑在 CATIA 进程里,Visual Studio 附加进程调试可以,但断点会卡死 CATIA,而且经常断在系统库里。更稳的做法是用CATTrace输出日志:
#include "CATTrace.h" CATTrace("MyNurbs", "Entering Execute, selected element = %s", _pSelectAgent->GetValue()->GetName());逻辑说明:CATTrace的第一个参数是通道名,第二个是格式串,后面是变量。输出写到CNext\resources\Log\MyNurbs.log。
参数说明:通道名要和CATRsc里注册的一致,否则日志不写。格式串里%s对CATUnicodeString要转ConvertToChar(),直接传会乱码。
注意:
CATTrace在 Release 版本里默认关闭,需要在环境变量里设CATTraceLevel=1才输出。调试时设,发布时去掉。
5. 避坑与排查:NURBS 插件开发中最容易翻车的 5 个点
5.1 现象:命令能弹出对话框,但确认后 CATIA 崩溃
原因:对话框里用了 CATIA 的CATDlgSelectorList或CATDlgCombo,但没有在BuildGraph里注册对应的 agent。CAA 的对话框控件和命令状态机是绑定的,控件事件没绑 agent,点击后回调到空指针。
解决:每个交互控件都要有对应的CATDialogAgent,并在AddTransition里关联。如果只是显示信息,用CATDlgLabel,不要用可交互控件。
5.2 现象:重建后的 NURBS 曲线形状和预期差很远
原因:SetControlPoints和SetWeights的数组顺序反了,或者节点矢量长度算错。CAA 不检查数组长度,写越界后内存里的数据是随机的。
解决:在RebuildNurbsCurve里加断言,nbCtrlPts和nbKnots必须满足nbKnots == nbCtrlPts + degree + 1。权重数组先全部初始化为 1.0,再逐个改,不要直接传未初始化的数组。
5.3 现象:插件在开发机上能用,换一台机器就加载失败
原因:开发机上装了 RADE,环境变量CATInstallPath指向 RADE 的库;目标机器只有 CATIA,没有 RADE,库路径不同。
解决:把插件依赖的库和 CATIA 自带的库分开,只链接 CATIA 安装目录下intel_a\code\bin里的库。RADE 特有的库不要链,否则目标机器找不到。发布时把插件 DLL 放到CNext\code\bin下,不要放intel_a。
5.4 现象:CATIA 启动变慢,菜单要等十几秒才出来
原因:IdentityCard.h里AddPrereqComponent加了太多框架,或者资源文件里注册了太多命令。CAA 启动时会遍历所有注册项,每多一个就多一次磁盘 IO。
解决:只注册当前插件需要的框架和命令。开发阶段可以全注册方便调试,发布前精简。资源文件里的LongHelp太长也会拖慢,控制在 200 字符以内。
5.5 现象:多文档环境下,插件改错了文档的几何体
原因:CATGeoFactory是从当前编辑器拿的,但CATPathElementAgent选中的元素可能属于另一个文档。CAA 的文档模型是多文档的,每个文档有自己的CATICGMContainer。
解决:在Execute里先通过CATPathElementAgent拿到元素的CATICGMContainer,再用这个容器创建CATGeoFactory,不要用全局的。改完后调CATICGMContainer::Update刷新视图。
6. 进阶:把 NURBS 光顺算法做成可配置的 CATIA 命令
6.1 用 CATDlgDialog 做参数面板
基础命令只能选曲线、点确认。实际项目里,光顺算法有迭代次数、权重因子、是否保持端点等参数。这些要放在对话框里。下面是一个参数面板的骨架:
// MyNurbsDialog.h #include "CATDlgDialog.h" #include "CATDlgSpinner.h" #include "CATDlgCheckButton.h" class MyNurbsDialog : public CATDlgDialog { CATDeclareClass; public: MyNurbsDialog(); virtual ~MyNurbsDialog(); int GetIterations() const; double GetWeightFactor() const; bool IsKeepEnds() const; private: CATDlgSpinner *_pIterSpinner; CATDlgSpinner *_pWeightSpinner; CATDlgCheckButton *_pKeepEndsCheck; };// MyNurbsDialog.cpp #include "MyNurbsDialog.h" MyNurbsDialog::MyNurbsDialog() : CATDlgDialog(NULL, "MyNurbsDialog", CATDlgWndBtnOKCancel) { _pIterSpinner = new CATDlgSpinner(this, "Iterations", CATDlgSpnInteger); _pIterSpinner->SetRange(1, 100); _pIterSpinner->SetValue(10); _pWeightSpinner = new CATDlgSpinner(this, "WeightFactor", CATDlgSpnFloat); _pWeightSpinner->SetRange(0.0, 1.0); _pWeightSpinner->SetValue(0.5); _pKeepEndsCheck = new CATDlgCheckButton(this, "KeepEnds", "保持端点"); _pKeepEndsCheck->SetState(CATDlgCheck); SetVisibility(CATDlgShow); } int MyNurbsDialog::GetIterations() const { return _pIterSpinner->GetValue(); } double MyNurbsDialog::GetWeightFactor() const { return _pWeightSpinner->GetValue(); } bool MyNurbsDialog::IsKeepEnds() const { return (_pKeepEndsCheck->GetState() == CATDlgCheck); }逻辑说明:CATDlgSpinner是数值输入控件,SetRange限定范围,SetValue设默认值。CATDlgCheckButton是复选框,GetState返回CATDlgCheck或CATDlgUncheck。对话框构造时传CATDlgWndBtnOKCancel,自动带确定和取消按钮。
参数说明:CATDlgSpnInteger和CATDlgSpnFloat决定 spinner 的类型,整数和浮点数不能混用。SetRange的上下界要合理,迭代次数超过 100 在 CATIA 里会卡死,因为每次迭代都要重建几何。
6.2 光顺算法的迭代控制与收敛判断
光顺算法的核心是迭代改控制点,直到相邻控制点的二阶差分小于阈值。下面是一个简化实现:
// NurbsSmoothing.cpp #include "NurbsSmoothing.h" #include "CATMathPoint.h" #include <cmath> void NurbsSmoothing::Run(CATINurbsCurve *pNurbs, int maxIter, double weightFactor, bool keepEnds) { int nbCtrlPts = 0; pNurbs->GetNumberOfControlPoints(nbCtrlPts); if (nbCtrlPts < 3) return; CATMathPoint *pPts = new CATMathPoint[nbCtrlPts]; for (int i = 0; i < nbCtrlPts; ++i) pNurbs->GetControlPoint(i, pPts[i]); CATMathPoint *pNewPts = new CATMathPoint[nbCtrlPts]; for (int iter = 0; iter < maxIter; ++iter) { double maxDelta = 0.0; for (int i = 0; i < nbCtrlPts; ++i) { if (keepEnds && (i == 0 || i == nbCtrlPts - 1)) { pNewPts[i] = pPts[i]; continue; } // 拉普拉斯光顺:新点 = 旧点 + 权重 * (邻居均值 - 旧点) CATMathPoint neighborAvg; if (i == 0) neighborAvg = pPts[1]; else if (i == nbCtrlPts - 1) neighborAvg = pPts[nbCtrlPts - 2]; else neighborAvg = (pPts[i-1] + pPts[i+1]) * 0.5; CATMathVector delta = neighborAvg - pPts[i]; pNewPts[i] = pPts[i] + delta * weightFactor; double d = delta.Norm(); if (d > maxDelta) maxDelta = d; } // 把新点拷回旧点,准备下一轮 for (int i = 0; i < nbCtrlPts; ++i) pPts[i] = pNewPts[i]; // 收敛判断:最大位移小于 1e-6 就停 if (maxDelta < 1e-6) break; } // 用光顺后的点重建曲线 double *pWeights = new double[nbCtrlPts]; for (int i = 0; i < nbCtrlPts; ++i) pNurbs->GetWeight(i, pWeights[i]); RebuildNurbsCurve(pFactory, pNurbs, pPts, pWeights); delete[] pPts; delete[] pNewPts; delete[] pWeights; }逻辑说明:拉普拉斯光顺的核心是每个点向邻居均值移动,weightFactor控制移动幅度。keepEnds为真时首尾点不动,保证曲线端点不变。收敛判断用最大位移,小于1e-6就提前退出,避免无谓迭代。
参数说明:maxIter一般设 10 到 50,超过 100 在 CATIA 里会明显卡顿。weightFactor设 0.3 到 0.7,太大曲线会缩成一条直线,太小光顺效果不明显。1e-6是经验阈值,和模型单位有关,毫米单位下合适,米单位下要改成1e-9。
6.3 验证光顺效果:曲率梳与偏差检查
光顺完不能只看形状,要用曲率梳验证。CAA 里可以用CATCurvatureAnalysis算曲率,但更简单的方法是把光顺前后的曲线都导出成点云,在外部工具里对比。
我一般会在插件里加一个「对比模式」:光顺前采样 100 个点,光顺后再采样 100 个点,算最大偏差和平均偏差,显示在对话框里。偏差超过模型公差的 10% 就警告。
// 采样对比 double ComputeMaxDeviation(CATINurbsCurve *pOld, CATINurbsCurve *pNew, int samples) { double maxDev = 0.0; for (int i = 0; i <= samples; ++i) { double t = (double)i / samples; CATMathPoint ptOld, ptNew; pOld->Eval(t, ptOld); pNew->Eval(t, ptNew); double d = ptOld.DistanceTo(ptNew); if (d > maxDev) maxDev = d; } return maxDev; }逻辑说明:Eval按参数t(0 到 1)求曲线上的点。DistanceTo算两点距离。采样数 100 够用,太多会慢。
参数说明:t的范围是 0 到 1,不是节点矢量的范围。Eval内部会做节点映射,不用手动转。
提示:光顺算法不要一次改太多点,分批改,每批改完刷新视图,让用户能看到中间结果。CATIA 的撤销栈有限,一次改太多,用户想回退都回不去。
这套东西做下来,一个能用的 NURBS 光顺插件大概两周到一个月,取决于你对 CAA 状态机和 CATIA 几何接口的熟悉程度。我自己的习惯是:先把命令跑通,能选曲线、能弹对话框、能重建一条最简单的曲线,再往上加算法。不要一上来就写光顺,否则调试时你分不清是命令没注册还是算法写错了。希望帮到你。
本文还有配套的精品资源,点击获取