# 坐标系动态适配设计方案
## 问题背景
客户模型坐标系与插件默认坐标系不同:
- **插件默认**: 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
{
///
/// 支持的坐标系类型
///
public enum CoordinateSystemType
{
///
/// Z轴向上 (标准Navisworks坐标系: Z-up, X-right, Y-back)
///
ZUp,
///
/// Y轴向上 (常见于Revit等: Y-up, X-right, Z-front)
///
YUp,
///
/// 自动检测
///
AutoDetect
}
///
/// 轴定义
///
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
{
///
/// 坐标系接口 - 抽象不同坐标系的差异
///
public interface ICoordinateSystem
{
///
/// 坐标系类型
///
CoordinateSystemType Type { get; }
///
/// 获取向上轴的索引 (0=X, 1=Y, 2=Z)
///
int UpAxisIndex { get; }
///
/// 获取水平面主轴索引 (通常是X)
///
int PrimaryHorizontalAxisIndex { get; }
///
/// 获取水平面次轴索引
///
int SecondaryHorizontalAxisIndex { get; }
///
/// 获取点的高度值(统一抽象)
///
double GetElevation(Point3D point);
///
/// 设置点的高度值,返回新点
///
Point3D SetElevation(Point3D point, double elevation);
///
/// 获取水平面坐标(返回Vector2D或Tuple)
///
(double h1, double h2) GetHorizontalCoords(Point3D point);
///
/// 从水平面坐标和高度构建3D点
///
Point3D CreatePoint(double h1, double h2, double elevation);
///
/// 获取垂直方向向量
///
Vector3D UpVector { get; }
///
/// 获取网格平面(用于2D网格的轴对应)
/// 返回两个轴的索引 (axis1, axis2)
///
(int axis1, int axis2) GridPlaneAxes { get; }
///
/// 将外部点转换为内部标准表示(如果必要)
///
Point3D ToInternal(Point3D externalPoint);
///
/// 将内部点转换为外部表示
///
Point3D ToExternal(Point3D internalPoint);
///
/// 获取用于垂直扫描的方向向量(通常是-UpVector)
///
Vector3D VerticalScanDirection { get; }
///
/// 获取包围盒的高度范围
///
(double min, double max) GetHeightRange(BoundingBox3D bounds);
///
/// 获取包围盒的水平范围
///
(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
{
///
/// Z轴向上坐标系 (Navisworks默认)
/// X = Right, Y = Back, Z = Up
///
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
{
///
/// Y轴向上坐标系 (Revit默认等)
/// X = Right, Y = Up, Z = Front
///
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
{
///
/// 坐标系管理器 - 全局访问点和自动检测
///
public class CoordinateSystemManager
{
private static readonly Lazy _instance =
new Lazy(() => new CoordinateSystemManager());
public static CoordinateSystemManager Instance => _instance.Value;
private ICoordinateSystem _current;
private CoordinateSystemType _configuredType = CoordinateSystemType.AutoDetect;
private CoordinateSystemManager()
{
// 默认使用Z-up
_current = new ZUpCoordinateSystem();
}
///
/// 当前活动的坐标系
///
public ICoordinateSystem Current => _current;
///
/// 配置坐标系类型
///
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;
}
}
///
/// 自动检测坐标系
/// 基于模型数据的统计分析
///
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();
}
}
///
/// 通过分析建筑元素检测坐标系
///
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*
*状态: 设计方案*