Files
envi-code/SourceCode/Code2026/cad_mcp_plugins/dwg_review_view.cpp
T
company-desktop ecb8436720 feat: 移植 cad_mcp_plugins,并对齐中望平台的符号与依赖
插件工程在 CadProject/src/cad_mcp_plugins(8 个 cpp/h),本次一并整合进 code2026。

移植改动:
- 源码拷到 SourceCode/Code2026/cad_mcp_plugins/;cadsdk.h -> KTCad.h
- 输出是普通 DLL(.dll),由 cad_mcp_frame 运行时 LoadLibrary,不注册 ARX 命令
- 新增 cad_mcp_plugins/cmakelists.txt:按 code2026 惯例
  (target_set_name_and_suffix ".dll" / target_link_objectarx / 预编译头 / SUB_ARX)
- mcp_plugin_api.h 从 cad_mcp_frame/inc 上移到 SourceCode/Code2026/inc:
  frame 与 plugins 共用,且在 ShsGlobalIncludes 的 include 路径里两边都可见;
  frame 的 CMake 去掉已删除的本地 inc 目录
- 顶层在同一个 C++11+ 条件里挂载 add_custom_module_2(cad_mcp_plugins CadMcpPlugins)
- 中望适配:源码里的 #pragma comment(lib,"rxapi.lib") 在中望下没有这个库(叫 ZwRx.lib),
  该 pragma 与导出名都按 #ifdef ZWCAD 分开(中望导出 zcrxGetApiVersion);
  frame 里 GetProcAddress 查插件版本号的函数名同样按平台分

验证:
- R230:cad_mcp_frame.arx 826 KB + cad_mcp_plugins.dll 866 KB,已自动部署
- Z2026:cad_mcp_frame.zrx 825 KB + cad_mcp_plugins.dll 864 KB,已自动部署
- 配置里 16 个 c++_dll 工具的工厂函数(CreateXxxTool)全部出现在 dll 导出表里
- 所有 .cpp/.h 为 UTF-8 with BOM

未做:没有在 CAD 里实跑。另外配置文件本身没有随构建部署(set_debug_post_build 只拷产物),
需要把 cad-mcp-frame.json(或改名 mcp_config.json)与 LISP 目录放到部署目录,
否则 frame 起来后 LoadAndRegister 失败会直接自关。详见 cad_mcp_frame-review.md。
2026-10-29 00:16:37 +08:00

