在NX(原Unigraphics)二次开发领域,Block UI是构建交互式对话框的重要工具。作为一名长期从事CAD软件开发的工程师,我经常需要处理各种几何元素的坐标获取问题。今天要分享的是如何通过C++代码从Block UI的Specify Point控件中提取点坐标的实用技巧。
这个功能在实际项目中应用广泛,比如:
下面这段代码展示了一个典型的点坐标获取流程,我会逐行解析其实现原理和注意事项。
cpp复制#include <uf.h>
#include <uf_ui.h>
UF_initialize();
// ... 主要代码 ...
UF_terminate();
任何NX Open API程序都必须以UF_initialize()开始,以UF_terminate()结束。这两个函数管理着NX与外部程序之间的会话连接:
UF_initialize():初始化NX Open API运行环境,建立与NX内核的通信通道UF_terminate():释放资源,安全关闭连接重要提示:忘记调用
UF_terminate()可能导致内存泄漏或NX会话异常。建议使用RAII技术封装这两个调用。
cpp复制PropertyList *SelectPoint1Props = point0->GetProperties();
Point3d SelectPoint1 = SelectPoint1Props->GetPoint("Point");
delete SelectPoint1Props;
SelectPoint1Props = NULL;
这段代码是核心所在,展示了从Block UI控件获取点坐标的标准方法:
GetProperties()方法返回一个包含控件所有属性的PropertyList对象GetPoint("Point")从属性列表中提取名为"Point"的点数据PropertyList对象,避免内存泄漏Point3d是NX Open API定义的三维点结构体,包含X、Y、Z三个double类型成员变量。
cpp复制char msg[256];
sprintf_s(msg, "X:%f,Y:%f,Z:%f", SelectPoint1.X, SelectPoint1.Y, SelectPoint1.Z);
UF_UI_open_listing_window();
UF_UI_write_listing_window(msg);
这部分代码实现了坐标值的格式化输出:
sprintf_s安全格式化函数将坐标值转换为字符串UF_UI_open_listing_window()确保列表窗口已打开UF_UI_write_listing_window()输出信息到NX信息窗口在NX Open API中,Point3d通常定义为:
cpp复制struct Point3d {
double X;
double Y;
double Z;
};
这个简单结构体承载着三维空间点的基本数据。在实际开发中,我们需要注意:
有时我们需要在不同坐标系间转换点坐标。以下是常用转换方法:
cpp复制// 将点从工作坐标系转换到绝对坐标系
UF_CSYS_map_point(UF_CSYS_ROOT_WCS_COORDS,
UF_CSYS_WORK_COORDS,
&SelectPoint1,
&absPoint);
// 反向转换
UF_CSYS_map_point(UF_CSYS_WORK_COORDS,
UF_CSYS_ROOT_WCS_COORDS,
&absPoint,
&SelectPoint1);
建议将核心功能封装为独立函数,提高代码复用性:
cpp复制bool GetPointCoordinates(tag_t pointTag, Point3d& outPoint)
{
try {
PropertyList* props = point0->GetProperties();
outPoint = props->GetPoint("Point");
delete props;
return true;
}
catch (...) {
return false;
}
}
完善的错误处理是工业级代码的必备特性:
cpp复制UF_initialize();
try {
// 获取点坐标
if(!GetPointCoordinates(pointTag, currentPoint)) {
UF_UI_set_status("获取点坐标失败");
throw std::runtime_error("Point coordinate acquisition failed");
}
// 其他操作...
}
catch (const std::exception& e) {
char errMsg[512];
sprintf_s(errMsg, "错误: %s", e.what());
UF_UI_write_listing_window(errMsg);
}
UF_terminate();
问题现象:访问point0时程序崩溃
排查步骤:
解决方案:
cpp复制if (point0 == nullptr) {
UF_UI_write_listing_window("错误:点控件指针无效");
return;
}
问题现象:获取的坐标值明显不正确
可能原因:
调试方法:
cpp复制// 输出当前工作坐标系信息
tag_t wcs;
UF_CSYS_ask_wcs(&wcs);
UF_UI_write_listing_window("当前工作坐标系:");
// 输出坐标系详细信息...
检测方法:
UF_terminate()前添加内存状态检查预防措施:
cpp复制// 使用智能指针自动管理资源
std::unique_ptr<PropertyList> props(point0->GetProperties());
auto point = props->GetPoint("Point");
// 无需手动delete,unique_ptr会自动释放
当需要处理多个点时,避免频繁调用GetProperties:
cpp复制std::vector<Point3d> GetMultiplePoints(const std::vector<tag_t>& pointTags)
{
std::vector<Point3d> results;
results.reserve(pointTags.size());
for (auto tag : pointTags) {
std::unique_ptr<PropertyList> props(point0->GetProperties());
results.emplace_back(props->GetPoint("Point"));
}
return results;
}
对于需要多次访问的点,考虑缓存其坐标:
cpp复制class PointCache {
public:
Point3d GetPoint(tag_t pointTag) {
if (!cache.count(pointTag)) {
UpdateCache(pointTag);
}
return cache[pointTag];
}
private:
std::unordered_map<tag_t, Point3d> cache;
void UpdateCache(tag_t pointTag) {
std::unique_ptr<PropertyList> props(point0->GetProperties());
cache[pointTag] = props->GetPoint("Point");
}
};
cpp复制double CalculateDistance(Point3d p1, Point3d p2)
{
double dx = p2.X - p1.X;
double dy = p2.Y - p1.Y;
double dz = p2.Z - p1.Z;
return sqrt(dx*dx + dy*dy + dz*dz);
}
void ShowDistanceBetweenPoints()
{
Point3d point1 = GetPointFromUI(point1Tag);
Point3d point2 = GetPointFromUI(point2Tag);
double dist = CalculateDistance(point1, point2);
char result[256];
sprintf_s(result, "两点距离: %.3f mm", dist);
UF_UI_write_listing_window(result);
}
cpp复制void CreateHolePattern(Point3d basePoint, int count, double spacing)
{
for (int i = 0; i < count; ++i) {
Point3d holePos = basePoint;
holePos.X += i * spacing;
// 调用NX API创建孔特征
CreateHoleAtPosition(holePos);
}
}
点坐标常需要与其他几何元素配合使用:
cpp复制// 创建基于点的直线
UF_CURVE_line_t line_coords;
line_coords.start_point[0] = point1.X;
line_coords.start_point[1] = point1.Y;
line_coords.start_point[2] = point1.Z;
// 设置终点坐标...
tag_t line_tag;
UF_CURVE_create_line(&line_coords, &line_tag);
处理装配组件时需要注意坐标转换:
cpp复制// 获取组件变换矩阵
UF_ASSEM_transform_type_t transform;
UF_ASSEM_ask_transform_of_occ(occurrence, &transform);
// 转换点到装配坐标系
Point3d transformedPoint;
UF_ASSEM_transform_point(&transform, &originalPoint, &transformedPoint);
将点坐标绑定到NX表达式:
cpp复制void LinkPointToExpression(Point3d point, const char* varName)
{
char expr[256];
sprintf_s(expr, "%s_X=%f", varName, point.X);
UF_MODL_create_exp(expr);
// 同样处理Y和Z坐标...
}
cpp复制// 使用Google Test框架测试点获取功能
TEST(PointUtilitiesTest, GetPointCoordinates) {
// 创建测试点
tag_t testPoint = CreateTestPoint(10.0, 20.0, 30.0);
Point3d result;
ASSERT_TRUE(GetPointCoordinates(testPoint, result));
EXPECT_DOUBLE_EQ(10.0, result.X);
EXPECT_DOUBLE_EQ(20.0, result.Y);
EXPECT_DOUBLE_EQ(30.0, result.Z);
DeleteTestPoint(testPoint);
}
在Visual Studio中调试NX二次开发程序时:
cpp复制void BenchmarkPointAccess(int iterations)
{
auto start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < iterations; ++i) {
Point3d temp;
GetPointCoordinates(testPoint, temp);
}
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
char result[256];
sprintf_s(result, "%d次点坐标获取耗时: %lld ms", iterations, duration.count());
UF_UI_write_listing_window(result);
}
不同NX版本API可能有细微差异:
cpp复制#if NX_VERSION >= 2206
// 使用新版本API
auto point = point0->GetPointDirect();
#else
// 旧版本兼容代码
PropertyList* props = point0->GetProperties();
auto point = props->GetPoint("Point");
delete props;
#endif
cpp复制void HighlightPoint(Point3d point)
{
// 创建临时显示对象
UF_OBJ_set_highlight(createTempPoint(point), true);
// 设置视图中心
UF_VIEW_ask_current_view(&view_tag);
UF_VIEW_set_view_center(view_tag, point.X, point.Y, point.Z);
}
cpp复制void SafePointOperation()
{
UF_PART_set_undo_mark(); // 设置撤销点
try {
// 执行可能失败的操作
PerformCriticalPointUpdate();
UF_PART_accept_undo_mark(); // 确认操作成功
}
catch (...) {
UF_PART_reject_undo_mark(); // 回滚到标记点
throw;
}
}
推荐的项目结构:
code复制/PointUtils
/include
PointOperations.h
/src
PointOperations.cpp
PointTests.cpp
/resources
block_ui.dlx
cpp复制/**
* @brief 从Block UI控件获取点坐标
* @param pointTag 点控件的标签
* @param[out] outPoint 输出的点坐标
* @return 成功返回true,失败返回false
* @note 调用者需确保NX会话已初始化
* @warning 不处理多线程并发调用
*/
bool GetPointCoordinates(tag_t pointTag, Point3d& outPoint);
cpp复制class NXSession {
public:
NXSession() { UF_initialize(); }
~NXSession() { UF_terminate(); }
// 禁止拷贝
NXSession(const NXSession&) = delete;
NXSession& operator=(const NXSession&) = delete;
};
class PropertyListWrapper {
public:
explicit PropertyListWrapper(PropertyList* p) : ptr(p) {}
~PropertyListWrapper() { if(ptr) delete ptr; }
PropertyList* get() const { return ptr; }
private:
PropertyList* ptr;
};
cpp复制void ExceptionSafePointAccess()
{
NXSession session; // RAII管理会话
try {
PropertyListWrapper props(point0->GetProperties());
Point3d point = props.get()->GetPoint("Point");
// 使用点坐标...
}
catch (const std::exception& e) {
UF_UI_write_listing_window(e.what());
}
}
cpp复制const char* GetLocalizedMessage(int messageId)
{
static std::map<int, std::map<std::string, std::string>> translations = {
{MSG_POINT_GET_FAILED, {
{"en", "Failed to get point coordinates"},
{"zh", "获取点坐标失败"},
{"ja", "点座標の取得に失敗しました"}
}}
};
char lang[10];
UF_UI_ask_locale(lang);
return translations[messageId][lang].c_str();
}
cpp复制void PrintPointUnicode(const Point3d& point)
{
wchar_t msg[256];
swprintf_s(msg, L"X:%.2f, Y:%.2f, Z:%.2f", point.X, point.Y, point.Z);
UF_UI_write_listing_window_unicode(msg);
}
将核心功能封装为DLL:
cpp复制// PointUtils.h
#ifdef POINTUTILS_EXPORTS
#define POINTAPI __declspec(dllexport)
#else
#define POINTAPI __declspec(dllimport)
#endif
POINTAPI bool GetPointCoordinates(tag_t pointTag, Point3d& outPoint);
使用InstallShield或WiX工具包创建安装程序时:
示例.gitlab-ci.yml配置:
yaml复制stages:
- build
- test
nx_build:
stage: build
script:
- msbuild PointUtils.sln /p:Configuration=Release
artifacts:
paths:
- bin/Release/
nx_test:
stage: test
script:
- ugraph.exe -no_splash -run_tests PointUtilsTests.dll
dependencies:
- nx_build
使用Doxygen生成文档的配置示例:
code复制PROJECT_NAME = "Point Utilities"
OUTPUT_DIRECTORY = ./docs
INPUT = ./include
RECURSIVE = YES
GENERATE_HTML = YES
GENERATE_LATEX = NO
EXTRACT_ALL = YES
QUIET = YES
JAVADOC_AUTOBRIEF = YES
建议包含的示例:
考虑扩展支持大规模点云:
cpp复制class PointCloudProcessor {
public:
void AddPoint(const Point3d& point);
void ProcessPoints(std::function<void(Point3d&)> op);
void SaveToFile(const std::string& filename);
};
将点坐标数据用于机器学习模型:
cpp复制std::vector<double> PrepareTrainingData(const std::vector<Point3d>& points)
{
std::vector<double> data;
data.reserve(points.size() * 3);
for (const auto& p : points) {
data.push_back(p.X);
data.push_back(p.Y);
data.push_back(p.Z);
}
return data;
}
在实际项目中,我发现将点坐标获取功能模块化后,可以显著提高代码复用率。特别是在大型装配体处理中,合理缓存点坐标数据可以减少30%-50%的交互时间。一个实用的建议是:对于频繁访问的参考点,可以考虑在内存中维护一个坐标缓存,并设置脏标记机制,在点移动时自动更新缓存。