博客 / 詳情

返回

(小程序查看DWG)在線CAD中的“組”(Group)功能開發

在使用CAD工具進行繪圖時,面對複雜的圖形結構,如何高效地管理多個對象成為提升工作效率的關鍵。CAD提供的“組”功能,正是為解決這一問題而設計的實用工具。本文將全面介紹 mxcad 中”組“的概念,以及如何實現組相關的功能開發。

一、什麼是“組”(Group)?

在CAD中,組(Group) 是指將多個圖形對象邏輯地組合在一起,形成一個可被統一操作的集合。組不會創建新的圖元實體,也不會改變對象本身的幾何屬性,僅是一種命名的對象集合,組對象包含特點如下:

  • 組內的對象保持獨立,可單獨編輯。
  • 選擇組中任意一個對象時,整個組可被選中(取決於系統設置)。
  • 每個組有唯一的名稱,便於識別和管理。
  • 支持嵌套:一個組可以包含另一個組,形成層級結構。
  • 組不作為獨立實體存儲在圖形數據庫中,僅作為對象的邏輯關聯存在。

二、組的核心功能開發

1. 創建組

該功能流程是從用户執行“創建組”命令開始。首先,系統初始化相關變量(如組名、描述和對象列表),並獲取當前圖形數據庫中的組管理字典。
隨後進入主循環,提示用户“選擇對象”。用户可以通過點擊或框選方式選擇一個或多個圖形對象,所選對象的ID將被保存到臨時列表中。
在選擇過程中,用户可隨時輸入關鍵字進行設置:

  • 輸入 N(名稱):進入命名流程,系統提示“輸入編組名”。此時可輸入 [查詢(A)] 來查看已存在的組名;若輸入 * 或直接回車,則列出所有組;否則查詢指定組信息。輸入名稱後,系統檢查是否重名,若無衝突則保存名稱並返回選擇狀態。
  • 輸入 D(説明):進入説明設置,提示“輸入編組説明”,用户輸入的文本將作為該組的描述信息。

當用户完成選擇並按 回車或空格鍵 確認後,系統開始創建組:

  • 首先檢查所選對象中是否有成員已屬於其他組。
  • 若存在此類情況,則彈出確認提示:“包含相同對象的組已經存在。仍要創建新的組?<N>”,並提供“是(Y)/否(N)”選項。
  • 若用户選擇“否”或取消操作,命令終止。
  • 若用户確認繼續或無衝突,則調用底層API創建組,並將之前輸入的描述信息賦值給新組。

最後,組創建完成,系統退出循環,命令執行結束。整個流程支持ESC中斷或新命令打斷,確保操作的安全性和靈活性。

根據上述流程調用 mxcad 內部API接口實現方法如下:

