# 空轨支持方案

## 一、需求分析

### 1.1 核心需求

1. **新增空轨属性**：在物流属性系统中增加"空轨"类型
2. **空轨路径生成**：
   - 空轨走向固定，只需设置起点和终点
   - 自动提取空轨几何体的下表面中心线作为路径
3. **路径类型区分**：路径需要标记为"地面路径"或"空轨路径"
4. **动画运行模式**：
   - 地面路径：物体中心点在路径点上
   - 空轨路径：物体悬挂在空轨下方运行（物体中心 = 路径点 - 物体高度/2）
5. **碰撞检测**：
   - 统一使用3D碰撞检测
   - 空轨路径需要排除空轨本身
6. **限制条件**：空轨暂时只支持人工路径，不支持自动路径规划

### 1.2 使用场景

- **地面路径**：转运车在楼面、走廊、通道上运行
- **空轨路径**：悬挂式输送设备在空轨上运行

### 1.3 已确认信息

1. ✅ **空轨模型**：已存在，不需要建模规范
2. ✅ **空轨类型**：不需要分类型，只有一个"空轨"类型
3. ✅ **物体悬挂**：物体悬挂在空轨下方
4. ✅ **碰撞检测**：需要排除空轨本身
5. ✅ **数据库迁移**：不需要，新字段使用默认值
6. ✅ **向后兼容**：不需要向后兼容性
7. ✅ **碰撞检测统一**：地面路径和空轨路径都使用3D碰撞检测
8. ✅ **轨道几何**：轨道不是水平的，需要精确计算几何体数据

---

## 二、架构影响分析

### 2.1 涉及的核心模块

| 模块 | 影响程度 | 改动内容 |
|------|---------|---------|
| **物流属性系统** | 🟢 低 | 新增"空轨"枚举值 |
| **路径数据模型** | 🟡 中 | 新增路径类型字段 |
| **路径规划系统** | 🟡 中 | 新增空轨路径生成逻辑 |
| **动画系统** | 🟡 中 | 根据路径类型调整物体位置 |
| **碰撞检测系统** | 🟢 低 | 空轨路径排除空轨本身 |
| **UI界面** | 🟡 中 | 路径类型选择、空轨路径生成 |

---

## 三、详细实施方案

### 3.1 第一阶段：基础数据结构（1天）

#### 3.1.1 新增空轨属性

**文件**：`CategoryAttributeManager.cs`

```csharp
public enum LogisticsElementType
{
    // ... 现有类型 ...
    
    /// <summary>
    /// 空轨 - 空中运输路径，物体悬挂运行，权重0.7
    /// </summary>
    空轨 = 9,
}
```

#### 3.1.2 路径类型枚举

**文件**：`PathPlanningModels.cs`

```csharp
/// <summary>
/// 路径类型
/// </summary>
public enum PathType
{
    /// <summary>
    /// 地面路径 - 物体在地面运行
    /// </summary>
    Ground = 0,
    
    /// <summary>
    /// 空轨路径 - 物体悬挂在空轨下方运行
    /// </summary>
    Rail = 1,
}
```

#### 3.1.3 路径数据模型扩展

**文件**：`PathRouteViewModel.cs`

```csharp
private PathType _pathType = PathType.Ground;

public PathType PathType
{
    get => _pathType;
    set => SetProperty(ref _pathType, value);
}
```

#### 3.1.4 路径数据库适配

**文件**：`PathDatabase.cs`

```csharp
// 保存路径时，保存路径类型
public void SavePath(PathRouteViewModel path)
{
    // ... 现有逻辑 ...
    // 新增：保存路径类型
}

// 加载路径时，读取路径类型
public PathRouteViewModel LoadPath(string pathId)
{
    // ... 现有逻辑 ...
    // 新增：读取路径类型
}
```

---

### 3.2 第二阶段：空轨路径生成（2天）

#### 3.2.1 复用现有几何计算

**现有代码复用**：

- `ChannelHeightDetector.AnalyzeChannelGeometry()` - 分析通道几何
- `ChannelHeightDetector.CalculatePreciseHeightAtPosition()` - 计算精确高度
- `GeometryHelper.ExtractTriangles()` - 提取三角形
- `GeometryHelper.RayTriangleIntersect()` - 射线-三角形交点检测

#### 3.2.2 新增底部高度检测方法

**文件**：`ChannelHeightDetector.cs`

新增方法：

