浏览代码

Lanya CPG Plug-In For Coreldraw X4-2023 Added English version and X4 version

蘭雅sRGB 3 天之前
父节点
当前提交
7926e0b268

+ 3 - 3
09_BoundaryGroup/ToolsBox.cpp

@@ -282,7 +282,7 @@ intptr_t CALLBACK ToolsBoxPlugin::DlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, L
         RECT rect;
         GetWindowRect(hwnd, &rect);
         // 计算新的宽度
-        int newWidth = rect.right - rect.left + 100; // 增加100单位
+        int newWidth = rect.right - rect.left + 120; // 增加120单位
         // 移动窗口到新的大小
         SetWindowPos(hwnd, NULL, rect.left, rect.top, newWidth, rect.bottom - rect.top, SWP_NOZORDER | SWP_NOACTIVATE);
         // 隐藏按钮 (假设按钮的句柄为 buttonHandle)
@@ -311,8 +311,8 @@ intptr_t CALLBACK ToolsBoxPlugin::DlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, L
         GetWindowRect(hDlg, &rect);
         int x = rect.left;
         int y = rect.top;
-        int h = 232; // 恢复宽高
-        int w = 207;
+        int h = 235; // 恢复宽高
+        int w = 210;
         SetWindowPos(hDlg, NULL, x, y, w, h, SWP_NOZORDER | SWP_NOACTIVATE);
         ShowWindow(::GetDlgItem(hDlg, MIN_TOOLS), !SW_HIDE);
         ShowWindow(::GetDlgItem(hDlg, EXPAND_TOOLS), !SW_HIDE);

+ 3 - 3
09_BoundaryGroup/ToolsBox.rc

@@ -32,9 +32,9 @@ FONT 8, "MS Shell Dlg", 135, 0, 1
     LTEXT           "Èݲî:(mm)", EXP_LT, 66, 28, 41, 8, SS_LEFT, WS_EX_LEFT
     EDITTEXT        EXP_TEXT, 102, 25, 22, 14, NOT WS_TABSTOP | ES_AUTOHSCROLL, WS_EX_LEFT
     PUSHBUTTON      "ÒìÐÎȺ×é", ID_BOUNDARY_GROUP, 4, 2, 59, 20, 0, WS_EX_LEFT
-    PUSHBUTTON      "¡Å", RENEW_TOOLS, 126, 23, 12, 17, BS_NOTIFY, WS_EX_LEFT
-    PUSHBUTTON      "¡Ä", MIN_TOOLS, 126, 6, 12, 17, BS_NOTIFY, WS_EX_LEFT
-    PUSHBUTTON      ">>", EXPAND_TOOLS, 127, 53, 13, 39, BS_NOTIFY, WS_EX_LEFT
+    PUSHBUTTON      "¡Å", RENEW_TOOLS, 125, 23, 12, 17, BS_NOTIFY, WS_EX_LEFT
+    PUSHBUTTON      "¡Ä", MIN_TOOLS, 125, 6, 12, 17, BS_NOTIFY, WS_EX_LEFT
+    PUSHBUTTON      ">", EXPAND_TOOLS, 125, 57, 12, 28, BS_NOTIFY, WS_EX_LEFT
 }
 
 

+ 3 - 1
09_BoundaryGroup/boxAutoGroup.cpp