import { McDbEntity, McDbGroup, McDbPolyline, McGePoint3d, McObjectId, MxCADSelectionSet, MxCADUiPrKeyWord, MxCADUiPrPoint, MxCADUiPrString, MxCADUtility, MxCpp } from "mxcad";
import { DetailedResult, MxFun, MrxDbgUiPrBaseReturn } from "mxdraw";
interface GroupObject {
    name: string,
    group: McDbGroup
}
// 根據實體查找組
const getGroupForEntity = (entity: McDbEntity): GroupObject[] => {
    const database = MxCpp.getCurrentDatabase()
    const groupDict = database.GetGroupDictionary()
    const handle = entity.getHandle()
    const groupNames = groupDict.getAllObjectName()
    const length = groupNames.length();
    let groupArr: GroupObject[] = [];
    for (let index = 0; index < length; index++) {
        const groupName = groupNames.at(index);
        const groupId = groupDict.getAt(groupName)
        const group = groupId.getMcDbObject() as McDbGroup
        if (!group) continue;
        const entityIds = group.getAllEntityId();
        entityIds.forEach(entityId => {
            if (entityId.getMcDbEntity()?.getHandle() === handle) groupArr.push({ name: groupName, group })
        });
    };
    return groupArr
}
// 創建組
async function Mx_Group() {
  let description = ""
  let ids: McObjectId[] = [];
  const database = MxCpp.getCurrentDatabase();
  const groupDict = database.GetGroupDictionary();
  const mxcad = MxCpp.getCurrentMxCAD();
  // 設定未命名組名
  const groupNames = groupDict.getAllObjectName();
  let num = 0;
  groupNames.forEach(item => {
    if (/^\*/.test(item)) {
      num += 1;
    }
  });
  let name: string = `*A${num + 1}`;
  // 創建組
  const createGroup = async () => {
    const isPresence = ids.some((id) => {
      return database.getEntitiesInTheGroup(id).length !== 0
    })
    if (isPresence) {
      const getKey = new MxCADUiPrKeyWord();
      getKey.setMessage(`包含相同對象的組已經存在。仍要創建新的組?<N>`);
      getKey.setKeyWords(`[是(Y)/否(N)]`);
      const key = await getKey.go();
      ids.forEach(id => {
        id.getMcDbEntity().highlight(false);
      })
      mxcad.updateDisplay();
      if (key?.toLocaleUpperCase() === "N") {
        return
      }
      if (!key) return
    }
    if (database.CreateGroup(ids, name)) {
      const groupId = groupDict.getAt(name)
      const group = groupId.getMcDbObject() as McDbGroup;
      if (description) group.description = description;
      if (/^\*/.test(name)) {
        MxPluginContext.useMessage().success('未命名組已創建');
      } else {
        MxPluginContext.useMessage().success(`組${name}已創建`);
      }
      ids.forEach(id => {
        id.getMcDbEntity().highlight(false);
      })
      mxcad.updateDisplay();
    };
  };
  while (true) {
    const getEntityPt = new MxCADUiPrPoint();
    getEntityPt.setMessage('選擇對象');
    getEntityPt.setKeyWords(`[名稱(N)/説明(D)]`);
    getEntityPt.setDisableOsnap(true);
    getEntityPt.setDisableDynInput(true);
    getEntityPt.disableAllTrace(true);
    const hoverSelectEnts: McDbEntity[] = [];
    getEntityPt.setUserDraw((pt, pw) => {
      if (hoverSelectEnts.length) hoverSelectEnts.forEach(ent => ent.highlight(false));
      hoverSelectEnts.length = 0;
      const entId = MxCADUtility.findEntAtPoint(pt.x, pt.y, pt.z, -1);
      if (entId.isValid() && !ids.map(item => item.id).includes(entId.id)) {
        const ent = entId.getMcDbEntity();
        const arr = getGroupForEntity(ent);
        if (arr.length) {
          const group = arr[0].group;
          group.getAllEntityId().forEach(id => {
            const ent = id.getMcDbEntity();
            ent.highlight(true);
            hoverSelectEnts.push(ent)
          })
        } else {
          ent.highlight(true);
          hoverSelectEnts.push(ent)
        }
      }
    });
    const pt = await getEntityPt.go();
    hoverSelectEnts.forEach(ent => ent.highlight(false));
    // 如果選擇關鍵字,則執行相關操作
    if (getEntityPt.getStatus() == MrxDbgUiPrBaseReturn.kKeyWord) {
      if (getEntityPt.isKeyWordPicked("N")) {
        while (true) {
          const getName = new MxCADUiPrString()
          getName.setMessage("輸入編組名")
          getName.setKeyWords(`[查詢(A)]`)
          const str = await getName.go()
          if (getName.getDetailedResult() === DetailedResult.kCodeAbort || getName.getDetailedResult() === DetailedResult.kEcsIn || getName.getDetailedResult() === DetailedResult.kNewCommadIn) return
          if (getEntityPt.getDetailedResult() === DetailedResult.kNullEnterIn || getEntityPt.getDetailedResult() === DetailedResult.kNullSpaceIn || getEntityPt.getDetailedResult() === DetailedResult.kMouseRightIn) {
            return createGroup()
          }
          if (getName.isKeyWordPicked("A")) {
            getName.setMessage("請輸入要列出的編碼組名"+ "<*>")
            getName.setKeyWords("")
            const name = await getName.go();
            if (getName.getDetailedResult() === DetailedResult.kCodeAbort || getName.getDetailedResult() === DetailedResult.kEcsIn || getName.getDetailedResult() === DetailedResult.kNewCommadIn) return
            if (name && name !== "*") {
              const groupId = groupDict.getAt(name)
              const group = groupId.getMcDbObject() as McDbGroup
              MxFun.acutPrintf(`\n 定義的編組:`)
              if (group) {
                MxFun.acutPrintf(`\n${group.name}`)
              }
            }
            else if (name === "*" || getName.getDetailedResult() === DetailedResult.kNullEnterIn || getName.getDetailedResult() === DetailedResult.kNullSpaceIn) {
              const groupIds = groupDict.getAllObject()
              MxFun.acutPrintf(`\n 定義的編組:`)
              groupIds.forEach((groupId) => {
                const group = groupId.getMcDbObject() as McDbGroup
                group && MxFun.acutPrintf(`\n ${group.name}`)
              })
            }
            continue;
          }
          if (!str) return;
          if (/^\*/.test(str)) {
            MxFun.acutPrintf(`*無效`);
            continue;
          }
          const groupId = groupDict.getAt(str)
          const group = groupId.getMcDbObject() as McDbGroup
          if (group && groupId.isValid()) {
            MxFun.acutPrintf(`編組${str} 已經存在`);
            continue;
          }
          name = str;
          if (ids.length) {
            ids.forEach(id => {
              const ent = id.getMcDbEntity();
              ent.highlight(false);
            })
            return createGroup();
          } else {
            break;
          }
        }
      } else if (getEntityPt.isKeyWordPicked('D')) {
        const getName = new MxCADUiPrString()
        getName.setMessage("輸入編組説明")
        const str = await getName.go();
        if (!str) break;
        description = str
        continue;
      }
    } else if (getEntityPt.getStatus() === MrxDbgUiPrBaseReturn.kNone) {
      if (!ids.length) {
        return MxPluginContext.useMessage().success('未選擇對象,未創建編組');
      } else {
        ids.forEach(id => {
          const ent = id.getMcDbEntity();
          ent.highlight(false);
        })
        return createGroup();
      }
    } else if (getEntityPt.getStatus() === MrxDbgUiPrBaseReturn.kCancel) {
      ids.forEach(id => {
        const ent = id.getMcDbEntity();
        ent.highlight(false);
      })
      return
    } else {
      // 判斷是否選中實體
      if (pt && hoverSelectEnts.length) {
        const selectIds = hoverSelectEnts.map(item => {
          item.highlight(true);
          return item.getObjectID()
        })
        ids.push(...selectIds);
        continue;
      } else if (pt && !hoverSelectEnts.length) {
        getEntityPt.setUserDraw((point, pw) => {
          const pts = [pt, new McGePoint3d(pt.x, point.y), point, new McGePoint3d(point.x, pt.y)]
          // 設置範圍框顏色即位置
          let pl = new McDbPolyline();
          pl.isClosed = true;
          pts.forEach(pt => pl.addVertexAt(pt));
          pw.setColor(0xFFFFFF);
          pw.drawMcDbEntity(pl);
          // 動態繪製矩形填充框
          const geometry = new THREE.BufferGeometry();
          geometry.setFromPoints([
            new THREE.Vector3(pt.x, pt.y, pt.z),
            new THREE.Vector3(pt.x, point.y, point.z),
            new THREE.Vector3(point.x, point.y, point.z),
            new THREE.Vector3(point.x, pt.y, pt.z)
          ]);
          geometry.attributes.uv = new THREE.BufferAttribute(new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]), 2);
          geometry.setIndex([0, 1, 2, 0, 2, 3]);
          // 創建材質(半透明的顏色)
          const material = new THREE.MeshBasicMaterial({
            color: 0x004D00,
            transparent: true,
            opacity: 0.5,
            side: THREE.DoubleSide
          });
          const mesh = new THREE.Mesh(geometry, material);
          pw.drawEntity(mesh);
        });
        const nextPt = await getEntityPt.go();
        if (!nextPt) break;
        const ss = new MxCADSelectionSet();
        await ss.crossingSelect(pt.x, pt.y, nextPt.x, nextPt.y);
        ss.forEach(id => {
          if (!ids.map(i => i.id).includes(id.id)) {
            const ent = id.getMcDbEntity();
            const arr = getGroupForEntity(ent);
            if (arr.length) {
              const group = arr[0].group;
              group.getAllEntityId().forEach(id => {
                id.getMcDbEntity().highlight(true)
                ids.push(id);
              })
            } else {
              ent.highlight(true);
              ids.push(id);
            }
          }
        });
        continue;
      } else {
        continue;
      };
    }
  }
}

