# 坐标系动态适配设计方案

## 问题背景

客户模型坐标系与插件默认坐标系不同：
- **插件默认**: Z-up (Z轴向上, X向右, Y向后)
- **客户模型**: Y-up (Y轴向上, X向右, Z向前)

这导致网格生成、高度检测、碰撞检测、路径渲染等功能出现问题。

---

## 架构设计

### 整体架构

```
┌─────────────────────────────────────────────────────────────┐
│                    业务逻辑层                                │
│  (PathPlanning, Collision Detection, Animation, etc.)       │
├─────────────────────────────────────────────────────────────┤
│                    坐标系抽象层                              │
│  ┌─────────────────┐  ┌─────────────────┐                   │
│  │  ICoordinateSystem │  │ CoordinateSystemManager │         │
│  └─────────────────┘  └─────────────────┘                   │
├─────────────────────────────────────────────────────────────┤
│                    具体实现层                                │
│  ┌───────────────┐  ┌───────────────┐  ┌─────────────────┐  │
│  │  ZUpCoordinateSystem   │  │  YUpCoordinateSystem   │  │...│
│  └───────────────┘  └───────────────┘  └─────────────────┘  │
└─────────────────────────────────────────────────────────────┘
                            │
                    ┌───────┴───────┐
                    ▼               ▼
            [Navisworks API]   [自定义逻辑]
```

---

## 核心组件实现

### 1. 坐标系类型枚举

**文件**: `src/Utils/CoordinateSystem/CoordinateSystemType.cs`

```csharp
namespace NavisworksTransport.Utils.CoordinateSystem
{
    /// <summary>
    /// 支持的坐标系类型
    /// </summary>
    public enum CoordinateSystemType
    {
        /// <summary>
        /// Z轴向上 (标准Navisworks坐标系: Z-up, X-right, Y-back)
        /// </summary>
        ZUp,
        
        /// <summary>
        /// Y轴向上 (常见于Revit等: Y-up, X-right, Z-front)
        /// </summary>
        YUp,
        
        /// <summary>
        /// 自动检测
        /// </summary>
        AutoDetect
    }

    /// <summary>
    /// 轴定义
    /// </summary>
    public enum Axis
    {
        Horizontal1,    // X轴对应（通常是Right）
        Horizontal2,    // Z/Y轴对应（通常是Front/Back）
        Vertical        // Y/Z轴对应（通常是Up）
    }
}
```

---

### 2. 坐标系接口

**文件**: `src/Utils/CoordinateSystem/ICoordinateSystem.cs`

```csharp
using Autodesk.Navisworks.Api;

namespace NavisworksTransport.Utils.CoordinateSystem
{
    /// <summary>
    /// 坐标系接口 - 抽象不同坐标系的差异
    /// </summary>
    public interface ICoordinateSystem
    {
        /// <summary>
        /// 坐标系类型
        /// </summary>
        CoordinateSystemType Type { get; }
        
        /// <summary>
        /// 获取向上轴的索引 (0=X, 1=Y, 2=Z)
        /// </summary>
        int UpAxisIndex { get; }
        
        /// <summary>
        /// 获取水平面主轴索引 (通常是X)
        /// </summary>
        int PrimaryHorizontalAxisIndex { get; }
        
        /// <summary>
        /// 获取水平面次轴索引
        /// </summary>
        int SecondaryHorizontalAxisIndex { get; }
        
        /// <summary>
        /// 获取点的高度值（统一抽象）
        /// </summary>
        double GetElevation(Point3D point);
        
        /// <summary>
        /// 设置点的高度值，返回新点
        /// </summary>
        Point3D SetElevation(Point3D point, double elevation);
        
        /// <summary>
        /// 获取水平面坐标（返回Vector2D或Tuple）
        /// </summary>
        (double h1, double h2) GetHorizontalCoords(Point3D point);
        
        /// <summary>
        /// 从水平面坐标和高度构建3D点
        /// </summary>
        Point3D CreatePoint(double h1, double h2, double elevation);
        
        /// <summary>
        /// 获取垂直方向向量
        /// </summary>
        Vector3D UpVector { get; }
        
        /// <summary>
        /// 获取网格平面（用于2D网格的轴对应）
        /// 返回两个轴的索引 (axis1, axis2)
        /// </summary>
        (int axis1, int axis2) GridPlaneAxes { get; }
        
        /// <summary>
        /// 将外部点转换为内部标准表示（如果必要）
        /// </summary>
        Point3D ToInternal(Point3D externalPoint);
        
        /// <summary>
        /// 将内部点转换为外部表示
        /// </summary>
        Point3D ToExternal(Point3D internalPoint);
        
        /// <summary>
        /// 获取用于垂直扫描的方向向量（通常是-UpVector）
        /// </summary>
        Vector3D VerticalScanDirection { get; }
        
        /// <summary>
        /// 获取包围盒的高度范围
        /// </summary>
        (double min, double max) GetHeightRange(BoundingBox3D bounds);
        
        /// <summary>
        /// 获取包围盒的水平范围
        /// </summary>
        (double min1, double max1, double min2, double max2) GetHorizontalRange(BoundingBox3D bounds);
    }
}
```

