279 lines
7.6 KiB
C++
279 lines
7.6 KiB
C++
#include "KTCad.h"
|
||||
|
|
#include "dwg_annotate_common.h"
|
|||
|
|
#include "dbcolor.h" // AcCmColor
|
|||
|
|
|
|||
|
|
#include <algorithm>
|
|||
|
|
#include <cmath>
|
|||
|
|
|
|||
|
|
// windows.h(经 afxwin.h)把 min/max 定义成宏,会顶掉下面的 std::min / std::max
|
|||
|
|
// (CadProject 那边的 cadsdk.h 里带了 NOMINMAX,本仓库的 KTCad.h 没有)。
|
|||
|
|
#undef min
|
|||
|
|
#undef max
|
|||
|
|
|
|||
|
|
|
|||
|
|
namespace dwg_annotate
|
|||
|
|
{
|
|||
|
|
|
|||
|
|
DocLock::DocLock()
|
|||
|
|
: m_bLocked(false), m_pDoc(NULL)
|
|||
|
|
{
|
|||
|
|
m_pDoc = acDocManager->curDocument();
|
|||
|
|
if (m_pDoc != NULL && acDocManager->lockDocument(m_pDoc) == Acad::eOk)
|
|||
|
|
m_bLocked = true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
DocLock::~DocLock()
|
|||
|
|
{
|
|||
|
|
if (m_bLocked && m_pDoc != NULL)
|
|||
|
|
acDocManager->unlockDocument(m_pDoc);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
using namespace dwg_review;
|
|||
|
|
|
|||
|
|
const char* kDefaultAnnotLayer = "AI_REVIEW";
|
|||
|
|
|
|||
|
|
// ===========================================================================
|
|||
|
|
// 编码
|
|||
|
|
// ===========================================================================
|
|||
|
|
|
|||
|
|
std::wstring utf8ToWStr(const std::string& s)
|
|||
|
|
{
|
|||
|
|
if (s.empty()) return std::wstring();
|
|||
|
|
int len = ::MultiByteToWideChar(CP_UTF8, 0, s.c_str(), (int)s.size(), NULL, 0);
|
|||
|
|
if (len <= 0) return std::wstring();
|
|||
|
|
std::wstring out((size_t)len, L'\0');
|
|||
|
|
::MultiByteToWideChar(CP_UTF8, 0, s.c_str(), (int)s.size(), &out[0], len);
|
|||
|
|
return out;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ===========================================================================
|
|||
|
|
// 图层
|
|||
|
|
// ===========================================================================
|
|||
|
|
|
|||
|
|
bool ensureLayer(AcDbDatabase* pDb, const std::string& name, int aciColor)
|
|||
|
|
{
|
|||
|
|
if (!pDb || name.empty()) return false;
|
|||
|
|
const std::wstring wn = utf8ToWStr(name);
|
|||
|
|
|
|||
|
|
// 已存在则直接返回
|
|||
|
|
AcDbLayerTable* pLT = nullptr;
|
|||
|
|
if (pDb->getLayerTable(pLT, AcDb::kForRead) == Acad::eOk && pLT)
|
|||
|
|
{
|
|||
|
|
const bool exists = pLT->has(wn.c_str());
|
|||
|
|
pLT->close();
|
|||
|
|
if (exists) return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 创建新图层
|
|||
|
|
if (pDb->getLayerTable(pLT, AcDb::kForWrite) != Acad::eOk || !pLT) return false;
|
|||
|
|
|
|||
|
|
AcDbLayerTableRecord* pRec = new AcDbLayerTableRecord();
|
|||
|
|
pRec->setName(wn.c_str());
|
|||
|
|
AcCmColor color;
|
|||
|
|
color.setColorIndex((Adesk::UInt16)aciColor);
|
|||
|
|
pRec->setColor(color);
|
|||
|
|
|
|||
|
|
const Acad::ErrorStatus es = pLT->add(pRec);
|
|||
|
|
pRec->close();
|
|||
|
|
pLT->close();
|
|||
|
|
return es == Acad::eOk || es == Acad::eDuplicateRecordName;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ===========================================================================
|
|||
|
|
// 空间
|
|||
|
|
// ===========================================================================
|
|||
|
|
|
|||
|
|
AcDbBlockTableRecord* openWriteSpace(AcDbDatabase* pDb, const std::string& space)
|
|||
|
|
{
|
|||
|
|
if (!pDb) return nullptr;
|
|||
|
|
|
|||
|
|
AcDbBlockTable* pBT = nullptr;
|
|||
|
|
if (pDb->getBlockTable(pBT, AcDb::kForRead) != Acad::eOk || !pBT) return nullptr;
|
|||
|
|
|
|||
|
|
AcDbBlockTableRecord* pRec = nullptr;
|
|||
|
|
if (space.empty() || space == "Model" || space == "model")
|
|||
|
|
{
|
|||
|
|
pBT->getAt(ACDB_MODEL_SPACE, pRec, AcDb::kForWrite);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
AcDbObjectId id;
|
|||
|
|
const std::wstring ws = utf8ToWStr(space);
|
|||
|
|
if (pBT->getAt(ws.c_str(), id) == Acad::eOk)
|
|||
|
|
acdbOpenObject(pRec, id, AcDb::kForWrite);
|
|||
|
|
}
|
|||
|
|
pBT->close();
|
|||
|
|
return pRec;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ===========================================================================
|
|||
|
|
// 参数解析
|
|||
|
|
// ===========================================================================
|
|||
|
|
|
|||
|
|
bool parsePoint(const nlohmann::json& j, AcGePoint3d& out)
|
|||
|
|
{
|
|||
|
|
if (j.is_array() && j.size() >= 2)
|
|||
|
|
{
|
|||
|
|
out.x = j[0].get<double>();
|
|||
|
|
out.y = j[1].get<double>();
|
|||
|
|
out.z = (j.size() >= 3) ? j[2].get<double>() : 0.0;
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
if (j.is_object() && j.contains("x") && j.contains("y"))
|
|||
|
|
{
|
|||
|
|
out.x = j["x"].get<double>();
|
|||
|
|
out.y = j["y"].get<double>();
|
|||
|
|
out.z = j.contains("z") ? j["z"].get<double>() : 0.0;
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
bool parsePoints(const nlohmann::json& arr, std::vector<AcGePoint3d>& out)
|
|||
|
|
{
|
|||
|
|
out.clear();
|
|||
|
|
if (!arr.is_array()) return false;
|
|||
|
|
for (size_t i = 0; i < arr.size(); ++i)
|
|||
|
|
{
|
|||
|
|
AcGePoint3d p;
|
|||
|
|
if (!parsePoint(arr[i], p)) { out.clear(); return false; }
|
|||
|
|
out.push_back(p);
|
|||
|
|
}
|
|||
|
|
return !out.empty();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
bool parseWindow(const nlohmann::json& args, double& x1, double& y1, double& x2, double& 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"))
|
|||
|
|
{
|
|||
|
|
x1 = w["min_x"].get<double>(); y1 = w["min_y"].get<double>();
|
|||
|
|
x2 = w["max_x"].get<double>(); y2 = w["max_y"].get<double>();
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
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>();
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
if (args.contains("p1") && args.contains("p2"))
|
|||
|
|
{
|
|||
|
|
AcGePoint3d a, b;
|
|||
|
|
if (parsePoint(args["p1"], a) && parsePoint(args["p2"], b))
|
|||
|
|
{
|
|||
|
|
x1 = (std::min)(a.x, b.x); y1 = (std::min)(a.y, b.y);
|
|||
|
|
x2 = (std::max)(a.x, b.x); y2 = (std::max)(a.y, b.y);
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
std::vector<std::string> collectHandlesArg(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;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ===========================================================================
|
|||
|
|
// 公共参数默认值
|
|||
|
|
// ===========================================================================
|
|||
|
|
|
|||
|
|
std::string getLayerArg(const nlohmann::json& args)
|
|||
|
|
{
|
|||
|
|
if (args.contains("layer") && args["layer"].is_string())
|
|||
|
|
return args["layer"].get<std::string>();
|
|||
|
|
return std::string(kDefaultAnnotLayer);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int getColorArg(const nlohmann::json& args, int def)
|
|||
|
|
{
|
|||
|
|
if (args.contains("color") && args["color"].is_number_integer())
|
|||
|
|
return args["color"].get<int>();
|
|||
|
|
return def;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
std::string getSpaceArg(const nlohmann::json& args)
|
|||
|
|
{
|
|||
|
|
if (args.contains("space") && args["space"].is_string())
|
|||
|
|
return args["space"].get<std::string>();
|
|||
|
|
return std::string("Model");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ===========================================================================
|
|||
|
|
// 实体写入
|
|||
|
|
// ===========================================================================
|
|||
|
|
|
|||
|
|
void applyLayerColor(AcDbEntity* pEnt, const std::string& layer, int aciColor)
|
|||
|
|
{
|
|||
|
|
if (!pEnt) return;
|
|||
|
|
if (!layer.empty())
|
|||
|
|
{
|
|||
|
|
const std::wstring wl = utf8ToWStr(layer);
|
|||
|
|
pEnt->setLayer(wl.c_str());
|
|||
|
|
}
|
|||
|
|
pEnt->setColorIndex((Adesk::UInt16)aciColor);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
AcDbObjectId appendToSpace(AcDbBlockTableRecord* pSpace, AcDbEntity* pEnt)
|
|||
|
|
{
|
|||
|
|
AcDbObjectId id = AcDbObjectId::kNull;
|
|||
|
|
if (!pEnt) return id;
|
|||
|
|
if (!pSpace)
|
|||
|
|
{
|
|||
|
|
pEnt->close();
|
|||
|
|
return id;
|
|||
|
|
}
|
|||
|
|
if (pSpace->appendAcDbEntity(id, pEnt) != Acad::eOk)
|
|||
|
|
id = AcDbObjectId::kNull;
|
|||
|
|
pEnt->close();
|
|||
|
|
return id;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
std::string handleOfId(AcDbDatabase* pDb, const AcDbObjectId& id)
|
|||
|
|
{
|
|||
|
|
if (!pDb || id == AcDbObjectId::kNull) return std::string();
|
|||
|
|
AcDbEntity* pEnt = nullptr;
|
|||
|
|
if (acdbOpenObject(pEnt, id, AcDb::kForRead) != Acad::eOk || !pEnt) return std::string();
|
|||
|
|
const std::string h = handleOf(pEnt);
|
|||
|
|
pEnt->close();
|
|||
|
|
return h;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ===========================================================================
|
|||
|
|
// 结果构造
|
|||
|
|
// ===========================================================================
|
|||
|
|
|
|||
|
|
nlohmann::json makeCreatedItem(AcDbDatabase* pDb, const AcDbObjectId& id,
|
|||
|
|
const std::string& type, const std::string& layer)
|
|||
|
|
{
|
|||
|
|
nlohmann::json item;
|
|||
|
|
item["handle"] = handleOfId(pDb, id);
|
|||
|
|
item["type"] = type;
|
|||
|
|
item["layer"] = layer;
|
|||
|
|
return item;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
nlohmann::json createdResult(const std::vector<nlohmann::json>& created)
|
|||
|
|
{
|
|||
|
|
nlohmann::json r;
|
|||
|
|
r["ok"] = true;
|
|||
|
|
r["count"] = (int)created.size();
|
|||
|
|
r["created"] = created;
|
|||
|
|
return makeJsonResult(r);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
} // namespace dwg_annotate
|