# 真实物体位姿来源迁移草案

## 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` 接管当前显示姿态读取
- 测试先行
- 分阶段接入