---

### 3. Z-Up 坐标系实现

**文件**: `src/Utils/CoordinateSystem/ZUpCoordinateSystem.cs`

```csharp
using Autodesk.Navisworks.Api;

namespace NavisworksTransport.Utils.CoordinateSystem
{
    /// <summary>
    /// Z轴向上坐标系 (Navisworks默认)
    /// X = Right, Y = Back, Z = Up
    /// </summary>
    public class ZUpCoordinateSystem : ICoordinateSystem
    {
        public CoordinateSystemType Type => CoordinateSystemType.ZUp;
        public int UpAxisIndex => 2;  // Z
        public int PrimaryHorizontalAxisIndex => 0;  // X
        public int SecondaryHorizontalAxisIndex => 1; // Y
        public Vector3D UpVector => new Vector3D(0, 0, 1);
        public Vector3D VerticalScanDirection => new Vector3D(0, 0, -1);
        public (int axis1, int axis2) GridPlaneAxes => (0, 1); // X, Y
        
        public double GetElevation(Point3D point) => point.Z;
        
        public Point3D SetElevation(Point3D point, double elevation) => 
            new Point3D(point.X, point.Y, elevation);
        
        public (double h1, double h2) GetHorizontalCoords(Point3D point) => 
            (point.X, point.Y);
        
        public Point3D CreatePoint(double h1, double h2, double elevation) => 
            new Point3D(h1, h2, elevation);
        
        public Point3D ToInternal(Point3D externalPoint) => externalPoint;
        public Point3D ToExternal(Point3D internalPoint) => internalPoint;
        
        public (double min, double max) GetHeightRange(BoundingBox3D bounds) => 
            (bounds.Min.Z, bounds.Max.Z);
        
        public (double min1, double max1, double min2, double max2) GetHorizontalRange(BoundingBox3D bounds) => 
            (bounds.Min.X, bounds.Max.X, bounds.Min.Y, bounds.Max.Y);
    }
}
```

---

### 4. Y-Up 坐标系实现

**文件**: `src/Utils/CoordinateSystem/YUpCoordinateSystem.cs`

