兼容方案定为第一项:不升 kCurrentVersionNumber,新版能完整读旧版图纸, 旧版打开新图时 region 类型的 AcDbX3dSolid 几何不可见。 补充说明 m_pSatBuffer 在旧版也会原样读写,所以旧版另存后回新版仍能恢复; 非 region 类型(点串、SubDMesh、管廊脚本)不受影响。 文档开头的原则、4.3.4 的不升版本理由同步。
406 lines
19 KiB
Markdown
406 lines
19 KiB
Markdown
# AcDbX3dSolid 重构计划
|
||
|
||
> 目标:解决 `AcDbX3dSolid` 用 `CString` 保存几何数据(`m_sScr` / `m_sszPoint2ds`)带来的体积翻倍、精度丢失、临时文件冲突和解析开销问题。
|
||
>
|
||
> 范围:`SourceCode/Code2026/CoreLibrary/AcDbX3dSolid.cpp`(5596 行)、`inc/AcDbX3dSolid.h`、`CoreCommand/CoreTools.cpp`。
|
||
>
|
||
> 原则:**单向兼容——新版必须能读老图纸,老版不保证能显示新图的 region 类型几何**(见 11.1);写入统一新格式;不改对象模型(`AcDbX3dSolid` 仍是可平凡复制的数据类)。
|
||
|
||
---
|
||
|
||
## 1. 现状
|
||
|
||
`AcDbX3dSolid` 不是 `AcDbEntity`,是"参数化几何单元",只存生成参数,按需重建 `AcDb3dSolid`。
|
||
|
||
两个 `CString` 承担了超出设计的职责:
|
||
|
||
| 字段 | 实际内容 |
|
||
| --- | --- |
|
||
| `m_sszPoint2ds` | 断面/路径点串、引导线、GSMarker 十进制整数 |
|
||
| `m_sScr` | **ACIS SAT 全文**(region 类,9 种类型)、管廊断面脚本(`kEarthWorm` / `kSHEarthWorm`) |
|
||
|
||
region 类的数据流:
|
||
|
||
```
|
||
AcDbRegion* ──RegionToString()──► %TEMP%\Region1.sat ──读文件──► char* ──ANSI→UTF-16──► CString m_sScr
|
||
│
|
||
dwgOutFields: writeString (2 字节/字符)
|
||
```
|
||
|
||
读取反向:`CString` → UTF-8 → `fixACISFormat` → 写 `%TEMP%\Region2.sat` → `acisIn`。
|
||
|
||
已知问题:
|
||
|
||
| # | 问题 | 位置 |
|
||
| --- | --- | --- |
|
||
| 1 | SAT 是 ASCII,却按 UTF-16 存 DWG,**体积翻倍** | `:4783` `writeString((LPCTSTR)m_sScr)` |
|
||
| 2 | 临时文件名写死(`Region1.sat` / `Region2.sat` / `Region212.sat`),多 CAD 实例并发会互相覆盖 | `CoreCommand/CoreTools.cpp` |
|
||
| 3 | `new char[dwFileLength]` 缺 `+1`,无结尾零字节,`CString` 赋值时越界读;`file.Read` 返回值未检查 | `CoreTools::RegionToString` |
|
||
| 4 | `dwgInFields` 末尾无条件 `ReCreate()`,**开图时每个单元一次临时文件往返** | `:4753` |
|
||
| 5 | 分隔符 `\|`、`@`、`@region@` 无转义、无校验 | 多处 |
|
||
| 6 | `Clone()` 内含 `ReCreate()`,绘制缓存建立时重复重建 | `:5593` + `:295` |
|
||
|
||
---
|
||
|
||
## 2. 已否决的方案
|
||
|
||
| 方案 | 否决原因 |
|
||
| --- | --- |
|
||
| **存构建曲线 + `AcDbRegion::createFromCurves`** | 接口只拿得到 `AcDbRegion*`,可能是复杂布尔结果;`AcDbRegion` 无反向导出边界 API(`dbregion.h` 只有 `getPerimeter`/`getArea`/`getPlane`/`getAreaProp`/`booleanOper`);`explode` 得到的是 ACIS 求交后的边,是近似 |
|
||
| **自写 / 复用 `AcDbDwgFiler` 做内存序列化** | R26 `readString(ACHAR**)` 已 `final`(`arx/R26.0/inc/dbfiler.h:148`),基类实现转发到 `readString(AcString&)`;`CMFCDbDwgFiler` 该函数直接 `return eInvalidInput`,且 `readAddress`/`writeAddress`/`seek` 全空实现。R26 下字符串读取必然失败 |
|
||
| **在 `AcDbX3dSolid` 内持有 `AcDbRegion` 对象,循环调 `dwgInFields`/`dwgOutFields`** | ① `std::vector<AcDbRegion>` 无法实例化:`AcDbObject(const AcDbObject&) = delete`(`dbobject.h:600`);② `dwgOutFields` 不写对象头,但 `AcDbEntity::dwgOutFields` 仍写 `layerId` 等 id → 未入库对象写出悬空引用;③ `Clone()` 必须深拷贝 region,而 region 不能拷贝 → 只能再序列化一遍,绘制路径成本翻倍 |
|
||
| **隐藏 BlockTableRecord / 扩展字典存 region 实体** | `AcDbX3dSolid` 非数据库对象,`Clone()` 是内存拷贝,`AcDbObjectId` 会被共享;改动面扩散到全部持有者 |
|
||
| **SAT 文本 + zlib 压缩** | 工程只有 `inc/zlib.h` 头,`CoreLibrary/unzip.cpp` 是 inflate 半边,无 deflate 实现,`dep_lib` 无 zlib 库;收益低于前三项 |
|
||
|
||
**结论:采用 SAT 的 ASCII 字节流存进 `m_pSatBuffer`。**
|
||
|
||
不改对象模型、不碰 filer、不加新成员、`Clone()` 仍是 `memcpy`。
|
||
|
||
---
|
||
|
||
## 3. 目标存储格式
|
||
|
||
### 3.1 为什么可以复用 `m_pSatBuffer`
|
||
|
||
`m_pSatBuffer` 的读写通道**已经打通并持久化**:
|
||
|
||
```cpp
|
||
// dwgOutFields :4822-4829
|
||
if (AcDbX3dSolid::kCurrentVersionNumber > 1) {
|
||
pFiler->writeUInt32(m_nSatLen);
|
||
if (m_pSatBuffer != NULL)
|
||
pFiler->writeBytes(m_pSatBuffer, m_nSatLen);
|
||
}
|
||
```
|
||
|
||
`dwgInFields` 有对称的 `readUInt32` + `readBytes`。**序列化层零改动**,只需把内容从"SAT 文本的 UTF-16 拷贝"换成"SAT 的 ASCII 字节"。
|
||
|
||
### 3.2 多 region 容器
|
||
|
||
9 种 region 类型里,5 种单 region,3 种双 region,1 种(`kLoft`)N 个 region。统一成一个容器:
|
||
|
||
```cpp
|
||
// 16 字节头
|
||
struct RegionBlobHeader {
|
||
Adesk::UInt32 magic; // 'SRGN' = 0x4E475253
|
||
Adesk::UInt16 version; // 1
|
||
Adesk::UInt16 reserved; // 0
|
||
Adesk::UInt32 nRegions; // region 个数
|
||
Adesk::UInt32 totalLen; // 总字节数(含头),用于跳过
|
||
};
|
||
|
||
// 后接 nRegions 个:
|
||
// Adesk::UInt32 len;
|
||
// char data[len]; // SAT 的 ASCII 字节,不含结尾 0
|
||
```
|
||
|
||
对应关系:
|
||
|
||
| 类型 | nRegions |
|
||
| --- | --- |
|
||
| `kExtrude2`(27) / `kSlicePipe`(26) / `kRevolve`(21) / `kRevolve2`(38) / `kSweep`(40) | 1 |
|
||
| `kConePipe`(30) / `kIntersect`(28) / `kSliceSolid`(29) | 2(外 + 内) |
|
||
| `kLoft`(41) | N |
|
||
|
||
`magic` 是**必需**的,不是可选项:`m_pSatBuffer` 是公共字节池,当前已有 4 种语义(`kFenceExtrude` 点数组、`kFrom3JS` SubDMesh、`kLoftReducer` 宽字符串、SAT 导入原始字节),必须自校验。
|
||
|
||
### 3.3 编解码
|
||
|
||
```cpp
|
||
// AcDbX3dSolid.cpp 匿名 namespace
|
||
bool PackRegions(const AcArray<AcDbRegion*>& regs, void*& pBuf, Adesk::UInt32& nLen);
|
||
bool UnpackRegions(const void* pBuf, Adesk::UInt32 nLen, AcArray<AcDbRegion*>& regs);
|
||
```
|
||
|
||
单 region 的 SAT 字节**不需要新函数**,直接复用已有的两个重载:
|
||
|
||
```cpp
|
||
// 输出 SAT 的 ASCII 字节,返回长度,pszTxt 由调用方 delete[]
|
||
Adesk::UInt64 CoreTools::RegionToString(const AcDbRegion* pReg, char*& pszTxt); // CoreTools.cpp:3114
|
||
// 从字节重建 region
|
||
AcDbRegion* CoreTools::StringToRegion(Adesk::UInt64 nLen, char* pszReg); // CoreTools.cpp:3581
|
||
```
|
||
|
||
`RegionToString(char*&)` 内部已经用 `MakeTempSatPath()` + `DeleteFile`,`StringToRegion` 内部已经过 `fixACISFormat`,阶段一、二落地后这两条链路都是现成的。`PackRegions` 负责把拿到的字节拷进容器并 `delete[]`。
|
||
|
||
---
|
||
|
||
## 4. 改动清单
|
||
|
||
> 阶段一、二已合并;阶段三暂缓,取舍与前提见第 11 节。以下保留完整方案,供重启时参考。
|
||
|
||
### 阶段一:临时文件唯一化(纯 bug 修复)
|
||
|
||
**位置**:`CoreCommand/CoreTools.cpp`
|
||
|
||
| 函数 | 现状 | 改法 |
|
||
| --- | --- | --- |
|
||
| `RegionToString` | `Region1.sat` | 唯一名 + 用完 `DeleteFile` |
|
||
| `StringToRegion(const CString&)` `:3477` | `Region2.sat` | 同上 |
|
||
| `StringToRegion(nLen, char*)` `:3525` | `Region212.sat` | 同上 |
|
||
|
||
唯一名用 `GetTempFileName` 或 `%TEMP%\{GUID}.sat`。
|
||
|
||
**同时修**:
|
||
|
||
```cpp
|
||
char* pszTxt = new char[dwFileLength + 1]; // 补 +1
|
||
::memset(pszTxt, 0, dwFileLength + 1);
|
||
UINT nRead = file.Read(pszTxt, (UINT)dwFileLength); // 检查短读
|
||
if (nRead != (UINT)dwFileLength) { delete[] pszTxt; return false; }
|
||
```
|
||
|
||
**风险**:低。**收益**:消除多实例并发时的几何错乱与越界读。
|
||
|
||
---
|
||
|
||
### 阶段二:懒重建
|
||
|
||
**位置**:`CoreLibrary/AcDbX3dSolid.cpp` / `inc/AcDbX3dSolid.h`
|
||
|
||
| 位置 | 现在 | 改成 |
|
||
| --- | --- | --- |
|
||
| `dwgInFields` 末尾 `:4753` | `ReCreate();` | `m_bNeedCreate = true;` |
|
||
| 拷贝构造末尾 `:295` | `ReCreate();` | `m_bNeedCreate = true;` |
|
||
| `inc/AcDbX3dSolid.h:255` | `AcDb3dSolid* m_pSolid;` | 加 `mutable` |
|
||
| `GetDraw3dSolid() const` `:3852` | — | 入口调 `EnsureCreated()` |
|
||
|
||
```cpp
|
||
mutable bool m_bNeedCreate; // 不序列化,默认 true
|
||
void EnsureCreated() { if (m_bNeedCreate) { m_bNeedCreate = false; ReCreate(); } }
|
||
```
|
||
|
||
**必须一起改的入口**(凡是读 `m_pSolid` 或依赖已建几何的):
|
||
|
||
- `GetInterface()`(`:3542` 附近)
|
||
- `TransformBy`(`:5496` 起)
|
||
- `GetDrawSubDMesh()`(`:3893`)
|
||
- `ToJson`
|
||
- `GetDraw2d()`(`:3933`)
|
||
|
||
**风险**:中。漏一个入口会出现"几何没建出来"。**收益**:开图不再逐单元走临时文件;`Clone()` 里的 `ReCreate()` 也一并省掉。
|
||
|
||
---
|
||
|
||
### 阶段三:region 数据二进制化
|
||
|
||
#### 4.3.1 写入点(`RegionToString` → `PackRegions`)
|
||
|
||
行号为当前 main(阶段一、二落地后),实施时以函数名定位。
|
||
|
||
| 行 | 类型 / 函数 | 现状 |
|
||
| --- | --- | --- |
|
||
| `:1371` | `kExtrude2` | `RegionToString(pReg, m_sScr)` |
|
||
| `:1436`、`:1439` | `kConePipe` | 外/内 → `sScr1`/`sScr2` → 拼 `m_sScr` |
|
||
| `:2041` | `kSlicePipe` | → `m_sScr` |
|
||
| `:2127` | `kIntersect` | → `sScr1` → `m_sScr` |
|
||
| `:2181` | `kSliceSolid` | → `sSrc1` → `m_sScr` |
|
||
| `:2211` | `kRevolve` | → `m_sScr` |
|
||
| `:2242` | `kRevolve2` | → `m_sScr` |
|
||
| `:5029` | `kLoft` | `strAll`(`@region@` 拼接)→ `m_sScr` |
|
||
| `:5087`、`:5101` | `kSweep` | → `m_sScr` |
|
||
| `:4882`、`:4895` | `SetSolidData` | `m_sScr = srcRegion` |
|
||
| `:5135`、`:5155`、`:5176`、`:5206` | `SetExturd2Data` / `SetRevolve2Data` / `SetSweepData` / `SetLoftData` | `m_sScr = sReg` |
|
||
| `:5516`、`:5528`、`:5542`、`:5562`、`:5589` | `TransformBy` | 变换后重新导出 |
|
||
|
||
#### 4.3.2 读取点(`StringToRegion(m_sScr)` → `UnpackRegions`)
|
||
|
||
| 行 | 类型 / 函数 |
|
||
| --- | --- |
|
||
| `:4366` | `kSlicePipe` |
|
||
| `:4378` | `kExtrude2` |
|
||
| `:4402` | `kIntersect` |
|
||
| `:4440` | `kSliceSolid` |
|
||
| `:4468` | `kRevolve` |
|
||
| `:4488`、`:4489` | `kConePipe` |
|
||
| `:4563` | `kRevolve2` |
|
||
| `:4587` | `kLoft`(循环) |
|
||
| `:4601` | `kSweep` |
|
||
| `:5321`、`:5345`、`:5370`、`:5453` | `ToJson` |
|
||
| `:5512`、`:5524`、`:5538`、`:5558`、`:5584` | `TransformBy` |
|
||
|
||
#### 4.3.3 序列化
|
||
|
||
**不改**。`m_pSatBuffer` 的 `writeUInt32` + `writeBytes` 已经在 `:4822-4829`,`dwgInFields` 有对称读。
|
||
|
||
只改一件事:region 类型写入时**不再写 `m_sScr`**(保持空字符串),管廊脚本类型不受影响。
|
||
|
||
#### 4.3.4 兼容判断
|
||
|
||
```cpp
|
||
bool HasNewRegionData() const {
|
||
if (!m_pSatBuffer || m_nSatLen < sizeof(RegionBlobHeader)) return false;
|
||
return ((const RegionBlobHeader*)m_pSatBuffer)->magic == kRegionBlobMagic;
|
||
}
|
||
```
|
||
|
||
读取:
|
||
|
||
```cpp
|
||
if (HasNewRegionData())
|
||
UnpackRegions(m_pSatBuffer, m_nSatLen, regs); // 新格式
|
||
else if (!m_sScr.IsEmpty())
|
||
ParseLegacyRegions(m_sScr, regs); // 老图纸,沿用现有切分逻辑
|
||
```
|
||
|
||
写入:只写新格式。
|
||
|
||
**不升 `kCurrentVersionNumber`**。理由:字段都在,靠"哪个非空"即可判别;升版本会让老版本 CAD 读新图时走 `eMakeMeProxy`,新图在所有老版本上彻底不可编辑。代价是老版本看不到 region 类型的几何,这是已定的取舍,见 11.1。
|
||
|
||
#### 4.3.5 `kLoftReducer` 纳入统一容器
|
||
|
||
`CreateLoftReducer`(`:3015`)当前已把多 region 串写进 `m_pSatBuffer`(`:3118-3121`),格式是 `TCHAR` 宽字符 + `TCHAR(0x00c1)` 分隔 —— 属于半迁移状态,体积没减半。
|
||
|
||
本次一并纳入统一容器:
|
||
|
||
- 写入:`PackRegions` 替代 `:3118-3121` 的 TCHAR 拷贝
|
||
- 读取:`GetLoftReducerSides`(`:4309` 附近)先验 `magic`;命中走 `UnpackRegions`,未命中回退到现有的 `0x00c1` 切分(老图纸)
|
||
- `kLoftReducer` 的 `m_sszPoint2ds` 存"下口|上口"点串(`:3090-3092`),与 region 容器互不影响,保持不变
|
||
|
||
#### 4.3.6 `m_sScr` 的去留
|
||
|
||
`kEarthWorm`(7) 与 `kSHEarthWorm`(23) **均已废弃**,不再产生新数据,但存量图纸仍需可读。
|
||
|
||
**本次保留 `m_sScr` 字段不删**,仅停止写入:
|
||
|
||
- 写入:region 类型不再写 `m_sScr`(保持空字符串)
|
||
- 读取:`kEarthWorm` / `kSHEarthWorm` 的脚本分支保留,保证老图纸可读
|
||
- 字段本身、`dwgOutFields` / `dwgInFields` 的 `writeString` / `readString` 调用全部保留
|
||
|
||
后续等存量图纸淘汰后,再单独清理字段和读写调用。
|
||
|
||
---
|
||
|
||
## 5. 公开接口处理
|
||
|
||
以下接口签名是 `CString`,外部有调用方(`AecDbBDPipeBase.cpp` 6 处、`Pool3dCmd/DTYGDlg.cpp` 1 处、`Pool3dCmd/DTYG_MODIFY.cpp` 8 处):
|
||
|
||
```cpp
|
||
bool GetExturd2Data(CString& sReg, AcGePoint3d& ptFr, AcGePoint3d& ptTo); // :5106
|
||
void SetExturd2Data(const CString& sReg, const AcGePoint3d&, const AcGePoint3d&); // :5115
|
||
bool GetRevolve2Data(CString& sReg, AcGePoint3d&, AcGeVector3d&, double&, double&); // :5126
|
||
void SetRevolve2Data(const CString& sReg, ...); // :5137
|
||
bool GetSweepData(CString& sReg, AcDbCurve*& pCurve); // :5150
|
||
void SetSweepData(const CString& sReg, const AcDbCurve* pCurve); // :5158
|
||
```
|
||
|
||
**保持签名不变**。内部实现改为:
|
||
|
||
- `SetXxxData`:把传入的文本按老分隔符解析成 region 数组 → `PackRegions`
|
||
- `GetXxxData`:`UnpackRegions` → 拼回原有文本格式返回
|
||
|
||
`CString` 无法可靠承载二进制(含 `\0` 会截断),所以对外仍是"文本 SAT"语义,转换只发生在这一层,调用方无感。这些是对话框级低频调用,开销可接受。
|
||
|
||
`SetSatBuffer` / `GetSatBuffer`(`:4971` / `:4980`)是公开裸接口,外部 `Pool3dCmd/DTYGDlg.cpp:805/4371` 在用。它们操作的是 SubDMesh 类数据,与 region 不冲突,靠 `magic` 隔离即可。
|
||
|
||
---
|
||
|
||
## 6. 风险清单
|
||
|
||
| # | 风险 | 影响 | 应对 |
|
||
| --- | --- | --- | --- |
|
||
| 1 | `m_pSatBuffer` 语义冲突 | 把点数组当 region 容器解析,崩溃 | 强制 `magic` 校验,不匹配一律走老路径 |
|
||
| 2 | 漏改某个 `ReCreate` 入口 | 几何不显示 | 阶段二逐入口核对,用 `EnsureCreated()` 统一收口 |
|
||
| 3 | 老图纸读取回归 | 存量图纸打不开 | 保留 `ParseLegacyRegions`,不做自动迁移 |
|
||
| 4 | `kLoft` 的 `@region@` 分隔 | 多 region 顺序错乱 | 容器改成"数量 + 长度前缀",顺序显式 |
|
||
| 5 | 跨模块链接 | `CoreTools::RegionToBuffer` 在 CoreCommand,CoreLibrary 是静态库 | 已有 `CoreTools::RegionToString` 先例,照现有模式 |
|
||
| 6 | 新旧格式并存 | 代码里长期保留两条读路径 | 可接受;等存量图纸淘汰后再清理 |
|
||
|
||
---
|
||
|
||
## 7. 验证清单
|
||
|
||
编译:仅 `R190` + `Debug`,目标 `AecRxBase`、`CoreLibrary_BAL_REMOTE`/`_TRIAL_YEAR`、`CoreCommand_BAL_REMOTE`/`_TRIAL_YEAR`、`CoreImplement`。
|
||
|
||
功能(必须在 CAD 内实测):
|
||
|
||
1. **新建 + 存盘 + 重开**:9 个 region 类型的几何与改前一致(对比 `getArea`、体积、`getGeomExtents`)
|
||
2. **老图纸兼容**:打开改动前保存的图,走 `m_sScr` 路径,几何正确
|
||
3. **移动 / 旋转**:`kExtrude2`、`kRevolve`、`kRevolve2`、`kLoft`、`kSweep` 变换后几何跟着动(`TransformBy` 的 5 处往返)
|
||
4. **复制粘贴 / 撤销**:验证 `Clone()` 路径(`AecDbXDraw::add3DCache`)不崩、不丢几何
|
||
5. **对话框往返**:`DTYGDlg`、`DTYG_MODIFY` 的 `GetXxxData`/`SetXxxData` 行为不变
|
||
6. **并发**:同时开两个 CAD 实例打开含 region 的图,几何无错乱(验证阶段一)
|
||
|
||
指标:
|
||
|
||
- 开图耗时(阶段二)
|
||
- 存盘后 DWG 体积(阶段三,预期 region 部分减半)
|
||
- `Clone()` 耗时(阶段二后应显著下降)
|
||
|
||
---
|
||
|
||
## 8. 明确不做
|
||
|
||
| 项 | 原因 |
|
||
| --- | --- |
|
||
| 存构建曲线 + `createFromCurves` | 拿不到原始曲线,布尔结果不可逆 |
|
||
| 自写 `AcDbDwgFiler` | R26 `readString(ACHAR**)` 已 `final` |
|
||
| `AcDbX3dSolid` 持有 `AcDbRegion` 对象 | 拷贝构造 `= delete`,`Clone()` 退化 |
|
||
| 隐藏 BTR / 扩展字典 | 对象模型改动过大,`AcDbObjectId` 共享问题 |
|
||
| zlib 压缩 | 工程无 deflate 实现,收益低于前三项 |
|
||
| 修 `CMFCDbDwgFiler` 的 R26 字符串问题 | 该类已不再使用 |
|
||
| 补 `ToJson` 的 `kEarthWorm` 分支 | `kEarthWorm` 已废弃 |
|
||
| 删除 `m_sScr` 字段 | `kEarthWorm` / `kSHEarthWorm` 已废弃,但存量图纸仍需可读;本次保留字段,后续单独清理 |
|
||
| 自动迁移老图纸 | 任何一次保存都会改写图纸数据,风险过高 |
|
||
|
||
---
|
||
|
||
## 9. 实施顺序
|
||
|
||
| 顺序 | 阶段 | 改动量 | 风险 | 收益 |
|
||
| --- | --- | --- | --- | --- |
|
||
| 1 | 一:临时文件唯一化 | 小 | 低 | 消除并发错乱、越界读 |
|
||
| 2 | 二:懒重建 | 中 | 中 | 开图性能(最大) |
|
||
| 3 | 三:region 二进制化 | 大 | 中高 | DWG 体积减半、去编码转换 |
|
||
|
||
每个阶段独立提交、独立 PR,前一阶段验证通过后再进下一阶段。
|
||
|
||
---
|
||
|
||
## 10. 已确认事项
|
||
|
||
| # | 事项 | 结论 |
|
||
| --- | --- | --- |
|
||
| 1 | `kLoftReducer`(22) 是否纳入统一容器 | **纳入**(4.3.5),老数据回退 `0x00c1` 切分 |
|
||
| 2 | `kSHEarthWorm`(23) 是否仍在使用 | **已废弃**,但 `m_sScr` 字段本次保留不删,仅停止写入(4.3.6) |
|
||
| 3 | 公开 `CString` 接口 | **签名不变**,内部转换,外部调用方零改动(第 5 节) |
|
||
|
||
---
|
||
|
||
## 11. 阶段三暂缓(2026-10-22)
|
||
|
||
**决定:暂不实施。** 阶段一(临时文件唯一化、越界读)、阶段二(懒重建)已合并,主要痛点已经解决;阶段三剩下的收益只有「region 部分 DWG 体积减半」,代价是新图在老版本插件里 region 几何不可见。当前时机未到。
|
||
|
||
### 11.1 兼容方向(已定)
|
||
|
||
**单向兼容**:新版插件完整读取旧版图纸(`m_sScr` 老路径永久保留);旧版插件打开新图时,region 类型的 `AcDbX3dSolid` 几何不可见。
|
||
|
||
不可见不等于丢数据:`m_pSatBuffer` 老版本也会原样读写(版本 > 1 的通道一直都在),所以旧版打开后另存、再回新版打开,几何仍能恢复。非 region 类型(`m_sszPoint2ds` 点串、SubDMesh、管廊脚本)不受影响。
|
||
|
||
| 选项 | 后果 | 结论 |
|
||
| --- | --- | --- |
|
||
| 不升版本号,接受老插件缺几何 | 老插件能开图、参数都在,region 类型几何不显示;换新版可恢复 | **已定,选这个** |
|
||
| 升到 5,老插件显式 proxy | 不会静默缺几何,但新图在所有老版本上彻底不可编辑,且不可回退 | 不选 |
|
||
| 保留双写(`m_sScr` 仍写老格式) | 体积变成 2N + N,比现状更差 | 否决 |
|
||
|
||
### 11.2 重启前还要定的事
|
||
|
||
1. `TransformBy`、`ToJson` 是否一起改——这两处在运行时会反复导出/解析 SAT,是阶段三改动量的大头
|
||
2. 老图纸里 region 类型的实际占比——占比低的话体积收益有限,可以永久搁置
|
||
|
||
### 11.3 实施时的技术前提(已核对)
|
||
|
||
- `kCurrentVersionNumber` 当前是 **4**。`m_pSatBuffer` 的读写通道在 `dwgOutFields:4822` / `dwgInFields:4754`,版本 > 1 即生效,阶段三不需要动序列化
|
||
- **不需要**新增 `CoreTools::RegionToBuffer`:`RegionToString(const AcDbRegion*, char*&)`(`CoreTools.cpp:3114`)已经输出 SAT 的 ASCII 字节和长度,内部用 `MakeTempSatPath` + `DeleteFile`
|
||
- 读端 `StringToRegion(Adesk::UInt64, char*)`(`CoreTools.cpp:3581`)已经过 `fixACISFormat`
|
||
- `m_pSatBuffer` 当前有 4 种语义:`kFenceExtrude` 点数组(`:1514`)、`kFrom3JS` SubDMesh(`:1019`/`:1048`)、`kLoftReducer` 宽字符串(`:3132`/`:5405`)、SAT 导入原始字节(`:2872`)。容器头的 magic 是必需的
|
||
- 拷贝构造对 `m_pSatBuffer` 做了深拷贝(`:274-280`),`Clone()` 不会共享指针
|
||
|
||
### 11.4 当前状态
|
||
|
||
| 阶段 | 状态 | PR |
|
||
| --- | --- | --- |
|
||
| 一:临时文件唯一化 | 已合并 | #22、#26 |
|
||
| 二:懒重建 | 已合并 | #25、#27 |
|
||
| 三:region 二进制化 | 暂缓 | — |
|