11 KiB
坐标系适配修改原则
核心原则
绝对禁止使用 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中是Y,Y-Up中是Z)elevation是高度值
关键:h1 和 h2 必须通过 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 状态: 修改原则