明经CAD社区

 找回密码
 注册

QQ登录

只需一步,快速开始

搜索
查看: 94|回复: 0

在线CAD的“组”Group功能二次开发(WEB CAD SDK)

[复制链接]
发表于 昨天 16:31 | 显示全部楼层 |阅读模式
本帖最后由 MxDraw 于 2025-11-14 16:32 编辑

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

一、什么是Group)?
CAD中,**组(Group** 是指将多个图形对象逻辑地组合在一起,形成一个可被统一操作的集合。组不会创建新的图元实体,也不会改变对象本身的几何属性,仅是一种**命名的对象集合**组对象包含特点如下:
- 组内的对象保持独立,可单独编辑。
- 选择组中任意一个对象时,整个组可被选中(取决于系统设置)。
- 每个组有唯一的名称,便于识别和管理。
- 支持嵌套:一个组可以包含另一个组,形成层级结构。
- 组不作为独立实体存储在图形数据库中,仅作为对象的逻辑关联存在。

二、组的核心功能开发
1. 创建组
该功能流程是从用户执行创建组命令开始。首先,系统初始化相关变量(如组名、描述和对象列表),并获取当前图形数据库中的组管理字典。随后进入主循环,提示用户选择对象。用户可以通过点击或框选方式选择一个或多个图形对象,所选对象的ID将被保存到临时列表中。
在选择过程中,用户可随时输入关键字进行设置:
- 输入 **N(名称)**:进入命名流程,系统提示输入编组名。此时可输入 `[查询(A)]` 来查看已存在的组名;若输入 `*` 或直接回车,则列出所有组;否则查询指定组信息。输入名称后,系统检查是否重名,若无冲突则保存名称并返回选择状态。
- 输入 **D(说明)**:进入说明设置,提示输入编组说明,用户输入的文本将作为该组的描述信息。

当用户完成选择并按 **回车或空格键** 确认后,系统开始创建组:
- 首先检查所选对象中是否有成员已属于其他组。
- 若存在此类情况,则弹出确认提示:包含相同对象的组已经存在。仍要创建新的组?<N>”,并提供(Y)/(N)”选项。
- 若用户选择或取消操作,命令终止。
- 若用户确认继续或无冲突,则调用底层API创建组,并将之前输入的描述信息赋值给新组。

最后,组创建完成,系统退出循环,命令执行结束。整个流程支持ESC中断或新命令打断,确保操作的安全性和灵活性。根据上述流程调用 mxcad 内部API接口实现方法如下:
  1. import { McDbEntity, McDbGroup, McDbPolyline, McGePoint3d, McObjectId, MxCADSelectionSet, MxCADUiPrKeyWord, MxCADUiPrPoint, MxCADUiPrString, MxCADUtility, MxCpp } from "mxcad";
  2. import { DetailedResult, MxFun, MrxDbgUiPrBaseReturn } from "mxdraw";
  3. interface GroupObject {
  4.     name: string,
  5.     group: McDbGroup
  6. }
  7. // 根据实体查找组
  8. const getGroupForEntity = (entity: McDbEntity): GroupObject[] => {
  9.     const database = MxCpp.getCurrentDatabase()
  10.     const groupDict = database.GetGroupDictionary()
  11.     const handle = entity.getHandle()
  12.     const groupNames = groupDict.getAllObjectName()
  13.     const length = groupNames.length();
  14.     let groupArr: GroupObject[] = [];
  15.     for (let index = 0; index < length; index++) {
  16.         const groupName = groupNames.at(index);
  17.         const groupId = groupDict.getAt(groupName)
  18.         const group = groupId.getMcDbObject() as McDbGroup
  19.         if (!group) continue;
  20.         const entityIds = group.getAllEntityId();
  21.         entityIds.forEach(entityId => {
  22.             if (entityId.getMcDbEntity()?.getHandle() === handle) groupArr.push({ name: groupName, group })
  23.         });
  24.     };
  25.     return groupArr
  26. }
  27. // 创建组
  28. async function Mx_Group() {
  29.   let description = ""
  30.   let ids: McObjectId[] = [];
  31.   const database = MxCpp.getCurrentDatabase();
  32.   const groupDict = database.GetGroupDictionary();
  33.   const mxcad = MxCpp.getCurrentMxCAD();
  34.   // 设定未命名组名
  35.   const groupNames = groupDict.getAllObjectName();
  36.   let num = 0;
  37.   groupNames.forEach(item => {
  38.     if (/^\*/.test(item)) {
  39.       num += 1;
  40.     }
  41.   });
  42.   let name: string = `*A${num + 1}`;
  43.   // 创建组
  44.   const createGroup = async () => {
  45.     const isPresence = ids.some((id) => {
  46.       return database.getEntitiesInTheGroup(id).length !== 0
  47.     })
  48.     if (isPresence) {
  49.       const getKey = new MxCADUiPrKeyWord();
  50.       getKey.setMessage(`包含相同对象的组已经存在。仍要创建新的组?<N>`);
  51.       getKey.setKeyWords(`[是(Y)/否(N)]`);
  52.       const key = await getKey.go();
  53.       ids.forEach(id => {
  54.         id.getMcDbEntity().highlight(false);
  55.       })
  56.       mxcad.updateDisplay();
  57.       if (key?.toLocaleUpperCase() === "N") {
  58.         return
  59.       }
  60.       if (!key) return
  61.     }
  62.     if (database.CreateGroup(ids, name)) {
  63.       const groupId = groupDict.getAt(name)
  64.       const group = groupId.getMcDbObject() as McDbGroup;
  65.       if (description) group.description = description;
  66.       if (/^\*/.test(name)) {
  67.         MxPluginContext.useMessage().success('未命名组已创建');
  68.       } else {
  69.         MxPluginContext.useMessage().success(`组${name}已创建`);
  70.       }
  71.       ids.forEach(id => {
  72.         id.getMcDbEntity().highlight(false);
  73.       })
  74.       mxcad.updateDisplay();
  75.     };
  76.   };
  77.   while (true) {
  78.     const getEntityPt = new MxCADUiPrPoint();
  79.     getEntityPt.setMessage('选择对象');
  80.     getEntityPt.setKeyWords(`[名称(N)/说明(D)]`);
  81.     getEntityPt.setDisableOsnap(true);
  82.     getEntityPt.setDisableDynInput(true);
  83.     getEntityPt.disableAllTrace(true);
  84.     const hoverSelectEnts: McDbEntity[] = [];
  85.     getEntityPt.setUserDraw((pt, pw) => {
  86.       if (hoverSelectEnts.length) hoverSelectEnts.forEach(ent => ent.highlight(false));
  87.       hoverSelectEnts.length = 0;
  88.       const entId = MxCADUtility.findEntAtPoint(pt.x, pt.y, pt.z, -1);
  89.       if (entId.isValid() && !ids.map(item => item.id).includes(entId.id)) {
  90.         const ent = entId.getMcDbEntity();
  91.         const arr = getGroupForEntity(ent);
  92.         if (arr.length) {
  93.           const group = arr[0].group;
  94.           group.getAllEntityId().forEach(id => {
  95.             const ent = id.getMcDbEntity();
  96.             ent.highlight(true);
  97.             hoverSelectEnts.push(ent)
  98.           })
  99.         } else {
  100.           ent.highlight(true);
  101.           hoverSelectEnts.push(ent)
  102.         }
  103.       }
  104.     });
  105.     const pt = await getEntityPt.go();
  106.     hoverSelectEnts.forEach(ent => ent.highlight(false));
  107.     // 如果选择关键字,则执行相关操作
  108.     if (getEntityPt.getStatus() == MrxDbgUiPrBaseReturn.kKeyWord) {
  109.       if (getEntityPt.isKeyWordPicked("N")) {
  110.         while (true) {
  111.           const getName = new MxCADUiPrString()
  112.           getName.setMessage("输入编组名")
  113.           getName.setKeyWords(`[查询(A)]`)
  114.           const str = await getName.go()
  115.           if (getName.getDetailedResult() === DetailedResult.kCodeAbort || getName.getDetailedResult() === DetailedResult.kEcsIn || getName.getDetailedResult() === DetailedResult.kNewCommadIn) return
  116.           if (getEntityPt.getDetailedResult() === DetailedResult.kNullEnterIn || getEntityPt.getDetailedResult() === DetailedResult.kNullSpaceIn || getEntityPt.getDetailedResult() === DetailedResult.kMouseRightIn) {
  117.             return createGroup()
  118.           }
  119.           if (getName.isKeyWordPicked("A")) {
  120.             getName.setMessage("请输入要列出的编码组名"+ "<*>")
  121.             getName.setKeyWords("")
  122.             const name = await getName.go();
  123.             if (getName.getDetailedResult() === DetailedResult.kCodeAbort || getName.getDetailedResult() === DetailedResult.kEcsIn || getName.getDetailedResult() === DetailedResult.kNewCommadIn) return
  124.             if (name && name !== "*") {
  125.               const groupId = groupDict.getAt(name)
  126.               const group = groupId.getMcDbObject() as McDbGroup
  127.               MxFun.acutPrintf(`\n 定义的编组:`)
  128.               if (group) {
  129.                 MxFun.acutPrintf(`\n${group.name}`)
  130.               }
  131.             }
  132.             else if (name === "*" || getName.getDetailedResult() === DetailedResult.kNullEnterIn || getName.getDetailedResult() === DetailedResult.kNullSpaceIn) {
  133.               const groupIds = groupDict.getAllObject()
  134.               MxFun.acutPrintf(`\n 定义的编组:`)
  135.               groupIds.forEach((groupId) => {
  136.                 const group = groupId.getMcDbObject() as McDbGroup
  137.                 group && MxFun.acutPrintf(`\n ${group.name}`)
  138.               })
  139.             }
  140.             continue;
  141.           }
  142.           if (!str) return;
  143.           if (/^\*/.test(str)) {
  144.             MxFun.acutPrintf(`*无效`);
  145.             continue;
  146.           }
  147.           const groupId = groupDict.getAt(str)
  148.           const group = groupId.getMcDbObject() as McDbGroup
  149.           if (group && groupId.isValid()) {
  150.             MxFun.acutPrintf(`编组${str} 已经存在`);
  151.             continue;
  152.           }
  153.           name = str;
  154.           if (ids.length) {
  155.             ids.forEach(id => {
  156.               const ent = id.getMcDbEntity();
  157.               ent.highlight(false);
  158.             })
  159.             return createGroup();
  160.           } else {
  161.             break;
  162.           }
  163.         }
  164.       } else if (getEntityPt.isKeyWordPicked('D')) {
  165.         const getName = new MxCADUiPrString()
  166.         getName.setMessage("输入编组说明")
  167.         const str = await getName.go();
  168.         if (!str) break;
  169.         description = str
  170.         continue;
  171.       }
  172.     } else if (getEntityPt.getStatus() === MrxDbgUiPrBaseReturn.kNone) {
  173.       if (!ids.length) {
  174.         return MxPluginContext.useMessage().success('未选择对象,未创建编组');
  175.       } else {
  176.         ids.forEach(id => {
  177.           const ent = id.getMcDbEntity();
  178.           ent.highlight(false);
  179.         })
  180.         return createGroup();
  181.       }
  182.     } else if (getEntityPt.getStatus() === MrxDbgUiPrBaseReturn.kCancel) {
  183.       ids.forEach(id => {
  184.         const ent = id.getMcDbEntity();
  185.         ent.highlight(false);
  186.       })
  187.       return
  188.     } else {
  189.       // 判断是否选中实体
  190.       if (pt && hoverSelectEnts.length) {
  191.         const selectIds = hoverSelectEnts.map(item => {
  192.           item.highlight(true);
  193.           return item.getObjectID()
  194.         })
  195.         ids.push(...selectIds);
  196.         continue;
  197.       } else if (pt && !hoverSelectEnts.length) {
  198.         getEntityPt.setUserDraw((point, pw) => {
  199.           const pts = [pt, new McGePoint3d(pt.x, point.y), point, new McGePoint3d(point.x, pt.y)]
  200.           // 设置范围框颜色即位置
  201.           let pl = new McDbPolyline();
  202.           pl.isClosed = true;
  203.           pts.forEach(pt => pl.addVertexAt(pt));
  204.           pw.setColor(0xFFFFFF);
  205.           pw.drawMcDbEntity(pl);
  206.           // 动态绘制矩形填充框
  207.           const geometry = new THREE.BufferGeometry();
  208.           geometry.setFromPoints([
  209.             new THREE.Vector3(pt.x, pt.y, pt.z),
  210.             new THREE.Vector3(pt.x, point.y, point.z),
  211.             new THREE.Vector3(point.x, point.y, point.z),
  212.             new THREE.Vector3(point.x, pt.y, pt.z)
  213.           ]);
  214.           geometry.attributes.uv = new THREE.BufferAttribute(new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]), 2);
  215.           geometry.setIndex([0, 1, 2, 0, 2, 3]);
  216.           // 创建材质(半透明的颜色)
  217.           const material = new THREE.MeshBasicMaterial({
  218.             color: 0x004D00,
  219.             transparent: true,
  220.             opacity: 0.5,
  221.             side: THREE.DoubleSide
  222.           });
  223.           const mesh = new THREE.Mesh(geometry, material);
  224.           pw.drawEntity(mesh);
  225.         });
  226.         const nextPt = await getEntityPt.go();
  227.         if (!nextPt) break;
  228.         const ss = new MxCADSelectionSet();
  229.         await ss.crossingSelect(pt.x, pt.y, nextPt.x, nextPt.y);
  230.         ss.forEach(id => {
  231.           if (!ids.map(i => i.id).includes(id.id)) {
  232.             const ent = id.getMcDbEntity();
  233.             const arr = getGroupForEntity(ent);
  234.             if (arr.length) {
  235.               const group = arr[0].group;
  236.               group.getAllEntityId().forEach(id => {
  237.                 id.getMcDbEntity().highlight(true)
  238.                 ids.push(id);
  239.               })
  240.             } else {
  241.               ent.highlight(true);
  242.               ids.push(id);
  243.             }
  244.           }
  245.         });
  246.         continue;
  247.       } else {
  248.         continue;
  249.       };
  250.     }
  251.   }
  252. }
