NavisworksTransport/doc/working/coordinate-system-adaptation-modification-principles.md

11 KiB
Raw Blame History

坐标系适配修改原则

核心原则

绝对禁止使用 if (cs.Type == CoordinateSystemType.ZUp) 分支判断坐标系类型!

坐标系框架的设计目标是:通过抽象方法自动隐藏坐标系差异,业务代码只使用抽象概念(高度、水平面),完全不需要知道当前是什么坐标系。


坐标系框架核心方法

1. 高度相关

// 获取高度(自动适配坐标系)
double elevation = cs.GetElevation(point);
// Z-Up: 返回 point.Z
// Y-Up: 返回 point.Y

// 设置高度(自动适配坐标系)
Point3D newPoint = cs.SetElevation(point, elevation);
// Z-Up: 返回 (point.X, point.Y, elevation)
// Y-Up: 返回 (point.X, elevation, point.Z)

2. 水平面相关

// 获取水平坐标(自动适配坐标系)
var (h1, h2) = cs.GetHorizontalCoords(point);
// Z-Up: 返回 (point.X, point.Y)
// Y-Up: 返回 (point.X, point.Z)

// 从水平坐标和高度构建点(自动适配坐标系)
Point3D newPoint = cs.CreatePoint(h1, h2, elevation);
// Z-Up: 返回 (h1, h2, elevation)
// Y-Up: 返回 (h1, elevation, h2)

3. 向量相关

// 获取向上向量
Vector3D upVector = cs.UpVector;
// Z-Up: 返回 (0, 0, 1)
// Y-Up: 返回 (0, 1, 0)

// 获取垂直扫描方向(用于障碍物检测)
Vector3D scanDirection = cs.VerticalScanDirection;
// Z-Up: 返回 (0, 0, -1)
// Y-Up: 返回 (0, -1, 0)

修改场景与模式

场景 1获取点的高度

错误

double height = point.Z;  // 硬编码Z轴

正确

var cs = CoordinateSystemManager.Instance.Current;
double elevation = cs.GetElevation(point);

场景 2设置点的高度

错误

Point3D newPoint = new Point3D(point.X, point.Y, point.Z + height);

正确

var cs = CoordinateSystemManager.Instance.Current;
var elevation = cs.GetElevation(point);
Point3D newPoint = cs.SetElevation(point, elevation + height);

场景 3保持X/Y修改高度

错误

Point3D newPoint = new Point3D(newX, newY, point.Z);

正确

var cs = CoordinateSystemManager.Instance.Current;
var elevation = cs.GetElevation(point);
Point3D newPoint = cs.SetElevation(new Point3D(newX, newY, point.Z), elevation);

注意SetElevation 会保留水平坐标,只修改高度轴。


场景 4计算水平方向的距离

错误

double deltaX = Math.Abs(point2.X - point1.X);
double deltaY = Math.Abs(point2.Y - point1.Y);

正确

var cs = CoordinateSystemManager.Instance.Current;
var (h1_1, h2_1) = cs.GetHorizontalCoords(point1);
var (h1_2, h2_2) = cs.GetHorizontalCoords(point2);
double deltaH1 = Math.Abs(h1_2 - h1_1);
double deltaH2 = Math.Abs(h2_2 - h2_1);

场景 5判断点是否在水平方向共线

错误

bool sameX = Math.Abs(prevPoint.X - currentPoint.X) < tolerance &&
             Math.Abs(currentPoint.X - nextPoint.X) < tolerance;
bool sameY = Math.Abs(prevPoint.Y - currentPoint.Y) < tolerance &&
             Math.Abs(currentPoint.Y - nextPoint.Y) < tolerance;
bool isCollinear = sameX || sameY;

正确

var cs = CoordinateSystemManager.Instance.Current;
var (prevH1, prevH2) = cs.GetHorizontalCoords(prevPoint.Position);
var (currH1, currH2) = cs.GetHorizontalCoords(currentPoint.Position);
var (nextH1, nextH2) = cs.GetHorizontalCoords(nextPoint.Position);