```csharp
using Autodesk.Navisworks.Api;

namespace NavisworksTransport.Utils.CoordinateSystem
{
    /// <summary>
    /// Y轴向上坐标系 (Revit默认等)
    /// X = Right, Y = Up, Z = Front
    /// </summary>
    public class YUpCoordinateSystem : ICoordinateSystem
    {
        public CoordinateSystemType Type => CoordinateSystemType.YUp;
        public int UpAxisIndex => 1;  // Y
        public int PrimaryHorizontalAxisIndex => 0;  // X
        public int SecondaryHorizontalAxisIndex => 2; // Z
        public Vector3D UpVector => new Vector3D(0, 1, 0);
        public Vector3D VerticalScanDirection => new Vector3D(0, -1, 0);
        public (int axis1, int axis2) GridPlaneAxes => (0, 2); // X, Z
        
        public double GetElevation(Point3D point) => point.Y;
        
        public Point3D SetElevation(Point3D point, double elevation) => 
            new Point3D(point.X, elevation, point.Z);
        
        public (double h1, double h2) GetHorizontalCoords(Point3D point) => 
            (point.X, point.Z);
        
        public Point3D CreatePoint(double h1, double h2, double elevation) => 
            new Point3D(h1, elevation, h2);
        
        public Point3D ToInternal(Point3D externalPoint) => new Point3D(
            externalPoint.X, 
            externalPoint.Z, 
            externalPoint.Y); // 转换为内部Z-up表示
        
        public Point3D ToExternal(Point3D internalPoint) => new Point3D(
            internalPoint.X, 
            internalPoint.Z, 
            internalPoint.Y); // 从内部Z-up转换回来
        
        public (double min, double max) GetHeightRange(BoundingBox3D bounds) => 
            (bounds.Min.Y, bounds.Max.Y);
        
        public (double min1, double max1, double min2, double max2) GetHorizontalRange(BoundingBox3D bounds) => 
            (bounds.Min.X, bounds.Max.X, bounds.Min.Z, bounds.Max.Z);
    }
}
```

---

### 5. 坐标系管理器

**文件**: `src/Utils/CoordinateSystem/CoordinateSystemManager.cs`

```csharp
using Autodesk.Navisworks.Api;

namespace NavisworksTransport.Utils.CoordinateSystem
{
    /// <summary>
    /// 坐标系管理器 - 全局访问点和自动检测
    /// </summary>
    public class CoordinateSystemManager
    {
        private static readonly Lazy<CoordinateSystemManager> _instance = 
            new Lazy<CoordinateSystemManager>(() => new CoordinateSystemManager());
        public static CoordinateSystemManager Instance => _instance.Value;

        private ICoordinateSystem _current;
        private CoordinateSystemType _configuredType = CoordinateSystemType.AutoDetect;

        private CoordinateSystemManager()
        {
            // 默认使用Z-up
            _current = new ZUpCoordinateSystem();
        }

        /// <summary>
        /// 当前活动的坐标系
        /// </summary>
        public ICoordinateSystem Current => _current;

        /// <summary>
        /// 配置坐标系类型
        /// </summary>
        public void Configure(CoordinateSystemType type)
        {
            _configuredType = type;
            
            switch (type)
            {
                case CoordinateSystemType.ZUp:
                    _current = new ZUpCoordinateSystem();
                    LogManager.Info("[坐标系管理器] 配置为 Z-Up 坐标系");
                    break;
                case CoordinateSystemType.YUp:
                    _current = new YUpCoordinateSystem();
                    LogManager.Info("[坐标系管理器] 配置为 Y-Up 坐标系");
                    break;
                case CoordinateSystemType.AutoDetect:
                    _current = AutoDetectCoordinateSystem();
                    break;
            }
        }

        /// <summary>
        /// 自动检测坐标系
        /// 基于模型数据的统计分析
        /// </summary>
        private ICoordinateSystem AutoDetectCoordinateSystem()
        {
            try
            {
                var doc = Application.ActiveDocument;
                if (doc == null || doc.Models.Count == 0)
                {
                    LogManager.Warning("[坐标系管理器] 无法自动检测，使用默认Z-Up");
                    return new ZUpCoordinateSystem();
                }

                // 获取模型整体包围盒
                var sceneBounds = doc.Models[0].RootItem.BoundingBox();
                
                // 策略1: 分析模型边界在Y和Z方向的分布
                // 如果Z方向的跨度明显小于X和Y，可能是Y-up（建筑通常更高而非更深）
                double xSpan = sceneBounds.Max.X - sceneBounds.Min.X;
                double ySpan = sceneBounds.Max.Y - sceneBounds.Min.Y;
                double zSpan = sceneBounds.Max.Z - sceneBounds.Min.Z;

                LogManager.Info($"[坐标系检测] 模型跨度: X={xSpan:F2}, Y={ySpan:F2}, Z={zSpan:F2}");

                // 启发式规则：如果Y跨度远大于Z跨度，可能是Y-up
                if (ySpan > zSpan * 3 && ySpan > xSpan * 0.5)
                {
                    LogManager.Info("[坐标系检测] 检测到 Y-Up 坐标系 (Y跨度显著)");
                    return new YUpCoordinateSystem();
                }

                // 策略2: 分析典型建筑元素（楼板、墙）的方向
                var coordinateSystem = AnalyzeBuildingElements();
                if (coordinateSystem != null) return coordinateSystem;

                LogManager.Info("[坐标系检测] 使用默认 Z-Up 坐标系");
                return new ZUpCoordinateSystem();
            }
            catch (Exception ex)
            {
                LogManager.Error($"[坐标系检测] 自动检测失败: {ex.Message}");
                return new ZUpCoordinateSystem();
            }
        }

        /// <summary>
        /// 通过分析建筑元素检测坐标系
        /// </summary>
        private ICoordinateSystem AnalyzeBuildingElements()
        {
            // 实现：检查楼板等水平元素的法向量
            // 如果主要水平面的法向量在Y方向，则是Y-up
            // 简化实现：可以根据项目需求扩展
            return null;
        }
    }
}
```