复制代码

2. 解除组
解除组的功能流程如下:
命令启动后,系统提示用户选择组,并支持通过关键字 `[名称(N)]` 切换为按名称分解模式。在用户操作过程中,系统启用悬停预览功能:当鼠标移动到某个对象上时,会自动查询该对象所属的组,并高亮显示该组内的所有成员对象,便于用户直观判断将要操作的范围。接下来,根据用户的选择进入不同分支:

1. 若用户输入 N(名称)
   - 进入按名称分解模式,提示输入编组名
   - 支持输入关键字 `[查询(A)]`
   - 若输入 `A`,可进一步输入要查询的组名;
   - 输入 `*` 或直接回车,则列出当前图形中所有已定义的组名;
   - 输入具体名称,则检查并显示该组是否存在。
   - 用户输入组名后,系统查找对应组:
   - 若存在,执行分解操作(清空组内对象并从组字典中移除),提示组 已分解
   - 若不存在,提示编组 未定义,并允许重新输入。

2. 若用户点击某个对象
   - 系统获取该对象,并查询其所属的所有组(一个对象可能属于多个组)。
   - 若对象仅属于一个组,则直接选中该组,准备分解。
   - 若对象属于多个组,则进入选择流程:
   - 提示对象是多个组的成员<接受>”,提供 `[接受(A)/下一个(N)]` 选项;
   - 选择 `A`:接受当前高亮的组;
   - 选择 `N`:切换到下一个组,并更新高亮显示;
   - 可循环切换,直到用户确认或取消。
   - 确定目标组后,记录其名称。