2. 解除組

解除組的功能流程如下:
命令啓動後,系統提示用户“選擇組”,並支持通過關鍵字 [名稱(N)] 切換為按名稱分解模式。在用户操作過程中,系統啓用懸停預覽功能:當鼠標移動到某個對象上時,會自動查詢該對象所屬的組,並高亮顯示該組內的所有成員對象,便於用户直觀判斷將要操作的範圍。
接下來,根據用户的選擇進入不同分支:

1.若用户輸入 N(名稱)
   - 進入“按名稱分解”模式,提示“輸入編組名”。
   - 支持輸入關鍵字 [查詢(A)]

  • 若輸入 A,可進一步輸入要查詢的組名;
       - 輸入 * 或直接回車,則列出當前圖形中所有已定義的組名;
       - 輸入具體名稱,則檢查並顯示該組是否存在。
       - 用户輸入組名後,系統查找對應組:
       - 若存在,執行分解操作(清空組內對象並從組字典中移除),提示“組 已分解”;
       - 若不存在,提示“編組 未定義”,並允許重新輸入。

2.若用户點擊某個對象
   - 系統獲取該對象,並查詢其所屬的所有組(一個對象可能屬於多個組)。
   - 若對象僅屬於一個組,則直接選中該組,準備分解。
   - 若對象屬於多個組,則進入選擇流程:
     - 提示“對象是多個組的成員<接受>”,提供 [接受(A)/下一個(N)] 選項;
     - 選擇 A:接受當前高亮的組;
     - 選擇 N:切換到下一個組,並更新高亮顯示;
     - 可循環切換,直到用户確認或取消。
   - 確定目標組後,記錄其名稱。

最後,系統根據選定的組名執行分解操作:

  • 從組字典中獲取該組對象;
  • 調用 clear() 清空組內成員引用;
  • 調用 remove() 從字典中刪除該組;
  • 提示“組 已分解”或“對象不是組成員”(如未選中有效組)。

操作完成後,清除所有高亮顯示的對象,確保界面恢復整潔,命令結束。其具體實現代碼如下:

