解决空轨计算提取三角形引起的程序崩溃问题。
This commit is contained in:
parent
b1da586b27
commit
779d043299
2
IFLOW.md
2
IFLOW.md
@ -319,7 +319,7 @@ msiexec /i NavisworksTransport.Setup.msi /qn /l*v install.log
|
||||
- **日志记录**: 使用内置的LogManager
|
||||
- **异常处理**: 通过GlobalExceptionHandler统一管理,不掩盖问题
|
||||
- **线程安全**: COM API调用必须在主UI线程(STA)执行
|
||||
- **单位系统**:
|
||||
- **单位系统**:
|
||||
- **内部计算**: 统一使用模型单位(避免频繁转换,提高性能)
|
||||
- **对外输出**: 转换为米(m)单位(用户界面、日志、数据库存储等)
|
||||
- **单位转换**: 使用 `UnitsConverter.ConvertToMeters()` 和 `UnitsConverter.ConvertFromMeters()` 进行转换
|
||||
|
||||
@ -546,6 +546,13 @@ namespace NavisworksTransport
|
||||
if (items == null || items.Count == 0)
|
||||
return;
|
||||
|
||||
// 确保在主线程上执行(Navisworks COM API 要求)
|
||||
if (!NavisworksApiHelper.IsOnUIThread())
|
||||
{
|
||||
NavisworksApiHelper.ExecuteOnUIThread(() => PreCalculateRailBaselinePaths(items));
|
||||
return;
|
||||
}
|
||||
|
||||
LogManager.Info($"[空轨] 开始预计算 {items.Count} 个空轨模型的基准路径");
|
||||
|
||||
int successCount = 0;
|
||||
@ -556,7 +563,7 @@ namespace NavisworksTransport
|
||||
try
|
||||
{
|
||||
// 调用RailGeometryHelper提取基准路径
|
||||
var baselinePath = PathPlanning.RailGeometryHelper.ExtractRailBaselinePath(item);
|
||||
var baselinePath = RailGeometryHelper.ExtractRailBaselinePath(item);
|
||||
|
||||
if (baselinePath != null && baselinePath.PathPoints != null && baselinePath.PathPoints.Count > 0)
|
||||
{
|
||||
|
||||
@ -407,6 +407,12 @@ namespace NavisworksTransport.PathPlanning
|
||||
/// <returns>成功提取的基准路径数量</returns>
|
||||
public static int ExtractAndRenderAllRailBaselines()
|
||||
{
|
||||
// 确保在主线程上执行(Navisworks COM API 要求)
|
||||
if (!NavisworksApiHelper.IsOnUIThread())
|
||||
{
|
||||
return NavisworksApiHelper.ExecuteOnUIThread(ExtractAndRenderAllRailBaselines);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
LogManager.Info("[空轨] 开始提取所有空轨模型的基准路径");
|
||||
@ -458,15 +464,6 @@ namespace NavisworksTransport.PathPlanning
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 兼容方法:从空轨几何体提取下表面中心线(一步完成)
|
||||
/// </summary>
|
||||
/// <param name="railModel">空轨模型项</param>
|
||||
/// <param name="startPoint">起点(3D坐标)</param>
|
||||
/// <param name="endPoint">终点(3D坐标)</param>
|
||||
/// <param name="samplingInterval">采样间隔(模型单位),默认0.5</param>
|
||||
/// <returns>路径点列表(包含精确的Z坐标)</returns>
|
||||
|
||||
/// <summary>
|
||||
/// 使用 OBB 包围体提取空轨下表面中心线(新方法,支持倾斜)
|
||||
@ -726,21 +723,23 @@ namespace NavisworksTransport.PathPlanning
|
||||
centroid.Y + axes[0].Y * (minX + maxX) / 2 + axes[1].Y * (minY + maxY) / 2 + axes[2].Y * (minZ + maxZ) / 2,
|
||||
centroid.Z + axes[0].Z * (minX + maxX) / 2 + axes[1].Z * (minY + maxY) / 2 + axes[2].Z * (minZ + maxZ) / 2
|
||||
);
|
||||
|
||||
|
||||
|
||||
var obb = new OrientedBoundingBox
|
||||
{
|
||||
|
||||
Center = obbCenter,
|
||||
Axes = axes,
|
||||
Extents = new Point3D(
|
||||
(maxX - minX) / 2,
|
||||
(maxY - minY) / 2,
|
||||
(maxZ - minZ) / 2
|
||||
)
|
||||
),
|
||||
// 保存投影范围信息,用于计算下表面
|
||||
ProjectedMinZ = minZ,
|
||||
ProjectedMaxZ = maxZ
|
||||
|
||||
};
|
||||
|
||||
// 保存投影范围信息,用于计算下表面
|
||||
obb.ProjectedMinZ = minZ;
|
||||
obb.ProjectedMaxZ = maxZ;
|
||||
|
||||
LogManager.Info($"[空轨] OBB各轴长度(模型单位): {obb.GetAxisLength(0):F2}, {obb.GetAxisLength(1):F2}, {obb.GetAxisLength(2):F2}");
|
||||
|
||||
@ -796,166 +795,6 @@ namespace NavisworksTransport.PathPlanning
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 兼容方法:从空轨几何体提取下表面中心线(一步完成)
|
||||
/// </summary>
|
||||
/// <param name="railModel">空轨模型项</param>
|
||||
/// <param name="startPoint">起点(3D坐标)</param>
|
||||
/// <param name="endPoint">终点(3D坐标)</param>
|
||||
/// <param name="samplingInterval">采样间隔(模型单位),默认0.5</param>
|
||||
/// <returns>路径点列表(包含精确的Z坐标)</returns>
|
||||
public static List<Point3D> ExtractRailBottomCenterLine(
|
||||
ModelItem railModel,
|
||||
Point3D startPoint,
|
||||
Point3D endPoint,
|
||||
double samplingInterval = 0.5)
|
||||
{
|
||||
try
|
||||
{
|
||||
LogManager.Info($"[空轨] 开始提取下表面中心线: {railModel.DisplayName}");
|
||||
LogManager.Info($"[空轨] 起点: ({startPoint.X:F2}, {startPoint.Y:F2}, {startPoint.Z:F2})");
|
||||
LogManager.Info($"[空轨] 终点: ({endPoint.X:F2}, {endPoint.Y:F2}, {endPoint.Z:F2})");
|
||||
LogManager.Info($"[空轨] 采样间隔: {samplingInterval:F2}");
|
||||
|
||||
// 1. 提取空轨下表面信息(带缓存)
|
||||
var surfaceInfo = ExtractRailBottomSurface(railModel);
|
||||
if (surfaceInfo == null || surfaceInfo.BottomTriangles.Count == 0)
|
||||
{
|
||||
throw new InvalidOperationException($"无法从空轨模型提取下表面三角形: {railModel.DisplayName}");
|
||||
}
|
||||
|
||||
LogManager.Info($"[空轨] 提取到 {surfaceInfo.BottomTriangles.Count} 个下表面三角形");
|
||||
|
||||
// 2. 提取骨架线
|
||||
var skeleton = ExtractSkeletonFromTriangles(surfaceInfo.BottomTriangles);
|
||||
LogManager.Info($"[空轨] 提取到 {skeleton.Count} 个骨架点");
|
||||
|
||||
// 3. 沿骨架线生成采样点
|
||||
var samplePoints = GenerateSamplePointsAlongSkeleton(skeleton, samplingInterval);
|
||||
LogManager.Info($"[空轨] 生成 {samplePoints.Count} 个采样点");
|
||||
|
||||
// 4. 对每个采样点,从下方射线投射获取精确高度
|
||||
var pathPoints = new List<Point3D>();
|
||||
int successCount = 0;
|
||||
int failCount = 0;
|
||||
|
||||
foreach (var samplePoint in samplePoints)
|
||||
{
|
||||
try
|
||||
{
|
||||
double bottomZ = GetBottomHeightByRaycast(
|
||||
samplePoint,
|
||||
surfaceInfo.BottomTriangles,
|
||||
surfaceInfo.MinZ);
|
||||
|
||||
if (bottomZ > surfaceInfo.MinZ - 1000) // 检查是否有效
|
||||
{
|
||||
pathPoints.Add(new Point3D(
|
||||
samplePoint.X,
|
||||
samplePoint.Y,
|
||||
bottomZ));
|
||||
successCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.Warning($"[空轨] 采样点 ({samplePoint.X:F2}, {samplePoint.Y:F2}) 未找到有效交点");
|
||||
failCount++;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Warning($"[空轨] 采样点 ({samplePoint.X:F2}, {samplePoint.Y:F2}) 射线投射失败: {ex.Message}");
|
||||
failCount++;
|
||||
}
|
||||
}
|
||||
|
||||
LogManager.Info($"[空轨] 提取完成: 成功 {successCount} 个点, 失败 {failCount} 个点");
|
||||
|
||||
if (pathPoints.Count < 2)
|
||||
{
|
||||
throw new InvalidOperationException($"提取的中心线点数不足: {pathPoints.Count} (最少需要2个)");
|
||||
}
|
||||
|
||||
return pathPoints;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"[空轨] 提取下表面中心线失败: {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 提取空轨下表面信息(带缓存)
|
||||
/// </summary>
|
||||
private static RailBottomSurfaceInfo ExtractRailBottomSurface(ModelItem railModel)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 生成缓存键(使用DisplayName作为唯一标识)
|
||||
var cacheKey = $"rail_{railModel.DisplayName}_{railModel.GetHashCode()}";
|
||||
|
||||
// 检查缓存
|
||||
if (_railCache.TryGetValue(cacheKey, out var cachedInfo))
|
||||
{
|
||||
// 检查缓存是否过期
|
||||
if (DateTime.Now - cachedInfo.CacheTime < _cacheValidDuration)
|
||||
{
|
||||
LogManager.Debug($"[空轨] 使用缓存的下表面信息");
|
||||
return cachedInfo;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.Debug($"[空轨] 缓存已过期,重新提取");
|
||||
_railCache.Remove(cacheKey);
|
||||
}
|
||||
}
|
||||
|
||||
// 提取所有三角形
|
||||
var allTriangles = GeometryHelper.ExtractTriangles(new[] { railModel });
|
||||
if (allTriangles.Count == 0)
|
||||
{
|
||||
LogManager.Warning($"[空轨] 模型 {railModel.DisplayName} 没有三角形数据");
|
||||
return null;
|
||||
}
|
||||
|
||||
LogManager.Info($"[空轨] 提取到 {allTriangles.Count} 个三角形");
|
||||
|
||||
// 筛选下表面三角形
|
||||
var bottomTriangles = FilterBottomTriangles(allTriangles);
|
||||
LogManager.Info($"[空轨] 筛选出 {bottomTriangles.Count} 个下表面三角形");
|
||||
|
||||
if (bottomTriangles.Count == 0)
|
||||
{
|
||||
LogManager.Warning($"[空轨] 没有找到下表面三角形");
|
||||
return null;
|
||||
}
|
||||
|
||||
// 计算包围盒
|
||||
var bounds = CalculateBoundingBox(bottomTriangles);
|
||||
|
||||
// 创建下表面信息
|
||||
var surfaceInfo = new RailBottomSurfaceInfo
|
||||
{
|
||||
BottomTriangles = bottomTriangles,
|
||||
Bounds = bounds,
|
||||
MinZ = bounds.Min.Z,
|
||||
MaxZ = bounds.Max.Z,
|
||||
CacheTime = DateTime.Now
|
||||
};
|
||||
|
||||
// 加入缓存
|
||||
_railCache[cacheKey] = surfaceInfo;
|
||||
|
||||
return surfaceInfo;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"[空轨] 提取下表面信息失败: {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 筛选下表面三角形(法线向下,Z < 0)
|
||||
/// </summary>
|
||||
@ -1018,222 +857,6 @@ namespace NavisworksTransport.PathPlanning
|
||||
return normal;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从下表面三角形提取骨架线(支持拐弯)
|
||||
/// </summary>
|
||||
private static List<Point3D> ExtractSkeletonFromTriangles(List<Triangle3D> bottomTriangles)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 1. 计算每个三角形的中心点
|
||||
var triangleCenters = new List<Point3D>();
|
||||
foreach (var triangle in bottomTriangles)
|
||||
{
|
||||
var center = new Point3D(
|
||||
(triangle.Point1.X + triangle.Point2.X + triangle.Point3.X) / 3,
|
||||
(triangle.Point1.Y + triangle.Point2.Y + triangle.Point3.Y) / 3,
|
||||
(triangle.Point1.Z + triangle.Point2.Z + triangle.Point3.Z) / 3
|
||||
);
|
||||
triangleCenters.Add(center);
|
||||
}
|
||||
|
||||
LogManager.Info($"[空轨] 计算了 {triangleCenters.Count} 个三角形中心点");
|
||||
|
||||
// 2. 按邻近度排序,形成路径
|
||||
var sortedPoints = SortPointsByProximity(triangleCenters);
|
||||
LogManager.Info($"[空轨] 排序后得到 {sortedPoints.Count} 个骨架点");
|
||||
|
||||
// 2.5 使用全局PCA压缩对称点(适用于直线路径)
|
||||
var compressedPath = CollapseSymmetricPointsByPCA(sortedPoints);
|
||||
LogManager.Info($"[空轨] PCA压缩后得到 {compressedPath.Count} 个骨架点");
|
||||
|
||||
// 3. 简化路径(移除过近的点)
|
||||
var simplifiedPath = SimplifyPath(compressedPath, 0.1); // 0.1米阈值
|
||||
LogManager.Info($"[空轨] 简化后得到 {simplifiedPath.Count} 个骨架点");
|
||||
|
||||
// 4. 平滑路径(可选)
|
||||
var smoothedPath = SmoothPath(simplifiedPath);
|
||||
LogManager.Info($"[空轨] 平滑后得到 {smoothedPath.Count} 个骨架点");
|
||||
|
||||
return smoothedPath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"[空轨] 提取骨架线失败: {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 按邻近度排序点(形成路径)
|
||||
/// </summary>
|
||||
private static List<Point3D> SortPointsByProximity(List<Point3D> points)
|
||||
{
|
||||
if (points.Count == 0) return new List<Point3D>();
|
||||
|
||||
var sorted = new List<Point3D>();
|
||||
var remaining = new List<Point3D>(points);
|
||||
|
||||
// 从边界点开始(选择X最小的点)
|
||||
var current = remaining.OrderBy(p => p.X).First();
|
||||
sorted.Add(current);
|
||||
remaining.Remove(current);
|
||||
|
||||
// 依次查找最近的点
|
||||
while (remaining.Count > 0)
|
||||
{
|
||||
Point3D nearest = null;
|
||||
double minDistance = double.MaxValue;
|
||||
|
||||
foreach (var point in remaining)
|
||||
{
|
||||
double distance = CalculateDistance2D(current, point);
|
||||
if (distance < minDistance)
|
||||
{
|
||||
minDistance = distance;
|
||||
nearest = point;
|
||||
}
|
||||
}
|
||||
|
||||
if (nearest != null && minDistance < 10.0) // 10米阈值,避免跳跃
|
||||
{
|
||||
sorted.Add(nearest);
|
||||
remaining.Remove(nearest);
|
||||
current = nearest;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 没有找到合适的邻近点,结束
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return sorted;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用全局PCA压缩对称点(适用于直线路径)
|
||||
/// </summary>
|
||||
private static List<Point3D> CollapseSymmetricPointsByPCA(List<Point3D> points)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (points.Count < 3)
|
||||
{
|
||||
return new List<Point3D>(points);
|
||||
}
|
||||
|
||||
LogManager.Info("[空轨] 开始PCA压缩对称点");
|
||||
|
||||
// 1. 计算质心
|
||||
var centroid = new Point3D(
|
||||
points.Average(p => p.X),
|
||||
points.Average(p => p.Y),
|
||||
points.Average(p => p.Z)
|
||||
);
|
||||
|
||||
// 2. 计算协方差矩阵
|
||||
double covXX = 0, covYY = 0, covZZ = 0;
|
||||
double covXY = 0, covXZ = 0, covYZ = 0;
|
||||
|
||||
foreach (var point in points)
|
||||
{
|
||||
double dx = point.X - centroid.X;
|
||||
double dy = point.Y - centroid.Y;
|
||||
double dz = point.Z - centroid.Z;
|
||||
|
||||
covXX += dx * dx;
|
||||
covYY += dy * dy;
|
||||
covZZ += dz * dz;
|
||||
covXY += dx * dy;
|
||||
covXZ += dx * dz;
|
||||
covYZ += dy * dz;
|
||||
}
|
||||
|
||||
covXX /= points.Count;
|
||||
covYY /= points.Count;
|
||||
covZZ /= points.Count;
|
||||
covXY /= points.Count;
|
||||
covXZ /= points.Count;
|
||||
covYZ /= points.Count;
|
||||
|
||||
// 3. 计算特征值和特征向量(3x3对称矩阵)
|
||||
// 使用数值方法计算特征值
|
||||
var eigenvalues = new double[3];
|
||||
var eigenvectors = new Point3D[3];
|
||||
|
||||
ComputeEigen3x3(covXX, covYY, covZZ, covXY, covXZ, covYZ, eigenvalues, eigenvectors);
|
||||
|
||||
// 4. 找到最小特征值对应的特征向量(压缩方向)
|
||||
int minIndex = 0;
|
||||
if (eigenvalues[1] < eigenvalues[minIndex]) minIndex = 1;
|
||||
if (eigenvalues[2] < eigenvalues[minIndex]) minIndex = 2;
|
||||
|
||||
var compressAxis = eigenvectors[minIndex];
|
||||
|
||||
LogManager.Info($"[空轨] PCA特征值: {eigenvalues[0]:F4}, {eigenvalues[1]:F4}, {eigenvalues[2]:F4}");
|
||||
LogManager.Info($"[空轨] 压缩方向: ({compressAxis.X:F3}, {compressAxis.Y:F3}, {compressAxis.Z:F3})");
|
||||
|
||||
// 5. 沿路径方向分段
|
||||
// 找到最大特征值对应的特征向量(路径方向)
|
||||
int maxIndex = 0;
|
||||
if (eigenvalues[1] > eigenvalues[maxIndex]) maxIndex = 1;
|
||||
if (eigenvalues[2] > eigenvalues[maxIndex]) maxIndex = 2;
|
||||
|
||||
var pathAxis = eigenvectors[maxIndex];
|
||||
|
||||
// 将点投影到路径方向
|
||||
var projected = points.Select(p => new
|
||||
{
|
||||
Point = p,
|
||||
Position = Dot(new Point3D((p - centroid).X, (p - centroid).Y, (p - centroid).Z), pathAxis)
|
||||
}).OrderBy(x => x.Position).ToList();
|
||||
|
||||
// 6. 分段压缩
|
||||
var segmentLength = 0.5; // 0.5米分段
|
||||
var segments = new List<List<Point3D>>();
|
||||
var currentSegment = new List<Point3D>();
|
||||
double currentStart = projected[0].Position;
|
||||
|
||||
foreach (var item in projected)
|
||||
{
|
||||
if (item.Position - currentStart < segmentLength)
|
||||
{
|
||||
currentSegment.Add(item.Point);
|
||||
}
|
||||
else
|
||||
{
|
||||
segments.Add(currentSegment);
|
||||
currentSegment = new List<Point3D> { item.Point };
|
||||
currentStart = item.Position;
|
||||
}
|
||||
}
|
||||
if (currentSegment.Count > 0)
|
||||
segments.Add(currentSegment);
|
||||
|
||||
// 7. 每段压缩到中心
|
||||
var compressed = new List<Point3D>();
|
||||
foreach (var segment in segments)
|
||||
{
|
||||
var segmentCentroid = new Point3D(
|
||||
segment.Average(p => p.X),
|
||||
segment.Average(p => p.Y),
|
||||
segment.Average(p => p.Z)
|
||||
);
|
||||
compressed.Add(segmentCentroid);
|
||||
}
|
||||
|
||||
LogManager.Info($"[空轨] PCA压缩完成: {points.Count}个点 -> {compressed.Count}个点");
|
||||
|
||||
return compressed;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Warning($"[空轨] PCA压缩失败: {ex.Message},返回原始点");
|
||||
return new List<Point3D>(points);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算3x3对称矩阵的特征值和特征向量
|
||||
/// </summary>
|
||||
@ -1327,113 +950,6 @@ namespace NavisworksTransport.PathPlanning
|
||||
eigenvectors[2] = new Point3D(V[0, 2], V[1, 2], V[2, 2]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 点积
|
||||
/// </summary>
|
||||
private static double Dot(Point3D a, Point3D b)
|
||||
{
|
||||
return a.X * b.X + a.Y * b.Y + a.Z * b.Z;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 点积(Vector3D版本)
|
||||
/// </summary>
|
||||
private static double Dot(Vector3D a, Point3D b)
|
||||
{
|
||||
return a.X * b.X + a.Y * b.Y + a.Z * b.Z;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 简化路径(移除过近的点)
|
||||
/// </summary>
|
||||
private static List<Point3D> SimplifyPath(List<Point3D> path, double threshold)
|
||||
{
|
||||
if (path.Count <= 2) return new List<Point3D>(path);
|
||||
|
||||
var simplified = new List<Point3D> { path[0] };
|
||||
|
||||
for (int i = 1; i < path.Count - 1; i++)
|
||||
{
|
||||
double distanceToPrev = CalculateDistance2D(path[i], simplified.Last());
|
||||
double distanceToNext = CalculateDistance2D(path[i], path[i + 1]);
|
||||
|
||||
if (distanceToPrev >= threshold || distanceToNext >= threshold)
|
||||
{
|
||||
simplified.Add(path[i]);
|
||||
}
|
||||
}
|
||||
|
||||
simplified.Add(path.Last());
|
||||
return simplified;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 平滑路径(简单移动平均)
|
||||
/// </summary>
|
||||
private static List<Point3D> SmoothPath(List<Point3D> path)
|
||||
{
|
||||
if (path.Count <= 2) return new List<Point3D>(path);
|
||||
|
||||
var smoothed = new List<Point3D>();
|
||||
|
||||
// 第一个点保持不变
|
||||
smoothed.Add(path[0]);
|
||||
|
||||
// 中间点使用移动平均
|
||||
for (int i = 1; i < path.Count - 1; i++)
|
||||
{
|
||||
var prev = path[i - 1];
|
||||
var curr = path[i];
|
||||
var next = path[i + 1];
|
||||
|
||||
var smoothedPoint = new Point3D(
|
||||
(prev.X + curr.X + next.X) / 3,
|
||||
(prev.Y + curr.Y + next.Y) / 3,
|
||||
(prev.Z + curr.Z + next.Z) / 3
|
||||
);
|
||||
|
||||
smoothed.Add(smoothedPoint);
|
||||
}
|
||||
|
||||
// 最后一个点保持不变
|
||||
smoothed.Add(path.Last());
|
||||
|
||||
return smoothed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 沿骨架线生成采样点
|
||||
/// </summary>
|
||||
private static List<Point3D> GenerateSamplePointsAlongSkeleton(
|
||||
List<Point3D> skeleton,
|
||||
double samplingInterval)
|
||||
{
|
||||
if (skeleton.Count < 2)
|
||||
{
|
||||
throw new ArgumentException("骨架线至少需要2个点");
|
||||
}
|
||||
|
||||
var samplePoints = new List<Point3D>();
|
||||
|
||||
// 计算骨架线总长度
|
||||
double totalLength = CalculatePathLength(skeleton);
|
||||
|
||||
// 计算采样点数
|
||||
int sampleCount = Math.Max(2, (int)Math.Ceiling(totalLength / samplingInterval));
|
||||
|
||||
LogManager.Info($"[空轨] 骨架线总长度: {totalLength:F2}, 采样点数: {sampleCount}");
|
||||
|
||||
// 沿骨架线均匀采样
|
||||
for (int i = 0; i < sampleCount; i++)
|
||||
{
|
||||
double progress = (double)i / (sampleCount - 1);
|
||||
var point = InterpolateOnPath(skeleton, progress);
|
||||
samplePoints.Add(point);
|
||||
}
|
||||
|
||||
return samplePoints;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在路径上插值(支持拐弯)
|
||||
/// </summary>
|
||||
@ -1554,35 +1070,5 @@ namespace NavisworksTransport.PathPlanning
|
||||
return double.MinValue;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算包围盒(复用Utils中的方法)
|
||||
/// </summary>
|
||||
private static BoundingBox3D CalculateBoundingBox(List<Triangle3D> triangles)
|
||||
{
|
||||
if (triangles.Count == 0)
|
||||
{
|
||||
return new BoundingBox3D(new Point3D(0, 0, 0), new Point3D(0, 0, 0));
|
||||
}
|
||||
|
||||
double minX = double.MaxValue, minY = double.MaxValue, minZ = double.MaxValue;
|
||||
double maxX = double.MinValue, maxY = double.MinValue, maxZ = double.MinValue;
|
||||
|
||||
foreach (var triangle in triangles)
|
||||
{
|
||||
minX = Math.Min(minX, Math.Min(triangle.Point1.X, Math.Min(triangle.Point2.X, triangle.Point3.X)));
|
||||
minY = Math.Min(minY, Math.Min(triangle.Point1.Y, Math.Min(triangle.Point2.Y, triangle.Point3.Y)));
|
||||
minZ = Math.Min(minZ, Math.Min(triangle.Point1.Z, Math.Min(triangle.Point2.Z, triangle.Point3.Z)));
|
||||
|
||||
maxX = Math.Max(maxX, Math.Max(triangle.Point1.X, Math.Max(triangle.Point2.X, triangle.Point3.X)));
|
||||
maxY = Math.Max(maxY, Math.Max(triangle.Point1.Y, Math.Max(triangle.Point2.Y, triangle.Point3.Y)));
|
||||
maxZ = Math.Max(maxZ, Math.Max(triangle.Point1.Z, Math.Max(triangle.Point2.Z, triangle.Point3.Z)));
|
||||
}
|
||||
|
||||
return new BoundingBox3D(
|
||||
new Point3D(minX, minY, minZ),
|
||||
new Point3D(maxX, maxY, maxZ));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -526,8 +526,8 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
|
||||
try
|
||||
{
|
||||
// 2. 纯业务逻辑执行(后台线程,不使用UIStateManager)
|
||||
var result = await Task.Run(() =>
|
||||
// 2. 业务逻辑执行(必须在主线程上执行,因为包含 Navisworks COM API 调用)
|
||||
var result = NavisworksApiHelper.ExecuteOnUIThread(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
Loading…
Reference in New Issue
Block a user