@@ -42,7 +42,7 @@ bool isOverlapped(const BoundingBox &a, const BoundingBox &b) {
 bool BBox_DrawRectangle(corel *cdr, double exp) {
   BoundingBox box;
   auto sr = cdr->ActiveSelectionRange; // 获得选择范围
-  auto al = cdr->ActiveLayer;          // 获得当前
+  auto al = cdr->ActiveVirtualLayer;          // 虚拟物件虚拟
   if (!sr || !al) return false;
   
   BeginOpt(cdr);
@@ -57,6 +57,8 @@ bool BBox_DrawRectangle(corel *cdr, double exp) {
     s->Outline->Color->RGBAssign(0, 255, 0);
     srs->Add(s);
   }
+
+  cdr->ActiveDocument->LogCreateShapeRange(srs);  // 虚拟物件添加到文档中
   srs->CreateSelection();
   
   sprintf(infobuf, "提示: 标记画框数量: %d 个\n容差值请使用小键盘输入", srs->Count);

+ 199 - 0
CorelDRAW_X4_SDK/BoundaryGroup.cpp

@@ -0,0 +1,199 @@
+#include "cdrapp.h"
+#include <cerrno>
+#include <cmath>
+#include <cstddef>
+
+bool isDPCurve(IVGShapePtr s) {
+  auto dpc = (s->Type == cdrRectangleShape) || (s->Type == cdrEllipseShape) ||
+             (s->Type == cdrCurveShape) || (s->Type == cdrPolygonShape) ||
+             (s->Type == cdrBitmapShape);
+  return dpc;
+}
+IVGShapePtr CreateBoundary(corel *cdr, IVGShapePtr s) {
+  IVGShapePtr scp;
+  if (cdr->VersionMajor < 17) {
+    // s->CreateSelection();
+    // cdr->FrameWork->Automation->InvokeItem( _bstr_t("b0491566-5ffe-450a-b17e-f2f496b4eb22"));
+    // scp = cdr->ActiveSelectionRange->Shapes->Item[1];
+    BoundingBox box;
+    s->GET_BOUNDING_BOX(box);
+    scp = cdr->ActiveLayer->CreateRectangle2(box.x, box.y, box.w, box.h, ZERO_4PC);
+  } 
+  return scp;
+}
+
+// VGCore::IVGShapePtr VGCore::IVGShape::CreateBoundary ( double x, double y, VARIANT_BOOL PlaceOnTop, VARIANT_BOOL DeleteSource ); VGCore::IVGShapePtr
+// VGCore::IVGShapeRange::CreateBoundary ( double x, double y, VARIANT_BOOL PlaceOnTop, VARIANT_BOOL DeleteSource );
+// VARIANT_BOOL VGCore::IVGCurve::IntersectsWith ( struct IVGCurve * Curve )
+bool isIntWith(corel *cdr, IVGShape *s1, IVGShape *s2) {
+  bool isIn = false;
+  if (isDPCurve(s1) && isDPCurve(s2)) {
+    isIn = s1->GetDisplayCurve()->IntersectsWith(s2->GetDisplayCurve());
+  } else if (isDPCurve(s1)) {
+    // 群组文字和OLE等其他类型,创建一个临时边界范围
+    auto scp = CreateBoundary(cdr, s2);
+    isIn = s1->GetDisplayCurve()->IntersectsWith(scp->GetDisplayCurve());
+    scp->Delete();
+
+  } else if (isDPCurve(s2)) {
+    auto scp = CreateBoundary(cdr, s1);
+    isIn = scp->GetDisplayCurve()->IntersectsWith(s2->GetDisplayCurve());
+    scp->Delete();
+
+  } else {
+    auto scp = CreateBoundary(cdr, s1);
+    auto scp2 = CreateBoundary(cdr, s2);
+    isIn = scp->GetDisplayCurve()->IntersectsWith(scp2->GetDisplayCurve());
+    scp->Delete();
+    scp2->Delete();
+  }
+  return isIn;
+}
+
+// 从矩形边界坐标 获得中心坐标
+void calculate_center(const BoundingBox &box, double &cx, double &cy) {
+  cx = box.x + (box.w / 2);
+  cy = box.y + (box.h / 2);
+}
+
+// VGCore::cdrPositionOfPointOverShape VGCore::IVGShape::IsOnShape ( double x, double y, double HotArea );
+// VGCore::cdrPositionOfPointOverShape VGCore::IVGCurve::IsOnCurve ( double x, double y, double HotArea );
+
+bool BoundaryGroup(corel *cdr, IVGShapeRange *sr, IVGShapeRange *srs) {
+
+  if (sr->Count < 2)
+    return false;
+
+  BoundingBox box, bound_box;
+  double x, y;
+  int OnSh = 0;
+  auto red = cdr->CreateCMYKColor(0, 100, 100, 0);
+
+  // 处理文字和影响的物件
+  auto txtbox = cdr->CreateShapeRange();
+  auto sr_text =
+      sr->Shapes->FindShapes(_bstr_t(), cdrTextShape, VARIANT_TRUE, _bstr_t());
+  if (sr_text->Count > 0) {
+    auto al = cdr->ActiveLayer;
+    for (auto i = 0; i != sr_text->Count; i++) {
+      sr_text->Shapes->Item[i + 1]->GET_BOUNDING_BOX(box);
+      auto s = al->CreateRectangle2(box.x, box.y, box.w, box.h, ZERO_4PC);
+      txtbox->Add(s);
+    }
+    sr->AddRange(txtbox);
+  }
+
+
+  // auto stmp = sr->CreateBoundary(0, 0, true, false); // 建立异性边界物件
+  // auto ef1 = stmp->CreateContour(
+  //     cdrContourOutside, 0.2, 1, cdrDirectFountainFillBlend, red, NULL, NULL, 0,
+  //     0, cdrContourSquareCap, cdrContourCornerMiteredOffsetBevel, 15);
+
+  // auto bounds = ef1->Separate();
+  // stmp->Delete();
+  // // bounds->SetOutlineProperties(0.076, NULL, cdr->CreateCMYKColor(100, 50, 0,
+  // // 0), NULL, NULL, cdrUndefined,  cdrUndefined, cdrOutlineUndefinedLineCaps,
+  // // cdrOutlineUndefinedLineJoin, NULL, NULL, NULL, NULL, NULL);
+
+  // auto sbox = bounds->BreakApartEx();
+  // sbox->ApplyUniformFill(red);
+
+
+  // 建立辅助的异性边界物件,需要填充颜色,搞了半天才搞定
+  IVGShapePtr bounds;
+  if (cdr->VersionMajor < 17) {
+    sr->CreateSelection();
+    cdr->FrameWork->Automation->InvokeItem( _bstr_t("b0491566-5ffe-450a-b17e-f2f496b4eb22"));
+    bounds = cdr->ActiveSelectionRange->Shapes->Item[1];
+    bounds->OrderToFront();
+  } 
+
+  bounds->Fill->UniformColor->RGBAssign(255, 0, 0);
+  auto sbox = bounds->BreakApartEx(); // 把边界 拆分为多个边界 用来分组
+
+
+  // 删除文字添加的方框
+  if (sr_text->Count > 0) {
+    sr->RemoveRange(txtbox);
+    txtbox->Delete();
+  }
+
+  // 按照边界框异形范围进行分组群组
+  auto srgp = cdr->CreateShapeRange();
+
+  for (int k = 0; k < sbox->Count && sr->Count; k++) {
+    sbox->Shapes->Item[k + 1]->GET_BOUNDING_BOX(bound_box);
+
+    for (int i = 0; i < sr->Count; i++) {
+      auto sh = sr->Shapes->Item[i + 1];
+      sh->GET_BOUNDING_BOX(box);   // 获得物件矩形边界坐标
+      calculate_center(box, x, y); // 获得物件中心坐标
+      OnSh = sbox->Shapes->Item[k + 1]->IsOnShape(x, y, -1);
+
+      if (OnSh) {
+        srgp->Add(sh);
+      } else if (isOverlapped(box, bound_box)) {
+        if (isIntWith(cdr, sbox->Shapes->Item[k + 1], sh))
+          srgp->Add(sh);
+      }
+    }
+
+    // 从Range中移除已分组的图形
+    sr->RemoveRange(srgp);
+    if (srgp->Count > 1) {
+      srs->Add(srgp->Group());
+    } else {
+      srs->AddRange(srgp);
+    }
+    srgp->RemoveAll();
+  }
+
+  // 删除辅助的异性边界物件
+  if(!debug_flg)
+    sbox->Delete();
+  return true;
+}
+
+// 测试运行 异形群组
+void run_BoundaryGroup(corel *cdr) {
+  auto start = std::chrono::high_resolution_clock::now(); // 开始时间
+  // if (cdr->VersionMajor < 17) {
+  //   sprintf(infobuf, "异形群组目前只支持X7以上版本!");  return; }
+
+  BeginOpt(cdr);
+
+  auto sr = cdr->ActiveSelectionRange;
+  auto srs = cdr->CreateShapeRange();
+  auto sr_box = cdr->CreateShapeRange();
+
+  int cnt = sr->Count;
+
+  // 取消选择,速度优化
+  cdr->ActiveDocument->ClearSelection();
+
+  if (cnt > 300) {
+    // 调用矩形分组,分步执行异形群组
+    if (BoxGrouping(cdr, sr, sr_box, 0.1)) {
+      for (int i = 0; i < sr_box->Count; i++) {
+        auto s = sr_box->Shapes->Item[i + 1];
+        if (!s->IsSimpleShape) {
+          auto sr2 = s->UngroupEx();
+          BoundaryGroup(cdr, sr2, srs);
+        }
+      }
+    }
+  } else {
+    BoundaryGroup(cdr, sr, srs);
+  }
+
+  srs->CreateSelection();
+
+  // 计算持续时间
+  double runtime = 0.0;
+  auto end = std::chrono::high_resolution_clock::now();
+  std::chrono::duration<double> duration = end - start;
+  runtime = duration.count();
+
+  sprintf(infobuf, "选择物件: %d 个进行异形群组\n群组: %d 组, 时间: %.2f秒", cnt, srs->Count, runtime);
+  EndOpt(cdr);
+}

+ 353 - 0
CorelDRAW_X4_SDK/ToolsBox.cpp

@@ -0,0 +1,353 @@
+#include "cdrapi.h"
+#include "cdrapp.h"
+#include "resource.h"
+#include <stdio.h>
+#include <windows.h>
+
+
+corel *cdr = NULL;
+static HINSTANCE g_hResource = NULL;
+HICON  g_hIcon;         // 窗口图标句柄
+bool debug_flg = false; // 调试->高级模式
+char infobuf[256] = {0};
+BOOL APIENTRY DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
+  if (fdwReason == DLL_PROCESS_ATTACH) {
+    g_hResource = (HINSTANCE)hinstDLL;
+  }
+  return TRUE;
+}
+
+class ToolsBoxPlugin : public VGCore::IVGAppPlugin {
+private:
+  volatile ULONG m_ulRefCount;
+  long m_lCookie;
+  static intptr_t CALLBACK DlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+public:
+  ToolsBoxPlugin();
+  VGCore::IVGApplication *m_pApp;
+  void OpenToolsBox();
+
+  // IUnknown
+public:
+  STDMETHOD(QueryInterface)(REFIID riid, void **ppvObject);
+  STDMETHOD_(ULONG, AddRef)(void) { return ++m_ulRefCount; }
+  STDMETHOD_(ULONG, Release)(void) {
+    ULONG ulCount = --m_ulRefCount;
+    if (ulCount == 0) {
+      delete this;
+    }
+    return ulCount;
+  }
+
+  // IDispatch
+public:
+  STDMETHOD(GetTypeInfoCount)(UINT *pctinfo) { return E_NOTIMPL; }
+  STDMETHOD(GetTypeInfo)(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo) {
+    return E_NOTIMPL;
+  }
+  STDMETHOD(GetIDsOfNames)
+  (REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId) {
+    return E_NOTIMPL;
+  }
+  STDMETHOD(Invoke)
+  (DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr);
+
+  // IVGAppPlugin
+public:
+  STDMETHOD(raw_OnLoad)(VGCore::IVGApplication *Application);
+  STDMETHOD(raw_StartSession)();
+  STDMETHOD(raw_StopSession)();
+  STDMETHOD(raw_OnUnload)();
+};
+
+ToolsBoxPlugin::ToolsBoxPlugin() {
+  m_pApp = NULL;
+  m_lCookie = 0;
+  m_ulRefCount = 1;
+}
+
+STDMETHODIMP ToolsBoxPlugin::QueryInterface(REFIID riid, void **ppvObject) {
+  HRESULT hr = S_OK;
+  m_ulRefCount++;
+  if (riid == IID_IUnknown) {
+    *ppvObject = (IUnknown *)this;
+  } else if (riid == IID_IDispatch) {
+    *ppvObject = (IDispatch *)this;
+  } else if (riid == __uuidof(VGCore::IVGAppPlugin)) {
+    *ppvObject = (VGCore::IVGAppPlugin *)this;
+  } else {
+    m_ulRefCount--;
+    hr = E_NOINTERFACE;
+  }
+  return hr;
+}
+
+STDMETHODIMP ToolsBoxPlugin::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid,WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr) {
+  switch (dispIdMember) {
+
+  case 0x0014: // DISPID_APP_ONPLUGINCMD
+    if (pDispParams != NULL && pDispParams->cArgs == 1) {
+      _bstr_t strCmd(pDispParams->rgvarg[0].bstrVal);
+      if (strCmd == _bstr_t("OpenToolsBox")) {
+        //   MessageBox(NULL, _bstr_t("OpenToolsBox"), _bstr_t("OpenToolsBox"),
+        //   MB_ICONSTOP);
+        OpenToolsBox();
+      }
+    }
+    break;
+
+  case 0x0015: // DISPID_APP_ONPLUGINCMDSTATE
+    if (pDispParams != NULL && pDispParams->cArgs == 3) {
+      _bstr_t strCmd(pDispParams->rgvarg[2].bstrVal);
+      if (strCmd == _bstr_t("OpenToolsBox")) {
+        *pDispParams->rgvarg[1].pboolVal = VARIANT_TRUE;
+      }
+    }
+    break;
+  }
+  return S_OK;
+}
+
+STDMETHODIMP ToolsBoxPlugin::raw_OnLoad(VGCore::IVGApplication *Application) {
+  m_pApp = Application;
+  if (m_pApp) {
+    m_pApp->AddRef();
+  }
+  return S_OK;
+}
+
+ToolsBoxPlugin* lycpg = nullptr;
+STDMETHODIMP ToolsBoxPlugin::raw_StartSession() {
+  // 接口转交给cdr
+  cdr = m_pApp;
+  lycpg = this;
+
+  try {
+    m_pApp->AddPluginCommand(_bstr_t("OpenToolsBox"), _bstr_t("Tools Box"), _bstr_t("打开工具窗口"));
+
+
+    m_lCookie = m_pApp->AdviseEvents(this);
+  } catch (_com_error &e) {
+    MessageBox(NULL, e.Description(), _bstr_t("Error"), MB_ICONSTOP);
+  }
+  return S_OK;
+}
+
+STDMETHODIMP ToolsBoxPlugin::raw_StopSession() {
+  try {
+    m_pApp->UnadviseEvents(m_lCookie);
+    m_pApp->RemovePluginCommand(_bstr_t("OpenToolsBox"));
+  } catch (_com_error &e) {
+    MessageBox(NULL, e.Description(), _bstr_t("Error"), MB_ICONSTOP);
+  }
+  return S_OK;
+}
+
+STDMETHODIMP ToolsBoxPlugin::raw_OnUnload() {
+  if (m_pApp) {
+    m_pApp->Release();
+    m_pApp = NULL;
+  }
+  return S_OK;
+}
+
+void ToolsBoxPlugin::OpenToolsBox() {
+  m_pApp->StartupMode = VGCore::cdrStartupDoNothing;
+
+  intptr_t nHandle = m_pApp->AppWindow->Handle;
+  HWND hAppWnd = reinterpret_cast<HWND>(nHandle);
+
+  // 创建非模态对话框
+  HWND hDlgWnd = CreateDialogParam(g_hResource, MAKEINTRESOURCE(IDD_TOOLS_BOX), hAppWnd, DlgProc, (LPARAM)m_pApp);
+  // 在创建对话框之前存储 m_pApp 指针
+  SetWindowLongPtr(hDlgWnd, DWLP_USER, (LONG_PTR)m_pApp);
+
+  // 获取屏幕的宽度和高度
+  RECT rect;
+  GetWindowRect(GetDesktopWindow(), &rect);
+  int screenWidth = rect.right - rect.left;
+  int screenHeight = rect.bottom - rect.top;
+
+  // 计算对话框窗口的宽度和高度
+  RECT dlgRect;
+  GetWindowRect(hDlgWnd, &dlgRect);
+  int w = dlgRect.right - dlgRect.left;
+  int h = dlgRect.bottom - dlgRect.top;
+
+  // 计算对话框窗口的左上角坐标,使其居中显示
+  int x = (screenWidth - w) / 2;
+  int y = (screenHeight - h) / 2;
+
+  // 创建窗口数据实例      // 从文件加载
+  WinData win = {x, y, w, h};
+  win = loadWinData("window.dat", win);
+
+  // 设置对话框窗口的位置
+  SetWindowPos(hDlgWnd, NULL, win.x, win.y, win.w, win.h, SWP_NOZORDER | SWP_NOACTIVATE);
+  // 设置对话框窗口的父窗口  // #define GWL_HWNDPARENT      (-8)
+  SetWindowLong(hDlgWnd, -8, (LONG)hAppWnd);
+
+  // 显示对话框窗口
+  ShowWindow(hDlgWnd, SW_SHOW);
+
+  // 使用 g_hResource 作为 HINSTANCE
+  g_hIcon = ::LoadIcon(g_hResource, MAKEINTRESOURCE(IDI_ICON1));
+  // 小图标:就是窗口左上角对应的那个图标
+  ::SendMessage(hDlgWnd, WM_SETICON, ICON_SMALL, (LPARAM)g_hIcon);
+
+}
+
+// MessageBox(NULL, "更新提示信息: 激活CorelDRAW窗口", "CPG代码测试", MB_ICONSTOP);
+#define UPDATE_INFO_ACTIVE_CDRWND                                              \
+  PutTextValue(hDlg, INFO_TEXT, infobuf);                                      \
+  Active_CorelWindows(hDlg);
+intptr_t CALLBACK ToolsBoxPlugin::DlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+  // 从附加数据中获取 m_pApp 指针
+  VGCore::IVGApplication *cdr = reinterpret_cast<VGCore::IVGApplication *>(
+      GetWindowLongPtr(hDlg, DWLP_USER));
+
+  if (uMsg == WM_COMMAND) {
+    try {
+      switch (LOWORD(wParam)) {
+
+      case ID_BOUNDARY_GROUP: {
+
+        if (BST_CHECKED == IsDlgButtonChecked(hDlg, DEBUG_FLG))
+          debug_flg = true;
+        else
+          debug_flg = false;
+        double exp = GetTextValue(hDlg, EXP_TEXT);
+
+        run_BoundaryGroup(cdr);
+        UPDATE_INFO_ACTIVE_CDRWND
+      } break;
+
+      case IDC_BOX_GROUP: {
+        double exp = GetTextValue(hDlg, EXP_TEXT);
+        AutoMakeSelection(cdr);
+        Box_AutoGroup(cdr, exp);
+        UPDATE_INFO_ACTIVE_CDRWND
+
+      } break;
+
+      case IDC_CQL_OUTLINE:
+        cql_OutlineColor(cdr);
+
+        UPDATE_INFO_ACTIVE_CDRWND
+        break;
+
+      case IDC_CQL_FILL:
+        cql_FillColor(cdr);
+
+        UPDATE_INFO_ACTIVE_CDRWND
+        break;
+
+      case IDC_CQL_SIZE:
+        cql_SameSize(cdr);
+
+        UPDATE_INFO_ACTIVE_CDRWND
+        break;
+
+      case IDC_CLEAR_FILL: {
+        double exp = GetTextValue(hDlg, EXP_TEXT);
+        AutoMakeSelection(cdr);
+        BBox_DrawRectangle(cdr, exp);
+
+        UPDATE_INFO_ACTIVE_CDRWND
+      } break;
+
+      case IDC_SR_FLIP:
+        Shapes_Filp(cdr);
+      break;
+
+      case IDC_CDR2AI: {
+        CdrCopy_to_AdobeAI(cdr);
+        sprintf(infobuf, "把CorelDRAW软件中选择物件复制到剪贴板,请切换到AI软件粘贴");
+        UPDATE_INFO_ACTIVE_CDRWND
+      } break;
+
+      case IDC_AI2CDR: {
+        AdobeAI_Copy_ImportCdr(cdr);
+        sprintf(infobuf, "请先在AI软件选择物件复制,再切换到CorelDRAW软件点执行本功能");
+        UPDATE_INFO_ACTIVE_CDRWND
+      } break;
+
+//////////// 窗口扩展、最小化、恢复按钮按钮////////////////////////////////////////////////
+      case EXPAND_TOOLS: {
+        // 获取当前窗口的句柄
+        HWND hwnd = GetActiveWindow();
+        // 获取当前窗口的矩形区域
+        RECT rect;
+        GetWindowRect(hwnd, &rect);
+        // 计算新的宽度
+        int newWidth = rect.right - rect.left + 100; // 增加100单位
+        // 移动窗口到新的大小
+        SetWindowPos(hwnd, NULL, rect.left, rect.top, newWidth, rect.bottom - rect.top, SWP_NOZORDER | SWP_NOACTIVATE);
+        // 隐藏按钮 (假设按钮的句柄为 buttonHandle)
+        ShowWindow(::GetDlgItem(hDlg, EXPAND_TOOLS), SW_HIDE);
+      } break;
+
+      case MIN_TOOLS: {
+        RECT rect;
+        GetWindowRect(hDlg, &rect);
+        int currentWidth = rect.right - rect.left;
+        int currentHeight = rect.bottom - rect.top;
+        int h = 98;
+        SetWindowPos(hDlg, NULL, rect.left, rect.top, currentWidth, h, SWP_NOZORDER | SWP_NOACTIVATE);
+        ShowWindow(::GetDlgItem(hDlg, MIN_TOOLS), SW_HIDE);
+        
+        int x = rect.left;
+        int y = rect.top;
+        int w = currentWidth;
+        // 保存窗口位置
+        WinData win = {x, y, w, h};
+        saveWinData("window.dat", &win);
+      } break;
+
+      case RENEW_TOOLS: {
+        RECT rect;
+        GetWindowRect(hDlg, &rect);
+        int x = rect.left;
+        int y = rect.top;
+        int h = 232; // 恢复宽高
+        int w = 207;
+        SetWindowPos(hDlg, NULL, x, y, w, h, SWP_NOZORDER | SWP_NOACTIVATE);
+        ShowWindow(::GetDlgItem(hDlg, MIN_TOOLS), !SW_HIDE);
+        ShowWindow(::GetDlgItem(hDlg, EXPAND_TOOLS), !SW_HIDE);
+
+        // 保存窗口位置
+        WinData win = {x, y, w, h};
+        saveWinData("window.dat", &win);
+
+      } break;
+
+      case IDOK:
+        break;
+      case IDCANCEL:
+        EndDialog(hDlg, 0);
+        break;
+      }
+
+    } catch (_com_error &e) {
+      MessageBox(NULL, e.Description(), "Error", MB_ICONSTOP);
+      EndOpt(cdr);
+    }
+
+  } else if (uMsg == WM_INITDIALOG) {
+    SetWindowText(::GetDlgItem(hDlg, EXP_TEXT), "0");
+    return 1;
+  }
+  return 0;
+}
+
+extern "C" __declspec(dllexport) DWORD APIENTRY AttachPlugin(VGCore::IVGAppPlugin **ppIPlugin) {
+  *ppIPlugin = new ToolsBoxPlugin;
+  return 0x100;
+}
+
+void open_lycpg() {
+    if (lycpg) {
+        lycpg->OpenToolsBox(); // 使用类的实例调用成员函数
+    }
+}