import { McDbEntity, McDbGroup, McDbPolyline, McGePoint3d, McObjectId, MxCADSelectionSet, MxCADUiPrKeyWord, MxCADUiPrPoint, MxCADUiPrString, MxCADUtility, MxCpp } from "mxcad";
import { DetailedResult, MxFun, MrxDbgUiPrBaseReturn } from "mxdraw";
// 解除編組
async function Mx_Ungroup() {
    const ents: McDbEntity[] = [];
    let groupArr: GroupObject[] = [];
    let name!: string;
    const database = MxCpp.getCurrentDatabase();
    const groupDict = database.GetGroupDictionary();
    let index: number = 0;
    const getEnt = new MxCADUiPrEntity();
    getEnt.setMessage('選擇組');
    getEnt.setKeyWords(`[名稱(N)]`);
    getEnt.setUserDraw((pt, pw) => {
        ents.forEach(ent => ent.highlight(false));
        ents.length = 0;
        const entId = MxCADUtility.findEntAtPoint(pt.x, pt.y, pt.z, -1);
        if (entId.isValid()) {
            const ent = entId.getMcDbEntity();
            groupArr = getGroupForEntity(ent);//getGroupForEntity參考上述創建組內代碼
            if (groupArr.length) {
                const group = groupArr[index].group;
                group.getAllEntityId().forEach(id => {
                    const entity = id.getMcDbEntity();
                    entity.highlight(true);
                    ents.push(entity);
                })
            }
        }
    });
    const entId = await getEnt.go();
    if (getEnt.getStatus() === MrxDbgUiPrBaseReturn.kKeyWord) {
        if (getEnt.isKeyWordPicked('N')) {
            while (true) {
                const getString = new MxCADUiPrString();
                getString.setMessage('輸入編組名');
                getString.setKeyWords(`[查詢(A)]`);
                const str = await getString.go();
                if (getString.getStatus() === MrxDbgUiPrBaseReturn.kOk) {
                    // 刪除組
                    const groupId = groupDict.getAt(str);
                    const group = groupId.getMcDbObject() as McDbGroup;
                    if (groupId.isValid() && group) {
                        group.clear();
                        groupDict.remove(str);
                        MxPluginContext.useMessage().success('組 ' + str + ' 已分解');
                        if (ents.length) ents.forEach(ent => ent.highlight(false));
                        return;
                    } else {
                        MxFun.acutPrintf('編組 ' + str + ' 未定義');
                        continue;
                    }
                } else if (getString.getStatus() === MrxDbgUiPrBaseReturn.kKeyWord) {
                    // 查詢組
                    getString.setMessage("請輸入要列出的編碼組名" + "<*>")
                    getString.setKeyWords("")
                    const name = await getString.go();
                    if (getString.getStatus() === MrxDbgUiPrBaseReturn.kOk) {
                        if (name && name !== "*") {
                            const groupId = groupDict.getAt(name)
                            const group = groupId.getMcDbObject() as McDbGroup
                            MxFun.acutPrintf(`\n 定義的編組:`)
                            if (group) {
                                MxFun.acutPrintf(`\n${group.name}`)
                            }
                        } else if (name === "*") {
                            const groupIds = groupDict.getAllObject()
                            MxFun.acutPrintf(`\n 定義的編組:`)
                            groupIds.forEach((groupId) => {
                                const group = groupId.getMcDbObject() as McDbGroup
                                group && MxFun.acutPrintf(`\n ${group.name}`)
                            })
                        }
                    } else if (getString.getStatus() === MrxDbgUiPrBaseReturn.kNone) {
                        const groupIds = groupDict.getAllObject()
                        MxFun.acutPrintf(`\n 定義的編組:`)
                        groupIds.forEach((groupId) => {
                            const group = groupId.getMcDbObject() as McDbGroup
                            group && MxFun.acutPrintf(`\n ${group.name}`)
                        })
                    }
                    continue;
                }
            }
        }
    } else if (getEnt.getStatus() === MrxDbgUiPrBaseReturn.kOk) {
        if (groupArr.length === 1) {
            name = groupArr[0].name
        } else if (groupArr.length > 1) {
            while (true) {
                const getKeys = new MxCADUiPrKeyWord();
                getKeys.setMessage('對象是多個組的成員<接受>')
                getKeys.setKeyWords('[接受(A)/下一個(N)]');
                let key = await getKeys.go();
                if (key === "A") {
                    name = groupArr[index].name;
                    break;
                } else if (key === "N") {
                    ents.forEach(ent => ent.highlight(false));
                    ents.length = 0;
                    index + 1 > groupArr.length - 1 ? index = 0 : index += 1;
                    const res = groupArr[index];
                    res.group.getAllEntityId().forEach(id => {
                        const ent = id.getMcDbEntity();
                        ent.highlight(true);
                        ents.push(ent);
                    });
                    continue;
                } else {
                    if (ents.length) ents.forEach(ent => ent.highlight(false));
                    return;
                }
            }
        }
        if (name) {
            const groupId = groupDict.getAt(name)
            const group = groupId.getMcDbObject() as McDbGroup
            if (group) {
                group.clear();
                groupDict.remove(name);
                MxPluginContext.useMessage().success(`組 ${name} 已分解`);
            } else {
                MxPluginContext.useMessage().success('對象不是組成員');
            }
        } else {
            MxPluginContext.useMessage().success('對象不是組成員');
        };
        if (ents.length) ents.forEach(ent => ent.highlight(false));
    }
}

3. 編輯組

