UG/NX二次开发:实体顶点获取的两种实现方案(UF函数与NXOpen)
简介面向UG/NX二次开发者的顶点获取项目源码完整演示了如何借助PK_BODY_ask_vertices等建模接口从任意体对象中提取全部顶点坐标。资源内包含可编译运行的C工程zip压缩包共6个文件既有cpp源文件实现核心逻辑也有Makefile构建脚本、可执行程序及md说明文档整体仅11KB结构精简便于快速查阅。代码以UF_PS_ask_ps_tag_of_object获取PK对象为起点随后遍历顶点集合用PK_VERTEX_ask_point与PK_POINT_ask读取坐标并完成毫米单位换算最后调用UF_CURVE_create_point在模型中生成点对象完整呈现了从“体”到“点”的API调用链路对学习UG/NX PK建模接口的开发者很有参考价值。已有125人学习适合从事NX二次开发或需要处理模型顶点数据的工程师。 UG/NX二次开发里顶点获取这个话题看着基础真做起来却容易翻车。我刚入门NX二次开发时接到的第一个小任务就是从实体模型里把所有顶点坐标提出来用来计算毛坯最大尺寸。那会儿我以为有现成函数一把梭结果翻遍API文档试了各种UF_MODL函数折腾一晚上才搞明白NX的顶点不是散落在三维空间里的点对象它藏在边Edge下面一条边管着两个顶点。搞清楚这个代码五分钟就能写出来。这篇博文把整个过程完整复盘一遍UF函数和NXOpen两套方案都给源码适合正在写类似功能的开发朋友直接抄作业。1. 为什么需要“顶点获取”——先看清楚应用场景再动手1.1 顶点数据在哪些自动化任务里是刚需顶点获取听起来是个底层小功能但实际工程里有一大堆任务都得靠它撑着。我遇到过比较典型的几类批量测量外形尺寸。几十个图档要统一算长宽高手动用测量命令一个个点效率太低写个程序遍历所有顶点取XYZ方向的极值一分钟处理完一个文件夹。包装排版计算。拿到零件轮廓顶点之后程序自动判断最优摆放角度再根据顶点分布计算包装箱尺寸省去大量人工试排。数控加工毛坯生成。加工前需要根据零件包围盒自动生成毛坯方块顶点坐标就是包围盒的输入数据。模型简化与比对。不同版本的模型顶点数量、位置如果发生明显变化从顶点数据就能快速判断差异区间。不同场景对“顶点”的定义其实不一样。有的要棱边端点有的要表面角点有的只关心XYZ方向极值点。所以写代码之前先把需求问清楚你要的是全部顶点还是某个方向上最靠外的点有没有要求去重有没有指定坐标系这些搞清楚后面能少走一半弯路。1.2 两条技术路线怎么选UF函数还是NXOpenNX二次开发目前主流是两套APIUF函数C/C和NXOpenC#/C。UF函数是老牌接口文档全、运行稳但风格是典型的过程式编程你得自己管理tag、自己释放内存。NXOpen是官方主推的面向对象接口类层次清晰而且NX的Journal录制功能导出的代码就是C#拿来改改就能用特别适合快速开发。我的建议是维护老系统、习惯C选UF函数完全没问题从零开始做新工具优先NXOpen C#开发效率高界面集成也方便。顶点获取这种小功能两套API在性能上没有本质差别选择标准就是你自己顺手、项目组技术栈统一。2. 吃透NX里的拓扑模型比急着写代码重要2.1 Body、Edge、Vertex的层级关系与API入口NX里一个实体的拓扑关系是Body体→ Face面→ Loop环→ Edge边→ Vertex顶点这就是标准的边界表示模型B-Rep。你眼睛看到的一个方块内部是6个面围成的每个面有若干条边每条边有两个顶点。关键问题是顶点并不直接挂在Body下面。所以你不能问NX“把体上所有顶点给我”这种话UF函数里也没有ask_body_vertices这种一杆子到底的接口。正确的姿势是两条路先从体拿边再从边掏顶点。UF函数对应的是UF_MODL_ask_body_edgesNXOpen对应的是body.GetEdges()。拿到边之后每条边的两个端点就是顶点对象。为什么要绕这个弯子因为B-Rep模型里面和边的数据是显式存储的顶点更多是作为边的端点存在没有单独维护一份“体→全部顶点”的索引表。理解了这一点你就不会再满世界找所谓“直接获取顶点”的接口了。2.2 顶点坐标、坐标系和单位里藏着的三个坑第一个坑是坐标系。顶点坐标返回的是建模坐标系绝对坐标系下的坐标不是工作坐标系WCS。如果模型本身不在原点你拿到的就是绝对坐标想转WCS得自己做变换或者用UF_CSYS_map_point这类函数来映射。第二个坑是单位。NX默认建模单位是毫米但有些项目标准是英寸。UF函数大部分接口返回的是建模单位你显示到界面时要按当前单位换算别想当然地当成毫米。我就见过一个团队把英寸当毫米用结果尺寸全部差了25.4倍整批数据报废。第三个坑是重复顶点。一条边两个顶点一个顶点被三条边共用。要是照着边遍历不去重一个立方体8个顶点会被枚举出24次。去重千万别用坐标值浮点运算的误差会让你欲哭无泪正确做法是用顶点对象的tag去重同一个顶点对象的tag是唯一的可靠又高效。2.3 记住一个常见误区顶点获取不等于包围盒很多人说要“获取顶点”其实心里想的是“我要算长宽高”。如果你只是要外形尺寸的极值根本不需要遍历顶点NX有专门的包围盒接口UF函数用UF_MODL_ask_bounding_boxNXOpen用body.GetBoundingBox()一条调用直接拿到最小点和最大点再一减就是长宽高。顶点获取真正派上用场的场景是你需要对顶点本身做处理比如标记某个角点、找距离指定点最近的顶点、把顶点按某种规则排序重排。所以写代码前先想清楚要的是极值信息还是顶点自身。方向对了代码量能少一半。3. 方案一C/C UF函数稳定可靠的经典路子3.1 核心API与完整源码UF函数实现顶点获取核心就三个接口UF_MODL_ask_body_edges拿到体上的所有边UF_CURVE_ask_vertex_data根据顶点tag取出坐标数据UF_OBJ_set_status高亮对象调试用完整源码如下我在关键位置加了注释#include uf.h #include uf_modl.h #include uf_curve.h #include uf_obj.h #include vector #include set // 获取Body上所有不重复顶点坐标结果按x,y,z顺序存入xyz数组 // 返回值顶点数量失败返回-1 int GetUniqueVertices(tag_t bodyTag, std::vectordouble xyz) { int numEdges 0; UF_MODL_edge_t* edges nullptr; // 第一步从体上取出所有边 int ret UF_MODL_ask_body_edges(bodyTag, numEdges, edges); if (ret ! 0 || numEdges 0) return -1; std::settag_t seenTags; // 用顶点tag去重 int vertCount 0; // 第二步遍历每条边取出两个端点 for (int i 0; i numEdges; i) { UF_MODL_edge_t e edges[i]; tag_t vTags[2] { e.vertex_0-tag, e.vertex_1-tag }; for (int j 0; j 2; j) { if (vTags[j] NULL_TAG) continue; if (seenTags.find(vTags[j]) ! seenTags.end()) continue; seenTags.insert(vTags[j]); // 第三步从顶点tag取出坐标 UF_CURVE_vertex_t vData; if (UF_CURVE_ask_vertex_data(vTags[j], vData) 0) { xyz.push_back(vData.point[0]); xyz.push_back(vData.point[1]); xyz.push_back(vData.point[2]); vertCount; } } } UF_free(edges); return vertCount; }3.2 逐段拆解body_edges → vertex_data → 去重这段代码的核心思路就是三步走。第一步UF_MODL_ask_body_edges会把体上所有边填充到UF_MODL_edge_t数组里。UF_MODL_edge_t结构体里有两个顶点成员不同NX版本成员名略有差异常见的是vertex_0和vertex_1类型是顶点对象指针通过-tag拿到tag。如果编译报错说vertex_0不是结构体指针去头文件里看一眼有的版本直接存的就是tag_t去掉-tag直接用就行。第二步去重这里我用的std::settag_t。为什么不用坐标数组做去重两个不同顶点坐标完全相同或者同一个顶点因为浮点误差坐标不完全一致都会导致判断失误。tag是NX内部为每个对象分配的唯一标识拿它去重最稳。一个简单立方体遍历边会得到24个“边端点”去重后正好是8个顶点。第三步UF_CURVE_ask_vertex_data返回的UF_CURVE_vertex_t结构体里有个point[3]数组就是顶点坐标。需要注意这个坐标是double类型的建模坐标系坐标不是WCS。3.3 高亮显示顶点调试可视化技巧调试几何相关代码最直观的验证方式就是把结果在图形区高亮出来。NX提供了对象状态设置接口代码一行搞定// 遍历所有已收集的顶点tag在图形区高亮 std::settag_t::iterator it; for (it seenTags.begin(); it ! seenTags.end(); it) { UF_OBJ_set_status(*it, UF_OBJ_HIGHLIGHT); }运行完代码NX图形区里哪些顶点被点亮一目了然。如果高亮点位和预期不符说明遍历逻辑或对象选择有问题立刻能发现。我习惯在开发阶段把这个调试开关做成可选参数正常交付时默认关闭。4. 方案二C# NXOpen录Journal改一改就行4.1 Journal录制与改进思路NX的Journal功能对新手非常友好。手动操作一遍“选择实体、进入测量、查看顶点”这类过程NX会同步生成一段C#代码保存成Journal文件。用Visual Studio打开你会发现里面的API调用全都替你写好了你只需要把界面操作换成语义明确的业务逻辑。我第一次写NXOpen的C#代码就是在Journal基础上改的。选体的代码、获取当前显示部件的代码直接复用。后面自己要写的其实只有遍历边和顶点那一小段。4.2 NXOpen获取顶点源码与说明下面是NXOpen C#实现顶点获取的核心代码保持和UF版本同样的去重逻辑using NXOpen; using System.Collections.Generic; public class VertexHelper { Session theSession Session.GetSession(); public ListPoint3d GetBodyVertices(Body body) { ListPoint3d result new ListPoint3d(); HashSetTag seen new HashSetTag(); // 遍历体的所有边再取每条边的顶点 foreach (Edge edge in body.GetEdges()) { foreach (Vertex vert in edge.GetVertices()) { if (seen.Add(vert.Tag)) { result.Add(vert.Point); } } } return result; } }body.GetEdges()一次拿到体上所有边edge.GetVertices()返回该边关联的顶点数组vert.Point是Point3d结构体直接包含XYZ坐标。去重用HashSetTag语义上和C版一模一样。这段代码还能顺手输出到Listing Window方便调试theSession.ListingWindow.Open(); theSession.ListingWindow.WriteLine(顶点数量: result.Count); foreach (Point3d pt in result) { theSession.ListingWindow.WriteLine( string.Format(X{0:F3}, Y{1:F3}, Z{2:F3}, pt.X, pt.Y, pt.Z)); }4.3 两套方案对比选哪套心里有数对比项UF函数C/CNXOpenC#API风格过程式函数直接调用面向对象类层次清晰适合项目老系统、维护型开发新工具、界面集成开发效率中等上手有门槛高Journal可直接改造运行性能接近原生略慢但可接受官方支持力度成熟稳定、更新慢主推方向、新接口优先我自己维护老工具会用UF函数涉及和数据库、报表系统对接的新项目就用NXOpen C#。代码量差异不大选哪个更多是团队技术栈和长期维护成本的事。5. 环境配置、编译调试与踩坑实录5.1 VS版本与NX版本对应速查配置开发环境是很多人第一次卡住的地方。Visual Studio版本和NX版本存在对应关系装错了会出现各种奇怪的编译或链接错误。大致对应如下NX版本区间推荐Visual StudioNX 1847~1899VS2017NX 1926~1953VS2019NX 1980及以上VS2019 / VS2022最稳妥的办法是打开NX安装目录下的NXBIN文件夹里面通常有README或批处理脚本明确写了当前版本对VS版本的要求。以那个为准别猜。5.2 项目配置要点包含目录、库目录和链接以VS2019 NX2206为例创建一个C空项目然后按下面配置项目属性 → VC目录 → 包含目录加上$(UGII_ROOT_DIR)\UGOPEN、$(UGII_ROOT_DIR)\NXOPEN、$(UGII_ROOT_DIR)\NXOpenCPP库目录同样加上这三个目录链接器 → 输入 → 附加依赖项加上libufun.lib、libnxopencpp.lib具体库名以NX版本头文件为准这里的UGII_ROOT_DIR是NX安装根目录比如C:\Program Files\Siemens\NX2206。也可以直接运行NX自带的批处理环境脚本然后在VS里勾选“从父级环境变量继承”这样路径不用手写。如果是C#项目直接在项目引用里添加NXOpen相关的程序集即可比C省事得多。5.3 实际项目中遇到的5个坑和排查方法下面这些坑都是我实际踩过的整理成速查表现象可能原因排查方向编译报错找不到头文件UGII_ROOT_DIR没配置或路径错误检查环境变量确认目录存在LNK2019无法解析的外部符号库文件没链接或VS版本不匹配对照附加依赖项确认NX版本绑定运行崩溃提示tag无效体被删除或文档切换每次运行前判断tag是否为NULL_TAG遍历后顶点数量是0选中的不是实体Body可能是片体或装配实例先确认对象类型片体没有实体边坐标和测量命令对不上坐标系或单位理解错误区分绝对坐标和WCS确认单位换算其中装配组件的问题最容易让人抓狂组件里的Body顶点坐标基于组件自己的坐标系不能直接当装配体的绝对坐标用。要先用装配相关接口拿到组件变换矩阵把点变换到绝对坐标系。这个坑我栽过——从组件里取出来的顶点单独看每个都是对的放到装配里位置完全对不上。6. 往上走一步从“顶点”到“几何信息”6.1 快速拿最小包围盒8个角点不用遍历顶点如果你要的不是全部顶点而是最小包围盒的8个角点直接用包围盒接口最省事。UF函数写法如下double minP[3], maxP[3]; UF_MODL_ask_bounding_box(bodyTag, minP, maxP); // 根据最小点和最大点组合出8个角点 for (int k 0; k 8; k) { double x (k 1) ? maxP[0] : minP[0]; double y (k 2) ? maxP[1] : minP[1]; double z (k 4) ? maxP[2] : minP[2]; // 这里就拿到了第k个角点坐标 }这段代码用位运算巧妙地枚举了8个角点的XYZ组合比手写8次赋值干净得多。掌握了包围盒接口再配合顶点遍历几乎所有外形相关的计算需求都能覆盖。6.2 组件与实例别拿错坐标处理装配体时一定要分清“部件体”和“组件实例”。同一个部件可能被装配进多个位置每个组件实例都有自己的变换矩阵。你直接遍历组件里的Body拿到的是部件自身坐标系下的顶点只有把顶点乘以组件变换矩阵才是装配环境下的真实位置。正确做法先通过UF_ASSEM_ask_component_data获取组件原点坐标和旋转矩阵再用标准矩阵变换把顶点转换到绝对坐标系。这部分建议封装成公用函数装配类需求会反复用到。6.3 还能扩展什么体积、质心、面积一起算顶点获取常常不是最终目的而是其他几何计算的前置步骤。NX里UF_MODL_ask_mass_props_3d能一次性拿体积、表面积、质心等质量属性。实际做零件报表时经常需要把顶点坐标、包围盒、体积、质心放在同一份报告里。别分开实现再手工合并数据把这几类几何信息的获取统一封装成一个工具类输入Body tag输出完整几何信息结构体后续任何项目都能复用。最后说个我自己的习惯凡是跟几何数据打交道的功能我一定会做三样东西——顶点高亮显示、数量输出、把原始坐标导到CSV里人工对比。坐标这种东西最容易“看起来对其实错”等出问题再回头调那就不是省事而是难受了。顶点获取本身只是一个小功能但把这一环做扎实了后面做测量、排版、毛坯、报表都会省心很多。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联
返回资讯列表 →