二进制
CorelDRAW_X4_SDK/vgcoreauto.tlb


+ 210 - 0
English/BoundaryGroup.cpp

@@ -0,0 +1,210 @@
+#include "cdrapp.h"
+#include <cerrno>
+#include <cmath>
+#include <cstddef>
+
+bool isDPCurve(IVGShapePtr s) {
+  auto dpc = (s->Type == cdrRectangleShape) || (s->Type == cdrEllipseShape) ||
+             (s->Type == cdrCurveShape) || (s->Type == cdrPolygonShape) ||
+             (s->Type == cdrBitmapShape);
+  return dpc;
+}
+IVGShapePtr CreateBoundary(corel *cdr, IVGShapePtr s) {
+  IVGShapePtr scp;
+  if (cdr->VersionMajor < 17) {
+    // s->CreateSelection();
+    // cdr->FrameWork->Automation->InvokeItem( _bstr_t("b0491566-5ffe-450a-b17e-f2f496b4eb22"));
+    // scp = cdr->ActiveSelectionRange->Shapes->Item[1];
+    BoundingBox box;
+    s->GET_BOUNDING_BOX(box);
+    scp = cdr->ActiveLayer->CreateRectangle2(box.x, box.y, box.w, box.h, ZERO_4PC);
+  } else {
+    //  这个 API X7 以上才支持,所以现在直接画矩形
+    scp = s->CreateBoundary(0, 0, true, false);    
+  }
+  return scp;
+}
+
+// VGCore::IVGShapePtr VGCore::IVGShape::CreateBoundary ( double x, double y, VARIANT_BOOL PlaceOnTop, VARIANT_BOOL DeleteSource ); VGCore::IVGShapePtr
+// VGCore::IVGShapeRange::CreateBoundary ( double x, double y, VARIANT_BOOL PlaceOnTop, VARIANT_BOOL DeleteSource );
+// VARIANT_BOOL VGCore::IVGCurve::IntersectsWith ( struct IVGCurve * Curve )
+bool isIntWith(corel *cdr, IVGShape *s1, IVGShape *s2) {
+  bool isIn = false;
+  if (isDPCurve(s1) && isDPCurve(s2)) {
+    isIn = s1->GetDisplayCurve()->IntersectsWith(s2->GetDisplayCurve());
+  } else if (isDPCurve(s1)) {
+    // 群组文字和OLE等其他类型,创建一个临时边界范围
+    auto scp = CreateBoundary(cdr, s2);
+    isIn = s1->GetDisplayCurve()->IntersectsWith(scp->GetDisplayCurve());
+    scp->Delete();
+
+  } else if (isDPCurve(s2)) {
+    auto scp = CreateBoundary(cdr, s1);
+    isIn = scp->GetDisplayCurve()->IntersectsWith(s2->GetDisplayCurve());
+    scp->Delete();
+
+  } else {
+    auto scp = CreateBoundary(cdr, s1);
+    auto scp2 = CreateBoundary(cdr, s2);
+    isIn = scp->GetDisplayCurve()->IntersectsWith(scp2->GetDisplayCurve());
+    scp->Delete();
+    scp2->Delete();
+  }
+  return isIn;
+}
+
+// 从矩形边界坐标 获得中心坐标
+void calculate_center(const BoundingBox &box, double &cx, double &cy) {
+  cx = box.x + (box.w / 2);
+  cy = box.y + (box.h / 2);
+}
+
+// VGCore::cdrPositionOfPointOverShape VGCore::IVGShape::IsOnShape ( double x, double y, double HotArea );
+// VGCore::cdrPositionOfPointOverShape VGCore::IVGCurve::IsOnCurve ( double x, double y, double HotArea );
+
+bool BoundaryGroup(corel *cdr, IVGShapeRange *sr, IVGShapeRange *srs) {
+
+  if (sr->Count < 2)
+    return false;
+
+  BoundingBox box, bound_box;
+  double x, y;
+  int OnSh = 0;
+  auto red = cdr->CreateCMYKColor(0, 100, 100, 0);
+
+  // 处理文字和影响的物件
+  auto txtbox = cdr->CreateShapeRange();
+  auto sr_text =
+      sr->Shapes->FindShapes(_bstr_t(), cdrTextShape, VARIANT_TRUE, _bstr_t());
+  if (sr_text->Count > 0) {
+    auto al = cdr->ActiveLayer;
+    for (auto i = 0; i != sr_text->Count; i++) {
+      sr_text->Shapes->Item[i + 1]->GET_BOUNDING_BOX(box);
+      auto s = al->CreateRectangle2(box.x, box.y, box.w, box.h, ZERO_4PC);
+      txtbox->Add(s);
+    }
+    sr->AddRange(txtbox);
+  }
+
+
+  // auto stmp = sr->CreateBoundary(0, 0, true, false); // 建立异性边界物件
+  // auto ef1 = stmp->CreateContour(
+  //     cdrContourOutside, 0.2, 1, cdrDirectFountainFillBlend, red, NULL, NULL, 0,
+  //     0, cdrContourSquareCap, cdrContourCornerMiteredOffsetBevel, 15);
+
+  // auto bounds = ef1->Separate();
+  // stmp->Delete();
+  // // bounds->SetOutlineProperties(0.076, NULL, cdr->CreateCMYKColor(100, 50, 0,
+  // // 0), NULL, NULL, cdrUndefined,  cdrUndefined, cdrOutlineUndefinedLineCaps,
+  // // cdrOutlineUndefinedLineJoin, NULL, NULL, NULL, NULL, NULL);
+
+  // auto sbox = bounds->BreakApartEx();
+  // sbox->ApplyUniformFill(red);
+
+
+  // 建立辅助的异性边界物件,需要填充颜色,搞了半天才搞定
+  IVGShapePtr bounds;
+  if (cdr->VersionMajor < 17) {
+    sr->CreateSelection();
+    cdr->FrameWork->Automation->InvokeItem( _bstr_t("b0491566-5ffe-450a-b17e-f2f496b4eb22"));
+    bounds = cdr->ActiveSelectionRange->Shapes->Item[1];
+    bounds->OrderToFront();
+  } else {
+    bounds = sr->CreateBoundary(0, 0, true, false); // 建立异性边界物件  
+  }
+
+  bounds->Fill->UniformColor->RGBAssign(255, 0, 0);
+  auto sbox = bounds->BreakApartEx(); // 把边界 拆分为多个边界 用来分组
+
+  if (sbox->Count > 2) {
+    //  VGCore::IVGShapeRange::Sort ( _bstr_t CompareExpression, long
+    //  StartIndex, long EndIndex, const _variant_t & Data );
+    sbox->Sort(bstr_t( "@shape1.width * @shape1.height > @shape2.width * @shape2.height"),  1, sbox->Count, _variant_t());
+  }
+
+  // 删除文字添加的方框
+  if (sr_text->Count > 0) {
+    sr->RemoveRange(txtbox);
+    txtbox->Delete();
+  }
+
+  // 按照边界框异形范围进行分组群组
+  auto srgp = cdr->CreateShapeRange();
+
+  for (int k = 0; k < sbox->Count && sr->Count; k++) {
+    sbox->Shapes->Item[k + 1]->GET_BOUNDING_BOX(bound_box);
+
+    for (int i = 0; i < sr->Count; i++) {
+      auto sh = sr->Shapes->Item[i + 1];
+      sh->GET_BOUNDING_BOX(box);   // 获得物件矩形边界坐标
+      calculate_center(box, x, y); // 获得物件中心坐标
+      OnSh = sbox->Shapes->Item[k + 1]->IsOnShape(x, y, -1);
+
+      if (OnSh) {
+        srgp->Add(sh);
+      } else if (isOverlapped(box, bound_box)) {
+        if (isIntWith(cdr, sbox->Shapes->Item[k + 1], sh))
+          srgp->Add(sh);
+      }
+    }
+
+    // 从Range中移除已分组的图形
+    sr->RemoveRange(srgp);
+    if (srgp->Count > 1) {
+      srs->Add(srgp->Group());
+    } else {
+      srs->AddRange(srgp);
+    }
+    srgp->RemoveAll();
+  }
+
+  // 删除辅助的异性边界物件
+  if(!debug_flg)
+    sbox->Delete();
+  return true;
+}
+
+// 测试运行 异形群组
+void run_BoundaryGroup(corel *cdr) {
+  auto start = std::chrono::high_resolution_clock::now(); // 开始时间
+  // if (cdr->VersionMajor < 17) {
+  //   sprintf(infobuf, "异形群组目前只支持X7以上版本!");  return; }
+
+  BeginOpt(cdr);
+
+  auto sr = cdr->ActiveSelectionRange;
+  auto srs = cdr->CreateShapeRange();
+  auto sr_box = cdr->CreateShapeRange();
+
+  int cnt = sr->Count;
+
+  // 取消选择,速度优化
+  cdr->ActiveDocument->ClearSelection();
+
+  if (cnt > 300) {
+    // 调用矩形分组,分步执行异形群组
+    if (BoxGrouping(cdr, sr, sr_box, 0.1)) {
+      for (int i = 0; i < sr_box->Count; i++) {
+        auto s = sr_box->Shapes->Item[i + 1];
+        if (!s->IsSimpleShape) {
+          auto sr2 = s->UngroupEx();
+          BoundaryGroup(cdr, sr2, srs);
+        }
+      }
+    }
+  } else {
+    BoundaryGroup(cdr, sr, srs);
+  }
+
+  srs->CreateSelection();
+
+  // 计算持续时间
+  double runtime = 0.0;
+  auto end = std::chrono::high_resolution_clock::now();
+  std::chrono::duration<double> duration = end - start;
+  runtime = duration.count();
+
+  // sprintf(infobuf, "选择物件: %d 个进行异形群组\n群组: %d 组, 时间: %.2f秒", cnt, srs->Count, runtime);
+  sprintf(infobuf,  "Selected objects: %d, Grouping time: %.2f seconds\nTotal groups: %d, Total time: %.2f seconds", cnt, srs->Count, runtime);
+  EndOpt(cdr);
+}

