
前言之前聊了 MxCAD 的内置命令和圆弧、多段线的绘制图有了接下来就是怎么选中它们。对象选择其实就两件事一是光标底下到底是哪个对象二是怎么把一批对象挑出来统一处理。在 MxCAD 里选择分两条路走一条靠 UI 交互点选、框选、累加都行另一条纯代码驱动比如全选、按 ID 或句柄直接找、按特征筛选。不管走哪条路都能挂上同一套筛选条件把范围锁死在特定类型或图层上。一、两个基础概念1. McObjectId选择结果与实体之间的中间层无论用哪种方式选择对象最终拿到的都是一个McObjectId对象 ID。它本身不携带实体的几何数据只描述这是数据库里的哪一个对象。要真正操作实体需要通过getMcDbEntity()从 ID 取出实体实例import { MxCADUiPrEntity } from mxcad; const getEnt new MxCADUiPrEntity(); getEnt.setMessage(请选择目标对象); const entId await getEnt.go(); if (!entId || !entId.id) return; const ent entId.getMcDbEntity(); if (ent) console.log(对象类型, ent.objectName);McObjectId还提供了几个常用判断方法entId.isValid(); // ID 是否有效 entId.isNull(); // ID 是否为空 entId.isKindOf(McDbLine); // 指向的对象是否为直线 entId.isErase(); // 指向的对象是否已被删除后续所有选择方式都可以理解为得到一个或一组McObjectId再通过getMcDbEntity()拿到实体做下一步操作。2. MxCADResbuf筛选条件MxCADResbuf用来描述哪些对象有资格被选中。它可以挂载实体类型、图层、颜色、块名等条件同时适用于有 UI 交互的选择和没有 UI 交互的选择。import { MxCADResbuf } from mxcad; const filter new MxCADResbuf(); filter.AddMcDbEntityTypes(LINE,CIRCLE); // 只允许选直线和圆后文每种选择方式都会用到这个筛选条件这里先建立概念具体用法在第五节展开。二、有 UI 交互的选择有 UI 交互的选择都需要用户参与或给定坐标返回一个选择集或对象 ID并且可以挂MxCADResbuf过滤。1. 点选1.1 交互式点选MxCADUiPrEntityMxCADUiPrEntity用于让用户在图纸上点选一个对象返回对应的McObjectId适合选中某个对象后继续操作的场景如编辑、附加属性、读取属性等。import { MxCADUiPrEntity } from mxcad; const getEnt new MxCADUiPrEntity(); getEnt.setMessage(请选择目标对象); const entId await getEnt.go(); if (!entId || !entId.id) return; const ent entId.getMcDbEntity(); if (ent) console.log(当前对象类型, ent.objectName);点选结束后还可以拿到点在哪里选的这个坐标以及区分用户是选中了对象、按了回车还是输入了关键字const pickPt getEnt.pickPoint(); // 拾取时的图纸坐标 const status getEnt.getStatus(); // 操作状态 if (getEnt.isKeyWordPicked(P)) { // 用户输入了关键字 P如上一个对象 }1.2 坐标点选pointSelect如果坐标已经确定不需要用户交互可以直接用坐标在该处点选对象import { MxCADSelectionSet, MxCADResbuf } from mxcad; const ss new MxCADSelectionSet(); const filter new MxCADResbuf(); filter.AddMcDbEntityTypes(TEXT,MTEXT); // 只挑文字对象 const index ss.pointSelect(point.x, point.y, filter); const ent ss.item(index).getMcDbEntity();pointSelect的第三个参数是筛选条件第四个参数是拾取容差。如果不走选择集MxCADUtility.findEntAtPoint()可以直接在某点取到命中的实体返回一个McObjectId适合点选即识别的场景import { MxCADUtility, MxCADResbuf, DxfCode } from mxcad; // 在 (x, y, z) 处查找命中的对象-1 表示用默认拾取范围 const objId MxCADUtility.findEntAtPoint(x, y, z, -1, null); if (objId.isValid()) { const ent objId.getMcDbEntity(); console.log(命中对象, ent?.objectName); } // 也可以带筛选条件只命中直线 const filter new MxCADResbuf([DxfCode.kEntityType, LINE]); const lineId MxCADUtility.findEntAtPoint(x, y, z, -1, filter);2. 框选2.1 交互式框选userSelectuserSelect让用户在图纸上拉框选择最接近桌面 CAD 中直接框选的操作import { MxCADSelectionSet, MxCADResbuf } from mxcad; const ss new MxCADSelectionSet(); const filter new MxCADResbuf(); filter.AddMcDbEntityTypes(INSERT); // 只选块参照 if (await ss.userSelect(选择要刷的目标块:, filter)) { const ids ss.getIds(); // 拿到全部选中的对象 ID }userSelect返回一个布尔值表示用户是否确认了选择true表示选中并确认false表示用户取消按 Esc或直接回车退出。const confirmed await ss.userSelect(选择要刷的目标块:, filter); if (confirmed) { // 用户确认选择处理 ss 中已选中的对象 } else { // 用户取消或退出 }如果需要更细地区分结束原因可以参考MxCADSelectionSetStatus枚举kSelected构造成功、kNone直接回车、kCanceled按 Esc 取消、kRejected无效关键字、kKeyword输入了关键字。框选完成后可以调用getSelectPoint()取回框选的两个对角点{pt1, pt2}也可以调用getSelectPoints()取回整条选择框的点阵。const {pt1, pt2} ss.getSelectPoint(); const points ss.getSelectPoints();2.2 程序化交叉窗选crossingSelect如果两个对角点已经确定可以用crossingSelect直接完成框选不需要用户交互ss.crossingSelect(x1, y1, x2, y2, filter);交叉窗选对应 AutoCAD 中的交叉 C框内对象和跨越框边界的对象都会被选中。这与窗选 W只选完全在框内的对象不同。2.3 栅栏选fenceSelect栅栏选对应 AutoCAD 的F用户画一条折线栅栏选中所有被栅栏穿过的对象if (await ss.fenceSelect(选择栅栏要穿过的对象:, filter)) { console.log(栅栏选中, ss.count()); }3. 累加选择MxCADSelectionSet默认isWhileSelect true即循环选择用户可以多次点选或框选每次选择都累加进同一个选择集直到确认结束。const ss new MxCADSelectionSet(); ss.isWhileSelect true; // 循环选择多次选择不断累加 ss.isSelectHighlight true; // 选中对象高亮显示 // 用户连续点选每次都往 ss 里累加结束后 const ids ss.getIds(); console.log(累计选中, ss.count());currentSelect(filter?)用于读取当前已选中的对象对应 AutoCAD 的 PICKFIRST 语义返回当前已选对象的数量// 读取当前已选对象数量 const n ss.currentSelect(); // 带条件只统计直线 const lineCount ss.currentSelect(new MxCADResbuf([DxfCode.kEntityType, LINE]));isFilterLockLayer是一个布尔开关控制遍历时是否跳过锁定图层上的对象默认不跳过ss.isFilterLockLayer true; // 遍历时跳过锁定图层上的对象三、无 UI 交互的选择无 UI 交互的选择不依赖用户操作由代码直接驱动适合脚本、批处理和自动化场景。1. 全选allSelectconst ss new MxCADSelectionSet(); ss.allSelect(); // 全选当前空间的所有对象也可以带条件只选特定类型、特定图层上的对象import { DxfCode } from mxcad; // 只选 0 层上的直线、圆弧、圆、多段线 ss.allSelect(new MxCADResbuf([ DxfCode.kEntityType, LINE,ARC,CIRCLE,LWPOLYLINE, DxfCode.kLayer, 0 ])); console.log(对象数, ss.count());2. 按已知 ID 查找McObjectIdMcObjectId的构造函数接受一个对象 IDnumber 类型可以直接由已知的 ID 还原出实体import { McObjectId } from mxcad; const id new McObjectId(knownId); if (id.isValid() !id.isNull()) { const ent id.getMcDbEntity(); console.log(ent?.objectName); }这是选择结果与后续操作之间的衔接方式drawEntity、allSelect、getIds()等返回的都是McObjectId链式调用getMcDbEntity()即可继续编辑。3. 按已知句柄查找handleToIdIndex句柄handle是对象在数据库中的字符串标识。数据库提供句柄 → ID的反查方法import { MxCpp } from mxcad; const db MxCpp.getCurrentMxCAD().getDatabase(); const id db.handleToIdIndex(ABCD1234); // 句柄 → McObjectId if (id id.isValid()) { const ent id.getMcDbEntity(); console.log(句柄对应实体, ent?.objectName); }反向操作是实体侧的getHandle()用于从实体取出句柄。ID、句柄、实体三者可以互相转换。4. 按实体特征查找按实体特征查找就是按对象自身的属性把符合条件的对象挑出来。根据特征的类型有三种由浅入深的方式。4.1 条件筛选allSelect配合MxCADResbuf用DxfCode指定特征维度可以直接筛选类型、图层、颜色、块名等// 找 0 层上、颜色为 1、类型为直线的对象 ss.allSelect(new MxCADResbuf([ DxfCode.kEntityType, LINE, DxfCode.kLayer, 0, DxfCode.kColor, 1 ]));4.2 遍历加属性判定条件筛选只能覆盖类型、图层、颜色、块名这类表级特征。如果要按几何量半径、长度、坐标识别对象需要全选后遍历逐个读取实体属性做判断ss.allSelect(new MxCADResbuf([DxfCode.kEntityType, CIRCLE])); ss.forEach((id) { const circle id.getMcDbEntity(); if (circle Math.abs(circle.radius - 50) 1e-6) { console.log(找到半径约 50 的圆, circle.objectName); } });4.3 空间边界筛选McDbSpatialFilter按空间边界点集围成的区域筛选对象典型用途是块参照的边界裁剪const spatialFilter blkRef.getSpatialFilterId().getMcDbObject(); spatialFilter.setBoundary(aryPoint); // 设置剪切边界点集 spatialFilter.setEnable(true);四、选择结果的读取与遍历无论用哪种方式选择选中的对象都存放在MxCADSelectionSet中通过以下方法读取ss.count(); // 选中对象数量 ss.item(i); // 按索引取第 i 个对象的 McObjectId ss.getIds(); // 取全部选中对象的 McObjectId 数组 ss.forEach((id) { // 遍历每个选中对象 const ent id.getMcDbEntity(); }); ss.clear(); // 清空选择集 ss.isNull(); // 判断选择集是否为空五、筛选条件 MxCADResbuf 的完整用法前面每种选择方式都能挂上MxCADResbuf这是筛选贯穿两种选择方式的关键。1. 构造筛选条件import { MxCADResbuf, DxfCode } from mxcad; // 方式一链式 add const f1 new MxCADResbuf(); f1.AddMcDbEntityTypes(LINE,CIRCLE); // 方式二构造时直接给「组码, 值」对 const f2 new MxCADResbuf([ DxfCode.kEntityType, LINE,ARC, DxfCode.kLayer, 0 ]);2. 可用的特征维度DxfCode枚举定义了筛选时可用的特征维度组码含义组码含义kEntityType实体类型kLayer图层kColor颜色kBlockName块名kExDataName扩展数据名kAppNameXData 类型实体类型值域包括LINE、ARC、CIRCLE、LWPOLYLINE、TEXT、MTEXT、INSERT、ELLIPSE、SPLINE、POINT、LinkLine。3. 同一个 filter 同时用于交互与程序化同一个MxCADResbuf实例既可以挂到有 UI 交互的选择上也可以挂到无 UI 交互的选择上const filter new MxCADResbuf([DxfCode.kEntityType, LINE]); // 交互侧 const getEnt new MxCADUiPrEntity(); getEnt.setFilter(filter); // 点选时只允许选直线 const ss new MxCADSelectionSet(); ss.userSelect(选择直线:, filter); // 框选时只选直线 ss.allSelect(filter); // 程序化全选时也只选直线4. 读取与遍历筛选条件MxCADResbuf也能反向读出其中的条件便于调试或复用filter.GetCount(); // 条件项数量 filter.AtString(0); // 取第 0 项字符串 filter.AtDouble(1); // 取第 1 项 double filter.AtPoint(2); // 取第 2 项点坐标 filter.AtObjectId(3); // 取第 3 项对象 ID六、把选择接上后续操作选择只是第一步选中对象后要做的是编辑、改属性、删除等。下面是一个选择 → 批量改属性的完整小命令展示选完之后的用法import { MxCADSelectionSet, MxCADResbuf, DxfCode } from mxcad; async function Mx_SelectAndSetLinetype() { const ss new MxCADSelectionSet(); const filter new MxCADResbuf([DxfCode.kEntityType, LINE]); if (!await ss.userSelect(选择要改线型的直线:, filter)) return; ss.forEach((id) { const ent id.getMcDbEntity(); if (ent) ent.linetype DASHED; // 批量改线型 }); }七、总结对象选择是用户意图与对象数据库之间的环节也是所有编辑命令的前置步骤。MxCAD 中对象选择分两类有 UI 交互点选MxCADUiPrEntity/pointSelect、框选userSelect/crossingSelect/fenceSelect、累加选择isWhileSelect。无 UI 交互全选allSelect、按已知 ID 查找new McObjectId、按已知句柄查找handleToIdIndex、按实体特征查找条件筛选 / 属性遍历 /McDbSpatialFilter。两类选择共用同一套筛选条件MxCADResbuf通过DxfCode指定类型、图层、颜色、块名等特征维度。选中的对象统一存放在MxCADSelectionSet中通过count、item、getIds、forEach读取再对每个McObjectId调用getMcDbEntity()拿到实体实例做后续操作。掌握对象选择后就可以进入修改工具把选中的对象真正移动、复制、旋转编辑起来。