网页CAD中组(Group)功能的二次开发

前言

在使用CAD工具进行绘图时,面对复杂的图形结构,如何高效地管理多个对象成为提升工作效率的关键。CAD提供的"组"功能,正是为解决这一问题而设计的实用工具。本文将全面介绍 mxcad 中"组"的概念,以及如何实现组相关的功能开发。

一、什么是"组"(Group)?

在CAD中,组(Group) 是指将多个图形对象逻辑地组合在一起,形成一个可被统一操作的集合。组不会创建新的图元实体,也不会改变对象本身的几何属性,仅是一种命名的对象集合

组对象包含特点如下:

  • 组内的对象保持独立,可单独编辑。

  • 选择组中任意一个对象时,整个组可被选中(取决于系统设置)。

  • 每个组有唯一的名称,便于识别和管理。

  • 支持嵌套:一个组可以包含另一个组,形成层级结构。

  • 组不作为独立实体存储在图形数据库中,仅作为对象的逻辑关联存在。

二、组的核心功能开发

1. 创建组

该功能流程是从用户执行"创建组"命令开始。首先,系统初始化相关变量(如组名、描述和对象列表),并获取当前图形数据库中的组管理字典。 随后进入主循环,提示用户"选择对象"。用户可以通过点击或框选方式选择一个或多个图形对象,所选对象的ID将被保存到临时列表中。

在选择过程中,用户可随时输入关键字进行设置:

  • 输入 N(名称) :进入命名流程,系统提示"输入编组名"。此时可输入 [查询(A)] 来查看已存在的组名;若输入 * 或直接回车,则列出所有组;否则查询指定组信息。输入名称后,系统检查是否重名,若无冲突则保存名称并返回选择状态。

  • 输入 D(说明):进入说明设置,提示"输入编组说明",用户输入的文本将作为该组的描述信息。

当用户完成选择并按 回车或空格键 确认后,系统开始创建组:

  • 首先检查所选对象中是否有成员已属于其他组。
  • 若存在此类情况,则弹出确认提示:"包含相同对象的组已经存在。仍要创建新的组?",并提供"是(Y)/否(N)"选项。

  • 若用户选择"否"或取消操作,命令终止。

  • 若用户确认继续或无冲突,则调用底层API创建组,并将之前输入的描述信息赋值给新组。

最后,组创建完成,系统退出循环,命令执行结束。整个流程支持ESC中断或新命令打断,确保操作的安全性和灵活性。

根据上述流程调用 mxcad 内部API接口实现方法如下:

ts 复制代码
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)] 切换为按名称分解模式。在用户操作过程中,系统启用悬停预览功能:当鼠标移动到某个对象上时,会自动查询该对象所属的组,并高亮显示该组内的所有成员对象,便于用户直观判断将要操作的范围。

接下来,根据用户的选择进入不同分支:

1.若用户输入 N(名称):

  • 进入"按名称分解"模式,提示"输入编组名"。

  • 支持输入关键字 [查询(A)]

  • 若输入 A,可进一步输入要查询的组名;

  • 输入 * 或直接回车,则列出当前图形中所有已定义的组名;

  • 输入具体名称,则检查并显示该组是否存在。

  • 用户输入组名后,系统查找对应组:

  • 若存在,执行分解操作(清空组内对象并从组字典中移除),提示"组 已分解";

  • 若不存在,提示"编组 未定义",并允许重新输入。

2.若用户点击某个对象:

  • 系统获取该对象,并查询其所属的所有组(一个对象可能属于多个组)。

  • 若对象仅属于一个组,则直接选中该组,准备分解。

  • 若对象属于多个组,则进入选择流程:

  • 提示"对象是多个组的成员<接受>",提供 [接受(A)/下一个(N)] 选项;

  • 选择 A:接受当前高亮的组;

  • 选择 N:切换到下一个组,并更新高亮显示;

  • 可循环切换,直到用户确认或取消。

  • 确定目标组后,记录其名称。

最后,系统根据选定的组名执行分解操作:

  • 从组字典中获取该组对象;

  • 调用 clear() 清空组内成员引用;

  • 调用 remove() 从字典中删除该组;

  • 提示"组 已分解"或"对象不是组成员"(如未选中有效组)。

操作完成后,清除所有高亮显示的对象,确保界面恢复整洁,命令结束。其具体实现代码如下:

ts 复制代码
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 已经存在"并要求重新输入;否则更新组名,并提示"修改组名成功"。

实现上述流程的具体功能代码如下:

ts 复制代码
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,从而允许后续通过点击组内任意成员来选中整个组。最后清除高亮并刷新显示,完成设置。该方法提升了组对象的操作便捷性,适用于需要快速选中成组元素的场景。其完整代码如下:

ts 复制代码
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();
    };
}

三、功能演示

相关推荐
JeffongTan3 小时前
在LWC中镶嵌VF Page获取用户IP
前端·javascript·salesforce
不cong明的亚子5 小时前
web live多屏互动平台
javascript·经验分享·音视频
陆枫Larry5 小时前
Astro 是什么
前端
是立不是利6 小时前
写给设计师的 CSS 博客美学指南
前端·css
dsyyyyy11017 小时前
JS复习,难点,易错点总结
开发语言·javascript·ecmascript
默_笙7 小时前
🏝 Docker 就是"房地产开发":从施工图纸到小区物业的容器化指南
前端·javascript
计算机魔术师7 小时前
Rohan Paul 谈用时间跨度衡量智能体能力,并引用 OpenAI 自动化研究实习生里程碑
前端
峰向AI7 小时前
告别 API 费用! 这个开源工具让你的 AI 助手在本地运行
github
kyriewen7 小时前
我手写了个极简版 React Router——才搞懂 v6 为什么砍了这么多 API
前端·javascript·程序员
a1117767 小时前
Shirone 二次元博客主题 开源
前端·开源