- `GetChannelBottomHeight(Point3D position, IEnumerable<ModelItem> channelItems)` - 获取通道底面高度
- `CalculateBottomPreciseHeightAtPosition(Point3D position, ChannelHeightInfo heightInfo, ModelItem channel)` - 计算底面精确高度
- `TryRaycastFromBelow(Point3D position, ModelItem channel, double floorHeight, out double height)` - 从下方射线投射
- `PerformRayTriangleIntersectionFromBelow(Point3D position, List<Triangle3D> triangles)` - 从下往上射线-三角形交点检测
- `TryGetGeometricBottomHeight(Point3D position, ModelItem channel, out double height)` - View API获取底面高度
- `AnalyzeBottomSurfaceHeightAtPosition(Point3D position, ModelItem channel, double floorHeight, double ceilingHeight)` - 分析底面表面高度
- `TryMultiPointBottomSampling(Point3D position, ModelItem channel)` - 多点采样获取底面高度

#### 3.2.3 空轨中心线提取工具

**文件**：`RailGeometryHelper.cs`（新建）

```csharp
/// <summary>
/// 空轨几何体辅助工具
/// 负责提取空轨的下表面中心线
/// </summary>
public static class RailGeometryHelper
{
    /// <summary>
    /// 从空轨几何体提取下表面中心线
    /// </summary>
    /// <param name="railModel">空轨模型项</param>
    /// <param name="startPoint">起点（2D坐标）</param>
    /// <param name="endPoint">终点（2D坐标）</param>
    /// <returns>路径点列表（包含精确的Z坐标）</returns>
    public static List<Point3D> ExtractRailCenterLine(
        ModelItem railModel, 
        Point3D startPoint, 
        Point3D endPoint)
    {
        var pathPoints = new List<Point3D>();
        
        try
        {
            LogManager.Debug($"[空轨] 开始提取空轨中心线: {railModel.DisplayName}");
            
            // 1. 创建高度检测器实例
            var heightDetector = new ChannelHeightDetector();
            var railItems = new List<ModelItem> { railModel };
            
            // 2. 计算起点和终点在X-Y平面上的投影距离
            double distance2D = Math.Sqrt(
                Math.Pow(endPoint.X - startPoint.X, 2) + 
                Math.Pow(endPoint.Y - startPoint.Y, 2)
            );
            
            if (distance2D < 0.01)
            {
                LogManager.Warning("[空轨] 起点和终点重合");
                return pathPoints;
            }
            
            // 3. 在起点和终点之间采样，计算每个采样点的精确Z坐标
            int sampleCount = Math.Max(2, (int)Math.Ceiling(distance2D / 0.5));  // 每0.5米一个采样点
            
            for (int i = 0; i < sampleCount; i++)
            {
                double t = (double)i / (sampleCount - 1);
                
                // 线性插值计算X、Y坐标
                double x = startPoint.X + t * (endPoint.X - startPoint.X);
                double y = startPoint.Y + t * (endPoint.Y - startPoint.Y);
                double z = startPoint.Z;  // 初始Z坐标
                
                // 调用现有的GetChannelBottomHeight方法获取精确底面高度
                var samplePoint = new Point3D(x, y, z);  // Z坐标会被方法更新
                var bottomHeight = heightDetector.GetChannelBottomHeight(samplePoint, railItems);
                
                pathPoints.Add(new Point3D(x, y, bottomHeight));
            }
            
            LogManager.Debug($"[空轨] 生成 {pathPoints.Count} 个路径点，总长度: {distance2D:F2}m");
        }
        catch (Exception ex)
        {
            LogManager.Error($"[空轨] 提取空轨中心线失败: {ex.Message}");
        }
        
        return pathPoints;
    }
}
```

#### 3.2.4 路径编辑器扩展

**文件**：`PathEditingViewModel.cs`

新增方法：

- `GeneratePathFromRail(ModelItem railModel, Point3D startPoint, Point3D endPoint)` - 从空轨生成路径
- `SelectRailModelCommand` - 选择空轨命令
- `SetPathStartPointCommand` - 设置起点命令
- `SetPathEndPointCommand` - 设置终点命令
- `GeneratePathFromRailCommand` - 生成路径命令

新增属性：

- `SelectedPathType` - 选中的路径类型
- `PathTypes` - 路径类型数组
- `IsRailPathType` - 是否为空轨路径类型
- `SelectedRailModel` - 选中的空轨模型
- `SelectedRailModelName` - 选中的空轨模型名称
- `PathStartPoint` - 路径起点
- `PathStartPointDisplay` - 路径起点显示
- `PathEndPoint` - 路径终点
- `PathEndPointDisplay` - 路径终点显示

---

### 3.3 第三阶段：动画系统适配（1天）

#### 3.3.1 物体位置计算

**文件**：`PathAnimationManager.cs`