最后,系统根据选定的组名执行分解操作:
- 从组字典中获取该组对象;
- 调用 `clear()` 清空组内成员引用;
- 调用 `remove()` 从字典中删除该组;
- 提示组 已分解对象不是组成员(如未选中有效组)。

操作完成后,清除所有高亮显示的对象,确保界面恢复整洁,命令结束。其具体实现代码如下:
<span style="font-weight: normal;"><font size="2">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));
    }
}</font></span>


3. 编辑组
编辑图形中已有对象组(Group)的交互式功能。其主要功能是允许用户通过选择对象或输入组名的方式,找到目标组,并对其进行**添加成员、删除成员或重命名**等操作。
命令启动后,系统首先提示选择组,并支持通过关键字 `[名称(N)]` 切换为按名称选择模式。在用户移动鼠标时,系统会启用悬停预览功能:自动检测光标下的对象,查询其所属的组,并高亮显示该组内的所有成员,帮助用户直观判断当前将要操作的对象范围。

如果用户点击了一个对象,系统会获取该对象所属的所有组:
- 若对象不属于任何组,则提示对象不是组成员
- 若只属于一个组,则直接进入编辑操作;
- 若属于多个组,则提示对象是多个组的成员<接受>”,并提供 `[接受(A)/下一个(N)]` 选项,用户可循环切换高亮不同的组,直到确认目标组。

