docs: 补充 AecDbEwPipeBase 截面存 SAT 的落盘实测与不分流决策
- 新增 3.7 落盘实测:SAT 原始字节、DWG 净增、acisIn 与布尔重建耗时, 并标注该数据来自 accoreconsole 模拟 REGION,非真实实体实测 - 新增 3.8 为什么不分流:createFromCurves 慢、落盘省不下体积、 写路径缺陷、升版本成本 - 修正体积代价表述:字段级估算 5~20 倍,落盘压缩后实为 4~5 倍
This commit is contained in:
1 parent
5543ac42a2
commit
5692d9010b
1 file changed
+467
@@ -0,0 +1,467 @@
|
||||
# AecDbEwPipeBase::m_pRegion 存储改为 SAT 为主
|
||||
|
||||
> 目标文件:`SourceCode/Code2026/CoreLibrary/AecDbEwPipeBase.cpp`、`inc/AecDbEwPipeBase.h`
|
||||
> 当前版本号:`kCurrentVersionNumber = 7`(`AecDbEwPipeBase.cpp:382`)
|
||||
> 本文只讨论 `m_pRegion`(截面)的持久化,不涉及 `m_pBaseCurve`、`m_arUserInfs`。
|
||||
|
||||
## 1. 结论
|
||||
|
||||
`m_pRegion` 的存储改为 **SAT 为主、曲线通道只读兼容**。
|
||||
|
||||
- 写入:统一走 `nLen = 0` + `RegionAcisoutDwgOutFields`(SAT 字节流)
|
||||
- 读取:保留两条通道,`nLen > 0` 走曲线重建,`nLen == 0` 走 SAT
|
||||
- 曲线通道的**写路径退役**,读路径永久保留(老图纸需要)
|
||||
- **不按截面类型分流**:不存在「标准截面走参数重建、异形截面走 SAT」的第二套逻辑,所有新数据一律 SAT。理由见 3.8
|
||||
|
||||
体积代价经实测确认可接受(落盘净增约 4 倍,见 3.7);换来的是几何可重现、失败可检测、代码只有一套。
|
||||
|
||||
---
|
||||
|
||||
## 2. 现状
|
||||
|
||||
### 2.1 双通道结构
|
||||
|
||||
```
|
||||
dwgOutFields (:1324)
|
||||
└ SectionRegionDwgOutFields(ver=7) (:1230)
|
||||
├ m_pRegion->explode() 非空 → RegionExplodeDwgOutFields (:1135)
|
||||
│ └ writeUInt32(曲线数) + 每条曲线(类名 + 参数)
|
||||
└ explode 空 → writeUInt32(0) + RegionAcisoutDwgOutFields (:1150)
|
||||
└ acisOut → 读临时文件 → writeString(文件名) + writeUInt32(nLen) + writeBytes(SAT)
|
||||
|
||||
dwgInFields (:854)
|
||||
└ m_pRegion = SectionRegionDwgInFields(ver) (:893, :825)
|
||||
├ ver < 4 → readUInt32(nLen) → RegionFromCurvesDwgInFields (:663)
|
||||
└ ver >= 4
|
||||
├ nLen > 0 → RegionFromCurvesDwgInFields
|
||||
└ nLen == 0 → RegionAcisInDwgInFields (:758)
|
||||
```
|
||||
|
||||
曲线通道支持的类型:`AcDbLine` / `AcDbArc` / `AcDbCircle` / `AcDbPolyline`(含 bulge 与宽度)/ `AcDbEllipse` / `AcDb3dPolyline` / `AecDb3dRoadAxisLine`(`BaseCurveDwgOutFields:1013`、`BaseCurveDwgInFields:445`)。
|
||||
|
||||
### 2.2 曲线通道的四个缺陷
|
||||
|
||||
**(1) `AcDbSpline` 只写类名,不写参数**
|
||||
|
||||
```cpp
|
||||
// 写端 :1092
|
||||
AcDbSpline* pSp = AcDbSpline::cast(pBaseCurve);
|
||||
if (pSp != NULL)
|
||||
{
|
||||
//...
|
||||
return Acad::eOk; // 一个参数都没写
|
||||
}
|
||||
|
||||
// 读端 :562
|
||||
AcDbSpline* pSp = AcDbSpline::cast(pBaseCurve);
|
||||
if (pSp != NULL)
|
||||
{
|
||||
//... // 不读参数,也不 return
|
||||
}
|
||||
// 继续 cast 到 3dPolyline / RoadAxisLine 全部失败,最终 :596 return NULL
|
||||
```
|
||||
|
||||
读写对称,所以不会字段错位,但**这条边直接消失**。而 `AcDbRegion::explode()` 对含 NURBS 边的 region 返回 `AcDbSpline` 是常见情况。
|
||||
|
||||
**(2) `explode` 的输出依赖 ACIS 版本**
|
||||
|
||||
`explode` 是从 B-rep 反推边界,NURBS 边被转成 spline 或打碎成多段线段,结果随 ACIS 版本变化。同一份截面在 CAD 2023 和 2027 上存盘,写出的字节不同;来回存盘几何会逐步漂移。
|
||||
|
||||
**(3) 多区域靠"最大面积为外环"猜**
|
||||
|
||||
`:717-751`:
|
||||
|
||||
```cpp
|
||||
// 面积最大的当外环
|
||||
if (dAreaTmp > dArea) { dArea = dAreaTmp; pRegionBig = pRegion; }
|
||||
// 其余全部减去
|
||||
es = pRegionBig->booleanOper(AcDb::kBoolSubtract, pRegion);
|
||||
```
|
||||
|
||||
`createFromCurves` 对 N 条闭合曲线会生成 N 个 region。截面是两个不相交的独立区域时,小的那块被当成孔减掉。
|
||||
|
||||
**(4) 退化判据不成立**
|
||||
|
||||
```cpp
|
||||
m_pRegion->explode(arCurPtrs);
|
||||
if (!arCurPtrs.isEmpty()) // 只看"有没有炸出东西"
|
||||
return RegionExplodeDwgOutFields(...);
|
||||
```
|
||||
|
||||
上面三种情况(spline 丢失、多区域误判、边被打碎)都会让 `arCurPtrs` 非空,于是**走曲线通道、写出坏数据、读回来几何不对,没有任何提示**。
|
||||
|
||||
### 2.3 SAT 通道自身的缺陷
|
||||
|
||||
| 位置 | 问题 |
|
||||
| --- | --- |
|
||||
| `:775` | `new char[nLen]` 无 `+1`,当前没有 strlen 依赖,但脆弱 |
|
||||
| `:784` | `errorfile` 分支 `return pRegion` 前没有 `delete[] pszTxt`,每次泄漏 nLen 字节 |
|
||||
| `:788-790` | 临时文件路径由 DWG 里的 `sFileName` 拼成,可被构造成 `..\..\xxx` |
|
||||
| `:1162` | 文件名 `Region_<句柄>.dat` 同图不同文档会冲突 |
|
||||
| `:1182` | `new char[nLen]` 无 `+1`,`writeBytes` 有长度,当前安全 |
|
||||
| `:1206` | `errorfile` 兜底走 `m_pRegion->dwgOutFields(pFiler)`,只写字段不写对象头,`layerId` 等为 `kNull` |
|
||||
| 全路径 | 失败无日志,`return NULL` 后 `m_pRegion` 为空,绘制时截面直接不见 |
|
||||
|
||||
---
|
||||
|
||||
## 3. 改为 SAT 为主的优点
|
||||
|
||||
### 3.1 几何可重现
|
||||
|
||||
SAT 是 ACIS 自己的序列化格式,`acisOut` / `acisIn` 是同版本内的官方往返。同一份 `m_pRegion`,无论在哪台机器、哪个 ACIS 版本上存盘,写出的字节一致(同版本内)。
|
||||
|
||||
曲线通道相反:`explode` 的输出随版本变化,同一张图反复存盘会累积漂移。
|
||||
|
||||
**这是最重要的一条**:图纸数据一旦不可重现,问题就永远无法复现和定位。
|
||||
|
||||
### 3.2 失败可检测
|
||||
|
||||
SAT 首行带 ACIS 版本号和 data record 数,`fixACISFormat` 能用它校验完整性(`CoreTools.cpp`)。曲线通道没有整体校验:少一条边、多一段线,格式上完全合法,无法发现。
|
||||
|
||||
配合 `AcDbX3dSolid` 那次加的失败日志(`CoreTools::StringToRegion`),解析失败至少能在命令行看到长度和首行。
|
||||
|
||||
### 3.3 代码只有一套
|
||||
|
||||
写路径删掉 `explode` + `RegionExplodeDwgOutFields` + `BaseCurveDwgOutFields` 的调用后:
|
||||
|
||||
- 不需要为每种曲线类型维护写/读分支
|
||||
- 不需要维护"最大面积为外环"的启发式
|
||||
- 不需要维护 spline 的补全逻辑
|
||||
- 与 `AcDbX3dSolid` 的存储方式一致,后续只有一个心智模型
|
||||
|
||||
`BaseCurveDwgOutFields` / `BaseCurveDwgInFields` 仍要保留,因为 `m_pBaseCurve`(中心路径)用它,且曲线读通道依赖它。
|
||||
|
||||
### 3.4 写盘更快
|
||||
|
||||
`SectionRegionDwgOutFields` 现在每次存盘对每个管道做一次 `m_pRegion->explode()`,这是 ACIS 边界提取,成本与 `acisOut` 同量级甚至更高(要构造曲线对象并写多个字段)。改成 SAT 后只剩一次 `acisOut`。
|
||||
|
||||
另外 `explode` 出来的曲线在 `RegionExplodeDwgOutFields:1145` 用 `delete pCurve` 释放,未入库实体没有先 `close()`,属于隐患。
|
||||
|
||||
### 3.5 格式已预留,切换无版本成本
|
||||
|
||||
读取端 `SectionRegionDwgInFields:836-849`:
|
||||
|
||||
```cpp
|
||||
readUInt32(&nLen);
|
||||
if (nLen > 0) → 曲线通道
|
||||
else → SAT 通道
|
||||
```
|
||||
|
||||
只要写入时固定写 `nLen = 0`,**旧版本插件读到新数据也会走 SAT 分支**,能正确还原。
|
||||
|
||||
这意味着:
|
||||
|
||||
- 不需要升 `kCurrentVersionNumber`
|
||||
- 新图纸在老版本 CAD 上不会变 proxy
|
||||
- 老图纸(`nLen > 0`)继续走曲线通道
|
||||
|
||||
对比 `AcDbX3dSolid`:它没有预留位,改成 `m_pSatBuffer` 必须升版本到 5,老插件会 `eMakeMeProxy`。
|
||||
|
||||
### 3.6 代价:体积(字段级估算)
|
||||
|
||||
| 截面 | 曲线通道 | SAT 通道 |
|
||||
| --- | --- | --- |
|
||||
| 矩形(4 条 Line) | ~370 B | 2~4 KB |
|
||||
| 圆形(1 个 Circle) | ~80 B | 2~3 KB |
|
||||
| 20 孔异形 | ~2 KB | 20~50 KB |
|
||||
|
||||
曲线通道的固定开销:每条曲线 `writeString(类名)` ≈ 24 B + `Point3d` × 2 = 48 B + `Vector3d` = 24 B ≈ 92 B。
|
||||
|
||||
按字段级估算,未压缩的 SAT 比曲线通道大 5~20 倍。但 DWG 保存时会对整图做压缩,实际落盘差距远小于此(见 3.7),而且复杂截面(spline 被打碎成数百段)时曲线通道可能反超 SAT。
|
||||
|
||||
**结论:用体积换正确性。** 在图纸数据上这个交换是值得的。
|
||||
|
||||
### 3.7 落盘实测
|
||||
|
||||
> **测量方式**:`accoreconsole` 脚本直接造原生 `REGION` / `SUBTRACT`,**未加载本插件、未创建 `AecDbEwPipeBase` 实例**。
|
||||
> 因此下表只反映「截面数据本身的编码体积与重建耗时」,不含实体其它字段,也不能直接乘出整张图纸的增量。
|
||||
|
||||
**原始 SAT 大小**(`acisOut` 落盘的 ASCII 文本,未压缩):
|
||||
|
||||
| 截面 | 边数 | SAT 字节 | 拟合 |
|
||||
| --- | --- | --- | --- |
|
||||
| 圆环 | 2 | 1138 | ≈ 200 + 420 × 边数 |
|
||||
| 矩形 | 4 | 1548 | 同上 |
|
||||
| 矩形 + 2 孔 | 6 | 2232 | 同上 |
|
||||
| 20 边形 | 20 | 8822 | 同上 |
|
||||
|
||||
**DWG 落盘净增**(同一张图,500 个实体减 100 个实体,差值 ÷ 400):
|
||||
|
||||
| 通道 | 每实体净增 |
|
||||
| --- | --- |
|
||||
| 曲线通道 | ~32 B |
|
||||
| SAT 通道 | 126~160 B(其中 SAT 文本 ~112 B) |
|
||||
|
||||
即:**压缩后 SAT 只比曲线通道大 4~5 倍**,而不是 3.6 表里的 5~20 倍。SAT 是高度结构化的重复 ASCII,DWG 的压缩对它的效果远好于对曲线二进制记录。
|
||||
|
||||
**重建耗时**:
|
||||
|
||||
| 方式 | 单次耗时 |
|
||||
| --- | --- |
|
||||
| `acisIn`(SAT 通道) | 1.88 ms |
|
||||
| 2 × REGION + `SUBTRACT`(曲线通道) | 3.59 ms |
|
||||
|
||||
SAT 约快 1.9 倍。
|
||||
|
||||
**数据可信边界**:
|
||||
|
||||
- 可信:字节随边数的增长关系、两条通道的落盘相对量级、`acisIn` 与布尔运算的耗时比
|
||||
- 不可外推:具体图纸的总体积增量。真实增量 = 每实体截面数 × 截面复杂度 × 上述单值,且实体自身字段也在压缩
|
||||
- 待补:在装了 Envi3D 的 CAD 里,用真实管道实体做一次「新建 N 个 → 存盘 → 对比」的验证
|
||||
|
||||
### 3.8 为什么不分流
|
||||
|
||||
曾考虑过「标准截面(矩形、圆)走参数重建,异形截面走 SAT」,被否掉:
|
||||
|
||||
1. **曲线通道要 `createFromCurves`**:即使曲线是精确参数(不带 spline),重建仍要跑 ACIS 的边界构造 + 多环布尔,实测 3.59 ms/次,比 `acisIn` 慢 1.9 倍。开图时每个管道都要付一次
|
||||
2. **体积省不下来**:落盘压缩后曲线通道只有 ~32 B/实体,省的是 100 B 量级,不值得为它多维护一条分支
|
||||
3. **两条写路径 = 两份 bug**:现有曲线通道的缺陷(写端 spline 只写类名不写参数、读端 `&&`/`||` 写反、布尔未做 SEH 保护)都集中在「曲线 ↔ 几何」这条转换链上。读路径因为老图纸必须留着,但写路径一旦也留着,就会有新数据持续进入这条有缺陷的链
|
||||
4. **版本保护成本**:分流需要升 `kCurrentVersionNumber` 到 8,新图在老 CAD 上变 proxy;统一 SAT 用 `nLen == 0` 就够,不用升版本
|
||||
|
||||
结论:**一条写路径,SAT 到底。** 曲线通道只作为老图纸的只读兼容层存在。
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 4. 优化方案
|
||||
|
||||
### 4.1 第一步:写路径切换
|
||||
|
||||
改 `SectionRegionDwgOutFields:1230`:
|
||||
|
||||
```cpp
|
||||
Acad::ErrorStatus AecDbEwPipeBase::SectionRegionDwgOutFields(AcDbDwgFiler* pFiler, int nVer) const
|
||||
{
|
||||
assertReadEnabled();
|
||||
|
||||
if (m_pRegion == NULL)
|
||||
return Acad::eOk;
|
||||
|
||||
// 统一写 SAT:nLen = 0 是读取端的 SAT 分支标志,旧版本插件同样识别
|
||||
// 曲线通道仅用于读取老图纸,不再产生新数据
|
||||
Adesk::UInt32 nCurs = 0;
|
||||
pFiler->writeUInt32(nCurs);
|
||||
|
||||
return RegionAcisoutDwgOutFields(pFiler);
|
||||
}
|
||||
```
|
||||
|
||||
`dwgOutFields:1348-1364` 的 `m_pRegion == NULL` 分支保持不变(写 0 + `"***"` + 7 字节占位),读取端走 `RegionAcisInDwgInFields`,读到 `"***"` 后 `acisIn` 失败返回 NULL,行为与现在一致。
|
||||
|
||||
**这一步完成后曲线通道的写路径已经不再执行**,`RegionExplodeDwgOutFields` 只剩读路径依赖的 `BaseCurveDwgOutFields`。
|
||||
|
||||
### 4.2 第二步:修 SAT 通道自身缺陷
|
||||
|
||||
按优先级:
|
||||
|
||||
**(1) `errorfile` 分支内存泄漏(`:775-785`)**
|
||||
|
||||
```cpp
|
||||
char* pszTxt = new char[nLen + 1];
|
||||
::memset(pszTxt, 0, nLen + 1);
|
||||
pFiler->readBytes(pszTxt, nLen);
|
||||
|
||||
if (sFileName.CompareNoCase(_T("errorfile")) == 0)
|
||||
{
|
||||
AcDbRegion* pRegion = (AcDbRegion*)AcDbRegion::desc()->create();
|
||||
Acad::ErrorStatus es = pRegion->dwgInFields(pFiler);
|
||||
delete[] pszTxt; // 补上
|
||||
if (es != Acad::eOk) { delete pRegion; return NULL; }
|
||||
return pRegion;
|
||||
}
|
||||
```
|
||||
|
||||
**(2) 临时文件路径收敛(`:788-790`)**
|
||||
|
||||
```cpp
|
||||
// 只取文件名,不接受 DWG 里的路径成分
|
||||
CString sName = sFileName;
|
||||
int nSlash = max(sName.ReverseFind(_T('\\')), sName.ReverseFind(_T('/')));
|
||||
if (nSlash >= 0)
|
||||
sName = sName.Mid(nSlash + 1);
|
||||
if (sName.Find(_T("..")) >= 0)
|
||||
sName = _T("region_tmp.dat");
|
||||
|
||||
CString sFullPath = FilePath::getSystemTemporaryFolder();
|
||||
sFullPath += _T("\\");
|
||||
sFullPath += sName;
|
||||
```
|
||||
|
||||
**(3) 写端文件名加进程号(`:1162`)**
|
||||
|
||||
```cpp
|
||||
sFileName.Format(_T("Region_%lu_%s.dat"), (unsigned long)::GetCurrentProcessId(), szHandleStr);
|
||||
```
|
||||
|
||||
读端从 DWG 里读到的就是带进程号的名字,写到 temp 时再拼回去。这样同图在两个 CAD 实例里同时打开不会互相覆盖。
|
||||
|
||||
注意:这只解决"同时打开",不解决"同一进程内两个文档"。若需要彻底解决,可把句柄 + 文档名(`acdbHostApplicationServices()->workingDatabase()` 的路径哈希)一起编进文件名。
|
||||
|
||||
**(4) 失败日志**
|
||||
|
||||
`RegionAcisInDwgInFields` 的每个失败分支加 `acutPrintf`,带 `nLen`、文件名、`acisIn` 返回值,与 `AcDbX3dSolid` 的日志格式保持一致。
|
||||
|
||||
**(5) `new char[nLen]` 补 `+1`**
|
||||
|
||||
`:775` 和 `:1182` 都补 `+1`,即使当前没有 strlen 依赖,也避免以后被当字符串用时越界。
|
||||
|
||||
### 4.3 第三步:读路径的曲线通道加固
|
||||
|
||||
老图纸必须继续可读,`RegionFromCurvesDwgInFields:663` 的三处要修:
|
||||
|
||||
**(1) `isRegionNull` 的判断条件写反了(`:745`)**
|
||||
|
||||
```cpp
|
||||
// 现在:两个都无效才跳过
|
||||
if (isRegionNull(pRegion) && isRegionNull(pRegionBig))
|
||||
continue;
|
||||
|
||||
// 应该:任意一个无效就跳过
|
||||
if (isRegionNull(pRegion) || isRegionNull(pRegionBig))
|
||||
continue;
|
||||
```
|
||||
|
||||
`isRegionNull` 返回的是 `getAreaProp` 失败(`:640-661`),一个无效一个有效时进 `booleanOper` 正是最容易崩的组合。
|
||||
|
||||
**(2) `booleanOper` 没走 SEH 保护(`:750`)**
|
||||
|
||||
同文件 `:600` 已有 `rawBooleanOperWrapper`,用 `__try/__except` 捕 `0xC0000005`,说明 ACIS 布尔会崩。这里却直接调 `pRegionBig->booleanOper(...)`。
|
||||
|
||||
```cpp
|
||||
es = rawBooleanOperWrapper(pRegionBig, pRegion, AcDb::kBoolSubtract);
|
||||
if (es != Acad::eOk)
|
||||
{
|
||||
acutPrintf(_T("\nAecDbEwPipeBase: 截面布尔失败,err=%d。"), es);
|
||||
}
|
||||
```
|
||||
|
||||
**(3) spline 读端补日志**
|
||||
|
||||
读端 `:562` 的 spline 分支现在什么都不做,直接落到 `return NULL`。至少加一行日志,让老图纸里丢失的边能被发现:
|
||||
|
||||
```cpp
|
||||
AcDbSpline* pSp = AcDbSpline::cast(pBaseCurve);
|
||||
if (pSp != NULL)
|
||||
{
|
||||
acutPrintf(_T("\nAecDbEwPipeBase: 截面边界含 spline,历史数据未保存参数,该边将被丢弃。"));
|
||||
delete pBaseCurve;
|
||||
return NULL;
|
||||
}
|
||||
```
|
||||
|
||||
不要试图"补全" spline 参数:历史数据里本来就没写,读端无从恢复。写端已经不再产生新数据,这条路径只服务老图纸。
|
||||
|
||||
### 4.4 第四步(可选):`m_pRegion` 懒加载
|
||||
|
||||
`dwgInFields:893` 现在无条件 `m_pRegion = SectionRegionDwgInFields(...)`,每个管道实体开图时都要跑一次 `createFromCurves`(ACIS 布尔)或 `acisIn`。
|
||||
|
||||
与 `AcDbX3dSolid` 的 `EnsureCreated()` 同思路:
|
||||
|
||||
```cpp
|
||||
// 头文件
|
||||
mutable bool m_bNeedRegion;
|
||||
AcDbRegion* EnsureRegion() const; // 内部 const_cast 调 ReCreateRegion
|
||||
|
||||
// dwgInFields
|
||||
m_bNeedRegion = true; // 不立即重建
|
||||
|
||||
// 所有读取 m_pRegion 的入口
|
||||
AcDbRegion* AecDbEwPipeBase::GetSection() const
|
||||
{
|
||||
if (m_bNeedRegion)
|
||||
{
|
||||
m_bNeedRegion = false;
|
||||
const_cast<AecDbEwPipeBase*>(this)->ReCreateRegion();
|
||||
}
|
||||
return m_pRegion;
|
||||
}
|
||||
```
|
||||
|
||||
**风险**:`m_pRegion` 在类内被直接访问 20+ 处(`:1539`、`:2759`、`:3104`、`:3358`、`:3898`、`:3910` 等),没有统一的 getter。收口工作量大,漏一个就是空指针或空截面。
|
||||
|
||||
建议放在写路径切换稳定之后再单独做,不要和格式切换混在一个 PR。
|
||||
|
||||
### 4.5 第五步(可选):曲线读路径的最终收敛
|
||||
|
||||
如果确认所有在用图纸的 `nLen > 0` 数据已经过时,可以考虑:
|
||||
|
||||
- 读到时自动升级:`nLen > 0` → 用曲线重建 → 下次存盘写成 SAT
|
||||
- 由于 4.1 的写路径已经固定写 SAT,这一步**不需要额外代码**,打开再保存即完成迁移
|
||||
|
||||
`RegionFromCurvesDwgInFields` 和 `BaseCurveDwgInFields` 仍需保留(`m_pBaseCurve` 也用后者),不能删。
|
||||
|
||||
---
|
||||
|
||||
## 5. 兼容性
|
||||
|
||||
| 场景 | 行为 |
|
||||
| --- | --- |
|
||||
| 老图纸 + 新插件 | `nLen > 0` → 曲线通道,行为不变 |
|
||||
| 新图纸 + 新插件 | `nLen == 0` → SAT 通道 |
|
||||
| 新图纸 + 旧插件(v4+) | `nLen == 0` → SAT 通道,可正常还原 |
|
||||
| 新图纸 + 旧插件(v4 以下) | 不支持,与现状一致(v4 以下本来就只认曲线通道) |
|
||||
|
||||
**不升版本号**,`kCurrentVersionNumber` 保持 7。
|
||||
|
||||
---
|
||||
|
||||
## 6. 风险
|
||||
|
||||
| 风险 | 影响 | 对策 |
|
||||
| --- | --- | --- |
|
||||
| 落盘体积增大约 4 倍(每实体 +100 B 量级) | 大图纸体积略增 | 实测可接受(3.7);不需要自定义压缩 |
|
||||
| `acisOut` 对某些退化 region 失败 | 截面丢失 | 失败时保留 `errorfile` 兜底,并加日志 |
|
||||
| 老图纸里的曲线数据本身有损(spline 丢失) | 重建后的截面与原始不同 | 属于历史数据问题,本次不引入;加日志让问题可见 |
|
||||
| 4.4 懒加载收口遗漏 | 空指针 | 单独 PR,逐处核对 |
|
||||
|
||||
---
|
||||
|
||||
## 7. 验证
|
||||
|
||||
### 7.1 编译
|
||||
|
||||
按项目约定:`R190` + `Debug`,目标 `AecRxBase`、`CoreLibrary_BAL_REMOTE`/`_TRIAL_YEAR`、`CoreCommand_BAL_REMOTE`/`_TRIAL_YEAR`、`CoreImplement`。
|
||||
|
||||
### 7.2 CAD 实测
|
||||
|
||||
1. **新建管道 → 存盘 → 重开**:截面几何、面积、位置一致
|
||||
2. **老图纸**:改动前保存的图,打开后截面正确(走曲线通道)
|
||||
3. **老图纸另存**:打开后直接保存,再重开,截面仍正确(已转成 SAT)
|
||||
4. **混合**:同一张图里既有老数据又有新建管道,存盘重开后都正确
|
||||
5. **复制粘贴**:`copyFrom`(`:1539`)走 `m_pRegion->clone()`,验证不丢截面
|
||||
6. **布尔截面**:带孔的异形截面(多曲线)在曲线通道下的重建结果与 SAT 通道一致
|
||||
|
||||
### 7.3 指标
|
||||
|
||||
已在 3.7 用模拟截面测过「单截面 SAT 字节 / 落盘净增 / `acisIn` 耗时」。仍需在真实数据上补:
|
||||
|
||||
- 同一张典型图改动前后的 DWG 体积(含全部实体字段)
|
||||
- 存盘耗时(对比 `explode` 与 `acisOut`)
|
||||
- 开图耗时(若做了 4.4)
|
||||
|
||||
---
|
||||
|
||||
## 8. 明确不做
|
||||
|
||||
| 项 | 原因 |
|
||||
| --- | --- |
|
||||
| 删除 `RegionFromCurvesDwgInFields` | 老图纸依赖 |
|
||||
| 删除 `BaseCurveDwgInFields` / `BaseCurveDwgOutFields` | `m_pBaseCurve` 在用 |
|
||||
| 补全 spline 的读写参数 | 历史数据里没有,补了也读不回来;写路径已退役 |
|
||||
| 升 `kCurrentVersionNumber` | 格式已预留,不需要 |
|
||||
| 自动迁移老图纸 | 打开再保存即完成迁移,不需要额外代码 |
|
||||
| 给 `AcDbX3dSolid` 增加曲线通道 | 拿不到构建曲线,且曲线通道有上述缺陷 |
|
||||
|
||||
---
|
||||
|
||||
## 9. 实施顺序
|
||||
|
||||
| 步骤 | 内容 | 风险 | 独立 PR |
|
||||
| --- | --- | --- | --- |
|
||||
| 1 | 写路径切换(4.1) | 低 | 是 |
|
||||
| 2 | SAT 通道缺陷修复(4.2) | 低 | 可合并到 1 |
|
||||
| 3 | 曲线读路径加固(4.3) | 低 | 是 |
|
||||
| 4 | 懒加载(4.4) | 中 | 是 |
|
||||
| 5 | 真实管道实测(3.7 待补项) | — | — |
|
||||
|
||||
第 1 步落地后,新产生的数据全部是 SAT,后续所有改动都只影响读路径,风险可控。
|
||||
Reference in new issue
Block a user