```csharp
/// <summary>
/// 计算物体在路径点上的位置
/// </summary>
private Point3D CalculateObjectPosition(Point3D pathPoint, PathType pathType)
{
    if (pathType == PathType.Rail)
    {
        // 空轨路径：物体悬挂在空轨下方
        // 物体中心点 = 空轨点 - 物体高度的一半
        double objectCenterZ = pathPoint.Z - _virtualObjectHeight / 2;
        return new Point3D(pathPoint.X, pathPoint.Y, objectCenterZ);
    }
    else
    {
        // 地面路径：物体中心点在路径点上
        return pathPoint;
    }
}
```

#### 3.3.2 传递路径类型

**文件**：`PathAnimationManager.cs`

修改方法：

- `GenerateAnimationFrames(List<Point3D> pathPoints, PathType pathType)` - 传递路径类型参数
- `PrecomputeCollisions(List<Point3D> pathPoints, PathType pathType, List<ModelItem> railModels)` - 传递路径类型和空轨模型列表
- `DetectFrameCollisions(AnimationFrame frame, PathType pathType, List<ModelItem> collisionExclusions)` - 传递路径类型和排除列表

---

### 3.4 第四阶段：碰撞检测适配（1天）

#### 3.4.1 空轨排除逻辑

**文件**：`PathAnimationManager.cs`

修改方法：

- `PrecomputeCollisions(List<Point3D> pathPoints, PathType pathType, List<ModelItem> railModels)` - 新增空轨排除列表
- `DetectFrameCollisions(AnimationFrame frame, PathType pathType, List<ModelItem> collisionExclusions)` - 使用排除列表过滤

**核心逻辑**：

```csharp
// 创建碰撞排除列表
var collisionExclusions = new List<ModelItem>();

// 如果是空轨路径，将空轨模型加入排除列表
if (pathType == PathType.Rail && railModels != null)
{
    collisionExclusions.AddRange(railModels);
    LogManager.Info($"[碰撞检测] 已排除 {railModels.Count} 个空轨模型");
}

// 在碰撞检测循环中跳过排除列表中的项目
foreach (var modelItem in allModelItems)
{
    // 跳过排除列表中的项目（包括空轨本身）
    if (collisionExclusions.Contains(modelItem))
    {
        continue;
    }
    
    // 统一使用3D碰撞检测
    double distance = BoundingBoxGeometryUtils.CalculateDistance(objectBoundingBox, modelBoundingBox);
    
    if (distance <= 0)  // 相交或接触
    {
        collisions.Add(new CollisionResult
        {
            Item1 = _animatedObject,
            Item2 = modelItem,
            Distance = distance
        });
    }
}
```

---

### 3.5 第五阶段：UI界面适配（1天）

#### 3.5.1 路径类型选择

**文件**：`PathEditingViewModel.cs`

新增属性：

- `PathTypes` - 路径类型数组
- `SelectedPathType` - 选中的路径类型
- `IsRailPathType` - 是否为空轨路径类型

#### 3.5.2 命令

**文件**：`PathEditingViewModel.cs`

新增命令：

- `GeneratePathFromRailCommand` - 从空轨生成路径命令
- `SelectRailModelCommand` - 选择空轨命令
- `SetPathStartPointCommand` - 设置起点命令
- `SetPathEndPointCommand` - 设置终点命令

#### 3.5.3 UI布局

**文件**：`LogisticsControlPanel.xaml`

新增控件：

- 路径类型选择 ComboBox
- 空轨选择 StackPanel（仅在选择空轨类型时显示）
  - 选择空轨按钮
  - 设置起点按钮
  - 设置终点按钮
  - 生成路径按钮

---

## 四、实施步骤

### 第一阶段：基础数据结构（1天）

1. ✅ 新增"空轨"物流属性类型
2. ✅ 新增路径类型枚举
3. ✅ 扩展路径数据模型
4. ✅ 适配数据库读写

### 第二阶段：空轨路径生成（2天）

1. ✅ 在 `ChannelHeightDetector` 中新增底部高度检测方法
2. ✅ 复用现有的几何计算代码
3. ✅ 实现 `RailGeometryHelper`（调用现有方法）
4. ✅ 扩展路径编辑器
5. ✅ UI界面适配
6. ✅ 集成测试

### 第三阶段：动画系统适配（1天）

1. ✅ 物体位置计算适配
2. ✅ 传递路径类型参数
3. ✅ 动画播放测试

### 第四阶段：碰撞检测适配（1天）

1. ✅ 实现空轨排除逻辑
2. ✅ 统一使用3D碰撞检测
3. ✅ 碰撞报告适配
4. ✅ 集成测试

### 第五阶段：测试和优化（1天）

1. ✅ 功能测试
2. ✅ 性能测试
3. ✅ 用户验收测试

**总计**：6天

---

## 五、风险评估