bool sameH1 = Math.Abs(prevH1 - currH1) < tolerance && Math.Abs(currH1 - nextH1) < tolerance;
bool sameH2 = Math.Abs(prevH2 - currH2) < tolerance && Math.Abs(currH2 - nextH2) < tolerance;
bool isCollinear = sameH1 || sameH2;

场景 6创建新点指定位置和高度

错误

// 错误1不知道哪个轴是高度
Point3D newPoint = new Point3D(x, y, z);

// 错误2直接用原始坐标作为水平坐标
Point3D newPoint = cs.CreatePoint(
    clickedGroundPoint.X,  // X 是 h1正确
    clickedGroundPoint.Y,  // 在 Y-Up 中 Y 是高度,不是 h2错误
    elevation);

正确

var cs = CoordinateSystemManager.Instance.Current;

// 先获取水平坐标,再创建点
var (h1, h2) = cs.GetHorizontalCoords(clickedGroundPoint);
Point3D newPoint = cs.CreatePoint(h1, h2, elevation);

注意CreatePoint 的参数是 (h1, h2, elevation),其中:

  • h1 是第一个水平轴坐标X轴
  • h2 是第二个水平轴坐标Z-Up中是YY-Up中是Z
  • elevation 是高度值

关键h1h2 必须通过 GetHorizontalCoords() 获取,不能直接使用原始点的 .X, .Y, .Z,因为 .Y 在 Y-Up 坐标系中是高度,不是水平坐标!


场景 7垂直扫描障碍物检测

错误

Vector3D scanDirection = new Vector3D(0, 0, -1);  // 硬编码向下扫描

正确

var cs = CoordinateSystemManager.Instance.Current;
Vector3D scanDirection = cs.VerticalScanDirection;

场景 8获取向上向量用于渲染、动画

错误

Vector3D upVector = new Vector3D(0, 0, 1);  // 硬编码Z轴向上

正确

var cs = CoordinateSystemManager.Instance.Current;
Vector3D upVector = cs.UpVector;

场景 9计算水平面上的向量右向量

错误

// 假设Z轴向上在XY平面内垂直于方向向量
Vector3D right = new Vector3D(-direction.Y, direction.X, 0);

正确

var cs = CoordinateSystemManager.Instance.Current;
var upVector = cs.UpVector;

// 使用叉积right = direction × upVector确保在水平面上
Vector3D right = new Vector3D(
    direction.Y * upVector.Z - direction.Z * upVector.Y,
    direction.Z * upVector.X - direction.X * upVector.Z,
    direction.X * upVector.Y - direction.Y * upVector.X
);

场景 10判断方向是否垂直于向上方向

错误

if (Math.Abs(direction.Z) > 0.9)  // 硬编码检查Z轴
{
    // 垂直方向
}

正确

var cs = CoordinateSystemManager.Instance.Current;
var upVector = cs.UpVector;

// 计算点积
double upDot = direction.X * upVector.X + direction.Y * upVector.Y + direction.Z * upVector.Z;
if (Math.Abs(upDot) > 0.9)  // 与向上方向平行(垂直方向)
{
    // 垂直方向
}

场景 11包围盒高度范围

错误

double minHeight = bounds.Min.Z;
double maxHeight = bounds.Max.Z;

正确

var cs = CoordinateSystemManager.Instance.Current;
var (minHeight, maxHeight) = cs.GetHeightRange(bounds);

场景 12包围盒水平范围

错误

double minX = bounds.Min.X;
double maxX = bounds.Max.X;
double minY = bounds.Min.Y;
double maxY = bounds.Max.Y;

正确

var cs = CoordinateSystemManager.Instance.Current;
var (minH1, maxH1, minH2, maxH2) = cs.GetHorizontalRange(bounds);

禁止模式

禁止 1使用 if 分支判断坐标系类型

// 禁止
if (cs.Type == CoordinateSystemType.ZUp)
    new Point3D(x, y, elevation)
else
    new Point3D(x, elevation, z)

原因:违背了框架的设计原则,业务代码不应该知道坐标系类型。

禁止 2直接访问 X/Y/Z 轴进行高度计算

