(WEB 端 CAD)网页 CAD 中的“组”功能开发
- 2025-11-14 四川
本文字数:25630 字
阅读完需:约 84 分钟

在使用 CAD 工具进行绘图时,面对复杂的图形结构,如何高效地管理多个对象成为提升工作效率的关键。CAD 提供的“组”功能,正是为解决这一问题而设计的实用工具。本文将全面介绍 mxcad 中”组“的概念,以及如何实现组相关的功能开发。
一、什么是“组”(Group)?
在 CAD 中,组(Group) 是指将多个图形对象逻辑地组合在一起,形成一个可被统一操作的集合。组不会创建新的图元实体,也不会改变对象本身的几何属性,仅是一种命名的对象集合,组对象包含特点如下:
组内的对象保持独立,可单独编辑。
选择组中任意一个对象时,整个组可被选中(取决于系统设置)。
每个组有唯一的名称,便于识别和管理。
支持嵌套:一个组可以包含另一个组,形成层级结构。
组不作为独立实体存储在图形数据库中,仅作为对象的逻辑关联存在。
二、组的核心功能开发
1. 创建组
该功能流程是从用户执行“创建组”命令开始。首先,系统初始化相关变量(如组名、描述和对象列表),并获取当前图形数据库中的组管理字典。
随后进入主循环,提示用户“选择对象”。用户可以通过点击或框选方式选择一个或多个图形对象,所选对象的 ID 将被保存到临时列表中。
在选择过程中,用户可随时输入关键字进行设置:
输入 N(名称):进入命名流程,系统提示“输入编组名”。此时可输入
[查询(A)]来查看已存在的组名;若输入*或直接回车,则列出所有组;否则查询指定组信息。输入名称后,系统检查是否重名,若无冲突则保存名称并返回选择状态。输入 D(说明):进入说明设置,提示“输入编组说明”,用户输入的文本将作为该组的描述信息。
当用户完成选择并按 回车或空格键 确认后,系统开始创建组:
首先检查所选对象中是否有成员已属于其他组。
若存在此类情况,则弹出确认提示:“包含相同对象的组已经存在。仍要创建新的组?<N>”,并提供“是(Y)/否(N)”选项。
若用户选择“否”或取消操作,命令终止。
若用户确认继续或无冲突,则调用底层 API 创建组,并将之前输入的描述信息赋值给新组。
最后,组创建完成,系统退出循环,命令执行结束。整个流程支持 ESC 中断或新命令打断,确保操作的安全性和灵活性。根据上述流程调用 mxcad 内部 API 接口实现方法如下:
import { McDbEntity, McDbGroup, McDbPolyline, McGePoint3d, McObjectId, MxCADSelectionSet, MxCADUiPrKeyWord, MxCADUiPrPoint, MxCADUiPrString, MxCADUtility, MxCpp } from "mxcad";import { DetailedResult, MxFun, MrxDbgUiPrBaseReturn } from "mxdraw";interface GroupObject { name: string, group: McDbGroup}// 根据实体查找组const getGroupForEntity = (entity: McDbEntity): GroupObject[] => { const database = MxCpp.getCurrentDatabase() const groupDict = database.GetGroupDictionary() const handle = entity.getHandle() const groupNames = groupDict.getAllObjectName() const length = groupNames.length(); let groupArr: GroupObject[] = []; for (let index = 0; index < length; index++) { const groupName = groupNames.at(index); const groupId = groupDict.getAt(groupName) const group = groupId.getMcDbObject() as McDbGroup if (!group) continue; const entityIds = group.getAllEntityId(); entityIds.forEach(entityId => { if (entityId.getMcDbEntity()?.getHandle() === handle) groupArr.push({ name: groupName, group }) }); }; return groupArr}// 创建组async function Mx_Group() { let description = "" let ids: McObjectId[] = []; const database = MxCpp.getCurrentDatabase(); const groupDict = database.GetGroupDictionary(); const mxcad = MxCpp.getCurrentMxCAD(); // 设定未命名组名 const groupNames = groupDict.getAllObjectName(); let num = 0; groupNames.forEach(item => { if (/^\*/.test(item)) { num += 1; } }); let name: string = `*A${num + 1}`; // 创建组 const createGroup = async () => { const isPresence = ids.some((id) => { return database.getEntitiesInTheGroup(id).length !== 0 }) if (isPresence) { const getKey = new MxCADUiPrKeyWord(); getKey.setMessage(`包含相同对象的组已经存在。仍要创建新的组?<N>`); getKey.setKeyWords(`[是(Y)/否(N)]`); const key = await getKey.go(); ids.forEach(id => { id.getMcDbEntity().highlight(false); }) mxcad.updateDisplay(); if (key?.toLocaleUpperCase() === "N") { return } if (!key) return } if (database.CreateGroup(ids, name)) { const groupId = groupDict.getAt(name) const group = groupId.getMcDbObject() as McDbGroup; if (description) group.description = description; if (/^\*/.test(name)) { MxPluginContext.useMessage().success('未命名组已创建'); } else { MxPluginContext.useMessage().success(`组${name}已创建`); } ids.forEach(id => { id.getMcDbEntity().highlight(false); }) mxcad.updateDisplay(); }; }; while (true) { const getEntityPt = new MxCADUiPrPoint(); getEntityPt.setMessage('选择对象'); getEntityPt.setKeyWords(`[名称(N)/说明(D)]`); getEntityPt.setDisableOsnap(true); getEntityPt.setDisableDynInput(true); getEntityPt.disableAllTrace(true); const hoverSelectEnts: McDbEntity[] = []; getEntityPt.setUserDraw((pt, pw) => { if (hoverSelectEnts.length) hoverSelectEnts.forEach(ent => ent.highlight(false)); hoverSelectEnts.length = 0; const entId = MxCADUtility.findEntAtPoint(pt.x, pt.y, pt.z, -1); if (entId.isValid() && !ids.map(item => item.id).includes(entId.id)) { const ent = entId.getMcDbEntity(); const arr = getGroupForEntity(ent); if (arr.length) { const group = arr[0].group; group.getAllEntityId().forEach(id => { const ent = id.getMcDbEntity(); ent.highlight(true); hoverSelectEnts.push(ent) }) } else { ent.highlight(true); hoverSelectEnts.push(ent) } } }); const pt = await getEntityPt.go(); hoverSelectEnts.forEach(ent => ent.highlight(false)); // 如果选择关键字,则执行相关操作 if (getEntityPt.getStatus() == MrxDbgUiPrBaseReturn.kKeyWord) { if (getEntityPt.isKeyWordPicked("N")) { while (true) { const getName = new MxCADUiPrString() getName.setMessage("输入编组名") getName.setKeyWords(`[查询(A)]`) const str = await getName.go() if (getName.getDetailedResult() === DetailedResult.kCodeAbort || getName.getDetailedResult() === DetailedResult.kEcsIn || getName.getDetailedResult() === DetailedResult.kNewCommadIn) return if (getEntityPt.getDetailedResult() === DetailedResult.kNullEnterIn || getEntityPt.getDetailedResult() === DetailedResult.kNullSpaceIn || getEntityPt.getDetailedResult() === DetailedResult.kMouseRightIn) { return createGroup() } if (getName.isKeyWordPicked("A")) { getName.setMessage("请输入要列出的编码组名"+ "<*>") getName.setKeyWords("") const name = await getName.go(); if (getName.getDetailedResult() === DetailedResult.kCodeAbort || getName.getDetailedResult() === DetailedResult.kEcsIn || getName.getDetailedResult() === DetailedResult.kNewCommadIn) return if (name && name !== "*") { const groupId = groupDict.getAt(name) const group = groupId.getMcDbObject() as McDbGroup MxFun.acutPrintf(`\n 定义的编组:`) if (group) { MxFun.acutPrintf(`\n${group.name}`) } } else if (name === "*" || getName.getDetailedResult() === DetailedResult.kNullEnterIn || getName.getDetailedResult() === DetailedResult.kNullSpaceIn) { const groupIds = groupDict.getAllObject() MxFun.acutPrintf(`\n 定义的编组:`) groupIds.forEach((groupId) => { const group = groupId.getMcDbObject() as McDbGroup group && MxFun.acutPrintf(`\n ${group.name}`) }) } continue; } if (!str) return; if (/^\*/.test(str)) { MxFun.acutPrintf(`*无效`); continue; } const groupId = groupDict.getAt(str) const group = groupId.getMcDbObject() as McDbGroup if (group && groupId.isValid()) { MxFun.acutPrintf(`编组${str} 已经存在`); continue; } name = str; if (ids.length) { ids.forEach(id => { const ent = id.getMcDbEntity(); ent.highlight(false); }) return createGroup(); } else { break; } } } else if (getEntityPt.isKeyWordPicked('D')) { const getName = new MxCADUiPrString() getName.setMessage("输入编组说明") const str = await getName.go(); if (!str) break; description = str continue; } } else if (getEntityPt.getStatus() === MrxDbgUiPrBaseReturn.kNone) { if (!ids.length) { return MxPluginContext.useMessage().success('未选择对象,未创建编组'); } else { ids.forEach(id => { const ent = id.getMcDbEntity(); ent.highlight(false); }) return createGroup(); } } else if (getEntityPt.getStatus() === MrxDbgUiPrBaseReturn.kCancel) { ids.forEach(id => { const ent = id.getMcDbEntity(); ent.highlight(false); }) return } else { // 判断是否选中实体 if (pt && hoverSelectEnts.length) { const selectIds = hoverSelectEnts.map(item => { item.highlight(true); return item.getObjectID() }) ids.push(...selectIds); continue; } else if (pt && !hoverSelectEnts.length) { getEntityPt.setUserDraw((point, pw) => { const pts = [pt, new McGePoint3d(pt.x, point.y), point, new McGePoint3d(point.x, pt.y)] // 设置范围框颜色即位置 let pl = new McDbPolyline(); pl.isClosed = true; pts.forEach(pt => pl.addVertexAt(pt)); pw.setColor(0xFFFFFF); pw.drawMcDbEntity(pl); // 动态绘制矩形填充框 const geometry = new THREE.BufferGeometry(); geometry.setFromPoints([ new THREE.Vector3(pt.x, pt.y, pt.z), new THREE.Vector3(pt.x, point.y, point.z), new THREE.Vector3(point.x, point.y, point.z), new THREE.Vector3(point.x, pt.y, pt.z) ]); geometry.attributes.uv = new THREE.BufferAttribute(new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]), 2); geometry.setIndex([0, 1, 2, 0, 2, 3]); // 创建材质(半透明的颜色) const material = new THREE.MeshBasicMaterial({ color: 0x004D00, transparent: true, opacity: 0.5, side: THREE.DoubleSide }); const mesh = new THREE.Mesh(geometry, material); pw.drawEntity(mesh); }); const nextPt = await getEntityPt.go(); if (!nextPt) break; const ss = new MxCADSelectionSet(); await ss.crossingSelect(pt.x, pt.y, nextPt.x, nextPt.y); ss.forEach(id => { if (!ids.map(i => i.id).includes(id.id)) { const ent = id.getMcDbEntity(); const arr = getGroupForEntity(ent); if (arr.length) { const group = arr[0].group; group.getAllEntityId().forEach(id => { id.getMcDbEntity().highlight(true) ids.push(id); }) } else { ent.highlight(true); ids.push(id); } } }); continue; } else { continue; }; } }}2. 解除组
解除组的功能流程如下:
命令启动后,系统提示用户“选择组”,并支持通过关键字 [名称(N)] 切换为按名称分解模式。在用户操作过程中,系统启用悬停预览功能:当鼠标移动到某个对象上时,会自动查询该对象所属的组,并高亮显示该组内的所有成员对象,便于用户直观判断将要操作的范围。
接下来,根据用户的选择进入不同分支:
若用户输入 N(名称):
- 进入“按名称分解”模式,提示“输入编组名”。
- 支持输入关键字
[查询(A)]:- 若输入
A,可进一步输入要查询的组名;- 输入
*或直接回车,则列出当前图形中所有已定义的组名;- 输入具体名称,则检查并显示该组是否存在。
- 用户输入组名后,系统查找对应组:
- 若存在,执行分解操作(清空组内对象并从组字典中移除),提示“组 已分解”;
- 若不存在,提示“编组 未定义”,并允许重新输入。
若用户点击某个对象:
- 系统获取该对象,并查询其所属的所有组(一个对象可能属于多个组)。
- 若对象仅属于一个组,则直接选中该组,准备分解。
- 若对象属于多个组,则进入选择流程:
- 提示“对象是多个组的成员<接受>”,提供
[接受(A)/下一个(N)]选项;- 选择
A:接受当前高亮的组;- 选择
N:切换到下一个组,并更新高亮显示;- 可循环切换,直到用户确认或取消。 - 确定目标组后,记录其名称。
最后,系统根据选定的组名执行分解操作:
从组字典中获取该组对象;
调用
clear()清空组内成员引用;调用
remove()从字典中删除该组;提示“组 已分解”或“对象不是组成员”(如未选中有效组)。
操作完成后,清除所有高亮显示的对象,确保界面恢复整洁,命令结束。其具体实现代码如下:
import { McDbEntity, McDbGroup, McDbPolyline, McGePoint3d, McObjectId, MxCADSelectionSet, MxCADUiPrKeyWord, MxCADUiPrPoint, MxCADUiPrString, MxCADUtility, MxCpp } from "mxcad";import { DetailedResult, MxFun, MrxDbgUiPrBaseReturn } from "mxdraw";// 解除编组async function Mx_Ungroup() { const ents: McDbEntity[] = []; let groupArr: GroupObject[] = []; let name!: string; const database = MxCpp.getCurrentDatabase(); const groupDict = database.GetGroupDictionary(); let index: number = 0; const getEnt = new MxCADUiPrEntity(); getEnt.setMessage('选择组'); getEnt.setKeyWords(`[名称(N)]`); getEnt.setUserDraw((pt, pw) => { ents.forEach(ent => ent.highlight(false)); ents.length = 0; const entId = MxCADUtility.findEntAtPoint(pt.x, pt.y, pt.z, -1); if (entId.isValid()) { const ent = entId.getMcDbEntity(); groupArr = getGroupForEntity(ent);//getGroupForEntity参考上述创建组内代码 if (groupArr.length) { const group = groupArr[index].group; group.getAllEntityId().forEach(id => { const entity = id.getMcDbEntity(); entity.highlight(true); ents.push(entity); }) } } }); const entId = await getEnt.go(); if (getEnt.getStatus() === MrxDbgUiPrBaseReturn.kKeyWord) { if (getEnt.isKeyWordPicked('N')) { while (true) { const getString = new MxCADUiPrString(); getString.setMessage('输入编组名'); getString.setKeyWords(`[查询(A)]`); const str = await getString.go(); if (getString.getStatus() === MrxDbgUiPrBaseReturn.kOk) { // 删除组 const groupId = groupDict.getAt(str); const group = groupId.getMcDbObject() as McDbGroup; if (groupId.isValid() && group) { group.clear(); groupDict.remove(str); MxPluginContext.useMessage().success('组 ' + str + ' 已分解'); if (ents.length) ents.forEach(ent => ent.highlight(false)); return; } else { MxFun.acutPrintf('编组 ' + str + ' 未定义'); continue; } } else if (getString.getStatus() === MrxDbgUiPrBaseReturn.kKeyWord) { // 查询组 getString.setMessage("请输入要列出的编码组名" + "<*>") getString.setKeyWords("") const name = await getString.go(); if (getString.getStatus() === MrxDbgUiPrBaseReturn.kOk) { if (name && name !== "*") { const groupId = groupDict.getAt(name) const group = groupId.getMcDbObject() as McDbGroup MxFun.acutPrintf(`\n 定义的编组:`) if (group) { MxFun.acutPrintf(`\n${group.name}`) } } else if (name === "*") { const groupIds = groupDict.getAllObject() MxFun.acutPrintf(`\n 定义的编组:`) groupIds.forEach((groupId) => { const group = groupId.getMcDbObject() as McDbGroup group && MxFun.acutPrintf(`\n ${group.name}`) }) } } else if (getString.getStatus() === MrxDbgUiPrBaseReturn.kNone) { const groupIds = groupDict.getAllObject() MxFun.acutPrintf(`\n 定义的编组:`) groupIds.forEach((groupId) => { const group = groupId.getMcDbObject() as McDbGroup group && MxFun.acutPrintf(`\n ${group.name}`) }) } continue; } } } } else if (getEnt.getStatus() === MrxDbgUiPrBaseReturn.kOk) { if (groupArr.length === 1) { name = groupArr[0].name } else if (groupArr.length > 1) { while (true) { const getKeys = new MxCADUiPrKeyWord(); getKeys.setMessage('对象是多个组的成员<接受>') getKeys.setKeyWords('[接受(A)/下一个(N)]'); let key = await getKeys.go(); if (key === "A") { name = groupArr[index].name; break; } else if (key === "N") { ents.forEach(ent => ent.highlight(false)); ents.length = 0; index + 1 > groupArr.length - 1 ? index = 0 : index += 1; const res = groupArr[index]; res.group.getAllEntityId().forEach(id => { const ent = id.getMcDbEntity(); ent.highlight(true); ents.push(ent); }); continue; } else { if (ents.length) ents.forEach(ent => ent.highlight(false)); return; } } } if (name) { const groupId = groupDict.getAt(name) const group = groupId.getMcDbObject() as McDbGroup if (group) { group.clear(); groupDict.remove(name); MxPluginContext.useMessage().success(`组 ${name} 已分解`); } else { MxPluginContext.useMessage().success('对象不是组成员'); } } else { MxPluginContext.useMessage().success('对象不是组成员'); }; if (ents.length) ents.forEach(ent => ent.highlight(false)); }}3. 编辑组
编辑图形中已有对象组(Group)的交互式功能。其主要功能是允许用户通过选择对象或输入组名的方式,找到目标组,并对其进行添加成员、删除成员或重命名等操作。命令启动后,系统首先提示“选择组”,并支持通过关键字 [名称(N)] 切换为按名称选择模式。在用户移动鼠标时,系统会启用悬停预览功能:自动检测光标下的对象,查询其所属的组,并高亮显示该组内的所有成员,帮助用户直观判断当前将要操作的对象范围。
如果用户点击了一个对象,系统会获取该对象所属的所有组:
若对象不属于任何组,则提示“对象不是组成员”;
若只属于一个组,则直接进入编辑操作;
若属于多个组,则提示“对象是多个组的成员<接受>”,并提供
[接受(A)/下一个(N)]选项,用户可循环切换高亮不同的组,直到确认目标组。
如果用户选择 [名称(N)] 模式,则进入按名称编辑流程:
提示“输入组的名称”,并支持
[查询(A)]关键字;输入
A后可查看所有组名(输入*)或查询特定组是否存在;输入有效组名后,若组存在,则加载并高亮其成员,进入编辑;若不存在,则提示“编组 xxx 不存在”,并允许重新输入。确定目标组后,系统弹出操作菜单:
[添加对象(A)/删除对象(R)/重命名(REN)]。添加对象(A):用户可通过单击或框选方式选择要加入的对象。系统会动态高亮预览可添加的对象(不包括已存在于组内的对象),支持窗口和交叉选择,完成后将所选对象追加到组中,并提示“添加对象成功!”。
删除对象(R):用户选择组内对象进行移除。系统仅允许删除当前组中的成员,选择后会从组中剔除这些对象,并通过清空后重新添加剩余对象的方式更新组内容。
重命名(REN):提示用户输入新名称。支持再次使用
[查询(A)]查看现有组名以避免冲突。若新名称已被其他组使用,则提示“编组 xxx 已经存在”并要求重新输入;否则更新组名,并提示“修改组名成功”。
实现上述流程的具体功能代码如下:
import { McDbEntity, McDbGroup, McDbPolyline, McGePoint3d, McObjectId, MxCADSelectionSet, MxCADUiPrKeyWord, MxCADUiPrPoint, MxCADUiPrString, MxCADUtility, MxCpp } from "mxcad";import { DetailedResult, MxFun, MrxDbgUiPrBaseReturn } from "mxdraw";// 编辑组async function Mx_Groupedit() { const ents: McDbEntity[] = [];//高亮实体数组 let groupArr: GroupObject[] = [];//实体组集合 let index: number = 0; let name: string = ''; const database = MxCpp.getCurrentDatabase(); const groupDict = database.GetGroupDictionary(); const mxcad = MxCpp.getCurrentMxCAD(); const editGroup = async () => { // 选中目标组 if (groupArr.length === 1) { name = groupArr[0].name } else if (groupArr.length > 1) { while (true) { const getKeys = new MxCADUiPrKeyWord(); getKeys.setMessage('对象是多个组的成员<接受>') getKeys.setKeyWords(`[接受(A)/下一个(N)]`); let key = await getKeys.go(); if (key === "A") { name = groupArr[index].name; break; } else if (key === "N") { ents.forEach(ent => ent.highlight(false)); ents.length = 0; index + 1 > groupArr.length - 1 ? index = 0 : index += 1; const res = groupArr[index]; res.group.getAllEntityId().forEach(id => { const ent = id.getMcDbEntity(); ent.highlight(true); ents.push(ent); }); continue; } else { continue; } } } else { name = ''; } // 操作目标组 if (name) { const groupId = groupDict.getAt(name) const group = groupId.getMcDbObject() as McDbGroup if (group) { // 进入编辑组 const getKey = new MxCADUiPrKeyWord(); getKey.setMessage(t('输入选项')); getKey.setKeyWords(`[添加对象(A)/删除对象(R)/重命名(REN)]`); const key = await getKey.go(); if (!key) return; if (key === 'A') { const selectIds: McObjectId[] = []; // 添加对象 while (true) { const getEntityPt = new MxCADUiPrPoint(); getEntityPt.setMessage('选择要添加到编组的对象'); getEntityPt.setDisableOsnap(true); getEntityPt.setDisableDynInput(true); getEntityPt.disableAllTrace(true); const hoverSelectEnts: McDbEntity[] = []; getEntityPt.setUserDraw((pt, pw) => { if (hoverSelectEnts.length) hoverSelectEnts.forEach(ent => { if (!ents.map(i => i.getObjectID().id).includes(ent.getObjectID().id)) ent.highlight(false); }); hoverSelectEnts.length = 0; const entId = MxCADUtility.findEntAtPoint(pt.x, pt.y, pt.z, -1); if (entId.isValid() && !selectIds.map(item => item.id).includes(entId.id) && !group.has(entId)) { const ent = entId.getMcDbEntity(); const arr = getGroupForEntity(ent); if (arr.length) { const group = arr[0].group; group.getAllEntityId().forEach(id => { const ent = id.getMcDbEntity(); ent.highlight(true); hoverSelectEnts.push(ent) }) } else { ent.highlight(true); hoverSelectEnts.push(ent) } } }); const pt = await getEntityPt.go(); if (!pt) { if (hoverSelectEnts.length) hoverSelectEnts.forEach(item => item.highlight(false)); break; } else { // 判断是否选中实体 if (hoverSelectEnts.length) { if (hoverSelectEnts.length) { hoverSelectEnts.forEach(ent => { selectIds.push(ent.getObjectID()); }) }; } else { getEntityPt.setUserDraw((point, pw) => { const pts = [pt, new McGePoint3d(pt.x, point.y), point, new McGePoint3d(point.x, pt.y)] // 设置范围框颜色即位置 let pl = new McDbPolyline(); pl.isClosed = true; pts.forEach(pt => pl.addVertexAt(pt)); pw.setColor(0xFFFFFF); pw.drawMcDbEntity(pl); // 动态绘制矩形填充框 const geometry = new THREE.BufferGeometry(); geometry.setFromPoints([ new THREE.Vector3(pt.x, pt.y, pt.z), new THREE.Vector3(pt.x, point.y, point.z), new THREE.Vector3(point.x, point.y, point.z), new THREE.Vector3(point.x, pt.y, pt.z) ]); geometry.attributes.uv = new THREE.BufferAttribute(new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]), 2); geometry.setIndex([0, 1, 2, 0, 2, 3]); // 创建材质(半透明的颜色) const material = new THREE.MeshBasicMaterial({ color: 0x004D00, transparent: true, opacity: 0.5, side: THREE.DoubleSide }); const mesh = new THREE.Mesh(geometry, material); pw.drawEntity(mesh); }); const nextPt = await getEntityPt.go(); if (!nextPt) break; const ss = new MxCADSelectionSet(); await ss.crossingSelect(pt.x, pt.y, nextPt.x, nextPt.y); ss.forEach(id => { if (!group.has(id) && !selectIds.map(i => i.id).includes(id.id)) { const ent = id.getMcDbEntity(); const arr = getGroupForEntity(ent); if (arr.length) { const group = arr[0].group; group.getAllEntityId().forEach(id => { id.getMcDbEntity()?.highlight(true); selectIds.push(id); }) } else { id.getMcDbEntity()?.highlight(true); selectIds.push(id); } } }); }; continue; } }; if (selectIds.length) { selectIds.forEach(id => { id.getMcDbEntity().highlight(false); group.append(id); }); MxPluginContext.useMessage().success('添加对象成功!'); } } else if (key === 'R') { const selectIds: McObjectId[] = []; while (true) { const getEntityPt = new MxCADUiPrPoint(); getEntityPt.setMessage('选择要从编组中删除的对象'); getEntityPt.setDisableOsnap(true); getEntityPt.setDisableDynInput(true); getEntityPt.disableAllTrace(true); const hoverSelectEnts: McDbEntity[] = []; getEntityPt.setUserDraw((pt, pw) => { const entId = MxCADUtility.findEntAtPoint(pt.x, pt.y, pt.z, -1); hoverSelectEnts.forEach(e => { if (!group.has(e.getObjectID())) { e.highlight(false) } }); hoverSelectEnts.length = 0; if (entId.isValid() && !selectIds.map(i => i.id).includes(entId.id)) { const ent = entId.getMcDbEntity(); ent.highlight(true); hoverSelectEnts.push(ent) } }); const pt = await getEntityPt.go(); if (!pt) { break; } else { // 判断是否选中实体 if (hoverSelectEnts.length) { hoverSelectEnts.forEach(ent => { ent.highlight(false); if (group.has(ent.getObjectID())) { selectIds.push(ent.getObjectID()) } else { MxFun.acutPrintf('对象不是组内元素,无法删除') } }) } else { getEntityPt.setUserDraw((point, pw) => { const pts = [pt, new McGePoint3d(pt.x, point.y), point, new McGePoint3d(point.x, pt.y)] // 设置范围框颜色即位置 let pl = new McDbPolyline(); pl.isClosed = true; pts.forEach(pt => pl.addVertexAt(pt)); pw.setColor(0xFFFFFF); pw.drawMcDbEntity(pl); // 动态绘制矩形填充框 const geometry = new THREE.BufferGeometry(); geometry.setFromPoints([ new THREE.Vector3(pt.x, pt.y, pt.z), new THREE.Vector3(pt.x, point.y, point.z), new THREE.Vector3(point.x, point.y, point.z), new THREE.Vector3(point.x, pt.y, pt.z) ]); geometry.attributes.uv = new THREE.BufferAttribute(new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]), 2); geometry.setIndex([0, 1, 2, 0, 2, 3]); // 创建材质(半透明的颜色) const material = new THREE.MeshBasicMaterial({ color: 0x004D00, transparent: true, opacity: 0.5, side: THREE.DoubleSide }); const mesh = new THREE.Mesh(geometry, material); pw.drawEntity(mesh); }); const nextPt = await getEntityPt.go(); if (!nextPt) break; const ss = new MxCADSelectionSet(); await ss.crossingSelect(pt.x, pt.y, nextPt.x, nextPt.y); ss.forEach(id => { if (group.has(id)) { const ent = id.getMcDbEntity(); ent.highlight(false); selectIds.push(ent.getObjectID()); } }); }; continue; } }; if (selectIds.length) { const newIds = ents.filter(ent => !selectIds.map(i => i.id).includes(ent.getObjectID().id)).map(ent => ent.getObjectID()); group.clear(); group.appendArray(newIds); } } else if (key === 'REN') { while (true) { const getName = new MxCADUiPrString() getName.setMessage("输入组的新名称" + `<${group.name}>`) getName.setKeyWords('查询(A)]') const str = await getName.go(); if (getName.getStatus() === MrxDbgUiPrBaseReturn.kKeyWord) { if (getName.isKeyWordPicked("A")) { getName.setMessage("请输入要列出的编码组名" + "<*>") const name = await getName.go(); if (getName.getStatus() === MrxDbgUiPrBaseReturn.kOk) { if (name && name !== "*") { const groupId = groupDict.getAt(name) const group = groupId.getMcDbObject() as McDbGroup MxFun.acutPrintf('定义的编组') if (group) { MxFun.acutPrintf(`\n${group.name}`) } } else if (name === "*") { const groupIds = groupDict.getAllObject() MxFun.acutPrintf(`\n 定义的编组:`) groupIds.forEach((groupId) => { const group = groupId.getMcDbObject() as McDbGroup group && MxFun.acutPrintf(`\n ${group.name}`) }) } } else { const groupIds = groupDict.getAllObject() MxFun.acutPrintf(`\n 定义的编组:`) groupIds.forEach((groupId) => { const group = groupId.getMcDbObject() as McDbGroup group && MxFun.acutPrintf(`\n ${group.name}`) }) } continue; } } else if (getName.getStatus() === MrxDbgUiPrBaseReturn.kOk) { const groupId = groupDict.getAt(str) const _group = groupId.getMcDbObject() as McDbGroup if (_group && groupId.isValid()) { MxFun.acutPrintf(`编组 ${str} 已经存在}`); continue; } else { group.name = str; MxPluginContext.useMessage().success('修改组名成功'); break; } } else { break; } } } } else { MxPluginContext.useMessage().success('对象不是组成员'); } } if (ents.length) ents.forEach(ent => ent.highlight(false)); mxcad.updateDisplay(); } const getEnt = new MxCADUiPrEntity(); getEnt.setMessage('选择组'); getEnt.setKeyWords('[名称(N)]'); getEnt.setUserDraw((pt, pw) => { ents.forEach(ent => ent.highlight(false)); ents.length = 0; const entId = MxCADUtility.findEntAtPoint(pt.x, pt.y, pt.z, -1); if (entId.isValid()) { const ent = entId.getMcDbEntity(); groupArr = getGroupForEntity(ent); if (groupArr.length) { const group = groupArr[index].group; group.getAllEntityId().forEach(id => { const entity = id.getMcDbEntity(); entity.highlight(true); ents.push(entity); }) } } }); const entId = await getEnt.go(); if (getEnt.getStatus() === MrxDbgUiPrBaseReturn.kKeyWord) { if (getEnt.isKeyWordPicked('N')) { // 选择关键字 while (true) { const getName = new MxCADUiPrString() getName.setMessage("输入组的名称") getName.setKeyWords('[查询(A)]') const str = await getName.go(); if (getName.getStatus() === MrxDbgUiPrBaseReturn.kKeyWord) { if (getName.isKeyWordPicked("A")) { getName.setMessage("请输入要列出的编码组名" + "<*>") getName.setKeyWords("") const name = await getName.go(); if (getName.getStatus() === MrxDbgUiPrBaseReturn.kOk) { if (name && name !== "*") { const groupId = groupDict.getAt(name) const group = groupId.getMcDbObject() as McDbGroup MxFun.acutPrintf('定义的编组') if (group) { MxFun.acutPrintf(`\n${group.name}`) } } else if (name === "*") { const groupIds = groupDict.getAllObject() MxFun.acutPrintf(`\n 定义的编组:`) groupIds.forEach((groupId) => { const group = groupId.getMcDbObject() as McDbGroup group && MxFun.acutPrintf(`\n ${group.name}`) }) } } else { const groupIds = groupDict.getAllObject() MxFun.acutPrintf(`\n 定义的编组:`) groupIds.forEach((groupId) => { const group = groupId.getMcDbObject() as McDbGroup group && MxFun.acutPrintf(`\n ${group.name}`) }) } continue; } } else if (getName.getStatus() === MrxDbgUiPrBaseReturn.kOk) { const groupId = groupDict.getAt(str) const group = groupId.getMcDbObject() as McDbGroup if (group && groupId.isValid()) { group.getAllEntityId().forEach(id => { const ent = id.getMcDbEntity(); ent.highlight(true); ents.push(ent); }) groupArr.push({ name: group.name, group }); editGroup() break; } else { MxFun.acutPrintf(`编组 ${str} 不存在`); continue; }; } else { break; } } } } else if (getEnt.getStatus() === MrxDbgUiPrBaseReturn.kOk) { editGroup(); } else { if (ents.length) ents.forEach(ent => ent.highlight(false)); }}4. 启用或禁用组选择
启用指定对象组的选择功能其执行过程如下:首先提示用户“请选择目标组”,并在鼠标悬停时自动检测光标下的对象,若该对象属于某个组,则实时高亮显示该组的所有成员,提供可视化反馈。用户点击对象后,系统获取其所属的第一个组,并将该组的 isSelectable 属性设置为 true,从而允许后续通过点击组内任意成员来选中整个组。最后清除高亮并刷新显示,完成设置。该方法提升了组对象的操作便捷性,适用于需要快速选中成组元素的场景。其完整代码如下:
import { MxCADUiPrEntity, MxCADUtility, MxCpp} from "mxcad";// 启用/禁用组选择async function Mx_SetGroupSelection() { const ents: McDbEntity[] = []; let groupArr: GroupObject[] = []; const getEnt = new MxCADUiPrEntity(); getEnt.setMessage('请选择目标组'); getEnt.setUserDraw((pt, pw) => { ents.forEach(ent => ent.highlight(false)); ents.length = 0; const entId = MxCADUtility.findEntAtPoint(pt.x, pt.y, pt.z, -1); if (entId.isValid()) { const ent = entId.getMcDbEntity(); groupArr = getGroupForEntity(ent); if (groupArr.length) { const group = groupArr[0].group; group.getAllEntityId().forEach(id => { const entity = id.getMcDbEntity(); entity.highlight(true); ents.push(entity); }) } } }); const entId = await getEnt.go(); if (groupArr.length) { const group = groupArr[0].group; group.isSelectable = true; ents.forEach(ent => { ent.highlight(false); }) MxCpp.getCurrentMxCAD().updateDisplay(); };}三、功能演示
WEB CAD SDK
还未添加个人签名 2024-10-16 加入
还未添加个人简介







评论