編輯圖形中已有對象組(Group)的交互式功能。其主要功能是允許用户通過選擇對象或輸入組名的方式,找到目標組,並對其進行添加成員、刪除成員或重命名等操作。
命令啓動後,系統首先提示“選擇組”,並支持通過關鍵字 [名稱(N)] 切換為按名稱選擇模式。在用户移動鼠標時,系統會啓用懸停預覽功能:自動檢測光標下的對象,查詢其所屬的組,並高亮顯示該組內的所有成員,幫助用户直觀判斷當前將要操作的對象範圍。

如果用户點擊了一個對象,系統會獲取該對象所屬的所有組:

  • 若對象不屬於任何組,則提示“對象不是組成員”;
  • 若只屬於一個組,則直接進入編輯操作;
  • 若屬於多個組,則提示“對象是多個組的成員<接受>”,並提供 [接受(A)/下一個(N)] 選項,用户可循環切換高亮不同的組,直到確認目標組。

如果用户選擇 [名稱(N)] 模式,則進入按名稱編輯流程:

  • 提示“輸入組的名稱”,並支持 [查詢(A)] 關鍵字;
  • 輸入 A 後可查看所有組名(輸入 *)或查詢特定組是否存在;
  • 輸入有效組名後,若組存在,則加載並高亮其成員,進入編輯;若不存在,則提示“編組 xxx 不存在”,並允許重新輸入。
    確定目標組後,系統彈出操作菜單:[添加對象(A)/刪除對象(R)/重命名(REN)]
  • 添加對象(A):用户可通過單擊或框選方式選擇要加入的對象。系統會動態高亮預覽可添加的對象(不包括已存在於組內的對象),支持窗口和交叉選擇,完成後將所選對象追加到組中,並提示“添加對象成功!”。
  • 刪除對象(R):用户選擇組內對象進行移除。系統僅允許刪除當前組中的成員,選擇後會從組中剔除這些對象,並通過清空後重新添加剩餘對象的方式更新組內容。
  • 重命名(REN):提示用户輸入新名稱。支持再次使用 [查詢(A)] 查看現有組名以避免衝突。若新名稱已被其他組使用,則提示“編組 xxx 已經存在”並要求重新輸入;否則更新組名,並提示“修改組名成功”。
    實現上述流程的具體功能代碼如下:

    import { McDbEntity, McDbGroup, McDbPolyline, McGePoint3d, McObjectId, MxCADSelectionSet, MxCADUiPrKeyWord, MxCADUiPrPoint, MxCADUiPrString, MxCADUtility, MxCpp } from "mxcad";
    import { DetailedResult, MxFun, MrxDbgUiPrBaseReturn } from "mxdraw";
    // 編輯組
    async function Mx_Groupedit() {
        const ents: McDbEntity[] = [];//高亮實體數組
        let groupArr: GroupObject[] = [];//實體組集合
        let index: number = 0;
        let name: string = '';
        const database = MxCpp.getCurrentDatabase();
        const groupDict = database.GetGroupDictionary();
        const mxcad = MxCpp.getCurrentMxCAD();
        const editGroup = async () => {
            // 選中目標組
            if (groupArr.length === 1) {
                name = groupArr[0].name
            } else if (groupArr.length > 1) {
                while (true) {
                    const getKeys = new MxCADUiPrKeyWord();
                    getKeys.setMessage('對象是多個組的成員<接受>')
                    getKeys.setKeyWords(`[接受(A)/下一個(N)]`);
                    let key = await getKeys.go();
                    if (key === "A") {
                        name = groupArr[index].name;
                        break;
                    } else if (key === "N") {
                        ents.forEach(ent => ent.highlight(false));
                        ents.length = 0;
                        index + 1 > groupArr.length - 1 ? index = 0 : index += 1;
                        const res = groupArr[index];
                        res.group.getAllEntityId().forEach(id => {
                            const ent = id.getMcDbEntity();
                            ent.highlight(true);
                            ents.push(ent);
                        });
                        continue;
                    } else {
                        continue;
                    }
                }
            } else {
                name = '';
            }
            // 操作目標組
            if (name) {
                const groupId = groupDict.getAt(name)
                const group = groupId.getMcDbObject() as McDbGroup
                if (group) {
                    // 進入編輯組
                    const getKey = new MxCADUiPrKeyWord();
                    getKey.setMessage(t('輸入選項'));
                    getKey.setKeyWords(`[添加對象(A)/刪除對象(R)/重命名(REN)]`);
                    const key = await getKey.go();
                    if (!key) return;
                    if (key === 'A') {
                        const selectIds: McObjectId[] = [];
                        // 添加對象
                        while (true) {
                            const getEntityPt = new MxCADUiPrPoint();
                            getEntityPt.setMessage('選擇要添加到編組的對象');
                            getEntityPt.setDisableOsnap(true);
                            getEntityPt.setDisableDynInput(true);
                            getEntityPt.disableAllTrace(true);
                            const hoverSelectEnts: McDbEntity[] = [];
                            getEntityPt.setUserDraw((pt, pw) => {
                                if (hoverSelectEnts.length) hoverSelectEnts.forEach(ent => {
                                    if (!ents.map(i => i.getObjectID().id).includes(ent.getObjectID().id)) ent.highlight(false);
                                });
                                hoverSelectEnts.length = 0;
                                const entId = MxCADUtility.findEntAtPoint(pt.x, pt.y, pt.z, -1);
                                if (entId.isValid() && !selectIds.map(item => item.id).includes(entId.id) && !group.has(entId)) {
                                    const ent = entId.getMcDbEntity();
                                    const arr = getGroupForEntity(ent);
                                    if (arr.length) {
                                        const group = arr[0].group;
                                        group.getAllEntityId().forEach(id => {
                                            const ent = id.getMcDbEntity();
                                            ent.highlight(true);
                                            hoverSelectEnts.push(ent)
                                        })
                                    } else {
                                        ent.highlight(true);
                                        hoverSelectEnts.push(ent)
                                    }
                                }
                            });
                            const pt = await getEntityPt.go();
                            if (!pt) {
                                if (hoverSelectEnts.length) hoverSelectEnts.forEach(item => item.highlight(false));
                                break;
                            } else {
                                // 判斷是否選中實體
                                if (hoverSelectEnts.length) {
                                    if (hoverSelectEnts.length) {
                                        hoverSelectEnts.forEach(ent => {
                                            selectIds.push(ent.getObjectID());
                                        })
                                    };
                                } else {
                                    getEntityPt.setUserDraw((point, pw) => {
                                        const pts = [pt, new McGePoint3d(pt.x, point.y), point, new McGePoint3d(point.x, pt.y)]
                                        // 設置範圍框顏色即位置
                                        let pl = new McDbPolyline();
                                        pl.isClosed = true;
                                        pts.forEach(pt => pl.addVertexAt(pt));
                                        pw.setColor(0xFFFFFF);
                                        pw.drawMcDbEntity(pl);
     
                                        // 動態繪製矩形填充框
                                        const geometry = new THREE.BufferGeometry();
                                        geometry.setFromPoints([
                                            new THREE.Vector3(pt.x, pt.y, pt.z),
                                            new THREE.Vector3(pt.x, point.y, point.z),
                                            new THREE.Vector3(point.x, point.y, point.z),
                                            new THREE.Vector3(point.x, pt.y, pt.z)
                                        ]);
                                        geometry.attributes.uv = new THREE.BufferAttribute(new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]), 2);
                                        geometry.setIndex([0, 1, 2, 0, 2, 3]);
                                        // 創建材質(半透明的顏色)
                                        const material = new THREE.MeshBasicMaterial({
                                            color: 0x004D00,
                                            transparent: true,
                                            opacity: 0.5,
                                            side: THREE.DoubleSide
                                        });
                                        const mesh = new THREE.Mesh(geometry, material);
                                        pw.drawEntity(mesh);
                                    });
                                    const nextPt = await getEntityPt.go();
                                    if (!nextPt) break;
                                    const ss = new MxCADSelectionSet();
                                    await ss.crossingSelect(pt.x, pt.y, nextPt.x, nextPt.y);
                                    ss.forEach(id => {
                                        if (!group.has(id) && !selectIds.map(i => i.id).includes(id.id)) {
                                            const ent = id.getMcDbEntity();
                                            const arr = getGroupForEntity(ent);
                                            if (arr.length) {
                                                const group = arr[0].group;
                                                group.getAllEntityId().forEach(id => {
                                                    id.getMcDbEntity()?.highlight(true);
                                                    selectIds.push(id);
                                                })
                                            } else {
                                                id.getMcDbEntity()?.highlight(true);
                                                selectIds.push(id);
                                            }
                                        }
                                    });
                                };
                                continue;
                            }
                        };
                        if (selectIds.length) {
                            selectIds.forEach(id => {
                                id.getMcDbEntity().highlight(false);
                                group.append(id);
                            });
                            MxPluginContext.useMessage().success('添加對象成功!');
                        }
                    } else if (key === 'R') {
                        const selectIds: McObjectId[] = [];
                        while (true) {
                            const getEntityPt = new MxCADUiPrPoint();
                            getEntityPt.setMessage('選擇要從編組中刪除的對象');
                            getEntityPt.setDisableOsnap(true);
                            getEntityPt.setDisableDynInput(true);
                            getEntityPt.disableAllTrace(true);
                            const hoverSelectEnts: McDbEntity[] = [];
                            getEntityPt.setUserDraw((pt, pw) => {
                                const entId = MxCADUtility.findEntAtPoint(pt.x, pt.y, pt.z, -1);
                                hoverSelectEnts.forEach(e => {
                                    if (!group.has(e.getObjectID())) {
                                        e.highlight(false)
                                    }
                                });
                                hoverSelectEnts.length = 0;
                                if (entId.isValid() && !selectIds.map(i => i.id).includes(entId.id)) {
                                    const ent = entId.getMcDbEntity();
                                    ent.highlight(true);
                                    hoverSelectEnts.push(ent)
                                }
                            });
                            const pt = await getEntityPt.go();
                            if (!pt) {
                                break;
                            } else {
                                // 判斷是否選中實體
                                if (hoverSelectEnts.length) {
                                    hoverSelectEnts.forEach(ent => {
                                        ent.highlight(false);
                                        if (group.has(ent.getObjectID())) {
                                            selectIds.push(ent.getObjectID())
                                        } else {
                                            MxFun.acutPrintf('對象不是組內元素,無法刪除')
                                        }
                                    })
                                } else {
                                    getEntityPt.setUserDraw((point, pw) => {
                                        const pts = [pt, new McGePoint3d(pt.x, point.y), point, new McGePoint3d(point.x, pt.y)]
                                        // 設置範圍框顏色即位置
                                        let pl = new McDbPolyline();
                                        pl.isClosed = true;
                                        pts.forEach(pt => pl.addVertexAt(pt));
                                        pw.setColor(0xFFFFFF);
                                        pw.drawMcDbEntity(pl);
     
                                        // 動態繪製矩形填充框
                                        const geometry = new THREE.BufferGeometry();
                                        geometry.setFromPoints([
                                            new THREE.Vector3(pt.x, pt.y, pt.z),
                                            new THREE.Vector3(pt.x, point.y, point.z),
                                            new THREE.Vector3(point.x, point.y, point.z),
                                            new THREE.Vector3(point.x, pt.y, pt.z)
                                        ]);
                                        geometry.attributes.uv = new THREE.BufferAttribute(new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]), 2);
                                        geometry.setIndex([0, 1, 2, 0, 2, 3]);
                                        // 創建材質(半透明的顏色)
                                        const material = new THREE.MeshBasicMaterial({
                                            color: 0x004D00,
                                            transparent: true,
                                            opacity: 0.5,
                                            side: THREE.DoubleSide
                                        });
                                        const mesh = new THREE.Mesh(geometry, material);
                                        pw.drawEntity(mesh);
                                    });
                                    const nextPt = await getEntityPt.go();
                                    if (!nextPt) break;
                                    const ss = new MxCADSelectionSet();
                                    await ss.crossingSelect(pt.x, pt.y, nextPt.x, nextPt.y);
                                    ss.forEach(id => {
                                        if (group.has(id)) {
                                            const ent = id.getMcDbEntity();
                                            ent.highlight(false);
                                            selectIds.push(ent.getObjectID());
                                        }
                                    });
                                };
                                continue;
                            }
                        };
                        if (selectIds.length) {
                            const newIds = ents.filter(ent => !selectIds.map(i => i.id).includes(ent.getObjectID().id)).map(ent => ent.getObjectID());
                            group.clear();
                            group.appendArray(newIds);
                        }
                    } else if (key === 'REN') {
                        while (true) {
                            const getName = new MxCADUiPrString()
                            getName.setMessage("輸入組的新名稱" + `<${group.name}>`)
                            getName.setKeyWords('查詢(A)]')
                            const str = await getName.go();
                            if (getName.getStatus() === MrxDbgUiPrBaseReturn.kKeyWord) {
                                if (getName.isKeyWordPicked("A")) {
                                    getName.setMessage("請輸入要列出的編碼組名" + "<*>")
                                    const name = await getName.go();
                                    if (getName.getStatus() === MrxDbgUiPrBaseReturn.kOk) {
                                        if (name && name !== "*") {
                                            const groupId = groupDict.getAt(name)
                                            const group = groupId.getMcDbObject() as McDbGroup
                                            MxFun.acutPrintf('定義的編組')
                                            if (group) {
                                                MxFun.acutPrintf(`\n${group.name}`)
                                            }
                                        } else if (name === "*") {
                                            const groupIds = groupDict.getAllObject()
                                            MxFun.acutPrintf(`\n 定義的編組:`)
                                            groupIds.forEach((groupId) => {
                                                const group = groupId.getMcDbObject() as McDbGroup
                                                group && MxFun.acutPrintf(`\n ${group.name}`)
                                            })
                                        }
                                    } else {
                                        const groupIds = groupDict.getAllObject()
                                        MxFun.acutPrintf(`\n 定義的編組:`)
                                        groupIds.forEach((groupId) => {
                                            const group = groupId.getMcDbObject() as McDbGroup
                                            group && MxFun.acutPrintf(`\n ${group.name}`)
                                        })
                                    }
                                    continue;
                                }
                            } else if (getName.getStatus() === MrxDbgUiPrBaseReturn.kOk) {
                                const groupId = groupDict.getAt(str)
                                const _group = groupId.getMcDbObject() as McDbGroup
                                if (_group && groupId.isValid()) {
                                    MxFun.acutPrintf(`編組 ${str} 已經存在}`);
                                    continue;
                                } else {
                                    group.name = str;
                                    MxPluginContext.useMessage().success('修改組名成功');
                                    break;
                                }
                            } else {
                                break;
                            }
                        }
                    }
                } else {
                    MxPluginContext.useMessage().success('對象不是組成員');
                }
            }
            if (ents.length) ents.forEach(ent => ent.highlight(false));
            mxcad.updateDisplay();
        }
        const getEnt = new MxCADUiPrEntity();
        getEnt.setMessage('選擇組');
        getEnt.setKeyWords('[名稱(N)]');
        getEnt.setUserDraw((pt, pw) => {
            ents.forEach(ent => ent.highlight(false));
            ents.length = 0;
            const entId = MxCADUtility.findEntAtPoint(pt.x, pt.y, pt.z, -1);
            if (entId.isValid()) {
                const ent = entId.getMcDbEntity();
                groupArr = getGroupForEntity(ent);
                if (groupArr.length) {
                    const group = groupArr[index].group;
                    group.getAllEntityId().forEach(id => {
                        const entity = id.getMcDbEntity();
                        entity.highlight(true);
                        ents.push(entity);
                    })
                }
            }
        });
        const entId = await getEnt.go();
        if (getEnt.getStatus() === MrxDbgUiPrBaseReturn.kKeyWord) {
            if (getEnt.isKeyWordPicked('N')) {
                // 選擇關鍵字
                while (true) {
                    const getName = new MxCADUiPrString()
                    getName.setMessage("輸入組的名稱")
                    getName.setKeyWords('[查詢(A)]')
                    const str = await getName.go();
                    if (getName.getStatus() === MrxDbgUiPrBaseReturn.kKeyWord) {
                        if (getName.isKeyWordPicked("A")) {
                            getName.setMessage("請輸入要列出的編碼組名" + "<*>")
                            getName.setKeyWords("")
                            const name = await getName.go();
                            if (getName.getStatus() === MrxDbgUiPrBaseReturn.kOk) {
                                if (name && name !== "*") {
                                    const groupId = groupDict.getAt(name)
                                    const group = groupId.getMcDbObject() as McDbGroup
                                    MxFun.acutPrintf('定義的編組')
                                    if (group) {
                                        MxFun.acutPrintf(`\n${group.name}`)
                                    }
                                } else if (name === "*") {
                                    const groupIds = groupDict.getAllObject()
                                    MxFun.acutPrintf(`\n 定義的編組:`)
                                    groupIds.forEach((groupId) => {
                                        const group = groupId.getMcDbObject() as McDbGroup
                                        group && MxFun.acutPrintf(`\n ${group.name}`)
                                    })
                                }
                            } else {
                                const groupIds = groupDict.getAllObject()
                                MxFun.acutPrintf(`\n 定義的編組:`)
                                groupIds.forEach((groupId) => {
                                    const group = groupId.getMcDbObject() as McDbGroup
                                    group && MxFun.acutPrintf(`\n ${group.name}`)
                                })
                            }
                            continue;
                        }
                    } else if (getName.getStatus() === MrxDbgUiPrBaseReturn.kOk) {
                        const groupId = groupDict.getAt(str)
                        const group = groupId.getMcDbObject() as McDbGroup
                        if (group && groupId.isValid()) {
                            group.getAllEntityId().forEach(id => {
                                const ent = id.getMcDbEntity();
                                ent.highlight(true);
                                ents.push(ent);
                            })
                            groupArr.push({ name: group.name, group });
                            editGroup()
                            break;
                        } else {
                            MxFun.acutPrintf(`編組 ${str} 不存在`);
                            continue;
                        };
                    } else {
                        break;
                    }
                }
            }
        } else if (getEnt.getStatus() === MrxDbgUiPrBaseReturn.kOk) {
            editGroup();
        } else {
            if (ents.length) ents.forEach(ent => ent.highlight(false));
        }
    }