+ 66 - 0
English/ToolsBox.rc

@@ -0,0 +1,66 @@
+// Generated by ResEdit 1.6.6
+// Copyright (C) 2006-2015
+// http://www.resedit.net
+
+#include <windows.h>
+#include <commctrl.h>
+#include <richedit.h>
+#include "resource.h"
+
+
+
+
+//
+// Dialog resources
+//
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+IDD_TOOLS_BOX DIALOGEX 0, 0, 134, 125
+STYLE DS_MODALFRAME | DS_SHELLFONT | WS_CAPTION | WS_POPUP | WS_SYSMENU
+
+// CAPTION "蘭雅 CPG 插件 福利群版"
+// FONT 8, "MS Shell Dlg", 135, 0, 1
+// {
+//     AUTOCHECKBOX    "调试->高级模式", DEBUG_FLG, 142, 8, 70, 8, 0, WS_EX_LEFT
+//     CTEXT           "蘭雅 CorelDRAW CPG 插件\n2024.8.30 异形群组版", INFO_TEXT, 5, 104, 124, 18, SS_CENTER, WS_EX_LEFT
+//     PUSHBUTTON      "方框智能群组", IDC_BOX_GROUP, 69, 2, 56, 20, 0, WS_EX_LEFT
+//     PUSHBUTTON      "CQL轮廓色相同", IDC_CQL_OUTLINE, 4, 62, 59, 19, 0, WS_EX_LEFT
+//     PUSHBUTTON      "边界画矩形", IDC_CLEAR_FILL, 69, 42, 56, 19, 0, WS_EX_LEFT
+//     PUSHBUTTON      "批量镜像", IDC_SR_FLIP, 69, 62, 56, 19, 0, WS_EX_LEFT
+//     PUSHBUTTON      "CQL颜色相同", IDC_CQL_FILL, 4, 82, 59, 19, 0, WS_EX_LEFT
+//     PUSHBUTTON      "CQL尺寸相同", IDC_CQL_SIZE, 69, 82, 56, 19, 0, WS_EX_LEFT
+//     PUSHBUTTON      "CDR复制到AI", IDC_CDR2AI, 4, 22, 59, 19, 0, WS_EX_LEFT
+//     PUSHBUTTON      "AI粘贴到CDR", IDC_AI2CDR, 4, 42, 59, 19, 0, WS_EX_LEFT
+//     LTEXT           "容差:(mm)", EXP_LT, 66, 28, 41, 8, SS_LEFT, WS_EX_LEFT
+//     EDITTEXT        EXP_TEXT, 102, 25, 22, 14, NOT WS_TABSTOP | ES_AUTOHSCROLL, WS_EX_LEFT
+//     PUSHBUTTON      "异形群组", ID_BOUNDARY_GROUP, 4, 2, 59, 20, 0, WS_EX_LEFT
+//     PUSHBUTTON      "∨", RENEW_TOOLS, 125, 23, 12, 17, BS_NOTIFY, WS_EX_LEFT
+//     PUSHBUTTON      "∧", MIN_TOOLS, 125, 6, 12, 17, BS_NOTIFY, WS_EX_LEFT
+//     PUSHBUTTON      ">", EXPAND_TOOLS, 125, 57, 12, 28, BS_NOTIFY, WS_EX_LEFT
+// }
+
+CAPTION "Lanya CPG BoxGroup"
+FONT 8, "MS Shell Dlg", 135, 0, 1
+{
+    AUTOCHECKBOX    "Debug -> Advanced Mode", DEBUG_FLG, 142, 8, 70, 8, 0, WS_EX_LEFT
+    CTEXT           "Lanya CorelDRAW CPG Plugin\n2024.8.30 Irregular Group Version", INFO_TEXT, 5, 104, 124, 18, SS_CENTER, WS_EX_LEFT
+    PUSHBUTTON      "Box SmartGroup", IDC_BOX_GROUP, 69, 2, 56, 20, 0, WS_EX_LEFT
+    PUSHBUTTON      "CQL Outline Same Color", IDC_CQL_OUTLINE, 4, 62, 59, 19, 0, WS_EX_LEFT
+    PUSHBUTTON      "Rectangle@Boundary", IDC_CLEAR_FILL, 69, 42, 56, 19, 0, WS_EX_LEFT
+    PUSHBUTTON      "Batch Flip", IDC_SR_FLIP, 69, 62, 56, 19, 0, WS_EX_LEFT
+    PUSHBUTTON      "CQL Fill Same Color", IDC_CQL_FILL, 4, 82, 59, 19, 0, WS_EX_LEFT
+    PUSHBUTTON      "CQL Size Same", IDC_CQL_SIZE, 69, 82, 56, 19, 0, WS_EX_LEFT
+    PUSHBUTTON      "CDR Copy to AI", IDC_CDR2AI, 4, 22, 59, 19, 0, WS_EX_LEFT
+    PUSHBUTTON      "AI Paste to CDR", IDC_AI2CDR, 4, 42, 59, 19, 0, WS_EX_LEFT
+    LTEXT           "Tolerance: (mm)", EXP_LT, 66, 28, 41, 8, SS_LEFT, WS_EX_LEFT
+    EDITTEXT        EXP_TEXT, 102, 25, 22, 14, NOT WS_TABSTOP | ES_AUTOHSCROLL, WS_EX_LEFT
+    PUSHBUTTON      "Irregular Group", ID_BOUNDARY_GROUP, 4, 2, 59, 20, 0, WS_EX_LEFT
+    PUSHBUTTON      "+", RENEW_TOOLS, 125, 23, 12, 17, BS_NOTIFY, WS_EX_LEFT
+    PUSHBUTTON      "^", MIN_TOOLS, 125, 6, 12, 17, BS_NOTIFY, WS_EX_LEFT
+    PUSHBUTTON      ">", EXPAND_TOOLS, 125, 57, 12, 28, BS_NOTIFY, WS_EX_LEFT
+}
+
+//
+// Icon resources
+//
+LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
+IDI_ICON1          ICON           ".\\LYVBA.ico"