如果用户选择 `[名称(N)]` 模式,则进入按名称编辑流程:
- 提示输入组的名称,并支持 `[查询(A)]` 关键字;
- 输入 `A` 后可查看所有组名(输入 `*`)或查询特定组是否存在;
- 输入有效组名后,若组存在,则加载并高亮其成员,进入编辑;若不存在,则提示编组 xxx 不存在,并允许重新输入。


确定目标组后,系统弹出操作菜单:`[添加对象(A)/删除对象(R)/重命名(REN)]`
- **添加对象(A**:用户可通过单击或框选方式选择要加入的对象。系统会动态高亮预览可添加的对象(不包括已存在于组内的对象),支持窗口和交叉选择,完成后将所选对象追加到组中,并提示添加对象成功!
- **删除对象(R**:用户选择组内对象进行移除。系统仅允许删除当前组中的成员,选择后会从组中剔除这些对象,并通过清空后重新添加剩余对象的方式更新组内容。
- **重命名(REN**:提示用户输入新名称。支持再次使用 `[查询(A)]` 查看现有组名以避免冲突。若新名称已被其他组使用,则提示编组 xxx 已经存在并要求重新输入;否则更新组名,并提示修改组名成功

实现上述流程的具体功能代码如下:
  1. import { McDbEntity, McDbGroup, McDbPolyline, McGePoint3d, McObjectId, MxCADSelectionSet, MxCADUiPrKeyWord, MxCADUiPrPoint, MxCADUiPrString, MxCADUtility, MxCpp } from "mxcad";
  2. import { DetailedResult, MxFun, MrxDbgUiPrBaseReturn } from "mxdraw";
  3. // 编辑组
  4. async function Mx_Groupedit() {
  5.     const ents: McDbEntity[] = [];//高亮实体数组
  6.     let groupArr: GroupObject[] = [];//实体组集合
  7.     let index: number = 0;
  8.     let name: string = '';
  9.     const database = MxCpp.getCurrentDatabase();
  10.     const groupDict = database.GetGroupDictionary();
  11.     const mxcad = MxCpp.getCurrentMxCAD();
  12.     const editGroup = async () => {
  13.         // 选中目标组
  14.         if (groupArr.length === 1) {
  15.             name = groupArr[0].name
  16.         } else if (groupArr.length > 1) {
  17.             while (true) {
  18.                 const getKeys = new MxCADUiPrKeyWord();
  19.                 getKeys.setMessage('对象是多个组的成员<接受>')
  20.                 getKeys.setKeyWords(`[接受(A)/下一个(N)]`);
  21.                 let key = await getKeys.go();
  22.                 if (key === "A") {
  23.                     name = groupArr[index].name;
  24.                     break;
  25.                 } else if (key === "N") {
  26.                     ents.forEach(ent => ent.highlight(false));
  27.                     ents.length = 0;
  28.                     index + 1 > groupArr.length - 1 ? index = 0 : index += 1;
  29.                     const res = groupArr[index];
  30.                     res.group.getAllEntityId().forEach(id => {
  31.                         const ent = id.getMcDbEntity();
  32.                         ent.highlight(true);
  33.                         ents.push(ent);
  34.                     });
  35.                     continue;
  36.                 } else {
  37.                     continue;
  38.                 }
  39.             }
  40.         } else {
  41.             name = '';
  42.         }
  43.         // 操作目标组
  44.         if (name) {
  45.             const groupId = groupDict.getAt(name)
  46.             const group = groupId.getMcDbObject() as McDbGroup
  47.             if (group) {
  48.                 // 进入编辑组
  49.                 const getKey = new MxCADUiPrKeyWord();
  50.                 getKey.setMessage(t('输入选项'));
  51.                 getKey.setKeyWords(`[添加对象(A)/删除对象(R)/重命名(REN)]`);
  52.                 const key = await getKey.go();
  53.                 if (!key) return;
  54.                 if (key === 'A') {
  55.                     const selectIds: McObjectId[] = [];
  56.                     // 添加对象
  57.                     while (true) {
  58.                         const getEntityPt = new MxCADUiPrPoint();
  59.                         getEntityPt.setMessage('选择要添加到编组的对象');
  60.                         getEntityPt.setDisableOsnap(true);
  61.                         getEntityPt.setDisableDynInput(true);
  62.                         getEntityPt.disableAllTrace(true);
  63.                         const hoverSelectEnts: McDbEntity[] = [];
  64.                         getEntityPt.setUserDraw((pt, pw) => {
  65.                             if (hoverSelectEnts.length) hoverSelectEnts.forEach(ent => {
  66.                                 if (!ents.map(i => i.getObjectID().id).includes(ent.getObjectID().id)) ent.highlight(false);
  67.                             });
  68.                             hoverSelectEnts.length = 0;
  69.                             const entId = MxCADUtility.findEntAtPoint(pt.x, pt.y, pt.z, -1);
  70.                             if (entId.isValid() && !selectIds.map(item => item.id).includes(entId.id) && !group.has(entId)) {
  71.                                 const ent = entId.getMcDbEntity();
  72.                                 const arr = getGroupForEntity(ent);
  73.                                 if (arr.length) {
  74.                                     const group = arr[0].group;
  75.                                     group.getAllEntityId().forEach(id => {
  76.                                         const ent = id.getMcDbEntity();
  77.                                         ent.highlight(true);
  78.                                         hoverSelectEnts.push(ent)
  79.                                     })
  80.                                 } else {
  81.                                     ent.highlight(true);
  82.                                     hoverSelectEnts.push(ent)
  83.                                 }
  84.                             }
  85.                         });
  86.                         const pt = await getEntityPt.go();
  87.                         if (!pt) {
  88.                             if (hoverSelectEnts.length) hoverSelectEnts.forEach(item => item.highlight(false));
  89.                             break;
  90.                         } else {
  91.                             // 判断是否选中实体
  92.                             if (hoverSelectEnts.length) {
  93.                                 if (hoverSelectEnts.length) {
  94.                                     hoverSelectEnts.forEach(ent => {
  95.                                         selectIds.push(ent.getObjectID());
  96.                                     })
  97.                                 };
  98.                             } else {
  99.                                 getEntityPt.setUserDraw((point, pw) => {
  100.                                     const pts = [pt, new McGePoint3d(pt.x, point.y), point, new McGePoint3d(point.x, pt.y)]
  101.                                     // 设置范围框颜色即位置
  102.                                     let pl = new McDbPolyline();
  103.                                     pl.isClosed = true;
  104.                                     pts.forEach(pt => pl.addVertexAt(pt));
  105.                                     pw.setColor(0xFFFFFF);
  106.                                     pw.drawMcDbEntity(pl);

  107.                                     // 动态绘制矩形填充框
  108.                                     const geometry = new THREE.BufferGeometry();
  109.                                     geometry.setFromPoints([
  110.                                         new THREE.Vector3(pt.x, pt.y, pt.z),
  111.                                         new THREE.Vector3(pt.x, point.y, point.z),
  112.                                         new THREE.Vector3(point.x, point.y, point.z),
  113.                                         new THREE.Vector3(point.x, pt.y, pt.z)
  114.                                     ]);
  115.                                     geometry.attributes.uv = new THREE.BufferAttribute(new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]), 2);
  116.                                     geometry.setIndex([0, 1, 2, 0, 2, 3]);
  117.                                     // 创建材质(半透明的颜色)
  118.                                     const material = new THREE.MeshBasicMaterial({
  119.                                         color: 0x004D00,
  120.                                         transparent: true,
  121.                                         opacity: 0.5,
  122.                                         side: THREE.DoubleSide
  123.                                     });
  124.                                     const mesh = new THREE.Mesh(geometry, material);
  125.                                     pw.drawEntity(mesh);
  126.                                 });
  127.                                 const nextPt = await getEntityPt.go();
  128.                                 if (!nextPt) break;
  129.                                 const ss = new MxCADSelectionSet();
  130.                                 await ss.crossingSelect(pt.x, pt.y, nextPt.x, nextPt.y);
  131.                                 ss.forEach(id => {
  132.                                     if (!group.has(id) && !selectIds.map(i => i.id).includes(id.id)) {
  133.                                         const ent = id.getMcDbEntity();
  134.                                         const arr = getGroupForEntity(ent);
  135.                                         if (arr.length) {
  136.                                             const group = arr[0].group;
  137.                                             group.getAllEntityId().forEach(id => {
  138.                                                 id.getMcDbEntity()?.highlight(true);
  139.                                                 selectIds.push(id);
  140.                                             })
  141.                                         } else {
  142.                                             id.getMcDbEntity()?.highlight(true);
  143.                                             selectIds.push(id);
  144.                                         }
  145.                                     }
  146.                                 });
  147.                             };
  148.                             continue;
  149.                         }
  150.                     };
  151.                     if (selectIds.length) {
  152.                         selectIds.forEach(id => {
  153.                             id.getMcDbEntity().highlight(false);
  154.                             group.append(id);
  155.                         });
  156.                         MxPluginContext.useMessage().success('添加对象成功!');
  157.                     }
  158.                 } else if (key === 'R') {
  159.                     const selectIds: McObjectId[] = [];
  160.                     while (true) {
  161.                         const getEntityPt = new MxCADUiPrPoint();
  162.                         getEntityPt.setMessage('选择要从编组中删除的对象');
  163.                         getEntityPt.setDisableOsnap(true);
  164.                         getEntityPt.setDisableDynInput(true);
  165.                         getEntityPt.disableAllTrace(true);
  166.                         const hoverSelectEnts: McDbEntity[] = [];
  167.                         getEntityPt.setUserDraw((pt, pw) => {
  168.                             const entId = MxCADUtility.findEntAtPoint(pt.x, pt.y, pt.z, -1);
  169.                             hoverSelectEnts.forEach(e => {
  170.                                 if (!group.has(e.getObjectID())) {
  171.                                     e.highlight(false)
  172.                                 }
  173.                             });
  174.                             hoverSelectEnts.length = 0;
  175.                             if (entId.isValid() && !selectIds.map(i => i.id).includes(entId.id)) {
  176.                                 const ent = entId.getMcDbEntity();
  177.                                 ent.highlight(true);
  178.                                 hoverSelectEnts.push(ent)
  179.                             }
  180.                         });
  181.                         const pt = await getEntityPt.go();
  182.                         if (!pt) {
  183.                             break;
  184.                         } else {
  185.                             // 判断是否选中实体
  186.                             if (hoverSelectEnts.length) {
  187.                                 hoverSelectEnts.forEach(ent => {
  188.                                     ent.highlight(false);
  189.                                     if (group.has(ent.getObjectID())) {
  190.                                         selectIds.push(ent.getObjectID())
  191.                                     } else {
  192.                                         MxFun.acutPrintf('对象不是组内元素,无法删除')
  193.                                     }
  194.                                 })
  195.                             } else {
  196.                                 getEntityPt.setUserDraw((point, pw) => {
  197.                                     const pts = [pt, new McGePoint3d(pt.x, point.y), point, new McGePoint3d(point.x, pt.y)]
  198.                                     // 设置范围框颜色即位置
  199.                                     let pl = new McDbPolyline();
  200.                                     pl.isClosed = true;
  201.                                     pts.forEach(pt => pl.addVertexAt(pt));
  202.                                     pw.setColor(0xFFFFFF);
  203.                                     pw.drawMcDbEntity(pl);

  204.                                     // 动态绘制矩形填充框
  205.                                     const geometry = new THREE.BufferGeometry();
  206.                                     geometry.setFromPoints([
  207.                                         new THREE.Vector3(pt.x, pt.y, pt.z),
  208.                                         new THREE.Vector3(pt.x, point.y, point.z),
  209.                                         new THREE.Vector3(point.x, point.y, point.z),
  210.                                         new THREE.Vector3(point.x, pt.y, pt.z)
  211.                                     ]);
  212.                                     geometry.attributes.uv = new THREE.BufferAttribute(new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]), 2);
  213.                                     geometry.setIndex([0, 1, 2, 0, 2, 3]);
  214.                                     // 创建材质(半透明的颜色)
  215.                                     const material = new THREE.MeshBasicMaterial({
  216.                                         color: 0x004D00,
  217.                                         transparent: true,
  218.                                         opacity: 0.5,
  219.                                         side: THREE.DoubleSide
  220.                                     });
  221.                                     const mesh = new THREE.Mesh(geometry, material);
  222.                                     pw.drawEntity(mesh);
  223.                                 });
  224.                                 const nextPt = await getEntityPt.go();
  225.                                 if (!nextPt) break;
  226.                                 const ss = new MxCADSelectionSet();
  227.                                 await ss.crossingSelect(pt.x, pt.y, nextPt.x, nextPt.y);
  228.                                 ss.forEach(id => {
  229.                                     if (group.has(id)) {
  230.                                         const ent = id.getMcDbEntity();
  231.                                         ent.highlight(false);
  232.                                         selectIds.push(ent.getObjectID());
  233.                                     }
  234.                                 });
  235.                             };
  236.                             continue;
  237.                         }
  238.                     };
  239.                     if (selectIds.length) {
  240.                         const newIds = ents.filter(ent => !selectIds.map(i => i.id).includes(ent.getObjectID().id)).map(ent => ent.getObjectID());
  241.                         group.clear();
  242.                         group.appendArray(newIds);
  243.                     }
  244.                 } else if (key === 'REN') {
  245.                     while (true) {
  246.                         const getName = new MxCADUiPrString()
  247.                         getName.setMessage("输入组的新名称" + `<${group.name}>`)
  248.                         getName.setKeyWords('查询(A)]')
  249.                         const str = await getName.go();
  250.                         if (getName.getStatus() === MrxDbgUiPrBaseReturn.kKeyWord) {
  251.                             if (getName.isKeyWordPicked("A")) {
  252.                                 getName.setMessage("请输入要列出的编码组名" + "<*>")
  253.                                 const name = await getName.go();
  254.                                 if (getName.getStatus() === MrxDbgUiPrBaseReturn.kOk) {
  255.                                     if (name && name !== "*") {
  256.                                         const groupId = groupDict.getAt(name)
  257.                                         const group = groupId.getMcDbObject() as McDbGroup
  258.                                         MxFun.acutPrintf('定义的编组')
  259.                                         if (group) {
  260.                                             MxFun.acutPrintf(`\n${group.name}`)
  261.                                         }
  262.                                     } else if (name === "*") {
  263.                                         const groupIds = groupDict.getAllObject()
  264.                                         MxFun.acutPrintf(`\n 定义的编组:`)
  265.                                         groupIds.forEach((groupId) => {
  266.                                             const group = groupId.getMcDbObject() as McDbGroup
  267.                                             group && MxFun.acutPrintf(`\n ${group.name}`)
  268.                                         })
  269.                                     }
  270.                                 } else {
  271.                                     const groupIds = groupDict.getAllObject()
  272.                                     MxFun.acutPrintf(`\n 定义的编组:`)
  273.                                     groupIds.forEach((groupId) => {
  274.                                         const group = groupId.getMcDbObject() as McDbGroup
  275.                                         group && MxFun.acutPrintf(`\n ${group.name}`)
  276.                                     })
  277.                                 }
  278.                                 continue;
  279.                             }
  280.                         } else if (getName.getStatus() === MrxDbgUiPrBaseReturn.kOk) {
  281.                             const groupId = groupDict.getAt(str)
  282.                             const _group = groupId.getMcDbObject() as McDbGroup
  283.                             if (_group && groupId.isValid()) {
  284.                                 MxFun.acutPrintf(`编组 ${str} 已经存在}`);
  285.                                 continue;
  286.                             } else {
  287.                                 group.name = str;
  288.                                 MxPluginContext.useMessage().success('修改组名成功');
  289.                                 break;
  290.                             }
  291.                         } else {
  292.                             break;
  293.                         }
  294.                     }
  295.                 }
  296.             } else {
  297.                 MxPluginContext.useMessage().success('对象不是组成员');
  298.             }
  299.         }
  300.         if (ents.length) ents.forEach(ent => ent.highlight(false));
  301.         mxcad.updateDisplay();
  302.     }
  303.     const getEnt = new MxCADUiPrEntity();
  304.     getEnt.setMessage('选择组');
  305.     getEnt.setKeyWords('[名称(N)]');
  306.     getEnt.setUserDraw((pt, pw) => {
  307.         ents.forEach(ent => ent.highlight(false));
  308.         ents.length = 0;
  309.         const entId = MxCADUtility.findEntAtPoint(pt.x, pt.y, pt.z, -1);
  310.         if (entId.isValid()) {
  311.             const ent = entId.getMcDbEntity();
  312.             groupArr = getGroupForEntity(ent);
  313.             if (groupArr.length) {
  314.                 const group = groupArr[index].group;
  315.                 group.getAllEntityId().forEach(id => {
  316.                     const entity = id.getMcDbEntity();
  317.                     entity.highlight(true);
  318.                     ents.push(entity);
  319.                 })
  320.             }
  321.         }
  322.     });
  323.     const entId = await getEnt.go();
  324.     if (getEnt.getStatus() === MrxDbgUiPrBaseReturn.kKeyWord) {
  325.         if (getEnt.isKeyWordPicked('N')) {
  326.             // 选择关键字
  327.             while (true) {
  328.                 const getName = new MxCADUiPrString()
  329.                 getName.setMessage("输入组的名称")
  330.                 getName.setKeyWords('[查询(A)]')
  331.                 const str = await getName.go();
  332.                 if (getName.getStatus() === MrxDbgUiPrBaseReturn.kKeyWord) {
  333.                     if (getName.isKeyWordPicked("A")) {
  334.                         getName.setMessage("请输入要列出的编码组名" + "<*>")
  335.                         getName.setKeyWords("")
  336.                         const name = await getName.go();
  337.                         if (getName.getStatus() === MrxDbgUiPrBaseReturn.kOk) {
  338.                             if (name && name !== "*") {
  339.                                 const groupId = groupDict.getAt(name)
  340.                                 const group = groupId.getMcDbObject() as McDbGroup
  341.                                 MxFun.acutPrintf('定义的编组')
  342.                                 if (group) {
  343.                                     MxFun.acutPrintf(`\n${group.name}`)
  344.                                 }
  345.                             } else if (name === "*") {
  346.                                 const groupIds = groupDict.getAllObject()
  347.                                 MxFun.acutPrintf(`\n 定义的编组:`)
  348.                                 groupIds.forEach((groupId) => {
  349.                                     const group = groupId.getMcDbObject() as McDbGroup
  350.                                     group && MxFun.acutPrintf(`\n ${group.name}`)
  351.                                 })
  352.                             }
  353.                         } else {
  354.                             const groupIds = groupDict.getAllObject()
  355.                             MxFun.acutPrintf(`\n 定义的编组:`)
  356.                             groupIds.forEach((groupId) => {
  357.                                 const group = groupId.getMcDbObject() as McDbGroup
  358.                                 group && MxFun.acutPrintf(`\n ${group.name}`)
  359.                             })
  360.                         }
  361.                         continue;
  362.                     }
  363.                 } else if (getName.getStatus() === MrxDbgUiPrBaseReturn.kOk) {
  364.                     const groupId = groupDict.getAt(str)
  365.                     const group = groupId.getMcDbObject() as McDbGroup
  366.                     if (group && groupId.isValid()) {
  367.                         group.getAllEntityId().forEach(id => {
  368.                             const ent = id.getMcDbEntity();
  369.                             ent.highlight(true);
  370.                             ents.push(ent);
  371.                         })
  372.                         groupArr.push({ name: group.name, group });
  373.                         editGroup()
  374.                         break;
  375.                     } else {
  376.                         MxFun.acutPrintf(`编组 ${str} 不存在`);
  377.                         continue;
  378.                     };
  379.                 } else {
  380.                     break;
  381.                 }
  382.             }
  383.         }
  384.     } else if (getEnt.getStatus() === MrxDbgUiPrBaseReturn.kOk) {
  385.         editGroup();
  386.     } else {
  387.         if (ents.length) ents.forEach(ent => ent.highlight(false));
  388.     }
  389. }