195 lines
6.1 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// ============================================================================
// dwg_review_view.cpp
// 图纸审查 - 视图 / 行动层工具(均为 c++_dll 后端)
// zoom_to_entity : 缩放到指定 handle(或 handle 列表)实体
// zoom_to_window : 缩放到指定 WCS 范围(逐图框审查用;对直线组图框尤其实用)
// highlight_entities : 按 handle 列表临时高亮(改色)
// ============================================================================
#include "KTCad.h"
#include "dwg_review_common.h"
#include "mcp_plugin_api.h"
#include <string>
#include <vector>
#include <cstdio>
using namespace dwg_review;
namespace
{
// 数字格式化为命令行可用的定点字符串(避免科学计数法)
std::wstring fmt(double v)
{
wchar_t buf[64];
swprintf_s(buf, 64, L"%.6f", v);
return std::wstring(buf);
}
// 缩放到 WCS 范围(四周按 marginFrac 留边),outView 回传实际使用范围
void zoomToBox(double x1, double y1, double x2, double y2,
double marginFrac, double outView[4])
{
double w = x2 - x1;
double h = y2 - y1;
if (w < 1e-6) w = 1.0;
if (h < 1e-6) h = 1.0;
const double mx = w * marginFrac, my = h * marginFrac;
outView[0] = x1 - mx; outView[1] = y1 - my;
outView[2] = x2 + mx; outView[3] = y2 + my;
std::wstring cmd = L"_.ZOOM _W ";
cmd += fmt(outView[0]) + L"," + fmt(outView[1]) + L" ";
cmd += fmt(outView[2]) + L"," + fmt(outView[3]) + L" ";
acDocManager->sendStringToExecute(acDocManager->curDocument(), cmd.c_str());
}
// 按句柄打开实体并取包围盒
bool bboxByHandle(const std::string& hs, BBox& out)
{
AcDbObjectId id;
if (!objectIdFromHandle(hs, id)) return false;
AcDbEntity* pEnt = nullptr;
if (acdbOpenObject(pEnt, id, AcDb::kForRead) != Acad::eOk || !pEnt) return false;
const bool ok = entityBBox(pEnt, out);
pEnt->close();
return ok;
}
// 从 args 里取句柄列表(支持 "handle" 或 "handles")
std::vector<std::string> collectHandles(const nlohmann::json& args)
{
std::vector<std::string> hs;
if (args.contains("handle") && args["handle"].is_string())
hs.push_back(args["handle"].get<std::string>());
if (args.contains("handles") && args["handles"].is_array())
{
for (size_t i = 0; i < args["handles"].size(); ++i)
if (args["handles"][i].is_string())
hs.push_back(args["handles"][i].get<std::string>());
}
return hs;
}
}
// ===========================================================================
// zoom_to_entity
// ===========================================================================
class mcp_Tool_ZoomToEntity : public mcp_Tool
{
public:
nlohmann::json Execute(const nlohmann::json& args) override
{
const std::vector<std::string> hs = collectHandles(args);
if (hs.empty()) return makeError(kErrBadParam, "handle or handles is required");
// 合并所有目标实体的包围盒
BBox box;
bool first = true;
for (size_t i = 0; i < hs.size(); ++i)
{
BBox b;
if (!bboxByHandle(hs[i], b)) continue;
if (first) { box = b; first = false; }
else
{
if (b.minX < box.minX) box.minX = b.minX;
if (b.minY < box.minY) box.minY = b.minY;
if (b.maxX > box.maxX) box.maxX = b.maxX;
if (b.maxY > box.maxY) box.maxY = b.maxY;
}
}
if (first) return makeError(kErrNotFound, "no valid entity for the given handle(s)");
double view[4];
zoomToBox(box.minX, box.minY, box.maxX, box.maxY, 0.05, view);
nlohmann::json r;
r["ok"] = true;
r["view"] = nlohmann::json::array({ view[0], view[1], view[2], view[3] });
return makeJsonResult(r);
}
};
EXPORT_MCP_TOOL(mcp_Tool_ZoomToEntity, CreateZoomToEntityTool)
// ===========================================================================
// zoom_to_window
// ===========================================================================
class mcp_Tool_ZoomToWindow : public mcp_Tool
{
public:
nlohmann::json Execute(const nlohmann::json& args) override
{
double x1, y1, x2, y2;
if (args.contains("window") && args["window"].is_object())
{
const nlohmann::json& w = args["window"];
if (!w.contains("min_x") || !w.contains("min_y") ||
!w.contains("max_x") || !w.contains("max_y"))
return makeError(kErrBadParam, "window requires min_x/min_y/max_x/max_y");
x1 = w["min_x"].get<double>(); y1 = w["min_y"].get<double>();
x2 = w["max_x"].get<double>(); y2 = w["max_y"].get<double>();
}
else if (args.contains("min_x") && args.contains("min_y") &&
args.contains("max_x") && args.contains("max_y"))
{
x1 = args["min_x"].get<double>(); y1 = args["min_y"].get<double>();
x2 = args["max_x"].get<double>(); y2 = args["max_y"].get<double>();
}
else
{
return makeError(kErrBadParam, "min_x/min_y/max_x/max_y (or window) is required");
}
const double margin = args.value("margin", 0.02);
double view[4];
zoomToBox(x1, y1, x2, y2, margin, view);
nlohmann::json r;
r["ok"] = true;
r["view"] = nlohmann::json::array({ view[0], view[1], view[2], view[3] });
return makeJsonResult(r);
}
};
EXPORT_MCP_TOOL(mcp_Tool_ZoomToWindow, CreateZoomToWindowTool)
// ===========================================================================
// highlight_entities
// ===========================================================================
class mcp_Tool_HighlightEntities : public mcp_Tool
{
public:
nlohmann::json Execute(const nlohmann::json& args) override
{
const std::vector<std::string> hs = collectHandles(args);
if (hs.empty()) return makeError(kErrBadParam, "handle or handles is required");
// restore=true 时把颜色复位为 ByLayer(256)
const bool restore = args.value("restore", false);
const int color = restore ? 256 : args.value("color", 1); // 默认红色
int count = 0;
for (size_t i = 0; i < hs.size(); ++i)
{
AcDbObjectId id;
if (!objectIdFromHandle(hs[i], id)) continue;
AcDbEntity* pEnt = nullptr;
if (acdbOpenObject(pEnt, id, AcDb::kForWrite) != Acad::eOk || !pEnt) continue;
pEnt->setColorIndex((Adesk::UInt16)color);
pEnt->draw();
pEnt->close();
++count;
}
nlohmann::json r;
r["ok"] = true;
r["count"] = count;
return makeJsonResult(r);
}
};
EXPORT_MCP_TOOL(mcp_Tool_HighlightEntities, CreateHighlightEntitiesTool)