# 真实物体位姿来源迁移草案 ## 1. 背景 当前真实物体位姿链路中,`fragment` 承担了两种不同语义: - 读取真实物体的**参考姿态** - 间接参与判断真实物体的**当前显示姿态** 这两个语义不应混在一起。 当前工程已确认: - `ModelItem.Transform` 不能默认视为真实物体当前姿态或原始姿态 - `fragment` 代表姿态适合做“参考姿态解释” - `ModelGeometry.ActiveTransform` 更适合做“当前实际显示姿态”读取 因此,本次迁移目标不是“简单移除 fragment”,而是: 1. 先把**参考姿态来源**与**当前显示变换来源**拆开 2. 优先把“当前显示姿态读取”切到准确变换 API 3. 保留 fragment 参考姿态链作为稳定基线 4. 在测试和 shadow 验证足够充分后,再评估参考姿态主来源是否切换 --- ## 2. 当前问题 ### 2.1 语义混用 当前真实物体链路同时关心: - 物体原始业务姿态是什么 - 物体此刻在 Navisworks 里实际显示成什么姿态 - 增量变换叠加后,应以哪一层为“当前状态” 如果这三者没有统一入口,就容易出现: - 起点姿态正确,但播放过程漂移 - `ResetPermanentTransform` 后内部状态与宿主状态不同步 - 通行空间、碰撞恢复、动画终点使用了不同的姿态基线 ### 2.2 fragment 并不是“当前姿态 API” fragment 的 `GetLocalToWorldMatrix()` 更接近几何层矩阵。 它适合: - 统计代表姿态 - 解释真实物体的原始参考框架 - 参与 COM 几何分析 它不应优先承担: - 当前 override 后显示姿态读取 - 动画增量变换后的宿主状态读回 ### 2.3 直接回退 `ModelItem.Transform` 风险很高 对于很多 Revit/复合件导入模型: - `ModelItem.Transform` 可能是单位旋转 - 不反映当前 override 后姿态 - 不足以表达真实物体的业务参考姿态 所以迁移不能走“fragment -> ModelItem.Transform”的单步替换。 --- ## 3. 迁移目标 ### 3.1 核心目标 建立两条明确分离的来源链: - **当前显示变换链** - 用于读取当前实际显示姿态 - 优先使用 `ModelGeometry.ActiveTransform` - **参考姿态链** - 用于表达真实物体的原始业务姿态 - 当前仍以 fragment 解释链为稳定主来源 ### 3.2 非目标 本阶段不做以下事情: - 不重写 `Ground / Hoisting / Rail` 的姿态求解器 - 不直接移除现有 fragment 参考姿态链 - 不修改 Canonical / Host / Asset 三层坐标语义 - 不在没有验证前,把 `ModelItem.Transform` 升级为真实姿态主来源 --- ## 4. 目标架构 ### 4.1 新的职责划分 建议新增两类 provider: #### A. `ICurrentDisplayTransformProvider` 职责: - 读取当前几何实际显示姿态 - 读取当前几何实际旋转 - 读取当前几何原始姿态与 override 后姿态 建议首个实现: - `GeometryActiveTransformProvider` 优先数据源: 1. `ModelGeometry.ActiveTransform` 2. `ModelGeometry.PermanentOverrideTransform` 3. `ModelGeometry.OriginalTransform` 4. 明确失败,不偷偷 fallback 到不可靠语义 #### B. `IReferencePoseProvider` 职责: - 提供真实物体参考姿态 - 输出解释后的 `Rotation + AxisX/Y/Z` - 对上层屏蔽具体来源是 fragment 还是 geometry 建议实现顺序: 1. `FragmentReferencePoseProvider` - 复用当前 `RealObjectReferencePoseResolver` 2. `GeometryReferencePoseProvider` - 后续用于 shadow 验证 - 初期不接主链 ### 4.2 上层调用关系 建议上层只依赖语义接口: - `PathAnimationManager` - 读取参考姿态时,只依赖 `IReferencePoseProvider` - 读取当前宿主实际姿态时,只依赖 `ICurrentDisplayTransformProvider` - `ModelItemTransformHelper` - 统一封装 `ActiveTransform / OriginalTransform / delta transform` - 成为“当前显示变换链”的基础工具层 - `RealObjectPlanarPoseSolver` - `CanonicalRailPoseBuilder` - 保持纯数学求解,不感知来源替换 --- ## 5. 分阶段迁移方案 ### 阶段 0:冻结语义与测试基线 目标: - 不改正式行为 - 先把关键业务场景和期望值固定下来 产出: - 参考姿态语义测试 - 当前显示姿态读取测试 - 增量变换恢复测试 - `YUp / ZUp` 双宿主测试矩阵 通过条件: - 现有 fragment 主链全部通过基线测试 ### 阶段 1:抽象来源接口,但不改行为 目标: - 只做代码结构调整 - 不改变当前正式结果 建议动作: - 新增 `ICurrentDisplayTransformProvider` - 新增 `IReferencePoseProvider` - 让 `PathAnimationManager` 从直接依赖 helper/fragment,改为依赖 provider - 默认配置仍然使用 `FragmentReferencePoseProvider` 通过条件: - 行为无变化 - 回归测试全绿 ### 阶段 2:迁移“当前显示姿态读取” 目标: - 把“当前姿态读取”切到准确变换 API 建议动作: - 将以下读当前姿态的场景统一迁到 `ModelGeometry.ActiveTransform` - 当前旋转读回 - 当前显示姿态调试日志 - override 后的实际姿态验证 - 动画恢复/碰撞恢复前的状态同步 注意: - 这一阶段不改“参考姿态”来源 - 只改“当前实际显示变换”的读取 通过条件: - 起点/播放/暂停/终点/恢复行为与当前主链一致 - `ResetPermanentTransform` 后内部跟踪状态不再依赖 `ModelItem.Transform` ### 阶段 3:引入 `GeometryReferencePoseProvider` shadow 验证 目标: - 并行验证 geometry 是否能稳定替代 fragment 参考姿态 建议动作: - 新增几何级参考姿态读取实现 - 不接正式主链 - 只在日志/调试模式下输出与 fragment 基线的差异 建议记录差异: - quaternion 夹角差 - `AxisX / AxisY / AxisZ` 与基线的夹角差 - 多 geometry 是否一致 - `YUp / ZUp` 下解释后是否仍满足宿主语义 判定规则建议: - 若 geometry 内部姿态不一致,则自动判定“不适合作为语义参考姿态主来源” - 若与 fragment 基线夹角超阈值,则继续保留 fragment 主来源 ### 阶段 4:按路径类型逐步接入 目标: - 在证据足够充分后,才让新的参考姿态来源参与正式链路 建议接入顺序: 1. `Rail` 2. `Ground` 3. `Hoisting` 原因: - `Rail` 当前姿态框架最明确,收益最大 - `Ground / Hoisting` 对“对象前进轴语义”更敏感 通过条件: - `Rail 0°` 基线不变 - Ground 逐帧姿态不退化 - 通行空间与真实物体仍共用同一尺寸语义 --- ## 6. 建议新增/调整的代码结构 ### 6.1 建议新增文件 - `src/Utils/CoordinateSystem/ICurrentDisplayTransformProvider.cs` - `src/Utils/CoordinateSystem/IReferencePoseProvider.cs` - `src/Utils/CoordinateSystem/GeometryActiveTransformProvider.cs` - `src/Utils/CoordinateSystem/FragmentReferencePoseProvider.cs` - `src/Utils/CoordinateSystem/GeometryReferencePoseProvider.cs` ### 6.2 建议优先调整的现有文件 - `src/Core/Animation/PathAnimationManager.cs` - 只依赖 provider - 去掉对 fragment/helper 的直接耦合 - `src/Utils/ModelItemTransformHelper.cs` - 成为统一的当前显示变换读取入口 - `src/Utils/CoordinateSystem/RealObjectReferencePoseResolver.cs` - 收敛为 fragment provider 的内部实现 - 不再直接被业务层广泛调用 ### 6.3 不建议本阶段改动的文件 - `CanonicalPlanarPoseBuilder` - `CanonicalRailPoseBuilder` - `CanonicalTrackedPositionResolver` - `RailPathPoseHelper` 原因: - 这些文件主要负责姿态求解与偏移语义 - 不是本次来源迁移的根因层 --- ## 7. 测试先行策略 ### 7.1 测试原则 本次迁移必须遵循: 1. 先补测试 2. 再抽象接口 3. 再切换读取来源 4. 最后才考虑正式接入 ### 7.2 第一批必须补的测试 #### A. 参考姿态基线测试 建议位置: - `UnitTests/CoordinateSystem/RealObjectReferencePoseResolverTests.cs` 锁定内容: - fragment 多片段平均后的代表姿态 - `Fragment默认Up` 为 `Y/Z` 时的解释结果 - `YUp / ZUp` 宿主下输出轴语义一致 #### B. 当前显示姿态读取测试 建议新增: - `UnitTests/CoordinateSystem/CurrentDisplayTransformProviderTests.cs` 锁定内容: - `ActiveTransform` 优先级 - `OriginalTransform / PermanentOverrideTransform / ActiveTransform` 的语义区分 - override 后读取结果与预期一致 #### C. 动画增量恢复测试 建议新增: - `UnitTests/CoordinateSystem/IncrementalTransformRestoreTests.cs` 锁定内容: - reset 后状态同步 - 当前跟踪姿态与宿主状态一致 - 不再错误依赖 `ModelItem.Transform` #### D. 业务回归测试 建议补到现有测试中: - `Ground + 真实物体` - `Rail + 真实物体` - `YUp / ZUp` - 起点 / 逐帧 / 终点 / restore ### 7.3 第二批 shadow 验证测试 建议新增: - `UnitTests/CoordinateSystem/GeometryReferencePoseProviderTests.cs` 锁定内容: - 单 geometry 对象能否稳定导出参考姿态 - 多 geometry 姿态不一致时是否正确拒绝 - geometry 基线与 fragment 基线差异是否在阈值内 --- ## 8. 验收标准 ### 8.1 阶段 1-2 验收 - 正式行为无肉眼可见退化 - 所有已有姿态回归测试通过 - `PathAnimationManager` 不再把“当前姿态读取”和“参考姿态来源”混用 ### 8.2 阶段 3 验收 - 能输出 geometry vs fragment 的稳定差异报告 - 能区分“可替代对象”和“不可替代对象” ### 8.3 阶段 4 验收 - `Rail 0°` 基线不变 - `Ground / Hoisting` 逐帧行为不退化 - 通行空间、碰撞恢复、终点诊断不出现新的语义分叉 --- ## 9. 当前推荐执行顺序 建议下一步按下面顺序推进: 1. 先补测试,不接正式代码 2. 抽 provider 接口,不改默认实现 3. 先切“当前显示姿态读取” 4. 做 geometry 参考姿态 shadow 验证 5. 证据足够后,再讨论 fragment 主来源是否退出 --- ## 10. 当前结论 本次迁移的关键不是“删掉 fragment”,而是: - 先把语义拆开 - 先把准确 API 用在该用的地方 - 先用测试固定业务场景 - 再用 shadow 验证去证明新的参考姿态来源是否真的可靠 在当前阶段,**最稳的路线**是: - fragment 继续作为真实物体参考姿态主来源 - `ModelGeometry.ActiveTransform` 接管当前显示姿态读取 - 测试先行 - 分阶段接入