---

## 影响范围分析

### 需要修改的模块

| 模块 | 修改策略 | 工作量 | 优先级 |
|------|----------|--------|--------|
| **GridMap** | 使用 `ICoordinateSystem` 替代直接的 `.X/.Y/.Z` 访问 | 中等 | P0 |
| **GridMapGenerator** | 垂直扫描方向使用 `VerticalScanDirection` | 中等 | P0 |
| **AutoPathFinder** | 高度计算抽象化 | 中等 | P0 |
| **PathPointRenderPlugin** | 渲染时坐标转换 | 较小 | P1 |
| **GeometryHelper** | 几何提取时考虑坐标系 | 中等 | P1 |
| **ChannelHeightDetector** | 垂直射线方向适配 | 较小 | P0 |
| **Animation/TimeLiner** | 物体移动方向适配 | 中等 | P2 |
| **SlopeAnalyzer** | 坡度计算适配 | 较小 | P1 |

---

## 配置文件支持

在 `default_config.toml` 中添加坐标系配置：

```toml
[coordinate_system]
# 坐标系类型: "ZUp", "YUp", "AutoDetect"
type = "AutoDetect"

# 手动指定时的轴映射（可选，用于特殊坐标系）
# up_axis = "Y"  # 或 "Z"
# right_axis = "X"
# front_axis = "Z"
```

---

## 代码修改示例

### GridMap.cs 修改示例

**修改前**:
```csharp
public Point3D GridToWorld3D(GridPoint2D gridPosition)
{
    var world2D = GridToWorld2D(gridPosition);
    var cell = Cells[gridPosition.X, gridPosition.Y];
    double z = 0;
    if (cell.HeightLayers != null && cell.HeightLayers.Count > 0)
    {
        z = cell.HeightLayers[0].Z;  // ❌ 直接访问Z
    }
    return new Point3D(world2D.X, world2D.Y, z);
}
```

**修改后**:
```csharp
public Point3D GridToWorld3D(GridPoint2D gridPosition)
{
    var cs = CoordinateSystemManager.Instance.Current;
    var world2D = GridToWorld2D(gridPosition);
    var cell = Cells[gridPosition.X, gridPosition.Y];
    
    double elevation = 0;
    if (cell.HeightLayers != null && cell.HeightLayers.Count > 0)
    {
        elevation = cell.HeightLayers[0].Elevation;  // ✅ 使用抽象的高度
    }
    
    // 使用坐标系创建点
    var (h1, h2) = cs.GetHorizontalCoords(world2D);
    return cs.CreatePoint(h1, h2, elevation);
}
```

---

## 实施路线图

### 阶段1：核心适配（P0）- 1-2周 ✅ 已完成

