# 自动调整物体姿态以最小化通行截面投影面积

更新时间：2026-05-27

## 1. 背景

动画检测中的“调整物体”窗口目前支持手工输入 X/Y/Z 三轴角度修正。用户希望增加一个“自动调整”按钮：对任意动画物体，在路径起点处自动寻找一个合适姿态，让物体经过路径上的门洞、狭窄通道时具有最大通过性。

Navisworks 当前主要提供 AABB 包围盒，真实物体的 fragment 代表姿态只能大概判断方向，不足以作为自动调整的精确依据。因此本功能不能依赖 fragment 推断“物体真实方向”，也不能只做绕宿主 up 轴的单轴旋转。单轴 yaw 调整手工即可完成，不满足目标。

## 2. 目标

在“调整物体”窗口增加自动调整能力：

- 支持三轴联动搜索，允许任意三维旋转。
- 允许物体横放、倒放、斜放，不施加运输姿态限制。
- 以路径起点方向为基准，最小化物体在门洞截面上的投影面积。
- 在投影面积相同或接近时，优先选择投影高度更小的姿态，让物体尽量平放。
- 结果写回现有 X/Y/Z 三个角度输入框，用户可继续手工微调。
- 不依赖 fragment 方向作为优化真值。
- 遵守宿主坐标系 / 内部坐标系 / 资产坐标系语义，不硬编码世界 Z 为 up。

## 3. 非目标

- 不在本阶段判断真实门洞尺寸，也不保证某条路径一定可通过。
- 不修改路径规划算法。
- 不重建物体 mesh 或 OBB。
- 不引入姿态限制，例如“保持底面朝下”或“禁止倒置”。
- 不用静默 fallback 掩盖优化失败；失败应明确提示。

## 4. 坐标系语义

UI 输入、日志、用户看到的 X/Y/Z 角度仍按宿主坐标系解释。

路径起点方向使用宿主坐标系点计算：

1. 取路径起点到下一个有效路径点的方向。
2. 剔除宿主 up 分量，得到水平路径前进方向 `HostPathForward`。
3. 通过 `HostCoordinateAdapter` 获取宿主 up：`HostUp`。
4. 截面横向方向为 `HostSide = normalize(cross(HostPathForward, HostUp))`。

如果路径起始段过短或剔除 up 后方向退化，应继续查找后续有效路径段；若仍无法得到方向，则自动调整失败并提示用户。

## 5. 目标函数

自动调整的主目标函数是门洞截面投影面积：

```text
Score(rotation) = ProjectedWidthAcrossPath(rotation) * ProjectedHeightAlongHostUp(rotation)
```

其中：

- `ProjectedWidthAcrossPath`：物体在 `HostSide` 方向上的投影宽度。
- `ProjectedHeightAlongHostUp`：物体在 `HostUp` 方向上的投影高度。
- 路径前进方向上的长度不参与目标函数。

该目标符合“穿门”语义：门洞主要限制的是横向净宽和竖向净高，而不是物体沿路径方向占多长。

在工程现场中，门洞通常“宽度大于高度”，因此仅比较面积不够。两个姿态可能具有相同或非常接近的面积，但一个姿态更高、更窄，另一个姿态更低、更宽；实际通行时应优先选择更低、更平放的姿态。

因此最终排序应采用词典序或近似平局策略：

1. 先最小化 `ProjectedArea`。
2. 当面积差异小于容差时，选择 `ProjectedHeightAlongHostUp` 更小的姿态。
3. 当面积和高度都接近时，再选择 `ProjectedWidthAcrossPath` 更小的姿态。

面积容差建议使用相对容差，例如 `1%`，避免因为 AABB 或浮点误差导致高度明显更优的姿态被面积微小差异淘汰。

## 6. 推荐算法

采用业界常见的黑盒无导数优化路线：

### 6.1 全局粗搜索

在完整三维旋转空间中生成候选姿态，避免只绕某一轴或只优化 yaw。

候选生成应满足：

- 覆盖三维姿态空间。
- 结果确定性，不依赖随机数。
- 粗搜索粒度可配置，默认优先保证交互速度。

可选实现：

- 均匀方向采样加 roll 角采样。
- Fibonacci sphere / icosphere 风格方向采样。
- 固定欧拉角网格作为初始版本，但需要避免只覆盖少量手工角度。

### 6.2 局部精修

