#include "cadsdk.h" #include "dwg_review_common.h" #include "mcp_plugin_api.h" // 复用 mcp_Tool_Utility 构造 MCP 结果信封 #include #include namespace dwg_review { // =========================================================================== // 字符串 / 实体基础读取 // =========================================================================== std::string wstrToUtf8(const std::wstring& w) { if (w.empty()) return std::string(); int len = ::WideCharToMultiByte(CP_UTF8, 0, w.c_str(), (int)w.size(), NULL, 0, NULL, NULL); if (len <= 0) return std::string(); std::string out((size_t)len, '\0'); ::WideCharToMultiByte(CP_UTF8, 0, w.c_str(), (int)w.size(), &out[0], len, NULL, NULL); return out; } std::string acharToUtf8(const ACHAR* s) { if (!s) return std::string(); #ifdef _UNICODE return wstrToUtf8(std::wstring(s)); #else return std::string(s); #endif } std::string handleOf(AcDbEntity* pEnt) { if (!pEnt) return std::string(); AcDbHandle h; pEnt->getAcDbHandle(h); ACHAR buf[64] = { 0 }; h.getIntoAsciiBuffer(buf, 64); return acharToUtf8(buf); } std::string layerOf(AcDbEntity* pEnt) { if (!pEnt) return std::string(); return acharToUtf8(pEnt->layer()); } std::string entityTypeName(AcDbEntity* pEnt) { if (!pEnt) return std::string(); // 注意判定顺序:派生类必须先于基类 if (AcDbBlockReference::cast(pEnt)) return "INSERT"; if (AcDbAttribute::cast(pEnt)) return "ATTRIB"; if (AcDbAttributeDefinition::cast(pEnt)) return "ATTDEF"; if (AcDbMText::cast(pEnt)) return "MTEXT"; if (AcDbText::cast(pEnt)) return "TEXT"; if (AcDbArc::cast(pEnt)) return "ARC"; // Arc 派生自 Circle if (AcDbCircle::cast(pEnt)) return "CIRCLE"; if (AcDbLine::cast(pEnt)) return "LINE"; if (AcDbPolyline::cast(pEnt)) return "LWPOLYLINE"; if (AcDb2dPolyline::cast(pEnt)) return "POLYLINE"; if (AcDbDimension::cast(pEnt)) return "DIMENSION"; if (AcDbEllipse::cast(pEnt)) return "ELLIPSE"; if (AcDbSpline::cast(pEnt)) return "SPLINE"; if (AcDbPoint::cast(pEnt)) return "POINT"; // 兜底:取类名去掉 AcDb 前缀并大写 std::string n = acharToUtf8(pEnt->isA()->name()); if (n.rfind("AcDb", 0) == 0) n = n.substr(4); for (size_t i = 0; i < n.size(); ++i) n[i] = (char)::toupper((unsigned char)n[i]); return n; } bool entityBBox(AcDbEntity* pEnt, BBox& out) { out = BBox(); if (!pEnt) return false; try { AcDbExtents ext; if (pEnt->getGeomExtents(ext) != Acad::eOk) return false; out.minX = ext.minPoint().x; out.minY = ext.minPoint().y; out.maxX = ext.maxPoint().x; out.maxY = ext.maxPoint().y; out.valid = true; return true; } catch (...) { return false; // 部分实体(如空块、特殊动态块)取范围会抛异常 } } std::string blockNameOf(AcDbBlockReference* pRef) { if (!pRef) return std::string(); AcDbBlockTableRecord* pRec = nullptr; std::string name; if (acdbOpenObject(pRec, pRef->blockTableRecord(), AcDb::kForRead) == Acad::eOk && pRec) { const ACHAR* nm = NULL; if (pRec->getName(nm) == Acad::eOk && nm) name = acharToUtf8(nm); pRec->close(); } return name; } bool objectIdFromHandle(const std::string& handleStr, AcDbObjectId& out) { AcDbDatabase* pDb = acdbHostApplicationServices()->workingDatabase(); if (!pDb) return false; std::wstring ws(handleStr.begin(), handleStr.end()); AcDbHandle h(ws.c_str()); // AcDbHandle(const ACHAR*) return pDb->getAcDbObjectId(out, false, h) == Acad::eOk; } // =========================================================================== // 匹配 / 过滤 // =========================================================================== bool wildcardMatch(const std::string& name, const std::string& pattern) { if (pattern.empty()) return true; // 空模式 = 全部匹配 size_t n = name.size(), m = pattern.size(); size_t i = 0, j = 0, star = (size_t)-1, mark = 0; while (i < n) { if (j < m && (pattern[j] == '?' || ::tolower((unsigned char)pattern[j]) == ::tolower((unsigned char)name[i]))) { ++i; ++j; } else if (j < m && pattern[j] == '*') { star = j++; mark = i; } else if (star != (size_t)-1) { j = star + 1; i = ++mark; } else { return false; } } while (j < m && pattern[j] == '*') ++j; return j == m; } bool bboxInWindow(const BBox& b, const CommonQuery& q) { if (!q.useWindow) return true; if (!b.valid) return false; if (b.maxX < q.minX || b.minX > q.maxX) return false; if (b.maxY < q.minY || b.minY > q.maxY) return false; return true; } bool layerAccepted(AcDbEntity* pEnt, const CommonQuery& q) { return wildcardMatch(layerOf(pEnt), q.layerFilter); } // =========================================================================== // 空间定位 / 遍历 // =========================================================================== AcDbBlockTableRecord* openSpace(AcDbDatabase* pDb, const std::string& space) { if (!pDb) return nullptr; AcDbBlockTable* pBT = nullptr; if (pDb->getBlockTable(pBT, AcDb::kForRead) != Acad::eOk) return nullptr; AcDbBlockTableRecord* pRec = nullptr; if (space.empty() || space == "Model" || space == "model") { pBT->getAt(ACDB_MODEL_SPACE, pRec, AcDb::kForRead); } else { AcDbObjectId id; std::wstring ws(space.begin(), space.end()); if (pBT->getAt(ws.c_str(), id) == Acad::eOk) acdbOpenObject(pRec, id, AcDb::kForRead); } pBT->close(); return pRec; } void forEachEntity(const CommonQuery& q, const EntityVisitor& visitor) { AcDbDatabase* pDb = acdbHostApplicationServices()->workingDatabase(); if (!pDb) return; AcDbBlockTableRecord* pSpace = openSpace(pDb, q.space); if (!pSpace) return; AcDbBlockTableRecordIterator* pIter = nullptr; if (pSpace->newIterator(pIter) != Acad::eOk || !pIter) { pSpace->close(); return; } for (pIter->start(); !pIter->done(); pIter->step()) { AcDbEntity* pEnt = nullptr; if (pIter->getEntity(pEnt, AcDb::kForRead) != Acad::eOk || !pEnt) continue; bool cont = true; if (layerAccepted(pEnt, q)) { if (q.useWindow) { BBox b; if (entityBBox(pEnt, b) && bboxInWindow(b, q)) cont = visitor(pEnt); } else { cont = visitor(pEnt); } } pEnt->close(); if (!cont) break; } delete pIter; pSpace->close(); } // =========================================================================== // 通用查询参数解析 // =========================================================================== CommonQuery parseCommonQuery(const nlohmann::json& args) { CommonQuery q; if (args.contains("space") && args["space"].is_string()) q.space = args["space"].get(); if (args.contains("layer_filter") && args["layer_filter"].is_string()) q.layerFilter = args["layer_filter"].get(); if (args.contains("limit") && args["limit"].is_number_integer()) q.limit = args["limit"].get(); if (args.contains("offset") && args["offset"].is_number_integer()) q.offset = args["offset"].get(); if (q.limit <= 0) q.limit = 200; if (q.limit > 1000) q.limit = 1000; if (q.offset < 0) q.offset = 0; 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")) { q.useWindow = true; q.minX = w["min_x"].get(); q.minY = w["min_y"].get(); q.maxX = w["max_x"].get(); q.maxY = w["max_y"].get(); } } return q; } // =========================================================================== // 结果封装 // =========================================================================== nlohmann::json pageResult(int total, int returned, int offset, const nlohmann::json& items) { nlohmann::json j; j["total"] = total; j["returned"] = returned; j["offset"] = offset; j["items"] = items; return j; } nlohmann::json makeError(int code, const std::string& msg) { nlohmann::json j; j["error"] = { { "code", code }, { "message", msg } }; return j; } nlohmann::json makeJsonResult(const nlohmann::json& data) { std::wstring w = mcp_Tool_Utility::utf8ToWString(data.dump()); return mcp_Tool_Utility::make_text_result(w); } nlohmann::json point2Json(double x, double y) { return nlohmann::json::array({ x, y }); } nlohmann::json point3Json(double x, double y, double z) { return nlohmann::json::array({ x, y, z }); } nlohmann::json bboxJson(const BBox& b) { return nlohmann::json::array({ b.minX, b.minY, b.maxX, b.maxY }); } } // namespace dwg_review