1. ✅ 创建坐标系抽象层（ICoordinateSystem + 实现类）
   - `src/Utils/CoordinateSystem/ICoordinateSystem.cs`
   - `src/Utils/CoordinateSystem/ZUpCoordinateSystem.cs`
   - `src/Utils/CoordinateSystem/YUpCoordinateSystem.cs`
   - `src/Utils/CoordinateSystem/CoordinateSystemType.cs`
   - `src/Utils/CoordinateSystem/CoordinateSystemManager.cs`

2. ✅ 修改 GridMap 和 GridMapGenerator
   - `GridMap.cs` 已使用 `ICoordinateSystem` 进行坐标转换
   - `GridMapGenerator.cs` 已适配坐标系

3. ✅ 修改 ChannelHeightDetector 的垂直扫描
   - 已使用 `VerticalScanDirection` 替代硬编码方向
   - 已使用 `GetElevation`/`SetElevation` 替代直接 Z 访问

4. ✅ 修改 ChannelBasedGridBuilder
   - 已适配坐标系进行三角形光栅化
   - 已使用坐标系进行法向量向上判断

5. ✅ 添加配置支持
   - `default_config.toml` 已添加 `[coordinate_system]` 配置节
   - `SystemConfig.cs` 已添加 `CoordinateSystemConfig` 类
   - `ConfigManager.cs` 已添加坐标系配置解析
   - `MainPlugin.cs` 已添加坐标系初始化调用

6. ✅ 基础测试验证
   - 构建成功，无编译错误
   - 所有坐标系相关代码已正确集成

### 阶段1 完成总结

**已完成的核心适配工作：**

| 组件 | 文件 | 适配内容 |
|------|------|----------|
| 坐标系抽象层 | `ICoordinateSystem.cs` | 定义坐标系接口，包含高度/水平坐标转换、扫描方向等 |
| Z-Up 坐标系 | `ZUpCoordinateSystem.cs` | X=Right, Y=Back, Z=Up 实现 |
| Y-Up 坐标系 | `YUpCoordinateSystem.cs` | X=Right, Y=Up, Z=Front 实现 |
| 坐标系管理器 | `CoordinateSystemManager.cs` | 单例模式，支持自动检测和手动配置 |
| 网格地图 | `GridMap.cs` | 使用坐标系进行世界/网格坐标转换 |
| 网格生成器 | `GridMapGenerator.cs` | 适配坐标系进行边界计算 |
| 通道网格构建 | `ChannelBasedGridBuilder.cs` | 三角形光栅化适配坐标系 |
| 高度检测器 | `ChannelHeightDetector.cs` | 垂直扫描使用坐标系扫描方向 |
| 配置系统 | `SystemConfig.cs`, `ConfigManager.cs` | 添加坐标系配置节 |
| 主插件 | `MainPlugin.cs` | 初始化坐标系管理器 |

**向后兼容性：**
- 默认使用 Z-Up 坐标系，确保现有用户不受影响
- 配置文件默认为 `AutoDetect`，自动检测文档坐标系
- 所有修改对现有功能透明

### 阶段2：完整适配（P1）- 1周

1. 修改 AutoPathFinder
2. 修改 GeometryHelper
3. 修改 PathPointRenderPlugin
4. 修改 SlopeAnalyzer

### 阶段3：优化完善（P2）- 1周

1. 动画系统适配
2. 性能优化（缓存转换结果）
3. 完整测试覆盖
4. 文档更新

---

## 测试策略

1. **准备测试模型**
   - Z-up 坐标系的模型（现有）
   - Y-up 坐标系的模型（客户提供或创建）

2. **验证功能**
   - 网格生成正确性
   - 路径规划结果一致性
   - 高度检测准确性
   - 渲染显示正确性

3. **回归测试**
   - 确保Z-up模型仍然正常工作
   - Y-up模型功能完整

---

## 注意事项

1. **向后兼容**: 默认保持 Z-up 行为，确保现有用户不受影响
2. **性能**: 坐标转换可能带来轻微性能开销，可通过缓存优化
3. **文档**: 更新 AGENTS.md 和 README.md，说明坐标系配置方法
4. **日志**: 在关键位置添加坐标系检测和使用的日志，便于调试

---

*文档创建时间: 2026-01-30*
*作者: AI Assistant*
*状态: 设计方案*
