新增 PCF→ISO 图(DWG)模块 PcfIso:P3D 一致的分图分层与图面样式

- 新增 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》。
This commit is contained in:
gjm committed 2026-10-08 08:29:49 +08:00
1 parent 164968b62e
commit cb38f5071b
6 files changed
+1725

No files matched your search

@@ -727,6 +727,10 @@ void InitApplication()
//ISO图: 自动分图(按管号/连通性/图幅把一个管道系统拆成多张 PCF) //ISO图: 自动分图(按管号/连通性/图幅把一个管道系统拆成多张 PCF)
ADD_CMD(EW_PCFSPLIT, EW_PCFSPLITCmdFnc); ADD_CMD(EW_PCFSPLIT, EW_PCFSPLITCmdFnc);
//ISO图: PCF 转 ISO 图(DWG)
ADD_CMD(EW_PCFISO, EW_PCFISOCmdFnc);
//ISO图: PCF 按连通性分图(P3D 规则)
ADD_CMD(EW_PCFISOSPLIT, EW_PCFISOSPLITCmdFnc);
//管廊漫游 //管廊漫游
ADD_CMD(EW_GLMY, EW_GLMYCmdFnc); ADD_CMD(EW_GLMY, EW_GLMYCmdFnc);
@@ -0,0 +1,119 @@
# PCF → ISO 分图规则说明(对齐 AutoCAD Plant 3D)
> 结论先行:Plant 3D(下称 P3D)把 PCF 出 ISO 时的“分图”,**主规则是连通性(模型)拆分**——
> 把 PCF 里的管道元件按“连接点是否重合”连成一张网,凡是不连通的部分各自成为一张 ISO 图。
> 我们按同一规则实现了 `EW_PCFISOSPLIT`,对同一份 PCF 得到了与 P3D **完全一致**的分图结果(6 张)。
---
## 1. P3D 的分图体系(由粗到细,共 5 级)
| 级别 | 名称 | 触发条件 | 本次是否触发 |
| --- | --- | --- | --- |
| 1 | 模型级 Model | 连通性断开 / 属性变化 | **是**(拆成 6 个模型) |
| 2 | 断点级 Break point | 用户或默认“打断点”(法兰/现场焊口/焊口/任意件) | 否(“未指定打断点”) |
| 3 | 管段级 Spool | 尺寸/重量超限 | 否(`SpoolSplit=false`) |
| 4 | 图幅级 Sheet | 单张图纸放不下 | 否 |
| 5 | 拥塞级 Congestion | 图面过密 | 否 |
P3D 处理顺序:**先按“模型”分组 → 每个模型尝试按断点/管段/图幅再拆 → 最后输出**。
日志里逐条出现“正在处理:『污水』/ 未指定打断点 / 已跳过模型拆分”,说明模型已经由第 1 级确定,第 2 级被跳过。
---
## 2. `IsoConfig.xml` 关键参数(本机 Check_ANSI-C 样式实测)
```
<DefaultSplitPoints> FlangeLike > WeldFieldItem > Weld > AnyItem (程序首选打断点)
<SplitPoints> FlangeLike, WeldFieldItem, Weld (用户首选打断点)
<SheetSplitPoint Change="false"> Connector=FieldWeld (图幅断点,不改焊口类型)
<Model Split="true" ModelSplitMethod="ModelBreakPoint">
<Properties PropertyChange="PIPING-SPEC" Filter=""/>
<Spool SpoolSplitMethod="OnSize" SizeLimits="480 120 96"
OverflowLength="6" MaxDirectionChanges="3" MaxWeight="0" MaxPipeLength="0"
PropertyChange="PIPING-SPEC"/>
<Auto DisableAutoSplit="false" SpoolSplit="false"
IdentifySpools="true" SplitOnCongestion="true" CongestionLevel="2"/>
<AdvancedDefault ForceOntoOneSheet="false" SplittingCongestion="50"/>
```
- `DefaultSplitPoints`:**程序**倾向在这些连接处断开(法兰类 → 现场焊口 → 焊口 → 任意件)。
- `SplitPoints`:**用户**倾向断开处;若未在界面里指定,则模型拆分被跳过。
- `SizeLimits="480 120 96"`:管段尺寸上限(英寸),`OverflowLength="6"` 为余量合并阈值,`MaxDirectionChanges="3"` 为允许的转向次数。
- `Auto/SplitOnCongestion` + `CongestionLevel=2`:图面过密时按拥塞度再拆。
---
## 3. 实测:一份 PCF → 6 张图
- 输入:`test1.pcf`(= 我们导出的 `ISO-024_污水.pcf`,管线号 **污水**,**60 个元件 + 43 道焊口**,单一管线号)。
- 因 P3D 默认工程是英制,先把 PCF 由 mm 换算成 inch(`test1_inch.pcf`)才能通过单位校验。
- P3D 日志关键行:
- `警告 - 在模型中找到了无效的元件(缺少中心点) - TEE ...`(共 5 个 TEE 缺中心点)
- `警告 - 在线号“污水”的模型中发现连接已断开。`
- `信息 - 加载了“6”个模型。` → 每个模型“未指定打断点 / 已跳过模型拆分” → 生成 **6** 张图 `污水-1..6.dwg`
产物内容(读图确认):
| 图号 | 内容 | 管径 |
| --- | --- | --- |
| 污水-1/2/3 | 三根**立管**(各带 3 个阀门、上下异径管) | 300 mm |
| 污水-4 | 一段 200 mm 斜管 | 200 mm |
| 污水-5 | 600 mm 主管折线(含弯头/三通) | 600 mm |
| 污水-6 | 一段 600 mm 直管 | 600 mm |
---
## 4. 规则提炼(我们实现所依据的规则)
1. **规则 A(连通)**:两个元件只要有**重合的连接点**(端点 END-POINT / 中心点 CENTRE-POINT / 支承点 CO-ORDS),就属于同一模型。
2. **规则 B(无效支管断开)**:三通等支管元件若**缺少 CENTRE-POINT**,P3D 判为“无效元件”,其**支管(BRANCH-POINT)不参与连通** → 主管在支管处断开。
> 本案例正是这条规则把 3 根立管从主管上“摘”了下来。
3. **规则 C(模型 = 一张图)**:每个连通子网输出一张 ISO。
4. 规则 D/E/F(断点、管段尺寸、拥塞)为可选层,本次未触发。
> 用规则 A+B 对 `test1.pcf` 做并查集,得到 **6 个连通子网**,与 P3D 的 6 张图**一一对应**。
---
## 5. 我们的实现(`EW_PCFISOSPLIT`)
代码:`SourceCode/Code2026/CoreCommand/PcfIso.cpp` / `PcfIso.h`
- `pointKey()`:把 3D 点量化成字符串键(容差 0.01),用于判定连接点是否重合。
- `collectConnPoints()`:收集一个元件参与连通的点;**缺少 CENTRE-POINT 时不含支管点**(规则 B)。
- `groupComponents()`:并查集求连通子网,按“最小元件下标”排序,保证编号稳定。
- `makeIsoDwgSplit()`:对每个子网调用 `renderToDwg()` 生成一张图,命名 `<文件名>-N.dwg`;只有一个子网时输出 `<文件名>.dwg`。
- `makeIsoDwgSplitFromPath()` / `EW_PCFISOSPLITCmdFnc()`:命令入口,输入 PCF 文件或目录 + 输出目录。
命令:`EW_PCFISOSPLIT` → 输入 PCF(或目录)→ 输入输出目录(回车=同目录)。
---
## 6. 结果对比
实跑 `EW_PCFISOSPLIT test1.pcf`,输出 **6** 张图,与 P3D 内容一一对应(仅编号顺序不同):
| Plant 3D | 我们 | 内容 |
| --- | --- | --- |
| 污水-1 | test1-1.dwg | 立管 A(300) |
| 污水-2 | test1-3.dwg | 立管 B(300) |
| 污水-3 | test1-4.dwg | 立管 C(300) |
| 污水-4 | test1-6.dwg | 200 mm 斜管 |
| 污水-5 | test1-2.dwg | 600 mm 主管折线 |
| 污水-6 | test1-5.dwg | 600 mm 直管 |
对比图见:`%TEMP%\compare_split_real.png`(左 P3D / 右我们)。
---
## 7. 尚未实现 / 后续可做
- **断点级**:按 `SplitPoints`(法兰/现场焊口/焊口)在连通网内部再拆。
- **管段级**:按 `SizeLimits`(尺寸上限、转向次数、余量合并)拆 spool。
- **图幅级 / 拥塞级**:图面放不下或过密时再拆。
- **图面样式**:P3D 的 ISO 是完整成品图(图框、指北针、标题栏、材料表 BOM、下料表、焊口表、尺寸标注、标高、续接标记);我们目前只画**单线骨架 + 管径标注**,这是下一步的主要差距。
---
*生成日期:2026-10-04*
File diff suppressed because it is too large. Load diff
+62
View File
@@ -0,0 +1,62 @@
#pragma once
/*
PCF -> ISO 图(DWG) 导出
================================================================
把 EW_PCF / EW_PCFSPLIT 导出的 PCF 中间文件解析后,按等轴测(30°)投影,
生成带图框/标题栏/材料表/切割表/尺寸/标高/焊口气泡/北针的 ISO 图并保存为 DWG。
本文件与主代码(pcf.cpp)分离,算法自包含,便于后续单独修改/替换。
坐标约定:PCF 用 3D 模型坐标(单位 mm),Z 向上。
等轴测投影(与 Plant 3D / Isogen 一致):
u = (x - y) * cos30 (+X 指向右上 30°)
v = (x + y) * sin30 + z (+Y 指向左上 30°,+Z 向上)
分图层级(与 P3D 的 IsoConfig.xml <Split> 对应):
1) 模型级 Model —— 连通性断开(含"缺少中心点的三通"支管断开)
2) 断点级 Break —— 法兰/现场焊口/焊口处切开
3) 管段级 Spool —— 尺寸超过 SizeLimits 时切开
4) 图幅级 Sheet —— 一张图放不下/过密时续接
*/
namespace PcfIso
{
// 分图选项。默认与 P3D 默认工程一致:只做"模型级"连通性分图。
struct SplitOptions
{
bool modelSplit; // 1) 连通性分图(默认 true)
bool breakPointSplit; // 2) 断点分图(法兰/现场焊口) 默认 false
bool spoolSplit; // 3) 管段尺寸分图 默认 false
bool sheetSplit; // 4) 图幅/拥塞分图 默认 true
double spoolMaxX; // 管段尺寸上限 X(mm)
double spoolMaxY; // 管段尺寸上限 Y(mm)
double spoolMaxZ; // 管段尺寸上限 Z(mm)
int maxDirChanges; // 最大转向次数
int maxCompPerSheet; // 单张图最大元件数(拥塞控制)
SplitOptions();
};
// 把一个 PCF 文件渲染为单张 ISO 图(带图面样式)并存成 DWG。
// pcfPath : 输入 PCF 文件路径
// dwgPath : 输出 DWG 文件路径(含 .dwg 扩展名)
// 返回是否成功。
bool makeIsoDwg(LPCTSTR pcfPath, LPCTSTR dwgPath);
// 输入可以是单个 .pcf 文件,也可以是一个目录(处理目录下所有 *.pcf)。
// outDir 为空时,输出到各 PCF 所在目录。
// 返回成功生成的 DWG 个数。
int makeIsoDwgFromPath(LPCTSTR inPath, LPCTSTR outDir);
// 分图(按 P3D 规则把一个 PCF 拆成多张 ISO 图),使用默认选项。
// 每个连通子网输出一个 DWG,命名为 <文件名>-N.dwg;只有一个子网时输出 <文件名>.dwg。
// outDir 为空时输出到 PCF 所在目录。返回成功生成的 DWG 个数。
int makeIsoDwgSplit(LPCTSTR pcfPath, LPCTSTR outDir);
// 分图(可指定分层选项)。
int makeIsoDwgSplitEx(LPCTSTR pcfPath, LPCTSTR outDir, const SplitOptions &opt);
// 输入单个 .pcf 或目录,逐个按上面的规则分图。
// 返回成功生成的 DWG 个数。
int makeIsoDwgSplitFromPath(LPCTSTR inPath, LPCTSTR outDir);
}
+2
View File
@@ -130,6 +130,8 @@ void EW_PDWGOUTCmdFnc();
void EW_IGSOUTCmdFnc(); void EW_IGSOUTCmdFnc();
void EW_PCFCmdFnc(); void EW_PCFCmdFnc();
void EW_PCFSPLITCmdFnc(); void EW_PCFSPLITCmdFnc();
void EW_PCFISOCmdFnc();
void EW_PCFISOSPLITCmdFnc();
void EW_GLMYCmdFnc(); void EW_GLMYCmdFnc();
void EW_ENODECmdFnc(); void EW_ENODECmdFnc();
void EW_CTRLVIEWCmdFnc(); void EW_CTRLVIEWCmdFnc();
@@ -100,6 +100,7 @@ set(SOURCES
"sortedmenuitemdlg.cpp" "sortedmenuitemdlg.cpp"
"collisiondetection.cpp" "collisiondetection.cpp"
"pcf.cpp" "pcf.cpp"
"PcfIso.cpp"
"EarthWorm.rc" "EarthWorm.rc"
# CoreCommand.def # CoreCommand.def
) )