NavisworksTransport/doc/working/coordinate-system-adaptation-design.md

19 KiB
Raw Blame History

坐标系动态适配设计方案

问题背景

客户模型坐标系与插件默认坐标系不同:

  • 插件默认: 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周 已完成

  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 状态: 设计方案