From bca461812502ab6d8f8606c83e6a032c8396cee1 Mon Sep 17 00:00:00 2001 From: company-desktop-computer Date: Thu, 22 Oct 2026 15:48:48 +0800 Subject: [PATCH] =?UTF-8?q?docs:=2013.2=20=E6=8C=89=E5=AE=9E=E9=99=85?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E6=9B=B4=E6=96=B0=EF=BC=8Cm=5FbNeedCreate=20?= =?UTF-8?q?=E6=87=92=E5=8A=A0=E8=BD=BD=20+=20Create=20=E4=BF=9D=E6=8C=81?= =?UTF-8?q?=E5=BC=BA=E5=88=B6=E9=87=8D=E5=BB=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AcDbX3dSolid-refactor-plan.md | 52 +++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/AcDbX3dSolid-refactor-plan.md b/AcDbX3dSolid-refactor-plan.md index 7fee39d..2c7ff22 100644 --- a/AcDbX3dSolid-refactor-plan.md +++ b/AcDbX3dSolid-refactor-plan.md @@ -561,39 +561,47 @@ void SaveLegacyDwg() ## 13. 开图速度优化方案(2026-10-22) -### 13.1 现状与成本模型 +### 13.1 现状与成本模型(改造前) - `AecDbX3dPartEntity::dwgInFields:1824` 在开图时无条件调用 `m_pPart->Create()`。全库扫描 `dwgInFields`,立即重建的只有这一处 - `AecDbX3dPart::Create()`(`:786`)先删掉 `m_pSolid`,再对每个 unite 成员调 `GetDraw3dSolid()`(`EnsureCreated` + `clone` + 旋转),逐个 `booleanOper(kBoolUnite)`,再跑 subtract 列表,最后整体旋转/平移、设颜色、把 `m_mapXData` 写成 `BIMDATA` XData - 单个 part 的成本约 N×(acisIn + clone) + (N−1)×布尔。N 大、布尔对象复杂时是非线性增长,这正是「复杂几何由 boolean 得到」的场景 - `Create()` **没有「已是最新」判断**,每次调用都全量重建;`AddX3dSolid`(`:151`)只 append、不置脏,所以调用方只能靠再调一次 `Create()` 保证几何同步 -### 13.2 方案 A:脏标记 + 懒创建(推荐,不改存储格式) +### 13.2 方案 A:懒加载标志 + 按需重建(已实现,PR #43) -分两步,可以分开提交。 +不改存储格式。分两步:A1 引入懒加载标志,A2 开图不建几何。 -**A1 让 `Create()` 幂等** +**A1 `m_bNeedCreate` + `EnsureCreated()`** | 位置 | 改动 | | --- | --- | -| `inc/AecDbX3dPart.h:140` 附近 | 增加 `bool m_bDirty;`,构造(`:50`)置 `true` | -| `Create()` `:786` | 开头 `if (!m_bDirty && m_pSolid != NULL) return true;`;进入构建前先 `m_bDirty = false`,写法同 `AcDbX3dSolid::EnsureCreated`(`:4620`),避免重入时重复构建 | -| 改参数的接口 | 置 `m_bDirty = true`:`AddX3dSolid:151`、`RemoveAt:504`、`RemoveAll3dSolidRec:468`、`SetOrgPoint:170`、`SetAxis:582`、`SetBoolOperFor:766`、`SetHideShow:602`、`SetHide:608`、`SetActive:639`、`PeelOffTheSkin:623`、`AddMove:644`/`:655`、`ResetMove:671`、`AddRotate:682`、`ResetRotate:705`、`CopyFrom:1255` | -| 新增 | `bool EnsureCreated() const`:`m_bDirty` 时 `const_cast` 调 `Create()` | +| `inc/AecDbX3dPart.h:144` | 增加 `mutable bool m_bNeedCreate;`。`mutable` 是为了让 `const` 读入口(`CreateDraw3dSolid`、`Draw3d`、`getOsnapPoints`、`GetForHouseSubtract`)能触发重建 | +| `inc/AecDbX3dPart.h:74` | 新增 `bool EnsureCreated() const;` | +| `inc/AecDbX3dPart.h:121` | 新增 `protected: bool Rebuild();`(无条件重建布尔结果) | +| 构造 `:51` / 拷贝构造 `:64` | 置 `true` | +| `Create()` `:802` | **保持强制重建语义**,直接 `return Rebuild();` | +| `Rebuild()` `:809` | 开头先 `m_bNeedCreate = false;` 再 `DELETE_PTR(m_pSolid)` 重建,写法同 `AcDbX3dSolid::EnsureCreated`(`:4620`),避免构建过程中再次触发 `EnsureCreated` 造成递归 | +| `EnsureCreated()` `:929` | `if (!m_bNeedCreate && m_pSolid != NULL) return true;` 否则 `const_cast` 调 `Rebuild()` | +| 置脏接口(15 处) | `AddX3dSolid:155`、`SetOrgPoint:175`、`RemoveAll3dSolidRec:474`、`RemoveAt:511`、`SetAxis:590`、`SetHide:617`、`PeelOffTheSkin:633`、`SetActive:650`、`AddMove:656`、`AddMove(matrix):668`、`ResetMove:685`、`AddRotate:697`、`ResetRotate:721`、`SetBoolOperFor:783`、`CopyFrom:1300` | + +`SetHideShow()` 不单独置脏——它内部转调 `SetHide()`,已覆盖。 + +`Create()` 为什么不做幂等早退:外部有 10 处是「先 `GetX3dSolid(0)` 拿到 x3dsolid 直接改(如 `AecDbBDPipeBase.cpp:941` 调 `TransformBy`),再调 `Create()`」。这类改动部件无法感知,`m_bNeedCreate` 仍是 `false`,若 `Create()` 早退就会返回旧几何。所以显式 `Create()` 一律重建,只有 `EnsureCreated()` 条件重建。 现状里 `AddMove`、`AddRotate` 等已经 `DELETE_PTR(m_pSolid)` 做失效,改成置脏后语义一致,额外好处是能区分「已失效但还没人要」和「已重建」。 **A2 开图不建几何** -- `AecDbX3dPartEntity::dwgInFields:1824` 去掉 `m_pPart->Create()`,由 `AecDbX3dPart::dwgInFields` 结束时置脏 +- `AecDbX3dPartEntity::dwgInFields:1823` 去掉 `m_pPart->Create()`,由 `AecDbX3dPart::dwgInFields` 结束时(`:1233`)置脏 - 读入口统一先 `EnsureCreated()`: | 入口 | 位置 | | --- | --- | -| `CreateDraw3dSolid()` | `AecDbX3dPart.cpp:940` | -| `Draw3d()` | `:969` | -| `getOsnapPoints()` | `:1097` | -| `GetForHouseSubtract()` | `:903` | +| `GetForHouseSubtract()` | `AecDbX3dPart.cpp:939` | +| `CreateDraw3dSolid()` | `:978` | +| `Draw3d()` | `:1009` | +| `getOsnapPoints()` | `:1145` | | `AecDbX3dPartEntity::Draw3d()` | `AecDbX3dPartEntity.cpp:1755`,经 `CreateDraw3dSolid` 已覆盖,确认即可 | `AecDbBDPipeBase.cpp:329` 那种「`CreateDraw3dSolid()` 返回空就 `Create()` 再试」的兜底可以删掉,`EnsureCreated` 已经包含在 `CreateDraw3dSolid` 里。 @@ -602,14 +610,13 @@ void SaveLegacyDwg() - 关闭/冻结图层、视口外、XREF 附着、审核、发布等不绘制的场景:完全省掉 - 全图都在视口内:开图时间下降,首次 regen 补回来,总时间基本持平 -- A1 单独也有收益:所有「改完参数就 `Create()`」的交互路径不再重复全量重建 **风险** -- `Create()` 有副作用:整体变换、`setColorIndex`、`m_mapXData.Write(m_pSolid, "BIMDATA")`(`:865-898`)。延迟后这些也延迟,凡是在建好之前读 solid 的代码都必须经过 `EnsureCreated` -- unite 列表为空时 `Create()` 返回 false 且 `m_pSolid == NULL`;`DTYG_TYPE` 且 `Hide` 的分支直接 `return true` 且不建体(`:792-799`)。`EnsureCreated` 的判定只看 `m_bDirty`,不能因为 `m_pSolid == NULL` 反复重试 -- `Create()` 非 const,`EnsureCreated` 需要 `const_cast` -- `CopyFrom:1255` 里对每个 x3dsolid 调了 `ReCreate()`,可以一起删掉——`AcDbX3dSolid` 已有 `m_bNeedCreate`,懒加载会处理 +- `Create()` 有副作用:整体变换、`setColorIndex`、`m_mapXData.Write(m_pSolid, "BIMDATA")`(`:903-924`)。延迟后这些也延迟,凡是在建好之前读 solid 的代码都必须经过 `EnsureCreated` +- unite 列表为空时 `Create()` 返回 false 且 `m_pSolid == NULL`;`DTYG_TYPE` 且 `Hide` 的分支直接 `return true` 且不建体(`:792-799`)。`EnsureCreated` 的判定同时看 `m_bNeedCreate` 和 `m_pSolid != NULL`,两个都为假才早退,不会因为 `m_pSolid == NULL` 反复重试 +- `Create()` 非 const,`EnsureCreated` 需要 `const_cast`,因此标志本身必须 `mutable` +- `CopyFrom:1321-1337` 里对每个 x3dsolid 仍调了 `ReCreate()`。`AcDbX3dSolid` 已有 `m_bNeedCreate`,这段可以删掉,属可选清理,本次未动 ### 13.3 方案 B:缓存布尔结果(改格式,暂不推荐) @@ -622,7 +629,7 @@ void SaveLegacyDwg() 体积上,布尔结果的 SAT 通常比输入参数之和大一个量级,复杂 part 可能到几百 KB,会打破「图纸大小可接受」这个前提。 -结论:先做 A,量出开图耗时里布尔占多少,再决定 B 值不值。 +结论:A 已落地(13.2),量出开图耗时里布尔占多少,再决定 B 值不值。 ### 13.4 方案 C:降低单次重建成本(可选) @@ -636,8 +643,7 @@ void SaveLegacyDwg() - 场景:全图可见 / 一半图层冻结 / 缩放到局部 / 作为 XREF 附着 - 判据:开图时长下降;首次 regen 的增量不超过开图省下的量;移动、改参数等交互不退化 -### 13.6 建议顺序 +### 13.6 进度与后续 -1. A1(`m_bDirty` + `Create()` 早退):改动小,先量交互路径的收益 -2. A2(`dwgInFields` 去掉 `Create()`):开图收益 -3. 量完再决定 B / C +1. ~~A1(`m_bNeedCreate` + `EnsureCreated()`)~~、~~A2(`dwgInFields` 去掉 `Create()`)~~:已实现,PR #43 +2. 待做:按 13.5 度量真实图纸的开图耗时,确认收益后再决定 B / C