- 新增 PcfIso.h/.cpp:PCF 解析、等轴测投影(朝向与 Plant 3D 一致)、 A3 图框/标题栏/材料表/切割表/尺寸/标高/焊口气泡/北针/跨图续接。 - 分图 4 级:模型连通性 → 断点(法兰/现场焊口) → 管段尺寸 → 图幅/拥塞。 - 新增命令 EW_PCFISO(单图)、EW_PCFISOSPLIT(分图,运行时选层级 1/2/3/4)。 - 注册到 EarthWorm.cpp、StdArx.h,并接入 cmakelists.txt。 - 附《PCF-ISO分图规则说明.md》。
1538 lines
50 KiB
C++
1538 lines
50 KiB
C++
#include "StdAfx.h"
|
|
#include "PcfIso.h"
|
|
|
|
#include <dbents.h>
|
|
#include <dbpl.h>
|
|
#include <dbsymtb.h>
|
|
#include <dbmain.h>
|
|
#include <acutads.h>
|
|
#include <geassign.h>
|
|
|
|
#include <fstream>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <map>
|
|
#include <set>
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
|
|
/*
|
|
PCF -> ISO 图(DWG) 实现
|
|
----------------------------------------------------------------
|
|
分层:
|
|
1) 解析 parsePcf() : PCF 文本 -> std::vector<IsoComp>
|
|
2) 投影 project() : 3D 模型点 -> 2D 等轴测点(与 P3D 一致)
|
|
3) 分图 splitPipeline() : 模型级/断点级/管段级/图幅级
|
|
4) 绘制 IsoDrawer : 封装"建图层/画线/画圆/画文字/画多段线/画弧"
|
|
5) 图面 renderSheet() : 图框/标题栏/材料表/切割表/尺寸/标高/气泡/北针/续接
|
|
6) 主流程 makeIsoDwg() : 解析 -> 分图 -> 逐张出图
|
|
*/
|
|
|
|
namespace
|
|
{
|
|
// ======================= 常量 =======================
|
|
const double ISO_COS30 = 0.8660254037844386; // cos(30°)
|
|
const double ISO_SIN30 = 0.5; // sin(30°)
|
|
|
|
// 头部属性(非组件),解析时直接跳过
|
|
const TCHAR *g_pszHeaderKeys[] =
|
|
{
|
|
_T("ISOGEN-FILES"), _T("UNITS-BORE"), _T("UNITS-CO-ORDS"),
|
|
_T("UNITS-BOLT-LENGTH"), _T("UNITS-BOLT-DIA"), _T("UNITS-WEIGHT"),
|
|
_T("PIPELINE-REFERENCE"), _T("MATERIALS"), NULL
|
|
};
|
|
|
|
// 图层名
|
|
LPCTSTR LAYER_PIPE = _T("ISO-PIPE"); // 管道
|
|
LPCTSTR LAYER_FITTING = _T("ISO-FITTING"); // 管件
|
|
LPCTSTR LAYER_VALVE = _T("ISO-VALVE"); // 阀门
|
|
LPCTSTR LAYER_WELD = _T("ISO-WELD"); // 焊口
|
|
LPCTSTR LAYER_SUPPORT = _T("ISO-SUPPORT"); // 支吊架
|
|
LPCTSTR LAYER_TEXT = _T("ISO-TEXT"); // 文字
|
|
LPCTSTR LAYER_TITLE = _T("ISO-TITLE"); // 标题
|
|
LPCTSTR LAYER_DIM = _T("ISO-DIM"); // 标注
|
|
LPCTSTR LAYER_FRAME = _T("ISO-FRAME"); // 图框
|
|
LPCTSTR LAYER_TABLE = _T("ISO-TABLE"); // 表格
|
|
LPCTSTR LAYER_SYMBOL = _T("ISO-SYMBOL"); // 符号(北针/续接)
|
|
|
|
// 图纸尺寸(A3 横放,单位 mm)与版区
|
|
const double SHEET_W = 420.0, SHEET_H = 297.0;
|
|
const double BORDER_L = 10.0, BORDER_B = 10.0, BORDER_R = 410.0, BORDER_T = 287.0;
|
|
// 标题栏(右下)
|
|
const double TB_L = 230.0, TB_B = 10.0, TB_R = 410.0, TB_T = 50.0;
|
|
// 材料表(右上)
|
|
const double BOM_L = 290.0, BOM_B = 185.0, BOM_R = 410.0, BOM_T = 287.0;
|
|
// 切割表(右中)
|
|
const double CUT_L = 290.0, CUT_B = 100.0, CUT_R = 410.0, CUT_T = 185.0;
|
|
// 绘图区
|
|
const double DRAW_L = 15.0, DRAW_B = 55.0, DRAW_R = 285.0, DRAW_T = 285.0;
|
|
|
|
// 前置声明(分组排序工具)
|
|
void sortGroups(std::vector<std::vector<int> > &g);
|
|
void sortGroup(std::vector<int> &g);
|
|
|
|
// ======================= 数据结构 =======================
|
|
// 带管径的 3D 点
|
|
struct IsoPt
|
|
{
|
|
AcGePoint3d pt;
|
|
double bore;
|
|
IsoPt() : pt(0.0, 0.0, 0.0), bore(0.0) {}
|
|
};
|
|
|
|
// 一个 PCF 组件
|
|
struct IsoComp
|
|
{
|
|
CString type; // PIPE / ELBOW / TEE ...
|
|
std::vector<IsoPt> ends; // END-POINT
|
|
std::vector<IsoPt> branches; // BRANCH1/2-POINT
|
|
std::vector<AcGePoint3d> coords; // CO-ORDS (SUPPORT)
|
|
AcGePoint3d centre; // CENTRE-POINT
|
|
bool hasCentre;
|
|
CString itemCode;
|
|
CString desc;
|
|
CString skey;
|
|
CString category;
|
|
CString spec; // PIPING-SPEC
|
|
std::vector<CString> msgs; // MESSAGE-TEXT
|
|
|
|
IsoComp() : centre(0.0, 0.0, 0.0), hasCentre(false) {}
|
|
|
|
// 主管管径(第一个端点的 bore)
|
|
double bore() const { return ends.empty() ? 0.0 : ends[0].bore; }
|
|
};
|
|
|
|
// ======================= 字符串工具 =======================
|
|
std::wstring trimWs(const std::wstring &s)
|
|
{
|
|
const size_t a = s.find_first_not_of(L" \t\r\n");
|
|
if (a == std::wstring::npos) return std::wstring();
|
|
const size_t b = s.find_last_not_of(L" \t\r\n");
|
|
return s.substr(a, b - a + 1);
|
|
}
|
|
|
|
std::wstring firstToken(const std::wstring &s)
|
|
{
|
|
const size_t a = s.find_first_not_of(L" \t");
|
|
if (a == std::wstring::npos) return std::wstring();
|
|
const size_t b = s.find_first_of(L" \t", a);
|
|
return (b == std::wstring::npos) ? s.substr(a) : s.substr(a, b - a);
|
|
}
|
|
|
|
std::wstring restToken(const std::wstring &s)
|
|
{
|
|
const size_t a = s.find_first_not_of(L" \t");
|
|
if (a == std::wstring::npos) return std::wstring();
|
|
const size_t b = s.find_first_of(L" \t", a);
|
|
if (b == std::wstring::npos) return std::wstring();
|
|
const size_t c = s.find_first_not_of(L" \t", b);
|
|
return (c == std::wstring::npos) ? std::wstring() : s.substr(c);
|
|
}
|
|
|
|
// 读 UTF-8 文本文件为宽字符串(去掉 BOM)
|
|
bool readUtf8File(LPCTSTR path, std::wstring &out)
|
|
{
|
|
HANDLE h = ::CreateFile(path, GENERIC_READ, FILE_SHARE_READ, NULL,
|
|
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (h == INVALID_HANDLE_VALUE) return false;
|
|
|
|
const DWORD size = ::GetFileSize(h, NULL);
|
|
std::string buf;
|
|
if (size > 0 && size != INVALID_FILE_SIZE)
|
|
{
|
|
buf.resize(size);
|
|
DWORD read = 0;
|
|
if (!::ReadFile(h, &buf[0], size, &read, NULL)) { ::CloseHandle(h); return false; }
|
|
buf.resize(read);
|
|
}
|
|
::CloseHandle(h);
|
|
|
|
const int n = ::MultiByteToWideChar(CP_UTF8, 0, buf.c_str(), (int)buf.size(), NULL, 0);
|
|
out.resize(n > 0 ? n : 0);
|
|
if (n > 0)
|
|
::MultiByteToWideChar(CP_UTF8, 0, buf.c_str(), (int)buf.size(), &out[0], n);
|
|
|
|
if (!out.empty() && out[0] == 0xFEFF) out.erase(out.begin()); // BOM
|
|
return true;
|
|
}
|
|
|
|
void parsePoint(const std::wstring &val, AcGePoint3d &pt, double &bore)
|
|
{
|
|
pt = AcGePoint3d(0.0, 0.0, 0.0);
|
|
bore = 0.0;
|
|
|
|
std::vector<double> v;
|
|
std::wstringstream ss(val);
|
|
double d = 0.0;
|
|
while (ss >> d) v.push_back(d);
|
|
|
|
if (v.size() >= 3) { pt.x = v[0]; pt.y = v[1]; pt.z = v[2]; }
|
|
if (v.size() >= 4) bore = v[3];
|
|
}
|
|
|
|
bool isHeaderKey(const std::wstring &kw)
|
|
{
|
|
for (int i = 0; g_pszHeaderKeys[i]; ++i)
|
|
if (_wcsicmp(kw.c_str(), g_pszHeaderKeys[i]) == 0) return true;
|
|
return false;
|
|
}
|
|
|
|
// ======================= PCF 解析 =======================
|
|
bool parsePcf(LPCTSTR path, std::vector<IsoComp> &comps, CString &pipelineRef)
|
|
{
|
|
std::wstring content;
|
|
if (!readUtf8File(path, content)) return false;
|
|
|
|
comps.clear();
|
|
pipelineRef.Empty();
|
|
|
|
IsoComp *pCur = NULL;
|
|
size_t pos = 0;
|
|
while (pos < content.size())
|
|
{
|
|
const size_t eol = content.find(L'\n', pos);
|
|
std::wstring line = (eol == std::wstring::npos)
|
|
? content.substr(pos) : content.substr(pos, eol - pos);
|
|
pos = (eol == std::wstring::npos) ? content.size() : eol + 1;
|
|
|
|
if (!line.empty() && line[line.size() - 1] == L'\r') line.erase(line.size() - 1);
|
|
if (line.empty()) continue;
|
|
|
|
const bool bIndent = (line[0] == L' ' || line[0] == L'\t');
|
|
|
|
if (!bIndent)
|
|
{
|
|
const std::wstring kw = firstToken(line);
|
|
|
|
if (_wcsicmp(kw.c_str(), L"PIPELINE-REFERENCE") == 0)
|
|
{
|
|
pipelineRef = restToken(line).c_str();
|
|
pCur = NULL;
|
|
continue;
|
|
}
|
|
if (isHeaderKey(kw)) { pCur = NULL; continue; }
|
|
|
|
comps.push_back(IsoComp());
|
|
pCur = &comps.back();
|
|
pCur->type = kw.c_str();
|
|
}
|
|
else
|
|
{
|
|
if (!pCur) continue;
|
|
|
|
const std::wstring s = trimWs(line);
|
|
const std::wstring key = firstToken(s);
|
|
const std::wstring val = restToken(s);
|
|
|
|
if (_wcsicmp(key.c_str(), L"END-POINT") == 0)
|
|
{
|
|
IsoPt p; parsePoint(val, p.pt, p.bore);
|
|
pCur->ends.push_back(p);
|
|
}
|
|
else if (_wcsicmp(key.c_str(), L"BRANCH1-POINT") == 0
|
|
|| _wcsicmp(key.c_str(), L"BRANCH2-POINT") == 0)
|
|
{
|
|
IsoPt p; parsePoint(val, p.pt, p.bore);
|
|
pCur->branches.push_back(p);
|
|
}
|
|
else if (_wcsicmp(key.c_str(), L"CENTRE-POINT") == 0)
|
|
{
|
|
double b = 0.0; parsePoint(val, pCur->centre, b);
|
|
pCur->hasCentre = true;
|
|
}
|
|
else if (_wcsicmp(key.c_str(), L"CO-ORDS") == 0)
|
|
{
|
|
AcGePoint3d pt; double b = 0.0; parsePoint(val, pt, b);
|
|
pCur->coords.push_back(pt);
|
|
}
|
|
else if (_wcsicmp(key.c_str(), L"ITEM-CODE") == 0) pCur->itemCode = val.c_str();
|
|
else if (_wcsicmp(key.c_str(), L"ITEM-DESCRIPTION") == 0) pCur->desc = val.c_str();
|
|
else if (_wcsicmp(key.c_str(), L"SKEY") == 0) pCur->skey = val.c_str();
|
|
else if (_wcsicmp(key.c_str(), L"CATEGORY") == 0) pCur->category = val.c_str();
|
|
else if (_wcsicmp(key.c_str(), L"PIPING-SPEC") == 0) pCur->spec = val.c_str();
|
|
else if (_wcsicmp(key.c_str(), L"MESSAGE-TEXT") == 0) pCur->msgs.push_back(val.c_str());
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// ======================= 等轴测投影(与 P3D 一致) =======================
|
|
AcGePoint2d project(const AcGePoint3d &p)
|
|
{
|
|
return AcGePoint2d((p.x - p.y) * ISO_COS30, (p.x + p.y) * ISO_SIN30 + p.z);
|
|
}
|
|
|
|
AcGePoint3d closestOnSegment(const AcGePoint3d &a, const AcGePoint3d &b, const AcGePoint3d &p)
|
|
{
|
|
const AcGeVector3d ab = b - a;
|
|
const double len2 = ab.lengthSqrd();
|
|
if (len2 < 1e-9) return a;
|
|
double t = (p - a).dotProduct(ab) / len2;
|
|
if (t < 0.0) t = 0.0;
|
|
if (t > 1.0) t = 1.0;
|
|
return a + ab * t;
|
|
}
|
|
|
|
void updateBBox(const AcGePoint2d &p, AcGePoint2d &mn, AcGePoint2d &mx, bool &bFirst)
|
|
{
|
|
if (bFirst) { mn = mx = p; bFirst = false; return; }
|
|
if (p.x < mn.x) mn.x = p.x;
|
|
if (p.y < mn.y) mn.y = p.y;
|
|
if (p.x > mx.x) mx.x = p.x;
|
|
if (p.y > mx.y) mx.y = p.y;
|
|
}
|
|
|
|
// 3D 包围盒(用 double 数组 [6])
|
|
void compBBox3d(const IsoComp &c, double bb[6], bool &bFirst)
|
|
{
|
|
for (size_t k = 0; k < c.ends.size(); ++k)
|
|
{
|
|
const AcGePoint3d &p = c.ends[k].pt;
|
|
if (bFirst) { bb[0]=bb[3]=p.x; bb[1]=bb[4]=p.y; bb[2]=bb[5]=p.z; bFirst=false; continue; }
|
|
if (p.x<bb[0])bb[0]=p.x; if (p.y<bb[1])bb[1]=p.y; if (p.z<bb[2])bb[2]=p.z;
|
|
if (p.x>bb[3])bb[3]=p.x; if (p.y>bb[4])bb[4]=p.y; if (p.z>bb[5])bb[5]=p.z;
|
|
}
|
|
for (size_t k = 0; k < c.branches.size(); ++k)
|
|
{
|
|
const AcGePoint3d &p = c.branches[k].pt;
|
|
if (bFirst) { bb[0]=bb[3]=p.x; bb[1]=bb[4]=p.y; bb[2]=bb[5]=p.z; bFirst=false; continue; }
|
|
if (p.x<bb[0])bb[0]=p.x; if (p.y<bb[1])bb[1]=p.y; if (p.z<bb[2])bb[2]=p.z;
|
|
if (p.x>bb[3])bb[3]=p.x; if (p.y>bb[4])bb[4]=p.y; if (p.z>bb[5])bb[5]=p.z;
|
|
}
|
|
if (c.hasCentre)
|
|
{
|
|
const AcGePoint3d &p = c.centre;
|
|
if (bFirst) { bb[0]=bb[3]=p.x; bb[1]=bb[4]=p.y; bb[2]=bb[5]=p.z; bFirst=false; }
|
|
else {
|
|
if (p.x<bb[0])bb[0]=p.x; if (p.y<bb[1])bb[1]=p.y; if (p.z<bb[2])bb[2]=p.z;
|
|
if (p.x>bb[3])bb[3]=p.x; if (p.y>bb[4])bb[4]=p.y; if (p.z>bb[5])bb[5]=p.z;
|
|
}
|
|
}
|
|
for (size_t k = 0; k < c.coords.size(); ++k)
|
|
{
|
|
const AcGePoint3d &p = c.coords[k];
|
|
if (bFirst) { bb[0]=bb[3]=p.x; bb[1]=bb[4]=p.y; bb[2]=bb[5]=p.z; bFirst=false; }
|
|
else {
|
|
if (p.x<bb[0])bb[0]=p.x; if (p.y<bb[1])bb[1]=p.y; if (p.z<bb[2])bb[2]=p.z;
|
|
if (p.x>bb[3])bb[3]=p.x; if (p.y>bb[4])bb[4]=p.y; if (p.z>bb[5])bb[5]=p.z;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ======================= 绘图器 =======================
|
|
class IsoDrawer
|
|
{
|
|
public:
|
|
explicit IsoDrawer(AcDbDatabase *pDb)
|
|
: m_pDb(pDb), m_pMS(NULL), m_offset(0.0, 0.0), m_scale(1.0) {}
|
|
~IsoDrawer() { close(); }
|
|
|
|
bool begin()
|
|
{
|
|
AcDbBlockTable *pBT = NULL;
|
|
if (m_pDb->getBlockTable(pBT, AcDb::kForRead) != Acad::eOk) return false;
|
|
const Acad::ErrorStatus es = pBT->getAt(ACDB_MODEL_SPACE, m_pMS, AcDb::kForWrite);
|
|
pBT->close();
|
|
if (es != Acad::eOk) return false;
|
|
|
|
initLayers();
|
|
return true;
|
|
}
|
|
|
|
void close()
|
|
{
|
|
if (m_pMS) { m_pMS->close(); m_pMS = NULL; }
|
|
}
|
|
|
|
void setOffset(const AcGePoint2d &o) { m_offset = o; }
|
|
void setScale(double s) { m_scale = (s > 1e-9) ? s : 1.0; }
|
|
|
|
// 图纸坐标(mm) -> 世界坐标(应用缩放与平移)
|
|
AcGePoint2d apply(const AcGePoint2d &p) const
|
|
{
|
|
return AcGePoint2d(p.x * m_scale + m_offset.x, p.y * m_scale + m_offset.y);
|
|
}
|
|
|
|
// ---- 基本图元 ----
|
|
AcDbObjectId addLine(const AcGePoint2d &a, const AcGePoint2d &b, LPCTSTR layer)
|
|
{
|
|
const AcGePoint2d p = apply(a), q = apply(b);
|
|
AcDbLine *pEnt = new AcDbLine(AcGePoint3d(p.x, p.y, 0.0), AcGePoint3d(q.x, q.y, 0.0));
|
|
return append(pEnt, layer);
|
|
}
|
|
|
|
AcDbObjectId addCircle(const AcGePoint2d &c, double r, LPCTSTR layer)
|
|
{
|
|
const double rr = r * m_scale;
|
|
if (!(rr > 1e-9)) return AcDbObjectId::kNull; // 半径<=0 会让 AcDbCircle 抛异常
|
|
const AcGePoint2d p = apply(c);
|
|
AcDbCircle *pEnt = new AcDbCircle(AcGePoint3d(p.x, p.y, 0.0), AcGeVector3d(0.0, 0.0, 1.0), rr);
|
|
return append(pEnt, layer);
|
|
}
|
|
|
|
AcDbObjectId addText(const AcGePoint2d &p, double h, LPCTSTR txt, LPCTSTR layer)
|
|
{
|
|
if (!txt || !*txt) return AcDbObjectId::kNull;
|
|
const AcGePoint2d q = apply(p);
|
|
AcDbText *pEnt = new AcDbText(AcGePoint3d(q.x, q.y, 0.0), txt, AcDbObjectId::kNull, h, 0.0);
|
|
return append(pEnt, layer);
|
|
}
|
|
|
|
// 带对齐/旋转的文字(rot 弧度)
|
|
AcDbObjectId addTextEx(const AcGePoint2d &p, double h, LPCTSTR txt, double rot, int hMode, LPCTSTR layer)
|
|
{
|
|
if (!txt || !*txt) return AcDbObjectId::kNull;
|
|
const AcGePoint2d q = apply(p);
|
|
AcDbText *pEnt = new AcDbText(AcGePoint3d(q.x, q.y, 0.0), txt, AcDbObjectId::kNull, h, rot);
|
|
if (hMode == 1) pEnt->setHorizontalMode(AcDb::kTextCenter);
|
|
else if (hMode == 2) pEnt->setHorizontalMode(AcDb::kTextRight);
|
|
pEnt->setVerticalMode(AcDb::kTextVertMid);
|
|
pEnt->setAlignmentPoint(AcGePoint3d(q.x, q.y, 0.0));
|
|
return append(pEnt, layer);
|
|
}
|
|
|
|
void addPolyline(const std::vector<AcGePoint2d> &pts, LPCTSTR layer)
|
|
{
|
|
if (pts.size() < 2) return;
|
|
AcDbPolyline *pEnt = new AcDbPolyline((unsigned int)pts.size());
|
|
for (size_t i = 0; i < pts.size(); ++i)
|
|
{
|
|
const AcGePoint2d q = apply(pts[i]);
|
|
pEnt->addVertexAt((unsigned int)i, q);
|
|
}
|
|
append(pEnt, layer);
|
|
}
|
|
|
|
// 矩形(4 条线)
|
|
void addRect(double l, double b, double r, double t, LPCTSTR layer)
|
|
{
|
|
addLine(AcGePoint2d(l, b), AcGePoint2d(r, b), layer);
|
|
addLine(AcGePoint2d(r, b), AcGePoint2d(r, t), layer);
|
|
addLine(AcGePoint2d(r, t), AcGePoint2d(l, t), layer);
|
|
addLine(AcGePoint2d(l, t), AcGePoint2d(l, b), layer);
|
|
}
|
|
|
|
// 虚线? 简化:用多段线模拟不了线型,直接画实线
|
|
AcDbObjectId layerIdOf(LPCTSTR name) { return layerId(name); }
|
|
|
|
private:
|
|
void initLayers()
|
|
{
|
|
struct { LPCTSTR name; int color; } defs[] =
|
|
{
|
|
{ LAYER_PIPE, 5 }, // 蓝
|
|
{ LAYER_FITTING, 3 }, // 绿
|
|
{ LAYER_VALVE, 1 }, // 红
|
|
{ LAYER_WELD, 1 }, // 红
|
|
{ LAYER_SUPPORT, 6 }, // 洋红
|
|
{ LAYER_TEXT, 7 },
|
|
{ LAYER_TITLE, 7 },
|
|
{ LAYER_DIM, 4 }, // 青
|
|
{ LAYER_FRAME, 7 },
|
|
{ LAYER_TABLE, 7 },
|
|
{ LAYER_SYMBOL, 7 },
|
|
};
|
|
for (int i = 0; i < _countof(defs); ++i)
|
|
layerId(defs[i].name, defs[i].color);
|
|
}
|
|
|
|
AcDbObjectId layerId(LPCTSTR name, int color = -1)
|
|
{
|
|
std::map<CString, AcDbObjectId>::iterator it = m_layers.find(name);
|
|
if (it != m_layers.end()) return it->second;
|
|
|
|
AcDbObjectId id = AcDbObjectId::kNull;
|
|
AcDbLayerTable *pLT = NULL;
|
|
if (m_pDb->getLayerTable(pLT, AcDb::kForRead) != Acad::eOk) return id;
|
|
|
|
AcDbLayerTableRecord *pLTR = NULL;
|
|
if (pLT->getAt(name, pLTR, AcDb::kForRead) == Acad::eOk)
|
|
{
|
|
id = pLTR->objectId();
|
|
pLTR->close();
|
|
}
|
|
else
|
|
{
|
|
pLT->close();
|
|
if (m_pDb->getLayerTable(pLT, AcDb::kForWrite) != Acad::eOk) return id;
|
|
pLTR = new AcDbLayerTableRecord;
|
|
pLTR->setName(name);
|
|
if (color >= 0)
|
|
{
|
|
AcCmColor clr;
|
|
clr.setColorIndex((Adesk::UInt16)color);
|
|
pLTR->setColor(clr);
|
|
}
|
|
if (pLT->add(pLTR) == Acad::eOk) id = pLTR->objectId();
|
|
pLTR->close();
|
|
}
|
|
pLT->close();
|
|
|
|
if (!id.isNull()) m_layers[name] = id;
|
|
return id;
|
|
}
|
|
|
|
AcDbObjectId append(AcDbEntity *pEnt, LPCTSTR layer)
|
|
{
|
|
if (!pEnt || !m_pMS) { delete pEnt; return AcDbObjectId::kNull; }
|
|
pEnt->setLayer(layerId(layer));
|
|
AcDbObjectId id;
|
|
if (m_pMS->appendAcDbEntity(id, pEnt) != Acad::eOk) id = AcDbObjectId::kNull;
|
|
pEnt->close();
|
|
return id;
|
|
}
|
|
|
|
AcDbDatabase *m_pDb;
|
|
AcDbBlockTableRecord *m_pMS;
|
|
AcGePoint2d m_offset;
|
|
double m_scale;
|
|
std::map<CString, AcDbObjectId> m_layers;
|
|
};
|
|
|
|
// ======================= 组件落图 =======================
|
|
void drawWeld(IsoDrawer &dr, const IsoComp &c, double txtH)
|
|
{
|
|
if (c.ends.empty()) return;
|
|
dr.addCircle(project(c.ends[0].pt), txtH * 0.3, LAYER_WELD);
|
|
}
|
|
|
|
void drawSupport(IsoDrawer &dr, const IsoComp &c, double txtH)
|
|
{
|
|
if (c.coords.empty()) return;
|
|
const AcGePoint2d p = project(c.coords[0]);
|
|
const double s = txtH * 0.8;
|
|
const AcGePoint2d a(p.x - s * 0.5, p.y - s * 0.5);
|
|
const AcGePoint2d b(p.x + s * 0.5, p.y + s * 0.5);
|
|
dr.addLine(AcGePoint2d(a.x, a.y), AcGePoint2d(b.x, a.y), LAYER_SUPPORT);
|
|
dr.addLine(AcGePoint2d(b.x, a.y), AcGePoint2d(b.x, b.y), LAYER_SUPPORT);
|
|
dr.addLine(AcGePoint2d(b.x, b.y), AcGePoint2d(a.x, b.y), LAYER_SUPPORT);
|
|
dr.addLine(AcGePoint2d(a.x, b.y), AcGePoint2d(a.x, a.y), LAYER_SUPPORT);
|
|
}
|
|
|
|
void drawComponent(IsoDrawer &dr, const IsoComp &c, double txtH)
|
|
{
|
|
const CString &t = c.type;
|
|
|
|
if (t == _T("WELD")) { drawWeld(dr, c, txtH); return; }
|
|
if (t == _T("SUPPORT")) { drawSupport(dr, c, txtH); return; }
|
|
|
|
if (c.ends.empty()) return;
|
|
|
|
LPCTSTR layer = LAYER_FITTING;
|
|
if (t == _T("PIPE")) layer = LAYER_PIPE;
|
|
else if (t == _T("VALVE")) layer = LAYER_VALVE;
|
|
|
|
// 弯头:两腿在拐点(CENTRE-POINT)相接,画折线
|
|
if (t == _T("ELBOW") && c.hasCentre && c.ends.size() >= 2)
|
|
{
|
|
std::vector<AcGePoint2d> pl;
|
|
pl.push_back(project(c.ends[0].pt));
|
|
pl.push_back(project(c.centre));
|
|
pl.push_back(project(c.ends[1].pt));
|
|
dr.addPolyline(pl, layer);
|
|
}
|
|
else if (c.ends.size() >= 2)
|
|
{
|
|
dr.addLine(project(c.ends[0].pt), project(c.ends[1].pt), layer);
|
|
}
|
|
else
|
|
{
|
|
// 只有一个端点(管帽等):画个小圆示意
|
|
dr.addCircle(project(c.ends[0].pt), txtH * 0.5, layer);
|
|
}
|
|
|
|
// 三通/四通:支管从主管上最近点引出
|
|
if (!c.branches.empty() && c.ends.size() >= 2)
|
|
{
|
|
for (size_t k = 0; k < c.branches.size(); ++k)
|
|
{
|
|
const AcGePoint3d junc = closestOnSegment(c.ends[0].pt, c.ends[1].pt, c.branches[k].pt);
|
|
dr.addLine(project(junc), project(c.branches[k].pt), layer);
|
|
}
|
|
}
|
|
|
|
// 直管管径标注
|
|
if (t == _T("PIPE") && c.ends.size() >= 2 && c.ends[0].bore > 0.0)
|
|
{
|
|
const AcGePoint2d a = project(c.ends[0].pt);
|
|
const AcGePoint2d b = project(c.ends[1].pt);
|
|
AcGePoint2d mid((a.x + b.x) * 0.5, (a.y + b.y) * 0.5);
|
|
|
|
const double dx = b.x - a.x, dy = b.y - a.y;
|
|
const double len = sqrt(dx * dx + dy * dy);
|
|
if (len > 1e-6) { mid.x += (-dy / len) * txtH * 0.8; mid.y += (dx / len) * txtH * 0.8; }
|
|
|
|
CString sBore;
|
|
sBore.Format(_T("%.0f"), c.ends[0].bore);
|
|
dr.addText(mid, txtH, sBore, LAYER_TEXT);
|
|
}
|
|
}
|
|
|
|
// 最后一个路径分隔符位置(同时识别 '\' 与 '/'),找不到返回 -1
|
|
int lastSep(const CString &s)
|
|
{
|
|
const int a = s.ReverseFind(_T('\\'));
|
|
const int b = s.ReverseFind(_T('/'));
|
|
return (b > a) ? b : a;
|
|
}
|
|
|
|
// 取文件名(不含扩展名)
|
|
CString fileStem(LPCTSTR path)
|
|
{
|
|
CString s(path);
|
|
const int p = lastSep(s);
|
|
CString name = (p >= 0) ? s.Mid(p + 1) : s;
|
|
const int dot = name.ReverseFind(_T('.'));
|
|
return (dot > 0) ? name.Left(dot) : name;
|
|
}
|
|
|
|
// ======================= 连通性(P3D "模型级") =======================
|
|
std::wstring pointKey(const AcGePoint3d &p)
|
|
{
|
|
std::wstringstream ss;
|
|
ss.setf(std::ios::fixed);
|
|
ss.precision(2);
|
|
ss << p.x << L'|' << p.y << L'|' << p.z;
|
|
return ss.str();
|
|
}
|
|
|
|
// 一个组件参与连通的连接点。
|
|
// 规则(与 P3D 一致):端点/中心点/支点参与;缺少 CENTRE-POINT 的支管组件
|
|
// 其支管不参与连通(主管在支管处断开)。
|
|
void collectConnPoints(const IsoComp &c, std::vector<AcGePoint3d> &pts)
|
|
{
|
|
pts.clear();
|
|
for (size_t k = 0; k < c.ends.size(); ++k) pts.push_back(c.ends[k].pt);
|
|
if (c.hasCentre) pts.push_back(c.centre);
|
|
for (size_t k = 0; k < c.coords.size(); ++k) pts.push_back(c.coords[k]);
|
|
if (!c.branches.empty() && c.hasCentre)
|
|
for (size_t k = 0; k < c.branches.size(); ++k) pts.push_back(c.branches[k].pt);
|
|
}
|
|
|
|
// 并查集工具
|
|
class UnionFind
|
|
{
|
|
public:
|
|
explicit UnionFind(int n) : m_p(n) { for (int i = 0; i < n; ++i) m_p[i] = i; }
|
|
int find(int x) { while (m_p[x] != x) { m_p[x] = m_p[m_p[x]]; x = m_p[x]; } return x; }
|
|
void unite(int a, int b) { int ra = find(a), rb = find(b); if (ra != rb) m_p[ra] = rb; }
|
|
private:
|
|
std::vector<int> m_p;
|
|
};
|
|
|
|
// 由"连通点集合"构造组件连通子网。excluded!=NULL 时不参与连通(如断点组件)。
|
|
std::vector<std::vector<int> > groupByPoints(const std::vector<IsoComp> &comps,
|
|
const std::set<int> *excluded)
|
|
{
|
|
const int n = (int)comps.size();
|
|
UnionFind uf(n);
|
|
std::map<std::wstring, int> firstAt;
|
|
std::vector<AcGePoint3d> pts;
|
|
|
|
for (int i = 0; i < n; ++i)
|
|
{
|
|
if (excluded && excluded->count(i)) continue;
|
|
collectConnPoints(comps[i], pts);
|
|
for (size_t k = 0; k < pts.size(); ++k)
|
|
{
|
|
const std::wstring key = pointKey(pts[k]);
|
|
std::map<std::wstring, int>::iterator it = firstAt.find(key);
|
|
if (it == firstAt.end()) { firstAt[key] = i; continue; }
|
|
uf.unite(i, it->second);
|
|
}
|
|
}
|
|
|
|
std::map<int, std::vector<int> > byRoot;
|
|
for (int i = 0; i < n; ++i)
|
|
{
|
|
if (excluded && excluded->count(i)) continue;
|
|
byRoot[uf.find(i)].push_back(i);
|
|
}
|
|
|
|
std::vector<std::vector<int> > out;
|
|
for (std::map<int, std::vector<int> >::iterator it = byRoot.begin(); it != byRoot.end(); ++it)
|
|
out.push_back(it->second);
|
|
sortGroups(out);
|
|
return out;
|
|
}
|
|
|
|
// 按"最小组件下标"排序
|
|
void sortGroups(std::vector<std::vector<int> > &g)
|
|
{
|
|
struct ByFirst
|
|
{
|
|
bool operator()(const std::vector<int> &a, const std::vector<int> &b) const
|
|
{ return a.front() < b.front(); }
|
|
};
|
|
std::sort(g.begin(), g.end(), ByFirst());
|
|
}
|
|
|
|
std::vector<std::vector<int> > groupComponents(const std::vector<IsoComp> &comps)
|
|
{
|
|
return groupByPoints(comps, NULL);
|
|
}
|
|
|
|
// ======================= 断点级 =======================
|
|
// 断点组件:法兰(FLANGE)、现场焊口(WELD 且类别/描述含 FIELD)。
|
|
bool isBreakPoint(const IsoComp &c)
|
|
{
|
|
if (c.type == _T("FLANGE")) return true;
|
|
if (c.type == _T("WELD"))
|
|
{
|
|
if (c.category.Find(_T("FIELD")) >= 0) return true;
|
|
if (c.desc.Find(_T("FIELD")) >= 0) return true;
|
|
if (c.skey.Find(_T("FIELD")) >= 0) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// 在断点处把每个连通组切开:断点组件不参与"连接中介",
|
|
// 之后再把它并入其任一邻接组(保证不丢件)。
|
|
std::vector<std::vector<int> > splitByBreakPoints(const std::vector<IsoComp> &comps,
|
|
const std::vector<std::vector<int> > &groups)
|
|
{
|
|
std::vector<std::vector<int> > out;
|
|
for (size_t gi = 0; gi < groups.size(); ++gi)
|
|
{
|
|
const std::vector<int> &g = groups[gi];
|
|
std::set<int> breaks;
|
|
for (size_t k = 0; k < g.size(); ++k)
|
|
if (isBreakPoint(comps[g[k]])) breaks.insert(g[k]);
|
|
|
|
if (breaks.empty()) { out.push_back(g); continue; }
|
|
|
|
// 非断点组件做连通(全库范围),再筛出属于本组的子组
|
|
std::set<int> inGroup(g.begin(), g.end());
|
|
std::vector<std::vector<int> > raw = groupByPoints(comps, &breaks);
|
|
std::vector<std::vector<int> > sub;
|
|
for (size_t s = 0; s < raw.size(); ++s)
|
|
{
|
|
std::vector<int> piece;
|
|
for (size_t k = 0; k < raw[s].size(); ++k)
|
|
if (inGroup.count(raw[s][k])) piece.push_back(raw[s][k]);
|
|
if (!piece.empty()) sub.push_back(piece);
|
|
}
|
|
// 把断点组件并入其邻接的第一个非断点组
|
|
std::map<int, int> compToGroup; // 组件下标 -> sub 组号
|
|
for (size_t s = 0; s < sub.size(); ++s)
|
|
for (size_t k = 0; k < sub[s].size(); ++k) compToGroup[sub[s][k]] = (int)s;
|
|
|
|
std::vector<AcGePoint3d> bp, op;
|
|
for (std::set<int>::iterator it = breaks.begin(); it != breaks.end(); ++it)
|
|
{
|
|
collectConnPoints(comps[*it], bp);
|
|
int target = -1;
|
|
for (size_t j = 0; j < g.size() && target < 0; ++j)
|
|
{
|
|
if (breaks.count(g[j])) continue;
|
|
collectConnPoints(comps[g[j]], op);
|
|
for (size_t a = 0; a < bp.size() && target < 0; ++a)
|
|
for (size_t b = 0; b < op.size(); ++b)
|
|
if (pointKey(bp[a]) == pointKey(op[b])) { target = compToGroup[g[j]]; break; }
|
|
}
|
|
if (target >= 0) sub[target].push_back(*it);
|
|
else { sub.push_back(std::vector<int>(1, *it)); } // 孤立断点自成一组
|
|
}
|
|
for (size_t s = 0; s < sub.size(); ++s) { sortGroup(sub[s]); out.push_back(sub[s]); }
|
|
}
|
|
sortGroups(out);
|
|
return out;
|
|
}
|
|
|
|
void sortGroup(std::vector<int> &g) { std::sort(g.begin(), g.end()); }
|
|
|
|
// ======================= 邻接表 =======================
|
|
void buildAdjacency(const std::vector<IsoComp> &comps, const std::vector<int> &g,
|
|
std::map<int, std::vector<int> > &adj)
|
|
{
|
|
adj.clear();
|
|
std::map<std::wstring, std::vector<int> > at;
|
|
std::vector<AcGePoint3d> pts;
|
|
for (size_t k = 0; k < g.size(); ++k)
|
|
{
|
|
const int i = g[k];
|
|
collectConnPoints(comps[i], pts);
|
|
for (size_t a = 0; a < pts.size(); ++a)
|
|
at[pointKey(pts[a])].push_back(i);
|
|
}
|
|
for (std::map<std::wstring, std::vector<int> >::iterator it = at.begin(); it != at.end(); ++it)
|
|
{
|
|
const std::vector<int> &v = it->second;
|
|
for (size_t a = 0; a < v.size(); ++a)
|
|
for (size_t b = a + 1; b < v.size(); ++b)
|
|
{
|
|
adj[v[a]].push_back(v[b]);
|
|
adj[v[b]].push_back(v[a]);
|
|
}
|
|
}
|
|
// 邻接表去重并排序,保证遍历顺序确定
|
|
for (std::map<int, std::vector<int> >::iterator it = adj.begin(); it != adj.end(); ++it)
|
|
{
|
|
std::vector<int> &v = it->second;
|
|
std::sort(v.begin(), v.end());
|
|
v.erase(std::unique(v.begin(), v.end()), v.end());
|
|
}
|
|
}
|
|
|
|
// ======================= 管段级 / 图幅级:按尺寸贪心切分 =======================
|
|
// 从种子出发 BFS,累积包围盒;超过上限时切断,另起一段(保证每段连通)。
|
|
std::vector<std::vector<int> > partitionBySize(const std::vector<IsoComp> &comps,
|
|
const std::vector<int> &g, double maxX, double maxY, double maxZ, int maxCount)
|
|
{
|
|
std::map<int, std::vector<int> > adj;
|
|
buildAdjacency(comps, g, adj);
|
|
|
|
std::map<int, bool> visited;
|
|
std::vector<std::vector<int> > parts;
|
|
|
|
for (size_t s = 0; s < g.size(); ++s)
|
|
{
|
|
const int seed = g[s];
|
|
if (visited[seed]) continue;
|
|
|
|
std::vector<int> part;
|
|
double bb[6]; for (int k = 0; k < 6; ++k) bb[k] = 0.0;
|
|
bool bHave = false;
|
|
std::vector<int> queue; queue.push_back(seed);
|
|
size_t qi = 0;
|
|
|
|
while (qi < queue.size())
|
|
{
|
|
const int n = queue[qi++];
|
|
if (visited[n]) continue;
|
|
|
|
// 组件 n 的 3D 包围盒
|
|
double cb[6]; bool bf = true; compBBox3d(comps[n], cb, bf);
|
|
|
|
// 并入后的包围盒
|
|
double nb[6];
|
|
if (!bHave) { for (int k = 0; k < 6; ++k) nb[k] = cb[k]; }
|
|
else
|
|
{
|
|
nb[0]=bb[0]; nb[1]=bb[1]; nb[2]=bb[2]; nb[3]=bb[3]; nb[4]=bb[4]; nb[5]=bb[5];
|
|
if (cb[0]<nb[0])nb[0]=cb[0]; if (cb[1]<nb[1])nb[1]=cb[1]; if (cb[2]<nb[2])nb[2]=cb[2];
|
|
if (cb[3]>nb[3])nb[3]=cb[3]; if (cb[4]>nb[4])nb[4]=cb[4]; if (cb[5]>nb[5])nb[5]=cb[5];
|
|
}
|
|
|
|
const double sx = nb[3]-nb[0], sy = nb[4]-nb[1], sz = nb[5]-nb[2];
|
|
const bool bExceed = (maxX>0 && sx>maxX) || (maxY>0 && sy>maxY) || (maxZ>0 && sz>maxZ)
|
|
|| (maxCount>0 && (int)part.size()>=maxCount);
|
|
if (bHave && bExceed) { break; } // 不再并入本段,留给下一段
|
|
|
|
// 接受 n
|
|
part.push_back(n);
|
|
visited[n] = true;
|
|
for (int k = 0; k < 6; ++k) bb[k] = nb[k];
|
|
bHave = true;
|
|
std::map<int, std::vector<int> >::iterator it = adj.find(n);
|
|
if (it != adj.end())
|
|
for (size_t a = 0; a < it->second.size(); ++a)
|
|
if (!visited[it->second[a]]) queue.push_back(it->second[a]);
|
|
}
|
|
|
|
if (!part.empty()) { sortGroup(part); parts.push_back(part); }
|
|
else { std::vector<int> one(1, seed); visited[seed] = true; parts.push_back(one); }
|
|
}
|
|
sortGroups(parts);
|
|
return parts;
|
|
}
|
|
|
|
// ======================= 分图主流程 =======================
|
|
std::vector<std::vector<int> > splitPipeline(const std::vector<IsoComp> &comps, const PcfIso::SplitOptions &opt)
|
|
{
|
|
std::vector<std::vector<int> > groups;
|
|
|
|
if (opt.modelSplit)
|
|
groups = groupComponents(comps);
|
|
else
|
|
{
|
|
std::vector<int> all; for (size_t i = 0; i < comps.size(); ++i) all.push_back((int)i);
|
|
groups.push_back(all);
|
|
}
|
|
|
|
if (opt.breakPointSplit)
|
|
groups = splitByBreakPoints(comps, groups);
|
|
|
|
if (opt.spoolSplit)
|
|
{
|
|
std::vector<std::vector<int> > next;
|
|
for (size_t i = 0; i < groups.size(); ++i)
|
|
{
|
|
std::vector<std::vector<int> > sub = partitionBySize(comps, groups[i],
|
|
opt.spoolMaxX, opt.spoolMaxY, opt.spoolMaxZ, 0);
|
|
for (size_t s = 0; s < sub.size(); ++s) next.push_back(sub[s]);
|
|
}
|
|
sortGroups(next);
|
|
groups = next;
|
|
}
|
|
|
|
if (opt.sheetSplit)
|
|
{
|
|
std::vector<std::vector<int> > next;
|
|
for (size_t i = 0; i < groups.size(); ++i)
|
|
{
|
|
std::vector<std::vector<int> > sub = partitionBySize(comps, groups[i],
|
|
0, 0, 0, opt.maxCompPerSheet);
|
|
for (size_t s = 0; s < sub.size(); ++s) next.push_back(sub[s]);
|
|
}
|
|
sortGroups(next);
|
|
groups = next;
|
|
}
|
|
return groups;
|
|
}
|
|
}
|
|
|
|
// ======================= 图面:图框/标题栏/表格/标注 =======================
|
|
namespace
|
|
{
|
|
// 估算文字宽度(单位: ASCII 字符宽),中文按 2 计
|
|
double strUnits(const CString &s)
|
|
{
|
|
double w = 0.0;
|
|
for (int i = 0; i < s.GetLength(); ++i)
|
|
w += ((unsigned short)s[i] > 0x2E80) ? 2.0 : 1.0;
|
|
return w;
|
|
}
|
|
|
|
// 按列宽裁剪文字,避免压到下一列
|
|
CString fitText(const CString &s, double colW, double txtH)
|
|
{
|
|
if (txtH <= 1e-6) return s;
|
|
const double maxUnits = colW / (txtH * 0.62);
|
|
if (strUnits(s) <= maxUnits) return s;
|
|
CString out;
|
|
double w = 0.0;
|
|
for (int i = 0; i < s.GetLength(); ++i)
|
|
{
|
|
const double u = ((unsigned short)s[i] > 0x2E80) ? 2.0 : 1.0;
|
|
if (w + u > maxUnits - 0.4) break;
|
|
out += s[i];
|
|
w += u;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
// 在图纸坐标里画一个单元格表格
|
|
void drawTable(IsoDrawer &dr, double l, double b, double r, double t,
|
|
const std::vector<double> &colW, const std::vector<CString> &rows, double rowH, LPCTSTR layer)
|
|
{
|
|
// 外框
|
|
dr.addRect(l, b, r, t, layer);
|
|
// 列分隔
|
|
double x = l;
|
|
for (size_t i = 0; i + 1 < colW.size(); ++i)
|
|
{
|
|
x += colW[i];
|
|
dr.addLine(AcGePoint2d(x, b), AcGePoint2d(x, t), layer);
|
|
}
|
|
// 行分隔 + 文字
|
|
double y = t;
|
|
for (size_t ri = 0; ri < rows.size(); ++ri)
|
|
{
|
|
dr.addLine(AcGePoint2d(l, y), AcGePoint2d(r, y), layer);
|
|
|
|
// 手动按 '|' 切分:不能用 CString::Tokenize——切到末尾后再调用
|
|
// 会以 iStart<0 触发 AtlThrow(E_INVALIDARG),抛出未捕获异常导致致命错误。
|
|
std::vector<CString> cells;
|
|
{
|
|
const CString &s = rows[ri];
|
|
int start = 0;
|
|
for (;;)
|
|
{
|
|
const int bar = s.Find(_T('|'), start);
|
|
if (bar < 0) { cells.push_back(s.Mid(start)); break; }
|
|
cells.push_back(s.Mid(start, bar - start));
|
|
start = bar + 1;
|
|
}
|
|
}
|
|
|
|
const double txtH = rowH * 0.5;
|
|
double cx = l;
|
|
for (size_t ci = 0; ci < colW.size(); ++ci)
|
|
{
|
|
if (ci < cells.size() && !cells[ci].IsEmpty())
|
|
{
|
|
const CString cell = fitText(cells[ci], colW[ci], txtH);
|
|
if (!cell.IsEmpty())
|
|
dr.addText(AcGePoint2d(cx + 1.0, y - rowH * 0.5), txtH, cell, LAYER_TEXT);
|
|
}
|
|
cx += colW[ci];
|
|
}
|
|
y -= rowH;
|
|
}
|
|
}
|
|
|
|
// 标题栏(右下):若干字段
|
|
void drawTitleBlock(IsoDrawer &dr, LPCTSTR pipeline, LPCTSTR sheetName, int idx, int total)
|
|
{
|
|
dr.addRect(TB_L, TB_B, TB_R, TB_T, LAYER_FRAME);
|
|
// 内部分隔
|
|
dr.addLine(AcGePoint2d(TB_L, TB_B + 13.0), AcGePoint2d(TB_R, TB_B + 13.0), LAYER_FRAME);
|
|
dr.addLine(AcGePoint2d(TB_L, TB_B + 26.0), AcGePoint2d(TB_R, TB_B + 26.0), LAYER_FRAME);
|
|
dr.addLine(AcGePoint2d(TB_L + 120.0, TB_B), AcGePoint2d(TB_L + 120.0, TB_T), LAYER_FRAME);
|
|
|
|
dr.addText(AcGePoint2d(TB_L + 3.0, TB_T - 6.0), 4.0, _T("管线号: "), LAYER_TEXT);
|
|
CString s; s.Format(_T("%s"), pipeline ? pipeline : _T(""));
|
|
dr.addText(AcGePoint2d(TB_L + 24.0, TB_T - 6.0), 4.0, s, LAYER_TEXT);
|
|
|
|
dr.addText(AcGePoint2d(TB_L + 3.0, TB_B + 19.0), 4.0, _T("图号: "), LAYER_TEXT);
|
|
dr.addText(AcGePoint2d(TB_L + 24.0, TB_B + 19.0), 4.0, sheetName ? sheetName : _T(""), LAYER_TEXT);
|
|
|
|
dr.addText(AcGePoint2d(TB_L + 3.0, TB_B + 6.0), 4.0, _T("比例: 1:1"), LAYER_TEXT);
|
|
CString sp; sp.Format(_T("第 %d / %d 张"), idx, total);
|
|
dr.addText(AcGePoint2d(TB_L + 60.0, TB_B + 6.0), 4.0, sp, LAYER_TEXT);
|
|
dr.addText(AcGePoint2d(TB_L + 125.0, TB_B + 6.0), 5.5, _T("等轴测图 (ISO)"), LAYER_TITLE);
|
|
dr.addText(AcGePoint2d(TB_L + 125.0, TB_T - 8.0), 4.0, _T("Envi3D 2026"), LAYER_TEXT);
|
|
}
|
|
|
|
// 材料表:按 类型+规格+管径 归并计数
|
|
void drawBom(IsoDrawer &dr, const std::vector<IsoComp> &comps, const std::vector<int> &g)
|
|
{
|
|
std::map<CString, int> counter;
|
|
std::map<CString, CString> nameOf;
|
|
for (size_t k = 0; k < g.size(); ++k)
|
|
{
|
|
const IsoComp &c = comps[g[k]];
|
|
if (c.type == _T("WELD")) continue;
|
|
CString nm = c.desc.IsEmpty() ? c.type : c.desc;
|
|
// 材料描述很长(含标准/材质),而表格列窄——只取首个逗号前的名称,过长再截断,避免压到下一列
|
|
const int comma = nm.Find(_T(','));
|
|
if (comma > 0) nm = nm.Left(comma);
|
|
if (nm.GetLength() > 12) nm = nm.Left(12);
|
|
CString key;
|
|
key.Format(_T("%s|%s|%.0f"), (LPCTSTR)c.type, (LPCTSTR)c.spec, c.bore());
|
|
counter[key]++;
|
|
nameOf[key] = nm;
|
|
}
|
|
|
|
std::vector<CString> rows;
|
|
rows.push_back(_T("材料表"));
|
|
rows.push_back(_T("序号|名称|规格|数量"));
|
|
int no = 1;
|
|
for (std::map<CString, int>::iterator it = counter.begin(); it != counter.end(); ++it)
|
|
{
|
|
CString s;
|
|
CString nm = nameOf[it->first];
|
|
int p = it->first.Find(_T('|'));
|
|
int q = it->first.Find(_T('|'), p + 1);
|
|
CString specPart = (q > p) ? it->first.Mid(p + 1, q - p - 1) : CString();
|
|
CString borePart = (q >= 0) ? it->first.Mid(q + 1) : CString();
|
|
CString spec;
|
|
spec.Format(_T("%s DN%s"), (LPCTSTR)specPart, (LPCTSTR)borePart);
|
|
s.Format(_T("%d|%s|%s|%d"), no++, (LPCTSTR)nm, (LPCTSTR)spec, it->second);
|
|
rows.push_back(s);
|
|
}
|
|
|
|
std::vector<double> colW; colW.push_back(12.0); colW.push_back(44.0); colW.push_back(42.0); colW.push_back(22.0);
|
|
double rowH = (BOM_T - BOM_B) / (double)(rows.size() + 1);
|
|
if (rowH > 7.0) rowH = 7.0;
|
|
drawTable(dr, BOM_L, BOM_B, BOM_R, BOM_T, colW, rows, rowH, LAYER_TABLE);
|
|
}
|
|
|
|
// 切割表:列出每段直管长度与编号
|
|
void drawCutList(IsoDrawer &dr, const std::vector<IsoComp> &comps, const std::vector<int> &g)
|
|
{
|
|
std::vector<CString> rows;
|
|
rows.push_back(_T("切割表"));
|
|
rows.push_back(_T("管段号|长度(mm)|管径"));
|
|
int no = 1;
|
|
for (size_t k = 0; k < g.size(); ++k)
|
|
{
|
|
const IsoComp &c = comps[g[k]];
|
|
if (c.type != _T("PIPE") || c.ends.size() < 2) continue;
|
|
const double len = (c.ends[1].pt - c.ends[0].pt).length();
|
|
CString s;
|
|
s.Format(_T("P%d|%.0f|DN%.0f"), no++, len, c.bore());
|
|
rows.push_back(s);
|
|
}
|
|
std::vector<double> colW; colW.push_back(24.0); colW.push_back(48.0); colW.push_back(48.0);
|
|
double rowH = (CUT_T - CUT_B) / (double)(rows.size() + 1);
|
|
if (rowH > 7.0) rowH = 7.0;
|
|
drawTable(dr, CUT_L, CUT_B, CUT_R, CUT_T, colW, rows, rowH, LAYER_TABLE);
|
|
}
|
|
|
|
// 指北针(等轴测):+Y 方向为北(左上 30°)
|
|
void drawNorthArrow(IsoDrawer &dr, const AcGePoint2d &at, double size)
|
|
{
|
|
// 北向单位向量(投影后的 +Y)
|
|
const AcGePoint2d dir(-ISO_COS30, ISO_SIN30);
|
|
const AcGePoint2d tip(at.x + dir.x * size, at.y + dir.y * size);
|
|
dr.addLine(at, tip, LAYER_SYMBOL);
|
|
// 箭头
|
|
const AcGePoint2d perp(-dir.y, dir.x);
|
|
const double hs = size * 0.28;
|
|
const AcGePoint2d b1(tip.x - dir.x * hs + perp.x * hs * 0.6, tip.y - dir.y * hs + perp.y * hs * 0.6);
|
|
const AcGePoint2d b2(tip.x - dir.x * hs - perp.x * hs * 0.6, tip.y - dir.y * hs - perp.y * hs * 0.6);
|
|
dr.addLine(tip, b1, LAYER_SYMBOL);
|
|
dr.addLine(tip, b2, LAYER_SYMBOL);
|
|
dr.addText(AcGePoint2d(tip.x + dir.x * size * 0.18, tip.y + dir.y * size * 0.18), size * 0.22, _T("N"), LAYER_SYMBOL);
|
|
}
|
|
|
|
// 续接标记:本图与其它图共享的"连接点"(被切开的接缝)处,画续接符号+目标图号
|
|
void drawContinuation(IsoDrawer &dr, const std::vector<IsoComp> &comps,
|
|
const std::vector<int> &g, int thisIdx,
|
|
const std::vector<std::vector<int> > &allGroups, double txtH)
|
|
{
|
|
if (allGroups.size() < 2) return;
|
|
|
|
// 点键 -> 触到的组集合
|
|
std::map<std::wstring, std::set<int> > pg;
|
|
std::vector<AcGePoint3d> pts;
|
|
for (size_t s = 0; s < allGroups.size(); ++s)
|
|
for (size_t k = 0; k < allGroups[s].size(); ++k)
|
|
{
|
|
collectConnPoints(comps[allGroups[s][k]], pts);
|
|
for (size_t a = 0; a < pts.size(); ++a) pg[pointKey(pts[a])].insert((int)s);
|
|
}
|
|
|
|
std::set<std::wstring> done;
|
|
for (size_t k = 0; k < g.size(); ++k)
|
|
{
|
|
collectConnPoints(comps[g[k]], pts);
|
|
for (size_t a = 0; a < pts.size(); ++a)
|
|
{
|
|
const std::wstring key = pointKey(pts[a]);
|
|
if (done.count(key)) continue;
|
|
std::map<std::wstring, std::set<int> >::iterator it = pg.find(key);
|
|
if (it == pg.end() || it->second.size() < 2) continue;
|
|
done.insert(key);
|
|
|
|
int target = -1;
|
|
for (std::set<int>::iterator jt = it->second.begin(); jt != it->second.end(); ++jt)
|
|
if (*jt != thisIdx) { target = *jt; break; }
|
|
if (target < 0) continue;
|
|
|
|
const AcGePoint2d p = project(pts[a]);
|
|
dr.addCircle(p, txtH * 0.35, LAYER_SYMBOL);
|
|
CString s; s.Format(_T("续接 %d"), target + 1);
|
|
dr.addText(AcGePoint2d(p.x + txtH * 0.6, p.y - txtH * 1.6), txtH * 0.8, s, LAYER_SYMBOL);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 焊口气泡:给每个 WELD 编号并画气泡+引线
|
|
int drawWeldBalloons(IsoDrawer &dr, const std::vector<IsoComp> &comps, const std::vector<int> &g, double txtH)
|
|
{
|
|
int no = 0;
|
|
for (size_t k = 0; k < g.size(); ++k)
|
|
{
|
|
const IsoComp &c = comps[g[k]];
|
|
if (c.type != _T("WELD") || c.ends.empty()) continue;
|
|
++no;
|
|
const AcGePoint2d p = project(c.ends[0].pt);
|
|
// 气泡左右交替,避免密集区气泡互相压叠
|
|
const double sx = (no % 2) ? 1.0 : -1.0;
|
|
const AcGePoint2d bp(p.x + sx * txtH * 3.2, p.y + txtH * 3.0);
|
|
dr.addLine(p, bp, LAYER_WELD);
|
|
dr.addCircle(bp, txtH * 0.9, LAYER_WELD);
|
|
CString s; s.Format(_T("%d"), no);
|
|
dr.addTextEx(bp, txtH * 0.7, s, 0.0, 1, LAYER_WELD);
|
|
}
|
|
return no;
|
|
}
|
|
|
|
// 尺寸标注:直管画对齐尺寸线(偏移)+长度文字
|
|
void drawDimensions(IsoDrawer &dr, const std::vector<IsoComp> &comps, const std::vector<int> &g, double txtH)
|
|
{
|
|
for (size_t k = 0; k < g.size(); ++k)
|
|
{
|
|
const IsoComp &c = comps[g[k]];
|
|
if (c.type != _T("PIPE") || c.ends.size() < 2) continue;
|
|
const double len = (c.ends[1].pt - c.ends[0].pt).length();
|
|
if (len < 1e-3) continue;
|
|
|
|
const AcGePoint2d a = project(c.ends[0].pt), b = project(c.ends[1].pt);
|
|
const double dx = b.x - a.x, dy = b.y - a.y;
|
|
const double l = sqrt(dx * dx + dy * dy);
|
|
if (l < 1e-6) continue;
|
|
const double ux = -dy / l, uy = dx / l; // 法向
|
|
const double off = txtH * 1.6;
|
|
const AcGePoint2d a2(a.x + ux * off, a.y + uy * off);
|
|
const AcGePoint2d b2(b.x + ux * off, b.y + uy * off);
|
|
// 尺寸线
|
|
dr.addLine(a2, b2, LAYER_DIM);
|
|
// 界线
|
|
dr.addLine(a, a2, LAYER_DIM);
|
|
dr.addLine(b, b2, LAYER_DIM);
|
|
// 文字
|
|
CString s; s.Format(_T("%.0f"), len);
|
|
const AcGePoint2d mid((a2.x + b2.x) * 0.5, (a2.y + b2.y) * 0.5);
|
|
dr.addTextEx(AcGePoint2d(mid.x + ux * txtH * 0.6, mid.y + uy * txtH * 0.6), txtH, s, 0.0, 1, LAYER_DIM);
|
|
}
|
|
}
|
|
|
|
// 标高:只在"开口端(度<=1)"或"最高/最低点"标注 EL +xxx,避免同一标高重复堆叠
|
|
void drawElevations(IsoDrawer &dr, const std::vector<IsoComp> &comps, const std::vector<int> &g, double txtH)
|
|
{
|
|
// 统计各连接点被多少组件共用(度)
|
|
std::map<std::wstring, int> deg;
|
|
std::vector<AcGePoint3d> pts;
|
|
double zmin = 1e18, zmax = -1e18;
|
|
for (size_t k = 0; k < g.size(); ++k)
|
|
{
|
|
const IsoComp &c = comps[g[k]];
|
|
collectConnPoints(c, pts);
|
|
for (size_t a = 0; a < pts.size(); ++a) deg[pointKey(pts[a])]++;
|
|
for (size_t e = 0; e < c.ends.size(); ++e)
|
|
{
|
|
if (c.ends[e].pt.z < zmin) zmin = c.ends[e].pt.z;
|
|
if (c.ends[e].pt.z > zmax) zmax = c.ends[e].pt.z;
|
|
}
|
|
}
|
|
|
|
// 两趟:先标最高/最低点,再标开口端;并用最小间距过滤,避免密集区文字互相压叠
|
|
std::set<std::wstring> done;
|
|
std::vector<AcGePoint2d> placed;
|
|
const double minGap = txtH * 6.0;
|
|
for (int pass = 0; pass < 2; ++pass)
|
|
{
|
|
for (size_t k = 0; k < g.size(); ++k)
|
|
{
|
|
const IsoComp &c = comps[g[k]];
|
|
for (size_t e = 0; e < c.ends.size(); ++e)
|
|
{
|
|
const AcGePoint3d &p3 = c.ends[e].pt;
|
|
const std::wstring key = pointKey(p3);
|
|
if (done.count(key)) continue;
|
|
|
|
const bool bOpen = (deg[key] <= 1);
|
|
const bool bExtreme = (fabs(p3.z - zmin) < 1e-6) || (fabs(p3.z - zmax) < 1e-6);
|
|
if (pass == 0) { if (!bExtreme) continue; }
|
|
else { if (bExtreme || !bOpen) continue; }
|
|
|
|
done.insert(key);
|
|
const AcGePoint2d p = project(p3);
|
|
bool ok = true;
|
|
for (size_t j = 0; j < placed.size(); ++j)
|
|
if ((placed[j] - p).length() < minGap) { ok = false; break; }
|
|
if (!ok) continue;
|
|
placed.push_back(p);
|
|
|
|
CString s;
|
|
s.Format(_T("EL +%.0f"), p3.z);
|
|
dr.addText(AcGePoint2d(p.x + txtH * 0.6, p.y + txtH * 1.2), txtH * 0.85, s, LAYER_TEXT);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ======================= 主流程 =======================
|
|
// 把一组组件渲染成一张带图面样式的 DWG。
|
|
static bool renderSheet(const std::vector<IsoComp> &comps, const std::vector<int> &g,
|
|
const std::vector<std::vector<int> > &allGroups,
|
|
LPCTSTR dwgPath, LPCTSTR pipeline, LPCTSTR sheetName, int idx, int total)
|
|
{
|
|
if (g.empty()) return false;
|
|
|
|
// 1) 投影求包围盒(模型坐标, mm)
|
|
AcGePoint2d mn(0.0, 0.0), mx(0.0, 0.0);
|
|
bool bFirst = true;
|
|
for (size_t k = 0; k < g.size(); ++k)
|
|
{
|
|
const IsoComp &c = comps[g[k]];
|
|
for (size_t i = 0; i < c.ends.size(); ++i) updateBBox(project(c.ends[i].pt), mn, mx, bFirst);
|
|
for (size_t i = 0; i < c.branches.size(); ++i) updateBBox(project(c.branches[i].pt), mn, mx, bFirst);
|
|
for (size_t i = 0; i < c.coords.size(); ++i) updateBBox(project(c.coords[i]), mn, mx, bFirst);
|
|
if (c.hasCentre) updateBBox(project(c.centre), mn, mx, bFirst);
|
|
}
|
|
if (bFirst) return false;
|
|
|
|
const double mw = mx.x - mn.x, mh = mx.y - mn.y;
|
|
|
|
// 2) 计算绘图区缩放(模型 -> 图纸 mm)
|
|
const double dw = DRAW_R - DRAW_L, dh = DRAW_T - DRAW_B;
|
|
const double pad = 0.10;
|
|
double sc = 1.0;
|
|
if (mw > 1e-6 || mh > 1e-6)
|
|
{
|
|
const double sx = (dw * (1.0 - pad)) / (mw > 1e-6 ? mw : 1.0);
|
|
const double sy = (dh * (1.0 - pad)) / (mh > 1e-6 ? mh : 1.0);
|
|
sc = (sx < sy) ? sx : sy;
|
|
}
|
|
// 图纸上模型占位尺寸
|
|
const double placedW = mw * sc, placedH = mh * sc;
|
|
const double ox = DRAW_L + (dw - placedW) * 0.5 - mn.x * sc;
|
|
const double oy = DRAW_B + (dh - placedH) * 0.5 - mn.y * sc;
|
|
|
|
// 3) 建库
|
|
AcDbDatabase *pDb = new AcDbDatabase(Adesk::kTrue, Adesk::kTrue);
|
|
bool bDrawn = false;
|
|
{
|
|
IsoDrawer dr(pDb);
|
|
if (!dr.begin()) { delete pDb; return false; }
|
|
try
|
|
{
|
|
// ---- 模型(用模型坐标;apply 会按 sc/offset 变换) ----
|
|
dr.setScale(sc);
|
|
dr.setOffset(AcGePoint2d(ox, oy));
|
|
|
|
// 模型文字高度:图纸 3mm 反算到模型坐标
|
|
const double modelTxtH = (sc > 1e-9) ? (3.0 / sc) : 3.0;
|
|
for (size_t k = 0; k < g.size(); ++k)
|
|
drawComponent(dr, comps[g[k]], modelTxtH);
|
|
|
|
// 标注(模型坐标,会被缩放)
|
|
drawDimensions(dr, comps, g, modelTxtH);
|
|
drawElevations(dr, comps, g, modelTxtH);
|
|
drawWeldBalloons(dr, comps, g, modelTxtH);
|
|
drawContinuation(dr, comps, g, idx - 1, allGroups, modelTxtH);
|
|
|
|
// ---- 图框/标题栏/表格/北针(图纸坐标:scale 复位为 1) ----
|
|
dr.setScale(1.0);
|
|
dr.setOffset(AcGePoint2d(0.0, 0.0));
|
|
|
|
dr.addRect(BORDER_L, BORDER_B, BORDER_R, BORDER_T, LAYER_FRAME);
|
|
drawTitleBlock(dr, pipeline, sheetName, idx, total);
|
|
drawBom(dr, comps, g);
|
|
drawCutList(dr, comps, g);
|
|
drawNorthArrow(dr, AcGePoint2d(DRAW_L + 18.0, DRAW_T - 22.0), 16.0);
|
|
|
|
bDrawn = true;
|
|
}
|
|
catch (...)
|
|
{
|
|
acutPrintf(_T("\n[ISO] 出图异常,已跳过该张。"));
|
|
}
|
|
|
|
dr.close();
|
|
}
|
|
|
|
const Acad::ErrorStatus es = bDrawn ? pDb->saveAs(dwgPath) : Acad::eNotApplicable;
|
|
delete pDb;
|
|
return (es == Acad::eOk);
|
|
}
|
|
|
|
// 计算输出目录(为空时取输入文件所在目录),并保证以反斜杠结尾
|
|
static CString resolveOutDir(LPCTSTR inFile, LPCTSTR outDir)
|
|
{
|
|
CString sDir(outDir);
|
|
if (sDir.IsEmpty())
|
|
{
|
|
CString sIn(inFile);
|
|
const int p = lastSep(sIn);
|
|
sDir = (p >= 0) ? sIn.Left(p) : CString();
|
|
}
|
|
if (!sDir.IsEmpty())
|
|
{
|
|
const TCHAR c = sDir[sDir.GetLength() - 1];
|
|
if (c != _T('\\') && c != _T('/')) sDir += _T('\\');
|
|
}
|
|
return sDir;
|
|
}
|
|
|
|
// ---------------- 选项默认值 ----------------
|
|
namespace PcfIso
|
|
{
|
|
SplitOptions::SplitOptions()
|
|
: modelSplit(true), breakPointSplit(false), spoolSplit(false), sheetSplit(true),
|
|
spoolMaxX(12192.0), spoolMaxY(3048.0), spoolMaxZ(2438.0), // 480/120/96 英寸 -> mm
|
|
maxDirChanges(3), maxCompPerSheet(200)
|
|
{
|
|
}
|
|
}
|
|
|
|
bool PcfIso::makeIsoDwg(LPCTSTR pcfPath, LPCTSTR dwgPath)
|
|
{
|
|
std::vector<IsoComp> comps;
|
|
CString pipelineRef;
|
|
if (!parsePcf(pcfPath, comps, pipelineRef)) return false;
|
|
if (comps.empty()) return false;
|
|
|
|
std::vector<int> all; for (size_t i = 0; i < comps.size(); ++i) all.push_back((int)i);
|
|
std::vector<std::vector<int> > allGroups(1, all);
|
|
const CString stem = fileStem(pcfPath);
|
|
return renderSheet(comps, all, allGroups, dwgPath, pipelineRef, stem, 1, 1);
|
|
}
|
|
|
|
int PcfIso::makeIsoDwgSplitEx(LPCTSTR pcfPath, LPCTSTR outDir, const SplitOptions &opt)
|
|
{
|
|
std::vector<IsoComp> comps;
|
|
CString pipelineRef;
|
|
if (!parsePcf(pcfPath, comps, pipelineRef)) return 0;
|
|
if (comps.empty()) return 0;
|
|
|
|
const std::vector<std::vector<int> > groups = splitPipeline(comps, opt);
|
|
const int total = (int)groups.size();
|
|
const CString sStem = fileStem(pcfPath);
|
|
const CString sDir = resolveOutDir(pcfPath, outDir);
|
|
|
|
int nOk = 0;
|
|
for (int gi = 0; gi < total; ++gi)
|
|
{
|
|
CString sDwg, sheetName;
|
|
if (total <= 1)
|
|
{
|
|
sDwg.Format(_T("%s%s.dwg"), (LPCTSTR)sDir, (LPCTSTR)sStem);
|
|
sheetName = sStem;
|
|
}
|
|
else
|
|
{
|
|
sDwg.Format(_T("%s%s-%d.dwg"), (LPCTSTR)sDir, (LPCTSTR)sStem, gi + 1);
|
|
sheetName.Format(_T("%s-%d"), (LPCTSTR)sStem, gi + 1);
|
|
}
|
|
if (renderSheet(comps, groups[gi], groups, sDwg, pipelineRef, sheetName, gi + 1, total)) ++nOk;
|
|
}
|
|
return nOk;
|
|
}
|
|
|
|
int PcfIso::makeIsoDwgSplit(LPCTSTR pcfPath, LPCTSTR outDir)
|
|
{
|
|
SplitOptions opt; // 默认:模型级 + 图幅级(与 P3D 默认一致)
|
|
return makeIsoDwgSplitEx(pcfPath, outDir, opt);
|
|
}
|
|
|
|
int PcfIso::makeIsoDwgFromPath(LPCTSTR inPath, LPCTSTR outDir)
|
|
{
|
|
CString sIn(inPath);
|
|
if (sIn.IsEmpty()) return 0;
|
|
|
|
CString sInTrim = sIn;
|
|
while (sInTrim.GetLength() > 3
|
|
&& (sInTrim[sInTrim.GetLength() - 1] == _T('\\') || sInTrim[sInTrim.GetLength() - 1] == _T('/')))
|
|
sInTrim = sInTrim.Left(sInTrim.GetLength() - 1);
|
|
|
|
const DWORD attr = ::GetFileAttributes(sInTrim);
|
|
const bool bDir = (attr != INVALID_FILE_ATTRIBUTES) && (attr & FILE_ATTRIBUTE_DIRECTORY);
|
|
|
|
int nOk = 0;
|
|
|
|
if (bDir)
|
|
{
|
|
CString sPattern = sInTrim;
|
|
if (sPattern[sPattern.GetLength() - 1] != _T('\\')) sPattern += _T('\\');
|
|
sPattern += _T("*.pcf");
|
|
|
|
CFileFind finder;
|
|
BOOL bWorking = finder.FindFile(sPattern);
|
|
while (bWorking)
|
|
{
|
|
bWorking = finder.FindNextFile();
|
|
if (finder.IsDots() || finder.IsDirectory()) continue;
|
|
|
|
CString sDwgDir = resolveOutDir(finder.GetFilePath(), outDir);
|
|
CString sDwg = sDwgDir + finder.GetFileTitle() + _T(".dwg");
|
|
if (makeIsoDwg(finder.GetFilePath(), sDwg)) ++nOk;
|
|
}
|
|
finder.Close();
|
|
}
|
|
else
|
|
{
|
|
CString sDwgDir = resolveOutDir(sInTrim, outDir);
|
|
CString sDwg = sDwgDir + fileStem(sInTrim) + _T(".dwg");
|
|
if (makeIsoDwg(sInTrim, sDwg)) ++nOk;
|
|
}
|
|
return nOk;
|
|
}
|
|
|
|
int PcfIso::makeIsoDwgSplitFromPath(LPCTSTR inPath, LPCTSTR outDir)
|
|
{
|
|
CString sIn(inPath);
|
|
if (sIn.IsEmpty()) return 0;
|
|
|
|
CString sInTrim = sIn;
|
|
while (sInTrim.GetLength() > 3
|
|
&& (sInTrim[sInTrim.GetLength() - 1] == _T('\\') || sInTrim[sInTrim.GetLength() - 1] == _T('/')))
|
|
sInTrim = sInTrim.Left(sInTrim.GetLength() - 1);
|
|
|
|
const DWORD attr = ::GetFileAttributes(sInTrim);
|
|
const bool bDir = (attr != INVALID_FILE_ATTRIBUTES) && (attr & FILE_ATTRIBUTE_DIRECTORY);
|
|
|
|
int nOk = 0;
|
|
if (bDir)
|
|
{
|
|
CString sPattern = sInTrim;
|
|
if (sPattern[sPattern.GetLength() - 1] != _T('\\')) sPattern += _T('\\');
|
|
sPattern += _T("*.pcf");
|
|
|
|
CFileFind finder;
|
|
BOOL bWorking = finder.FindFile(sPattern);
|
|
while (bWorking)
|
|
{
|
|
bWorking = finder.FindNextFile();
|
|
if (finder.IsDots() || finder.IsDirectory()) continue;
|
|
nOk += makeIsoDwgSplit(finder.GetFilePath(), outDir);
|
|
}
|
|
finder.Close();
|
|
}
|
|
else
|
|
{
|
|
nOk += makeIsoDwgSplit(sInTrim, outDir);
|
|
}
|
|
return nOk;
|
|
}
|
|
|
|
// ======================= 命令入口 =======================
|
|
void EW_PCFISOCmdFnc()
|
|
{
|
|
TCHAR szIn[512] = { 0 };
|
|
if (acedGetString(FALSE, _T("\n请输入 PCF 文件或所在目录: "), szIn) != RTNORM)
|
|
return;
|
|
CString sIn = szIn;
|
|
if (sIn.IsEmpty()) { acutPrintf(_T("\n未输入路径,取消。")); return; }
|
|
|
|
TCHAR szOut[512] = { 0 };
|
|
if (acedGetString(FALSE, _T("\n请输入输出 DWG 目录<直接回车=与 PCF 同目录>: "), szOut) != RTNORM)
|
|
return;
|
|
|
|
const int n = PcfIso::makeIsoDwgFromPath(sIn, szOut);
|
|
acutPrintf(_T("\nISO 图导出完成,共生成 %d 个 DWG。"), n);
|
|
}
|
|
|
|
void EW_PCFISOSPLITCmdFnc()
|
|
{
|
|
TCHAR szIn[512] = { 0 };
|
|
if (acedGetString(FALSE, _T("\n请输入 PCF 文件或所在目录: "), szIn) != RTNORM)
|
|
return;
|
|
CString sIn = szIn;
|
|
if (sIn.IsEmpty()) { acutPrintf(_T("\n未输入路径,取消。")); return; }
|
|
|
|
TCHAR szOut[512] = { 0 };
|
|
if (acedGetString(FALSE, _T("\n请输入输出 DWG 目录<直接回车=与 PCF 同目录>: "), szOut) != RTNORM)
|
|
return;
|
|
|
|
// 分图层级:1=模型级(默认) 2=+断点级 3=+管段级 4=+全部
|
|
TCHAR szLv[32] = { 0 };
|
|
if (acedGetString(FALSE, _T("\n分图层级 1=模型(默认) 2=+断点 3=+管段 4=+全部<回车=1>: "), szLv) != RTNORM)
|
|
return;
|
|
const int lv = (_ttoi(szLv) >= 1 && _ttoi(szLv) <= 4) ? _ttoi(szLv) : 1;
|
|
|
|
PcfIso::SplitOptions opt;
|
|
opt.modelSplit = true;
|
|
opt.breakPointSplit = (lv >= 2);
|
|
opt.spoolSplit = (lv >= 3);
|
|
opt.sheetSplit = true;
|
|
|
|
CString sInTrim = sIn;
|
|
while (sInTrim.GetLength() > 3
|
|
&& (sInTrim[sInTrim.GetLength() - 1] == _T('\\') || sInTrim[sInTrim.GetLength() - 1] == _T('/')))
|
|
sInTrim = sInTrim.Left(sInTrim.GetLength() - 1);
|
|
|
|
const DWORD attr = ::GetFileAttributes(sInTrim);
|
|
const bool bDir = (attr != INVALID_FILE_ATTRIBUTES) && (attr & FILE_ATTRIBUTE_DIRECTORY);
|
|
|
|
int n = 0;
|
|
if (bDir)
|
|
{
|
|
CString sPattern = sInTrim;
|
|
if (sPattern[sPattern.GetLength() - 1] != _T('\\')) sPattern += _T('\\');
|
|
sPattern += _T("*.pcf");
|
|
CFileFind finder;
|
|
BOOL bWorking = finder.FindFile(sPattern);
|
|
while (bWorking)
|
|
{
|
|
bWorking = finder.FindNextFile();
|
|
if (finder.IsDots() || finder.IsDirectory()) continue;
|
|
n += PcfIso::makeIsoDwgSplitEx(finder.GetFilePath(), szOut, opt);
|
|
}
|
|
finder.Close();
|
|
}
|
|
else
|
|
{
|
|
n += PcfIso::makeIsoDwgSplitEx(sInTrim, szOut, opt);
|
|
}
|
|
|
|
acutPrintf(_T("\nISO 分图完成(层级 %d),共生成 %d 个 DWG。"), lv, n);
|
|
}
|