// 禁止
double height = point.Z;  // 或者 point.Y取决于坐标系
Point3D newPoint = new Point3D(x, y, point.Z + height);

原因:在 Y-Up 坐标系中Y 是高度轴Z 是水平轴。

禁止 3直接使用 X/Y 进行水平方向计算

// 禁止
double deltaX = Math.Abs(point2.X - point1.X);
double deltaY = Math.Abs(point2.Y - point1.Y);

原因:在 Y-Up 坐标系中Y 是高度轴,不是水平方向。

禁止 4使用 SetElevation 构造新点时传入错误的第三个参数

// 禁止
Point3D newPoint = cs.SetElevation(
    new Point3D(clickedGroundPoint.X, clickedGroundPoint.Y, clickedGroundPoint.Z),
    elevation
);

原因:在 Y-Up 坐标系中,SetElevation 会返回 (X, elevation, Z),第三个参数 Z 被保留,而不是设置高度。

正确做法:使用 CreatePoint 或确保理解 SetElevation 的行为。


修改检查清单

修改任何涉及坐标的代码时,必须检查:

  • 是否直接访问了 .Z.Y 轴进行高度计算?
  • 是否直接使用了 X/Y 轴进行水平方向计算?
  • 是否使用了 if (cs.Type == ...) 分支判断?
  • 是否使用了硬编码的向量(如 (0, 0, 1))表示向上方向?
  • 是否使用了硬编码的叉积(如 (-direction.Y, direction.X, 0))表示水平面上的向量?

如果以上任何一项为"是",则必须使用坐标系抽象方法进行修改。


常见错误案例

错误案例 1使用 SetElevation 构造新点

// ❌ 错误
Point3D newPoint = cs.SetElevation(
    new Point3D(clickedGroundPoint.X, clickedGroundPoint.Y, clickedGroundPoint.Z),
    cs.GetElevation(previousPoint)
);

问题:在 Y-Up 坐标系中,SetElevation 返回 (X, elevation, Z)Z 轴被保留为 clickedGroundPoint.Z(水平坐标),这是错误的。

正确做法

// ✅ 正确
var cs = CoordinateSystemManager.Instance.Current;
var elevation = cs.GetElevation(previousPoint);
var (h1, h2) = cs.GetHorizontalCoords(clickedGroundPoint);
Point3D newPoint = cs.CreatePoint(h1, h2, elevation);

错误案例 2直接修改 Z 轴创建新点

// ❌ 错误
Point3D newPoint = new Point3D(x, y, point.Z + height);

问题:在 Y-Up 坐标系中Z 轴是水平方向,不是高度方向。

正确做法

// ✅ 正确
var cs = CoordinateSystemManager.Instance.Current;
var elevation = cs.GetElevation(point);
Point3D newPoint = cs.SetElevation(new Point3D(x, y, point.Z), elevation + height);

错误案例 3使用 if 分支

// ❌ 错误
if (cs.Type == CoordinateSystemType.ZUp)
{
    newPoint = new Point3D(x, y, elevation);
}
else
{
    newPoint = new Point3D(x, elevation, z);
}

问题:违背了框架的设计原则。

正确做法

// ✅ 正确
var cs = CoordinateSystemManager.Instance.Current;
var newPoint = cs.CreatePoint(x, z, elevation);

文件结构

坐标系框架位于 src/Utils/CoordinateSystem/

src/Utils/CoordinateSystem/
├── CoordinateSystemType.cs          # 坐标系类型枚举
├── ICoordinateSystem.cs             # 坐标系接口
├── ZUpCoordinateSystem.cs           # Z-Up 实现
├── YUpCoordinateSystem.cs           # Y-Up 实现
└── CoordinateSystemManager.cs       # 坐标系管理器(单例)

使用示例

获取坐标系实例

using NavisworksTransport.Utils.CoordinateSystem;

var cs = CoordinateSystemManager.Instance.Current;

添加 using 引用

在需要使用坐标系的文件顶部添加:

using NavisworksTransport.Utils.CoordinateSystem;

文档创建时间: 2026-01-31 作者: AI Assistant 状态: 修改原则