增加吊装路径水平环形检测

This commit is contained in:
tian 2026-02-25 01:37:15 +08:00
parent 369605e12f
commit cb7e1c0e17
2 changed files with 317 additions and 8 deletions

View File

@ -1732,6 +1732,11 @@ namespace NavisworksTransport
// 注意TotalLength 现在是计算属性,自动从几何数据计算,无需手动更新 // 注意TotalLength 现在是计算属性,自动从几何数据计算,无需手动更新
// 检测并移除同一高度层的矩形环路
LogManager.Info($"[吊装路径] 开始检测矩形环路,当前路径点数: {CurrentRoute?.Points?.Count ?? 0}");
bool loopsRemoved = RemoveRectangularLoops(CurrentRoute);
LogManager.Info($"[吊装路径] 矩形环路检测完成,是否移除环路: {loopsRemoved}");
// 重新渲染路径 // 重新渲染路径
_renderPlugin?.RenderPath(CurrentRoute); _renderPlugin?.RenderPath(CurrentRoute);
@ -1936,15 +1941,15 @@ namespace NavisworksTransport
var nextPoint = points[startIndex + 1]; var nextPoint = points[startIndex + 1];
// 使用 Info 级别日志以便调试 // 使用 Info 级别日志以便调试
LogManager.Info($"[斜线处理] 检查索引 {startIndex} 和 {startIndex + 1} 之间的连线"); LogManager.Debug($"[斜线处理] 检查索引 {startIndex} 和 {startIndex + 1} 之间的连线");
LogManager.Info($"[斜线处理] 当前点 [{startIndex}]: {currentPoint.Name}, 位置: ({currentPoint.Position.X:F2}, {currentPoint.Position.Y:F2}, {currentPoint.Position.Z:F2})"); LogManager.Debug($"[斜线处理] 当前点 [{startIndex}]: {currentPoint.Name}, 位置: ({currentPoint.Position.X:F2}, {currentPoint.Position.Y:F2}, {currentPoint.Position.Z:F2})");
LogManager.Info($"[斜线处理] 下一点 [{startIndex + 1}]: {nextPoint.Name}, 位置: ({nextPoint.Position.X:F2}, {nextPoint.Position.Y:F2}, {nextPoint.Position.Z:F2})"); LogManager.Debug($"[斜线处理] 下一点 [{startIndex + 1}]: {nextPoint.Name}, 位置: ({nextPoint.Position.X:F2}, {nextPoint.Position.Y:F2}, {nextPoint.Position.Z:F2})");
double deltaX = Math.Abs(nextPoint.Position.X - currentPoint.Position.X); double deltaX = Math.Abs(nextPoint.Position.X - currentPoint.Position.X);
double deltaY = Math.Abs(nextPoint.Position.Y - currentPoint.Position.Y); double deltaY = Math.Abs(nextPoint.Position.Y - currentPoint.Position.Y);
double deltaZ = Math.Abs(nextPoint.Position.Z - currentPoint.Position.Z); double deltaZ = Math.Abs(nextPoint.Position.Z - currentPoint.Position.Z);
LogManager.Info($"[斜线处理] deltaX={deltaX:F2}, deltaY={deltaY:F2}, deltaZ={deltaZ:F2}"); LogManager.Debug($"[斜线处理] deltaX={deltaX:F2}, deltaY={deltaY:F2}, deltaZ={deltaZ:F2}");
// 检测三种斜线 // 检测三种斜线
bool isXYDiagonal = deltaX > 0.01 && deltaY > 0.01 && deltaZ < 0.001; // XY平面斜线 bool isXYDiagonal = deltaX > 0.01 && deltaY > 0.01 && deltaZ < 0.001; // XY平面斜线
@ -1952,11 +1957,11 @@ namespace NavisworksTransport
bool isYZDiagonal = deltaY > 0.01 && deltaZ > 0.001 && deltaX < 0.01; // YZ平面斜线 bool isYZDiagonal = deltaY > 0.01 && deltaZ > 0.001 && deltaX < 0.01; // YZ平面斜线
bool isXYZDiagonal = deltaX > 0.01 && deltaY > 0.01 && deltaZ > 0.001; // XYZ空间斜线 bool isXYZDiagonal = deltaX > 0.01 && deltaY > 0.01 && deltaZ > 0.001; // XYZ空间斜线
LogManager.Info($"[斜线处理] 斜线检测结果: XY={isXYDiagonal}, XZ={isXZDiagonal}, YZ={isYZDiagonal}, XYZ={isXYZDiagonal}"); LogManager.Debug($"[斜线处理] 斜线检测结果: XY={isXYDiagonal}, XZ={isXZDiagonal}, YZ={isYZDiagonal}, XYZ={isXYZDiagonal}");
if (!isXYDiagonal && !isXZDiagonal && !isYZDiagonal && !isXYZDiagonal) if (!isXYDiagonal && !isXZDiagonal && !isYZDiagonal && !isXYZDiagonal)
{ {
LogManager.Info($"[斜线处理] 未检测到斜线,跳过处理"); LogManager.Debug($"[斜线处理] 未检测到斜线,跳过处理");
return false; return false;
} }
@ -2090,8 +2095,8 @@ namespace NavisworksTransport
points[i].Index = i; points[i].Index = i;
} }
LogManager.Info($"[斜线处理] 已插入中间点: {intermediatePoint.Name}, 位置: ({intermediatePoint.Position.X:F2}, {intermediatePoint.Position.Y:F2}, {intermediatePoint.Position.Z:F2}) - 方向: {direction}"); LogManager.Debug($"[斜线处理] 已插入中间点: {intermediatePoint.Name}, 位置: ({intermediatePoint.Position.X:F2}, {intermediatePoint.Position.Y:F2}, {intermediatePoint.Position.Z:F2}) - 方向: {direction}");
LogManager.Info($"[斜线处理] 插入位置: 索引 {insertPosition}, 当前路径点总数: {points.Count}"); LogManager.Debug($"[斜线处理] 插入位置: 索引 {insertPosition}, 当前路径点总数: {points.Count}");
return true; return true;
} }
@ -2226,6 +2231,302 @@ namespace NavisworksTransport
return hasChanges; return hasChanges;
} }
/// <summary>
/// 检测并移除各水平层内的矩形环路
/// 限制条件:
/// 1. 只处理同一水平层内的点Z坐标必须完全相同只考虑浮点数精度误差
/// 2. 不允许处理起点索引0和终点最后一个点
/// 3. 不允许跨层检测不同Z高度的线段不互相检测
/// 4. 支持多层吊装路径(多个不同高度的水平层)
/// 算法原理按Z坐标分组严格相等对每个水平层分别检测正交路径中的相交线段
/// </summary>
/// <param name="route">路径</param>
/// <returns>是否移除了环路</returns>
public bool RemoveRectangularLoops(PathRoute route)
{
if (route == null || route.Points.Count < 4)
{
return false;
}
bool hasChanges = false;
var points = route.Points;
// 浮点数精度容差用于判断Z坐标是否相等
const double epsilon = 1e-9;
LogManager.Debug($"[环路检测] 开始检测,路径点数: {points.Count}");
// 按Z坐标分组找出所有水平层排除起点和终点
// 使用严格相等比较(只考虑浮点数精度误差)
var layers = new Dictionary<double, List<int>>();
for (int i = 1; i < points.Count - 1; i++) // 跳过起点(0)和终点(Count-1)
{
double z = points[i].Position.Z;
// 查找是否已有相同Z坐标的层考虑浮点数精度
bool found = false;
foreach (var key in layers.Keys)
{
if (Math.Abs(z - key) < epsilon)
{
layers[key].Add(i);
found = true;
break;
}
}
if (!found)
{
layers[z] = new List<int> { i };
}
}
LogManager.Debug($"[环路检测] 发现{layers.Count}个水平层");
// 按Z坐标从高到低排序处理
var sortedLayers = layers.OrderByDescending(l => l.Key).ToList();
int totalDeletedPoints = 0; // 记录已删除的点数,用于更新后续层的索引
// 对每个水平层分别进行环路检测
for (int layerIdx = 0; layerIdx < sortedLayers.Count; layerIdx++)
{
var layer = sortedLayers[layerIdx];
double layerZ = layer.Key;
var layerIndices = layer.Value.OrderBy(idx => idx).ToList();
// 更新索引:减去之前层删除的点数
for (int i = 0; i < layerIndices.Count; i++)
{
layerIndices[i] -= totalDeletedPoints;
}
// 过滤掉无效索引小于0或超出范围
layerIndices = layerIndices.Where(idx => idx >= 0 && idx < points.Count).ToList();
if (layerIndices.Count < 2)
{
LogManager.Debug($"[环路检测] 层 Z={layerZ:F2} 剩余点太少,跳过");
continue; // 该层点太少,无法形成环路
}
LogManager.Debug($"[环路检测] 处理水平层 Z={layerZ:F2},包含{layerIndices.Count}个,索引范围: {layerIndices.First()}-{layerIndices.Last()}");
// 对该层进行环路检测
int pointsBefore = points.Count;
bool layerHasChanges = RemoveLoopsInLayer(points, layerIndices, epsilon);
int pointsDeleted = pointsBefore - points.Count;
totalDeletedPoints += pointsDeleted;
if (layerHasChanges)
{
hasChanges = true;
}
}
if (hasChanges)
{
LogManager.Debug($"[环路检测] 完成,当前路径点数: {points.Count}");
}
return hasChanges;
}
/// <summary>
/// 对单个水平层进行环路检测和移除
/// </summary>
/// <param name="points">路径点列表</param>
/// <param name="layerIndices">该层的点索引列表(已排序)</param>
/// <param name="epsilon">浮点数精度容差</param>
/// <returns>是否移除了环路</returns>
private bool RemoveLoopsInLayer(List<PathPoint> points, List<int> layerIndices, double epsilon)
{
if (layerIndices.Count < 2)
{
return false;
}
bool hasChanges = false;
double layerZ = points[layerIndices[0]].Position.Z;
// 在该层内进行环路检测
// n和m是layerIndices列表中的索引位置
int n = 0; // 第一个线段的起始索引在layerIndices中的位置
int m = 2; // 第二个线段相对于n的偏移在layerIndices中的位置
while (n + m + 1 < layerIndices.Count)
{
// 获取实际的路径点索引
int idx1 = layerIndices[n]; // 线段1起点
int idx2 = layerIndices[n + 1]; // 线段1终点
int idx3 = layerIndices[n + m]; // 线段2起点
int idx4 = layerIndices[n + m + 1]; // 线段2终点
// 获取线段坐标
var p1 = points[idx1].Position;
var p2 = points[idx2].Position;
var p3 = points[idx3].Position;
var p4 = points[idx4].Position;
// 确保所有点都在同一层水平线段Z坐标严格相同只考虑浮点数精度
if (Math.Abs(p1.Z - layerZ) > epsilon ||
Math.Abs(p2.Z - layerZ) > epsilon ||
Math.Abs(p3.Z - layerZ) > epsilon ||
Math.Abs(p4.Z - layerZ) > epsilon)
{
m++;
if (n + m + 1 >= layerIndices.Count)
{
m = 2;
n++;
}
continue;
}
// 检测两条正交线段是否相交
Point3D intersection;
if (GetOrthogonalSegmentIntersection(p1, p2, p3, p4, out intersection))
{
LogManager.Debug($"[环路检测] 层Z={layerZ:F2} 发现相交: 线段{idx1}({points[idx1].Name}-{points[idx2].Name}) 与 线段{idx3}({points[idx3].Name}-{points[idx4].Name})");
LogManager.Debug($"[环路检测] 交点: ({intersection.X:F4}, {intersection.Y:F4}, {intersection.Z:F4})");
// 检查中间点是否包含特殊点
bool containsSpecialPoint = false;
for (int k = idx1 + 1; k <= idx3; k++)
{
if (points[k].Type != PathPointType.WayPoint ||
points[k].Name == "提升点" ||
points[k].Name == "下降点" ||
points[k].Name == "起吊点" ||
points[k].Name == "落地点")
{
containsSpecialPoint = true;
break;
}
}
if (containsSpecialPoint)
{
LogManager.Debug("[环路检测] 环路包含特殊点,跳过");
m++;
if (n + m + 1 >= layerIndices.Count)
{
m = 2;
n++;
}
continue;
}
// 用交点替换 points[idx2]
points[idx2].Position = intersection;
points[idx2].Name = "路径点";
// 删除中间点 (idx2+1 到 idx3)
int d = idx3 - idx2; // 要删除的点数
for (int i = idx3 + 1; i < points.Count; i++)
{
points[i - d] = points[i];
}
// 移除末尾多余的点
points.RemoveRange(points.Count - d, d);
// 更新layerIndices因为删除了点
for (int i = 0; i < layerIndices.Count; i++)
{
if (layerIndices[i] > idx2 && layerIndices[i] <= idx3)
{
// 这些点被删除了,标记为-1
layerIndices[i] = -1;
}
else if (layerIndices[i] > idx3)
{
// 这些点的索引需要向前移动
layerIndices[i] -= d;
}
}
layerIndices.RemoveAll(idx => idx == -1);
// 从交点重新开始检测
m = 2;
hasChanges = true;
LogManager.Info($"[环路优化] 层Z={layerZ:F2} 已移除环路,删除{d}个点");
continue;
}
m++;
if (n + m + 1 >= layerIndices.Count)
{
m = 2;
n++;
}
}
return hasChanges;
}
/// <summary>
/// 计算两条正交线段的交点
/// </summary>
/// <returns>是否相交(线段内部相交,非端点)</returns>
private bool GetOrthogonalSegmentIntersection(Point3D a1, Point3D a2, Point3D b1, Point3D b2, out Point3D intersection)
{
intersection = new Point3D(0, 0, 0);
// 判断线段A是水平还是垂直
bool aIsHorizontal = Math.Abs(a1.Y - a2.Y) < 0.001;
bool aIsVertical = Math.Abs(a1.X - a2.X) < 0.001;
// 判断线段B是水平还是垂直
bool bIsHorizontal = Math.Abs(b1.Y - b2.Y) < 0.001;
bool bIsVertical = Math.Abs(b1.X - b2.X) < 0.001;
// 必须一条水平一条垂直才可能形成矩形环路
if ((aIsHorizontal && bIsHorizontal) || (aIsVertical && bIsVertical))
{
return false;
}
// 确保a是水平b是垂直
if (aIsVertical && bIsHorizontal)
{
// 交换
var temp1 = a1; var temp2 = a2;
a1 = b1; a2 = b2;
b1 = temp1; b2 = temp2;
aIsHorizontal = true; aIsVertical = false;
bIsHorizontal = false; bIsVertical = true;
}
if (!aIsHorizontal || !bIsVertical)
{
return false;
}
// 水平线段A: y = a1.Y, x范围 [min(a1.X,a2.X), max(a1.X,a2.X)]
// 垂直线段B: x = b1.X, y范围 [min(b1.Y,b2.Y), max(b1.Y,b2.Y)]
double aY = a1.Y;
double bX = b1.X;
double aMinX = Math.Min(a1.X, a2.X);
double aMaxX = Math.Max(a1.X, a2.X);
double bMinY = Math.Min(b1.Y, b2.Y);
double bMaxY = Math.Max(b1.Y, b2.Y);
// 检查垂直线段的X是否在水平线段的X范围内不包括端点
// 且水平线段的Y是否在垂直线段的Y范围内不包括端点
const double eps = 0.001;
bool xInRange = bX > aMinX + eps && bX < aMaxX - eps;
bool yInRange = aY > bMinY + eps && aY < bMaxY - eps;
if (xInRange && yInRange)
{
intersection = new Point3D(bX, aY, a1.Z);
return true;
}
return false;
}
/// <summary> /// <summary>
/// 更新路径点位置(保存修改) /// 更新路径点位置(保存修改)
/// </summary> /// </summary>
@ -2396,6 +2697,10 @@ namespace NavisworksTransport
// 吊装路径:正交化路径(处理斜线和清除多余点) // 吊装路径:正交化路径(处理斜线和清除多余点)
OrthogonalizePath(route); OrthogonalizePath(route);
// 吊装路径:检测并移除矩形环路
LogManager.Info($"[吊装路径] 修改路径点后检测矩形环路");
RemoveRectangularLoops(route);
} }
else else
{ {

View File

@ -2689,6 +2689,10 @@ namespace NavisworksTransport.UI.WPF.ViewModels
if (coreRoute.PathType == PathType.Hoisting) if (coreRoute.PathType == PathType.Hoisting)
{ {
_pathPlanningManager?.OrthogonalizePath(coreRoute); _pathPlanningManager?.OrthogonalizePath(coreRoute);
// 吊装路径:检测并移除矩形环路
LogManager.Info($"[吊装路径] 删除路径点后检测矩形环路");
_pathPlanningManager?.RemoveRectangularLoops(coreRoute);
} }
// 调用PathPlanningManager的3D删除方法 // 调用PathPlanningManager的3D删除方法