网页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();
    };
}

三、功能演示

相关推荐
讨厌吃蛋黄酥4 小时前
🔥 JavaScript异步之谜:单线程如何实现“同时”做多件事?99%的人都理解错了!
前端·javascript·面试
华仔啊4 小时前
别再纠结Pinia和Vuex了!一篇文章彻底搞懂区别与选择
前端·vue.js
徐同保4 小时前
Redux和@reduxjs/toolkit同时在Next.js项目中使用
开发语言·前端·javascript
~无忧花开~4 小时前
CSS学习笔记(二):CSS动画核心属性全解析
开发语言·前端·css·笔记·学习·css3·动画
渣哥4 小时前
Spring Boot 本质揭秘:约定优于配置 + 自动装配
javascript·后端·面试
颜酱4 小时前
了解 pnpm 的优势,然后将已有项目的 yarn 换成 pnpm
前端·javascript·前端工程化
米诺zuo5 小时前
不同的项目仓库配置不同的github账号
github
海在掘金611275 小时前
从"鬼知道这对象有啥"到"一目了然" - TS接口的实战魔力
前端
spionbo5 小时前
Vue 模拟键盘组件封装方法与使用技巧详解
前端