复制代码

4. 启用或禁用组选择
启用指定对象组的选择功能其执行过程如下:首先提示用户请选择目标组,并在鼠标悬停时自动检测光标下的对象,若该对象属于某个组,则实时高亮显示该组的所有成员,提供可视化反馈。用户点击对象后,系统获取其所属的第一个组,并将该组的 `isSelectable` 属性设置为 `true`,从而允许后续通过点击组内任意成员来选中整个组。最后清除高亮并刷新显示,完成设置。该方法提升了组对象的操作便捷性,适用于需要快速选中成组元素的场景。其完整代码如下:
  1. import { MxCADUiPrEntity, MxCADUtility, MxCpp} from "mxcad";
  2. // 启用/禁用组选择
  3. async function Mx_SetGroupSelection() {
  4.     const ents: McDbEntity[] = [];
  5.     let groupArr: GroupObject[] = [];
  6.     const getEnt = new MxCADUiPrEntity();
  7.     getEnt.setMessage('请选择目标组');
  8.     getEnt.setUserDraw((pt, pw) => {
  9.         ents.forEach(ent => ent.highlight(false));
  10.         ents.length = 0;
  11.         const entId = MxCADUtility.findEntAtPoint(pt.x, pt.y, pt.z, -1);
  12.         if (entId.isValid()) {
  13.             const ent = entId.getMcDbEntity();
  14.             groupArr = getGroupForEntity(ent);
  15.             if (groupArr.length) {
  16.                 const group = groupArr[0].group;
  17.                 group.getAllEntityId().forEach(id => {
  18.                     const entity = id.getMcDbEntity();
  19.                     entity.highlight(true);
  20.                     ents.push(entity);
  21.                 })
  22.             }
  23.         }
  24.     });
  25.     const entId = await getEnt.go();
  26.     if (groupArr.length) {
  27.         const group = groupArr[0].group;
  28.         group.isSelectable = true;
  29.         ents.forEach(ent => {
  30.             ent.highlight(false);
  31.         })
  32.         MxCpp.getCurrentMxCAD().updateDisplay();
  33.     };
  34. }
复制代码

三、功能演示

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

x
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

小黑屋|手机版|CAD论坛|CAD教程|CAD下载|联系我们|关于明经|明经通道 ( 粤ICP备05003914号 )  
©2000-2023 明经通道 版权所有 本站代码,在未取得本站及作者授权的情况下,不得用于商业用途

GMT+8, 2025-11-15 13:05 , Processed in 0.238038 second(s), 23 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表