4. 啓用或禁用組選擇

啓用指定對象組的選擇功能其執行過程如下:首先提示用户“請選擇目標組”,並在鼠標懸停時自動檢測光標下的對象,若該對象屬於某個組,則實時高亮顯示該組的所有成員,提供可視化反饋。用户點擊對象後,系統獲取其所屬的第一個組,並將該組的 isSelectable 屬性設置為 true,從而允許後續通過點擊組內任意成員來選中整個組。最後清除高亮並刷新顯示,完成設置。該方法提升了組對象的操作便捷性,適用於需要快速選中成組元素的場景。其完整代碼如下:

import { MxCADUiPrEntity, MxCADUtility, MxCpp} from "mxcad";
// 啓用/禁用組選擇
async function Mx_SetGroupSelection() {
    const ents: McDbEntity[] = [];
    let groupArr: GroupObject[] = [];
    const getEnt = new MxCADUiPrEntity();
    getEnt.setMessage('請選擇目標組');
    getEnt.setUserDraw((pt, pw) => {
        ents.forEach(ent => ent.highlight(false));
        ents.length = 0;
        const entId = MxCADUtility.findEntAtPoint(pt.x, pt.y, pt.z, -1);
        if (entId.isValid()) {
            const ent = entId.getMcDbEntity();
            groupArr = getGroupForEntity(ent);
            if (groupArr.length) {
                const group = groupArr[0].group;
                group.getAllEntityId().forEach(id => {
                    const entity = id.getMcDbEntity();
                    entity.highlight(true);
                    ents.push(entity);
                })
            }
        }
    });
    const entId = await getEnt.go();
    if (groupArr.length) {
        const group = groupArr[0].group;
        group.isSelectable = true;
        ents.forEach(ent => {
            ent.highlight(false);
        })
        MxCpp.getCurrentMxCAD().updateDisplay();
    };
}

三、功能演示

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.