19 KiB
19 KiB
坐标系动态适配设计方案
问题背景
客户模型坐标系与插件默认坐标系不同:
- 插件默认: 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
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
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
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
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
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 中添加坐标系配置:
[coordinate_system]
# 坐标系类型: "ZUp", "YUp", "AutoDetect"
type = "AutoDetect"
# 手动指定时的轴映射(可选,用于特殊坐标系)
# up_axis = "Y" # 或 "Z"
# right_axis = "X"
# front_axis = "Z"
代码修改示例
GridMap.cs 修改示例
修改前:
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);
}
修改后:
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周 ✅ 已完成
-
✅ 创建坐标系抽象层(ICoordinateSystem + 实现类)
src/Utils/CoordinateSystem/ICoordinateSystem.cssrc/Utils/CoordinateSystem/ZUpCoordinateSystem.cssrc/Utils/CoordinateSystem/YUpCoordinateSystem.cssrc/Utils/CoordinateSystem/CoordinateSystemType.cssrc/Utils/CoordinateSystem/CoordinateSystemManager.cs
-
✅ 修改 GridMap 和 GridMapGenerator
GridMap.cs已使用ICoordinateSystem进行坐标转换GridMapGenerator.cs已适配坐标系
-
✅ 修改 ChannelHeightDetector 的垂直扫描
- 已使用
VerticalScanDirection替代硬编码方向 - 已使用
GetElevation/SetElevation替代直接 Z 访问
- 已使用
-
✅ 修改 ChannelBasedGridBuilder
- 已适配坐标系进行三角形光栅化
- 已使用坐标系进行法向量向上判断
-
✅ 添加配置支持
default_config.toml已添加[coordinate_system]配置节SystemConfig.cs已添加CoordinateSystemConfig类ConfigManager.cs已添加坐标系配置解析MainPlugin.cs已添加坐标系初始化调用
-
✅ 基础测试验证
- 构建成功,无编译错误
- 所有坐标系相关代码已正确集成
阶段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周
- 修改 AutoPathFinder
- 修改 GeometryHelper
- 修改 PathPointRenderPlugin
- 修改 SlopeAnalyzer
阶段3:优化完善(P2)- 1周
- 动画系统适配
- 性能优化(缓存转换结果)
- 完整测试覆盖
- 文档更新
测试策略
-
准备测试模型
- Z-up 坐标系的模型(现有)
- Y-up 坐标系的模型(客户提供或创建)
-
验证功能
- 网格生成正确性
- 路径规划结果一致性
- 高度检测准确性
- 渲染显示正确性
-
回归测试
- 确保Z-up模型仍然正常工作
- Y-up模型功能完整
注意事项
- 向后兼容: 默认保持 Z-up 行为,确保现有用户不受影响
- 性能: 坐标转换可能带来轻微性能开销,可通过缓存优化
- 文档: 更新 AGENTS.md 和 README.md,说明坐标系配置方法
- 日志: 在关键位置添加坐标系检测和使用的日志,便于调试
文档创建时间: 2026-01-30 作者: AI Assistant 状态: 设计方案