+ 251 - 0
English/boxAutoGroup.cpp

@@ -0,0 +1,251 @@
+#include "cdrapp.h"
+
+// 扩展边界框
+void expand_bounding_boxes(std::vector<BoundingBox>& boxes, double exp) {
+    for (auto& box : boxes) {
+        // 扩展宽度和高度
+        box.w += 2 * exp; // 左右各扩展
+        box.h += 2 * exp; // 上下各扩展
+        // 调整左下角坐标
+        box.x -= exp; // 向左扩展
+        box.y -= exp; // 向下扩展
+    }
+}
+
+double get_bounding_box_area(BoundingBox box) { return box.w * box.h; }
+
+// 自定义比较函数,按 BoundingBox 的面积大小进行排序
+bool compare_bounding_boxes(const std::pair<int, BoundingBox> &a,
+                            const std::pair<int, BoundingBox> &b) {
+  return get_bounding_box_area(a.second) > get_bounding_box_area(b.second);
+}
+
+// 查找父节点
+int find(std::vector<int>& parent, int x) {
+    if (parent[x] != x) {
+        parent[x] = find(parent, parent[x]);
+    }
+    return parent[x];
+}
+
+// 合并两个集合
+void unionSet(std::vector<int>& parent, int x, int y) {
+    int xroot = find(parent, x);
+    int yroot = find(parent, y);
+    parent[xroot] = yroot;
+}
+// 检查两个矩形是否重叠
+bool isOverlapped(const BoundingBox &a, const BoundingBox &b) {
+  return a.x < b.x + b.w && a.x + a.w > b.x && a.y < b.y + b.h && a.y + a.h > b.y;
+} // 函数使用AABB(Axis-Aligned Bounding Box)碰撞检测
+
+bool BBox_DrawRectangle(corel *cdr, double exp) {
+  BoundingBox box;
+  auto sr = cdr->ActiveSelectionRange; // 获得选择范围
+  auto al = cdr->ActiveVirtualLayer;          // 虚拟物件虚拟层
+  if (!sr || !al) return false;
+  
+  BeginOpt(cdr);
+  auto srs = cdr->CreateShapeRange();
+
+  // CorelDRAW Shapes 物件 Item 编号从1开始
+  for (auto i = 0; i != sr->Count; i++) {
+    sr->Shapes->Item[i + 1]->GET_BOUNDING_BOX(box); // 获得Shapes的BoundingBox,赋值到box
+    if (fabs(exp) > 0.02 ) { box.w += 2 * exp; box.h += 2 * exp; box.x -= exp; box.y -= exp; }
+    
+    auto s = al->CreateRectangle2(box.x, box.y, box.w, box.h, ZERO_4PC); // 使用BoundingBox box 创建一个矩形
+    s->Outline->Color->RGBAssign(0, 255, 0);
+    srs->Add(s);
+  }
+
+  cdr->ActiveDocument->LogCreateShapeRange(srs);  // 虚拟物件添加到文档中
+  srs->CreateSelection();
+
+  // sprintf(infobuf, "提示: 标记画框数量: %d 个\n容差值请使用小键盘输入", srs->Count);
+sprintf(infobuf, "Tip: Number of marked frames: %d\nPlease use the numeric keypad to enter the tolerance value", srs->Count);
+
+  EndOpt(cdr);
+  return true;
+}
+
+bool AutoMakeSelection(corel *cdr) {
+  auto sr = cdr->ActiveSelectionRange; 
+  if (0 == sr->Count) {
+    auto all = cdr->ActiveDocument->ActivePage->Shapes->All();
+    all->CreateSelection();
+  }
+  return true;
+}
+
+// 快速分组重叠的区域, 使用算法"Union-Find" 算法。这个算法可以有效地处理这种并集问题。
+// 算法的时间复杂度为 O(n^2),其中 n 是矩形的数量。如果矩形数量较多,可以考虑使用更高效的算法,
+// 例如使用四叉树(Quadtree)或者区间树(Interval Tree)等数据结构来加速计算。
+bool Box_AutoGroup(corel *cdr, double exp) {
+  BoundingBox box;
+
+  auto sr = cdr->ActiveSelectionRange; // 获得选择范围
+  auto al = cdr->ActiveLayer;          // 获得当前层
+  if (!sr || !al) return false;
+
+  auto start = std::chrono::high_resolution_clock::now(); // 开始时间
+  BeginOpt(cdr);
+
+  std::vector<BoundingBox> boxes;
+  std::vector<int> parent;
+
+  // CorelDRAW Shapes 物件 Item 编号从1开始
+  for (auto i = 0; i != sr->Count; i++) {
+    sr->Shapes->Item[i + 1]->GET_BOUNDING_BOX(box);
+    boxes.push_back(box);
+    parent.push_back(i);
+  }
+
+  // 扩展边界框,或者收缩边界框
+  if (fabs(exp) > 0.02 ) {
+    expand_bounding_boxes(boxes, exp);
+  }
+
+  // 实现 Union-Find 算法来合并重叠的区域
+  for (int i = 0; i < boxes.size(); i++) {
+    for (int j = i + 1; j < boxes.size(); j++) {
+      if (isOverlapped(boxes[i], boxes[j])) {
+        unionSet(parent, i, j);
+      }
+    }
+  }
+
+  double runtime[2] = {0,0};
+  auto end = std::chrono::high_resolution_clock::now(); 
+  std::chrono::duration<double> duration = end - start;
+  runtime[0] = duration.count();
+
+ // 输出分组结果到文件
+  // std::ofstream output_file("D:\\group.txt");
+  // if (output_file.is_open()) {
+  //     std::map<int, std::vector<int>> groups;
+  //     for (int i = 0; i < parent.size(); i++) {
+  //         int root = find(parent, i);
+  //         groups[root].push_back(i + 1); // CorelDRAW Shapes 物件 Item 编号从1开始
+  //     }
+
+  //     for (const auto& group : groups) {
+  //         output_file << "Group: ";
+  //         for (int index : group.second) {
+  //             output_file << index << " ";
+  //         }
+  //         output_file << std::endl;
+  //     }
+
+  //   auto end = std::chrono::high_resolution_clock::now(); // 结束时间
+  //   // 计算持续时间
+  //   std::chrono::duration<double> duration = end - start;
+  //   output_file << "Execution time: " << duration.count() << " seconds\n";
+
+  //   output_file.close();
+  // } 
+
+  // 输出分组结果
+  std::map<int, std::vector<int>> groups;
+  for (int i = 0; i < parent.size(); i++) {
+    int root = find(parent, i);
+    groups[root].push_back(i + 1); // CorelDRAW Shapes 物件 Item 编号从1开始
+  }
+
+  auto srgp = cdr->CreateShapeRange();
+  auto srs = cdr->CreateShapeRange();
+
+  cdr->ActiveDocument->ClearSelection();
+// 原来 没有取消选择 最初速度
+// Execution time: 63.0305 seconds
+
+// srgp->GET_BOUNDING_BOX(box); 
+// al->CreateRectangle2(box.x, box.y, box.w, box.h, ZERO_4PC); // 使用边界 创建一个矩形
+// box边界 转左上和右下坐标 box.x, box.y + box.h, box.x + box.w, box.y
+// auto sh = cdr->ActivePage->SelectShapesFromRectangle(box.x, box.y + box.h, box.x + box.w, box.y, false);
+// sh->Group();
+// 使用 SelectShapesFromRectangle 框选的形状进行群组
+// Execution time: 2.44753 seconds
+
+// cdr->ActiveDocument->ClearSelection(); // 使用取消选择
+// Execution time: 1.7432 seconds
+
+// srgp->CreateSelection();
+// cdr->ActiveSelectionRange->Group();
+// Execution time: 1.87662 seconds
+
+  // 分组分别进行群组
+  for (const auto& group : groups) {
+      for (int index : group.second) 
+        srgp->Add(sr->Shapes->Item[index]);
+      
+      if(sr->Count >1)
+        srs->Add(srgp->Group());
+      else
+        srs->AddRange(srgp);  
+
+      srgp->RemoveAll();
+  }
+  srs->CreateSelection();
+
+  // 计算持续时间
+  duration = std::chrono::high_resolution_clock::now() - start;
+  runtime[1] = duration.count();
+
+  // sprintf(infobuf, "选择物件: %d 个, 分组: %.2f秒\n总共群组: %d 组, 总时间: %.2f秒", sr->Count, runtime[0] + 0.01, srs->Count, runtime[1] + 0.02);
+  sprintf(infobuf, "Selected objects: %d, Grouping time: %.2f seconds\nTotal groups: %d, Total time: %.2f seconds", sr->Count, runtime[0] + 0.01, srs->Count, runtime[1] + 0.02);
+  EndOpt(cdr);
+  return true;
+}
+
+// 按矩形框范围分组,供其他功能调用 
+bool BoxGrouping(corel *cdr, IVGShapeRange *sr, IVGShapeRange *srs, double exp ) {
+  BoundingBox box;
+  auto al = cdr->ActiveLayer;          // 获得当前层
+  if (!sr || !al) return false;
+
+  std::vector<BoundingBox> boxes;
+  std::vector<int> parent;
+
+  // CorelDRAW Shapes 物件 Item 编号从1开始
+  for (auto i = 0; i != sr->Count; i++) {
+    sr->Shapes->Item[i + 1]->GET_BOUNDING_BOX(box);
+    boxes.push_back(box);
+    parent.push_back(i);
+  }
+
+  // 扩展边界框,或者收缩边界框
+  if (fabs(exp) > 0.02 ) {
+    expand_bounding_boxes(boxes, exp);
+  }
+
+  // 实现 Union-Find 算法来合并重叠的区域
+  for (int i = 0; i < boxes.size(); i++) {
+    for (int j = i + 1; j < boxes.size(); j++) {
+      if (isOverlapped(boxes[i], boxes[j])) {
+        unionSet(parent, i, j);
+      }
+    }
+  }
+
+  // 输出分组结果
+  std::map<int, std::vector<int>> groups;
+  for (int i = 0; i < parent.size(); i++) {
+    int root = find(parent, i);
+    groups[root].push_back(i + 1);
+  }
+
+  auto srgp = cdr->CreateShapeRange();
+  // 分组分别进行群组
+  for (const auto& group : groups) {
+      for (int index : group.second) 
+        srgp->Add(sr->Shapes->Item[index]);
+      
+      if(sr->Count >1)
+        srs->Add(srgp->Group());
+      else{
+        srs->AddRange(srgp); 
+      }
+      srgp->RemoveAll();
+  }
+  return true;
+}