| 风险项 | 风险等级 | 缓解措施 |
|--------|---------|---------|
| 空轨几何体提取失败 | 🟢 低 | 空轨模型已存在，直接使用 |
| 物体悬挂位置计算错误 | 🟡 中 | 充分测试，提供可视化验证 |
| 碰撞检测误报 | 🟡 中 | 排除空轨本身，调整检测参数 |
| 性能下降 | 🟢 低 | 空轨路径数量少，影响有限 |
| 几何计算性能 | 🟡 中 | 复用现有代码，优化采样策略 |

---

## 六、总结

### 6.1 方案优势

- ✅ **代码复用最大化**：充分利用现有的几何计算代码
- ✅ **架构统一**：空轨和地面路径使用相同的高度检测框架
- ✅ **改动最小化**：只需新增底部检测方法，其他全部复用
- ✅ **易于维护**：几何计算逻辑集中管理
- ✅ **向后兼容**：不需要迁移，新字段使用默认值
- ✅ **碰撞检测统一**：统一使用3D检测，简化逻辑

### 6.2 关键技术点

1. **复用现有代码**：`ChannelHeightDetector` 的几何计算方法
2. **新增底部检测**：`GetChannelBottomHeight()` 方法
3. **物体悬挂计算**：物体中心 = 空轨点 - 物体高度/2
4. **碰撞检测统一**：统一使用3D检测
5. **排除空轨本身**：通过排除列表过滤空轨模型
6. **精确几何计算**：支持倾斜、弯曲的轨道

### 6.3 技术难点

- 需要处理大量三角形面片（性能优化）
- 需要处理重叠三角形（选择最高点）
- 需要处理采样点不在任何三角形内的情况（使用默认值）

### 6.4 预期成果

- ✅ 支持空轨路径生成
- ✅ 支持物体悬挂运行
- ✅ 支持空轨碰撞检测（排除空轨本身）
- ✅ 保持与现有地面路径功能的兼容性

---

## 七、附录

### 7.1 文件清单

#### 新增文件

- `RailGeometryHelper.cs` - 空轨几何体辅助工具

#### 修改文件

- `CategoryAttributeManager.cs` - 新增空轨枚举
- `PathPlanningModels.cs` - 新增路径类型枚举
- `PathRouteViewModel.cs` - 新增路径类型字段
- `PathDatabase.cs` - 适配路径类型读写
- `ChannelHeightDetector.cs` - 新增底部高度检测方法
- `PathEditingViewModel.cs` - 扩展路径编辑功能
- `PathAnimationManager.cs` - 适配物体位置和碰撞检测
- `LogisticsControlPanel.xaml` - UI界面适配

### 7.2 方法清单

#### 新增方法（ChannelHeightDetector.cs）

- `GetChannelBottomHeight(Point3D position, IEnumerable<ModelItem> channelItems)` - 获取通道底面高度
- `CalculateBottomPreciseHeightAtPosition(Point3D position, ChannelHeightInfo heightInfo, ModelItem channel)` - 计算底面精确高度
- `TryRaycastFromBelow(Point3D position, ModelItem channel, double floorHeight, out double height)` - 从下方射线投射
- `PerformRayTriangleIntersectionFromBelow(Point3D position, List<Triangle3D> triangles)` - 从下往上射线-三角形交点检测
- `TryGetGeometricBottomHeight(Point3D position, ModelItem channel, out double height)` - View API获取底面高度
- `AnalyzeBottomSurfaceHeightAtPosition(Point3D position, ModelItem channel, double floorHeight, double ceilingHeight)` - 分析底面表面高度
- `TryMultiPointBottomSampling(Point3D position, ModelItem channel)` - 多点采样获取底面高度

#### 新增方法（RailGeometryHelper.cs）

- `ExtractRailCenterLine(ModelItem railModel, Point3D startPoint, Point3D endPoint)` - 提取空轨中心线

#### 新增方法（PathEditingViewModel.cs）

- `GeneratePathFromRail(ModelItem railModel, Point3D startPoint, Point3D endPoint)` - 从空轨生成路径

#### 新增方法（PathAnimationManager.cs）

- `CalculateObjectPosition(Point3D pathPoint, PathType pathType)` - 计算物体位置

#### 修改方法（PathAnimationManager.cs）

- `GenerateAnimationFrames(List<Point3D> pathPoints, PathType pathType)` - 传递路径类型参数
- `PrecomputeCollisions(List<Point3D> pathPoints, PathType pathType, List<ModelItem> railModels)` - 传递路径类型和空轨模型列表
- `DetectFrameCollisions(AnimationFrame frame, PathType pathType, List<ModelItem> collisionExclusions)` - 传递路径类型和排除列表

---