DWG 数据库进阶与用户坐标系交互
从ZRX程序向CAD发送命令创建数据库对象
为什么要通过发送命令来操作CAD
作用
- 更简单: 对于新手很友好,直接就像用户操作一样输入命令
- 更稳定: 可以采用已有的命令逻辑,避免重复实现
- 否认“低级”: 通过命令类方式操作不是技术低级,是调用系统自带的高效程序
实际场景对应
- 需要快速创建形状/拷贝/移动/绘制时
- 例如自动处理多个图形的复制操作
zcedCommand 和 resbuf 进阶说明
zcedCommand
是什么?
它是一个函数,用于 在 ZRX 程序里模拟用户手动输入 CAD 命令 的效果。
简单来说,就是用 C++ 把很复杂的 CAD 命令动作用程序化。
与用户操作对应
在 CAD 界面手动输入:
_.Copy
等同于 C++ 程序里:
zcedCommand(RTSTR, _T("_.Copy"), ...);
示例
zcedCommand(
RTSTR, _T("_.Copy"), // 扩展名 + Copy
RTENAME, en, // 选择对象
RTSTR, _T(""), // 确定
RTSTR, _T("_Mode"),
RTSTR, _T("_Single"),
RT3DPOINT, apt, // 指定点
RTSTR, _T("100,100,0"),
RTNONE);
RTSTR
表示一个字符串RTENAME
表示一个对象名 (选择一条线)RT3DPOINT
是三维点RTNONE
结束命令
_
:调用global名称 .
:无视Undefine调用原本的命令名称
基础结构名词对应
简写 | 说明 |
---|---|
zds_point | C 格式的 3D 坐标,类型是 double[3] |
ZcGePoint3d | 官方更方便用的 3D 点类 |
zds_name | CAD 内部对象编号 (后续用于 zced/数据库操作) |
ZcDbObjectId | ZRX 数据库对象标识 |
数据结构转换
ZcGePoint3d pt = asPnt3d(zds_point);
zds_point pt2 = asDblArray(pt);
resbuf 介绍
是 CAD 系统内部用来 表示多种类型的数据类型链表结构,用于和系统命令进行交换
创建表示数据的链表
用在 zcedCommand、输入交互、存储扩展数据等场景
装一系列参数,传给
zcedCommand
;存储扩展字典(XData)或块参数等。
常用函数:
zcutBuildList(...)
创建 resbuf 链zcutRelRb(rb)
释放 resbuf
结构:
struct resbuf {
short restype; // 数据类型标志
union { // 数据值
int rint;
double rreal;
char* rstring;
...
} resval;
resbuf* rbnext; // 指向下一个节点
};
resbuf 遍历示例
resbuf* pTmp = pRb;
while (pTmp)
{
...
pTmp = pTmp->rbnext;
}
创建/释放 resbuf
resbuf* pRb = zcutBuildList(RTSTR, _T("Line"), RTNONE); // 创建
zcutRelRb(pRb); // 释放
如何进行用户交互
选点
// 语法-选点
zcedGetPoint(
const zds_point* pBase,
LPCTSTR prompt,
zds_point outPt);
// 语法-选角点
// 在指定第一个对角点后,提示用户输入对角范围的第二点,用于画矩形、多边形等。
zds_point p1, p2;
zcedGetPoint(NULL, _T("\n输入第一个角点:"), p1);
zcedGetCorner(p1, _T("\n输入对角点:"), p2);
// 示例
zcedGetPoint(NULL, _T("\n输入起点:"), pt1);
zcedGetPoint(pt1, _T("输入终点: "), pt2)
zcedGetCorner(pt1, _T(“\n输入对角点:"), pt2);
- 参数:
参数 | 含义 |
---|---|
pBase | 基点(可用于相对提示),NULL 表示绝对坐标 |
prompt | 提示信息 |
outPt | 接收用户输入的三维点 |
- CAD 对应:命令行中
Specify first point:
或鼠标点击。
选实体
// 语法:
int zcedEntSel(
LPCTSTR prompt,
zds_name outEnt,
zds_point outPt);
zcedEntSel(_T(“\n选择一个实体: "), en, apt);
- 返回值:
RTNORM
表示正常选择。 - CAD 对应:命令行
Select an object:
并拾取图形。
选择嵌套的实体
zds_name en;//选择到的实体
BOOL pickflag = FALSE;
zds_point pt;//如果pickflag为FALSE,则这个参数表示用户选到的点,如果为TRUE,参数传入的点表示从该点选择实体
LPCWSTR prompt = _T(“\n选择一个嵌套的实体: ");
zds_matrix xformres;//把嵌套的实体从ECS转换成WCS的矩阵
resbuf *rb = nullptr;//保存返回的结果
zcedNEntSelP(prompt, en, pt, pickflag, xformres, &rb);
if (rb)//返回的结果要释放掉
{
zcutRelRb(rb);
}
选择集
- 定义:一次性获取满足条件的多个实体,返回一个选择集(Selection Set)。
- 对应场景:批量操作,如同时移动或删除多个对象,或者框选某一范围内的所有图元。
- 函数:
zcedSSGet
- 用法:获取多个实体,如框选、全选。
- 示例:
zds_name ss;
// 手动逐一选择
zcedSSGet(NULL, NULL, NULL, NULL, ss);
// 全选
zcedSSGet(_T("X"), NULL, NULL, NULL, ss);
模式代码 | 描述 | CAD 对应命令行选项 |
---|---|---|
NULL | 手动逐一选择 | 框选 / CTRL+点击等 |
"X" | 选取所有实体 | ALL |
"W" | 窗口选择(完全包含) | Window |
"C" | 窗口交叉(包括部分包含) | Crossing |
- 遍历:
int len = 0;
zcedSSLength(ss, &len);
for (int i = 0; i < len; i++) {
zds_name ent;
zcedSSName(ss, i, ent);
// 转换到 ZcDbObjectId...
}
// 详细解释:
int len = 0;
zcedSSLength(ss, &len);
for (int i = 0; i < len; ++i) {
zds_name en;
zcedSSName(ss, i, en); // 获取第 i 个实体名
ZcDbObjectId id;
zcdbGetObjectId(id, en); // 转换为对象 ID
ZcDbEntity* pEnt = nullptr;
zcdbOpenZcDbEntity(pEnt, id, ZcDb::kForRead); // 打开实体
// ... 处理实体,如获取属性、移动等
pEnt->close();
}
非常复杂,请仔细看文档
zcedSSGet(NULL, NULL, NULL, NULL, ss);//手动选择
zcedSSGet(_T("X"), NULL, NULL, NULL, ss);//选择所有实体
zcedSSGet(_T("W"), pt1, pt2, NULL, ss);//框选实体(完全包含)
zcedSSGet(_T("C"), pt1, pt2, NULL, ss);//框选实体(包括部分包含)
zcedSSFree
选择集的过滤器
- 定义:在获取选择集时,先用
resbuf
链表定义筛选条件,只选中符合条件的实体。 - 作用:大幅提高自动化脚本的可靠性和效率,例如只对特定类型或属性的对象操作。
zds_name ss;
// 构造筛选条件:选取所有半径 50~100 的圆,或颜色为红色(索引 1)的圆
resbuf* pFilter = zcutBuildList(
RTDXF0, _T("CIRCLE"), // 只筛选圆
-4, _T("<OR"),
-4, _T("<AND"),
-4, _T(">"), 40, 50.0, // 半径 > 50
-4, _T("<"), 40, 100.0, // 半径 < 100
-4, _T("AND>"),
62, 1, // 颜色索引 = 1 (红)
-4, _T("OR>"),
RTNONE);
// 使用过滤器获取选择集
zcedSSGet(NULL, NULL, NULL, pFilter, ss);
// 释放过滤器链表
zcutRelRb(pFilter);
// 遍历选择集同上
int len = 0;
zcedSSLength(ss, &len);
for (int i = 0; i < len; ++i) {
zds_name en;
zcedSSName(ss, i, en);
ZcDbObjectId id;
zcdbGetObjectId(id, en);
ZcDbEntity* pEnt = nullptr;
zcdbOpenZcDbEntity(pEnt, id, ZcDb::kForRead);
// ... 处理实体
pEnt->close();
}
// 释放选择集
zcedSSFree(ss);
输入字符串
ZcString str;
zcedGetString(0, _T("\n输入字符串:"), str);
/整数/实数/角度
API | 用途 |
---|---|
zcedGetString | 输入字符串 |
zcedGetInt | 输入整数 |
zcedGetReal | 输入实数 |
zcedGetAngle | 输入角度(返回弧度) |
输入数字
int n;
zcedGetInt(_T("\n输入整数"), &n);
zds_real r;//也可以用double,不太严谨但更方便
zcedGetReal(_T("\n输入实数"), &r);
输入角度:受ANGBASE和ANGDIR影响
zds_real ang;
zcedGetAngle(NULL, _T("\n输入角度:"), &ang);
zds_point pt;
zcedGetPoint(NULL, _T("\n选择基点:"), pt);
zds_real ang;
zcedGetAngle(pt, _T("\n输入角度:"), &ang);
输入长度:zcedGetDist(与角度类似)
快捷键 zcedInitGet(RSG_NONULL, _T("Ja NEIN,N _ YES,Y No"));
快捷键由空格分隔 大写的字母是快捷键 逗号后也是快捷键 如有下划线,前面是local名称,后面是global名称,数量不同时从左往右匹配
zds_point pt;
zcedInitGet(RSG_NONULL, _T("3P,3 2P,2 Ttr"));
auto rc = zcedGetPoint(NULL, _T("Specify the center point of circle or [3P/2P/Ttr (tangency tangency radius)]: "), pt);
if (rc == RTNORM)//输入了点
{
...
}
else if (rc == RTKWORD)//输入了关键字
{
ZcString kword;
zcedGetInput(kword);
zcutPrintf(kword);
}
坐标系概述
坐标系 | 定义 | 使用场景 |
---|---|---|
WCS | 世界坐标系,CAD 默认绝对坐标系 | 所有数据存储、内部计算 |
UCS | 用户坐标系,用户可定义的绘图坐标系 | 绘制特殊角度/方向图形,如斜切、旋转图形 |
DCS | 显示坐标系,由当前视图/相机位置决定 | 屏幕显示、捕捉光标移动 |
OCS | 对象坐标系,对象自身的二维坐标映射 | 二维多段线(POLYLINE)高度 + 法向量存储方式 |
MCS | 模型坐标系,大多数情况下等同 WCS | 块(BLOCK) 插入变换时的参照系 |
Details
W(orld)CS:世界坐标系,绝对坐标 U(ser)CS:用户坐标系,用户所使用的坐标系 D(isplay)CS:显示坐标系,由当前相机的位置、目标位置、up方向决定 O(bject)CS:对象坐标系,二维多段线,节省存储空间(三维点->二维点+标高+法向量) M(odel)CS:模型坐标系,块,大部分情况下跟WCS一样
所有获取用户输入的接口,如无特殊说明,得到的都是UCS坐标 与用户输入无关的接口,如无特殊说明,输入输出都是WCS坐标
// WCS -> UCS
zds_point wpt = {10, 20, 0}, upt;
resbuf orig = {RTSHORT}; orig.resval.rint = 0; // WCS
resbuf dest = {RTSHORT}; dest.resval.rint = 1; // UCS
zcedTrans(wpt, &orig, &dest, 0, upt);
// UCS -> WCS
zcedTrans(upt, &dest, &orig, 0, wpt);
课堂习题与作业解析
任务:交互式绘制直线
这是今天的核心练习。它不再是像昨天那样在代码里写死坐标来画图,而是让用户亲自在CAD界面上通过鼠标点击来决定直线的位置。这完美地应用了课堂上讲的用户交互中的“选点”功能。
功能说明: 执行 testLine
命令后,程序会提示用户在图纸上依次点击两点,然后根据用户选择的这两点来创建一条直线。
算法思路:
调用
zcedGetPoint
函数,提示用户选择直线的起点,并获取用户点击的点的坐标。再次调用
zcedGetPoint
函数,提示用户选择终点,并将起点作为“基点”(Base Point)传入,这样用户在选择时会看到一条从起点到当前鼠标位置的橡皮筋线,交互体验更好。获取到的坐标是
zds_point
类型(C风格的数组),需要将其转换为数据库操作更常用的ZcGePoint3d
类型(C++类)。使用这两个
ZcGePoint3d
点创建一个ZcDbLine
对象。通过标准的数据库操作流程(获取数据库 -> 获取块表 -> 以可写方式获取模型空间),将这条直线添加到模型空间中。
依次关闭所有打开的对象,释放资源。
代码解析:
void testLine()
{
// 定义两个 zds_point 类型的变量来接收用户输入的坐标。
// zds_point 是一个简单的 double[3] 数组,专门用于与 zced* 系列的交互函数配合。
zds_point pt1, pt2;
// 第一步:让用户点击起点
// zcedGetPoint 是核心交互函数,用于获取用户在屏幕上的点选。
// 参数1 (NULL): 基点。为NULL表示没有基点,用户的选择是绝对的。
// 参数2 ("..."): 提示字符串,会显示在CAD的命令行。
// 参数3 (pt1): 用于接收用户点击坐标的输出变量。
// 返回值 (RTNORM): 表示用户正常点击了一个点。如果用户按了ESC,则返回其他值。
if (zcedGetPoint(NULL, _T("\n请选择起点:"), pt1) != RTNORM)
return; // 如果用户取消操作,则直接退出函数
// 第二步:让用户点击终点
// 再次调用 zcedGetPoint。
// 参数1 (pt1): 此时将刚才获取的起点 pt1 作为基点。
// 效果:CAD界面会显示一条从 pt1 到当前鼠标位置的动态虚线(橡皮筋效果)。
if (zcedGetPoint(pt1, _T("\n请选择终点:"), pt2) != RTNORM)
return;
// 第三步:坐标类型转换
// 将 C 风格的 zds_point 数组转换为面向对象的 ZcGePoint3d 类。
// 数据库实体(如 ZcDbLine)的构造函数需要的是 ZcGePoint3d 类型。
ZcGePoint3d startPt(pt1[0], pt1[1], pt1[2]);
ZcGePoint3d endPt(pt2[0], pt2[1], pt2[2]);
// 第四步:创建直线实体对象 (在内存中)
ZcDbLine* pLine = new ZcDbLine(startPt, endPt);
// 第五步:将实体添加到数据库 (这部分是昨天的知识)
// 5.1 获取当前数据库
ZcDbDatabase* pDb = zcdbHostApplicationServices()->workingDatabase();
// 5.2 获取块表
ZcDbBlockTable* pBT = nullptr;
if (pDb->getBlockTable(pBT, ZcDb::kForRead) != Acad::eOk || !pBT)
return;
// 5.3 以可写方式获取模型空间
ZcDbBlockTableRecord* pBTR = nullptr;
if (pBT->getAt(ACDB_MODEL_SPACE, pBTR, ZcDb::kForWrite) != Acad::eOk || !pBTR)
{
pBT->close();
return;
}
// 5.4 将直线实体追加到模型空间
ZcDbObjectId lineId;
pBTR->appendAcDbEntity(lineId, pLine);
// 第六步:关闭所有打开的数据库对象
pLine->close();
pBTR->close();
pBT->close();
}
核心知识点:
zcedGetPoint()
: 这是实现与用户点交互的关键。通过它,我们的程序可以暂停,等待用户的鼠标输入,从而让程序更加灵活和实用。坐标系 (UCS vs WCS): 课堂上重点讲了坐标系。需要特别注意的是,所有
zcedGet*
系列的交互函数,默认获取的坐标都是在当前的用户坐标系(UCS)下。而数据库中存储实体几何信息通常使用世界坐标系(WCS)。在这个简单的例子中,我们假设UCS和WCS是重合的,所以没有做坐标转换。但在复杂的应用中,如果用户改变了UCS,就需要使用zcedTrans
函数将点从UCS转换到WCS,然后再存入数据库。数据类型转换: 理解
zds_point
和ZcGePoint3d
之间的区别与联系。前者用于与老的C-API风格的交互函数(zced*
)通信,后者是现代C++数据库API(ZcDb*
,ZcGe*
)的标准。