对粗搜索评分最好的若干候选执行无导数局部优化，例如：

- Powell method。
- Nelder-Mead simplex。

Navisworks AABB 目标函数不平滑，也不可求导，因此不使用梯度法。局部优化的终止条件应包含：

- 最大迭代次数。
- 角度步长阈值。
- 评分改善阈值。

### 6.3 输出角度

优化器内部可用四元数表示候选姿态，最终需要转换为当前窗口使用的宿主 X/Y/Z 三轴角度修正，并写回：

- `RotationXDegrees`
- `RotationYDegrees`
- `RotationZDegrees`

角度顺序必须遵守项目当前约束：X -> Y -> Z，对应 `qz * qy * qx`。

## 7. 评估方式

第一阶段建议实现为纯几何优化器：

- 输入物体三轴尺寸。
- 输入宿主坐标系下的 `HostPathForward` / `HostSide` / `HostUp`。
- 输入候选旋转。
- 输出截面投影宽度、高度、面积。

这样可以用单元测试充分锁定算法，不需要启动 Navisworks。

真实物体的 fragment 代表姿态不作为优化真值。若后续发现数学预测与 Navisworks 实际 AABB 存在明显偏差，再增加“慢速精确模式”：

1. 保存当前动画物体状态。
2. 临时应用候选姿态到路径起点。
3. 读取 Navisworks 更新后的 AABB。
4. 计算截面投影面积。
5. 恢复状态。

慢速模式必须复用 `PathAnimationManager` / `ModelItemTransformHelper` 的现有姿态链路，不能绕过主链路直接写入临时补丁。

## 8. UI 行为

“调整物体”窗口增加一个“自动调整”按钮。

建议行为：

1. 用户打开“调整物体”窗口。
2. 点击“自动调整”。
3. 系统根据当前路径起点方向、当前物体尺寸和宿主坐标系执行优化。
4. 将最优 X/Y/Z 角度写入输入框。
5. 用户可继续修改角度。
6. 点击“确认”后沿用现有 `ObjectStartPlacementRequest.CreateRotationCorrection(...)` 链路。

自动调整失败时，窗口不关闭，并显示明确错误：

- 未选择路径。
- 路径点不足。
- 起点方向退化。
- 未选择动画物体。
- 物体尺寸无效。
- 优化器未找到有效姿态。

## 9. 测试计划

新增纯几何单元测试，建议放在 `UnitTests/CoordinateSystem/`。

必须覆盖：

- 长方体通过三轴旋转得到比初始姿态更小的截面面积。
- 三轴优化能找到单轴 yaw 无法达到的更小截面面积。
- `YUp` 和 `ZUp` 下 `HostUp` 高度语义正确。
- 路径横向宽度和宿主 up 高度共同参与目标函数。
- 面积接近时优先选择投影高度更小的平放姿态。
- 任意旋转允许倒置，不强制保持原 up 方向。
- 优化结果确定性：同一输入多次运行输出一致。
- 退化路径方向会失败而不是回退默认方向。

后续接入 UI 后，补充 ViewModel 或窗口级测试：

- 点击自动调整后会更新 X/Y/Z 输入值。
- 自动调整后确认仍走现有角度修正请求。
- 失败时保留原输入值。

## 10. 实现落点

候选落点：

- `src/Utils/CoordinateSystem/ObjectPassageProjectionOptimizer.cs`
- `UnitTests/CoordinateSystem/ObjectPassageProjectionOptimizerTests.cs`
- `src/UI/WPF/Views/EditRotationWindow.xaml`
- `src/UI/WPF/Views/EditRotationWindow.xaml.cs`
- `src/UI/WPF/ViewModels/AnimationControlViewModel.cs`

如果需要从 ViewModel 提供路径和尺寸上下文，可新增一个小的请求对象，避免窗口直接依赖大型 ViewModel。

## 11. 风险与注意事项

- Euler 角存在多解和万向节锁风险；优化内部应优先用四元数，Euler 只作为 UI 输出格式。
- AABB 只能近似真实几何，通过性结果应理解为基于包围盒的保守估计。
- 对真实物体，fragment 仅可作为现有动画链路的弱参考，不作为自动调整的优化真值。
- 对 Ground / Hoisting / Rail 的路径姿态链路不得引入旧 yaw fallback。
- 若候选姿态会改变落位补偿，必须同步更新通行空间尺寸和起点预览。
