NavisworksTransport/src/Utils/GeometryHelper.cs

1284 lines
48 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Autodesk.Navisworks.Api;
using ComApi = Autodesk.Navisworks.Api.Interop.ComApi;
using ComApiBridge = Autodesk.Navisworks.Api.ComApi.ComApiBridge;
namespace NavisworksTransport
{
/// <summary>
/// 几何提取器
/// </summary>
public class GeometryHelper
{
/// <summary>
/// 默认轮廓提取容差(米)
/// </summary>
private const double DEFAULT_OUTLINE_TOLERANCE_METERS = 0.5;
/// <summary>
/// 1厘米容差- 用于精确几何计算
/// </summary>
private const double TOLERANCE_1CM_METERS = 0.01;
/// <summary>
/// 提取模型项的顶视图轮廓
/// </summary>
/// <param name="modelItem">模型项</param>
/// <param name="tolerance">容差</param>
/// <returns>轮廓点集合</returns>
public static List<Point3D> ExtractTopViewOutline(ModelItem modelItem, double tolerance = DEFAULT_OUTLINE_TOLERANCE_METERS)
{
var points = new List<Point3D>();
try
{
LogManager.Debug($"开始提取模型 {modelItem.DisplayName} 的几何数据");
LogManager.Debug($"模型项类型: {modelItem.GetType().Name}");
LogManager.Debug($"模型项GUID: {modelItem.InstanceGuid}");
// 详细检查模型是否有几何数据
bool hasGeometry = modelItem.HasGeometry;
LogManager.Debug($"[关键检查] ModelItem.HasGeometry = {hasGeometry}");
if (!hasGeometry)
{
LogManager.Debug("[关键发现] 模型项没有几何数据 - HasGeometry 返回 false");
LogManager.Debug($"模型项详细信息:");
LogManager.Debug($" - 显示名称: {modelItem.DisplayName}");
LogManager.Debug($" - 实例GUID: {modelItem.InstanceGuid}");
// 检查子项数量来判断是否为叶节点
int childrenCount = modelItem.Children.Count();
LogManager.Debug($" - 子项数量: {childrenCount}");
LogManager.Debug($" - 是否为叶节点: {childrenCount == 0}");
// 尝试检查包围盒
try
{
var bbox = modelItem.BoundingBox();
if (bbox != null)
{
LogManager.Debug($" - 包围盒存在: {bbox.Min} - {bbox.Max}");
}
else
{
LogManager.Debug($" - 包围盒不存在");
}
}
catch (Exception bboxEx)
{
LogManager.Warning($" - 获取包围盒失败: {bboxEx.Message}");
}
return points;
}
LogManager.Debug("[几何检查通过] 模型项有几何数据,继续处理");
// 处理多实例问题 - 确保只获取当前实例的几何
var targetItem = modelItem;
int instanceCount = targetItem.Instances.Count();
LogManager.Debug($"原始模型实例数: {instanceCount}");
while (targetItem.Instances.Count() > 1)
{
targetItem = targetItem.Parent;
if (targetItem == null) break;
LogManager.Debug($"向上查找父项: {targetItem?.DisplayName}, 实例数: {targetItem?.Instances.Count()}");
}
if (targetItem == null)
{
LogManager.Warning("无法找到合适的几何节点");
return points;
}
// 再次检查目标项的几何状态
bool targetHasGeometry = targetItem.HasGeometry;
LogManager.Debug($"[目标项检查] 使用几何节点: {targetItem.DisplayName}");
LogManager.Debug($"[目标项检查] 实例数: {targetItem.Instances.Count()}");
LogManager.Debug($"[目标项检查] HasGeometry = {targetHasGeometry}");
if (!targetHasGeometry)
{
LogManager.Warning("[目标项问题] 目标节点也没有几何数据");
return points;
}
// 使用优化的几何提取方法
var triangles = ExtractTriangles(new[] { targetItem });
LogManager.Debug($"提取到 {triangles.Count} 个三角形");
if (triangles.Count > 0)
{
// 生成轮廓
points = GenerateOutlineFromTriangles(triangles, tolerance);
LogManager.Info($"生成轮廓包含 {points.Count} 个点");
}
else
{
LogManager.Warning("未提取到三角形数据");
}
}
catch (Exception ex)
{
LogManager.Error($"提取几何数据失败: {ex.Message}");
LogManager.Error($"堆栈跟踪: {ex.StackTrace}");
}
return points;
}
/// <summary>
/// 批量提取三角形(优化版本)
/// </summary>
/// <param name="modelItems">模型项集合</param>
/// <returns>三角形集合</returns>
public static List<Triangle3D> ExtractTriangles(IEnumerable<ModelItem> modelItems)
{
var triangles = new List<Triangle3D>();
ComApi.InwOpSelection comSelection = null;
Progress progress = null;
try
{
// 批量转换为 COM 选择 - 性能优化关键
var modelCollection = new ModelItemCollection();
int itemCount = 0;
foreach (var item in modelItems)
{
modelCollection.Add(item);
itemCount++;
}
LogManager.Info($"[批量提取] 开始批量提取 {itemCount} 个模型项的三角形");
var comState = ComStateManager.GetState();
comSelection = ComApiBridge.ToInwOpSelection(modelCollection);
LogManager.Debug($"[批量提取] COM 选择创建成功,路径数: {comSelection.Paths().Count}");
// 获取所有片段
var allFragments = GetAllFragments(comSelection);
LogManager.Info($"[批量提取] 获取到 {allFragments.Count} 个片段");
// 🎯 开始进度条
progress = Application.BeginProgress("提取几何体",
$"正在提取 {allFragments.Count} 个片段的三角形数据...");
try
{
int processedFragments = 0;
foreach (var fragmentInfo in allFragments)
{
// 🎯 检查用户是否取消
if (progress.IsCanceled)
{
LogManager.Info($"[批量提取] 用户取消操作,已处理 {processedFragments}/{allFragments.Count}");
break;
}
try
{
var callback = new OptimizedGeometryCallback(fragmentInfo.TransformMatrix);
fragmentInfo.Fragment.GenerateSimplePrimitives(
ComApi.nwEVertexProperty.eNORMAL,
callback);
var fragmentTriangles = callback.GetTriangles();
triangles.AddRange(fragmentTriangles);
processedFragments++;
// 🎯 更新进度(每处理完一个片段更新一次)
progress.Update((double)processedFragments / allFragments.Count);
}
catch (Exception ex)
{
LogManager.Error($"[批量提取] 处理片段失败: {ex.Message}");
}
}
LogManager.Info($"[批量提取] 片段处理完成,共提取 {triangles.Count} 个三角形");
}
finally
{
// 释放所有片段COM对象
foreach (var fragmentInfo in allFragments)
{
try
{
if (fragmentInfo.Fragment != null)
{
Marshal.ReleaseComObject(fragmentInfo.Fragment);
}
}
catch (Exception ex)
{
LogManager.Warning($"[批量提取] 释放片段COM对象失败: {ex.Message}");
}
}
}
}
catch (Exception ex)
{
LogManager.Error($"[批量提取] 提取三角形失败: {ex.Message}");
}
finally
{
// 🎯 确保进度条被关闭
if (progress != null)
{
Application.EndProgress();
}
// 释放COM对象避免内存泄漏
if (comSelection != null)
{
Marshal.ReleaseComObject(comSelection);
}
}
return triangles;
}
/// <summary>
/// 获取所有片段
/// </summary>
/// <param name="selection">COM 选择</param>
/// <returns>所有片段信息集合</returns>
public static List<FragmentInfo> GetAllFragments(ComApi.InwOpSelection selection)
{
var fragmentList = new List<FragmentInfo>();
int totalPathCount = 0;
int totalFragmentCount = 0;
try
{
foreach (ComApi.InwOaPath3 path in selection.Paths())
{
totalPathCount++;
int fragmentsInPath = 0;
try
{
foreach (ComApi.InwOaFragment3 fragment in path.Fragments())
{
fragmentsInPath++;
totalFragmentCount++;
try
{
// 获取片段的路径信息(仅用于日志记录)
var pathArray = ((Array)fragment.path.ArrayData).ToArray<int>();
var pathKey = string.Join(",", pathArray);
// 获取变换矩阵
var transform = (ComApi.InwLTransform3f3)(object)fragment.GetLocalToWorldMatrix();
// 🔧 关键修复预处理Transform矩阵并立即释放COM对象
double[] transformMatrix = null;
if (transform != null)
{
try
{
// 提取矩阵数据到普通数组
var matrixArray = (Array)(object)transform.Matrix;
transformMatrix = matrixArray.ToArray<double>();
}
catch (Exception ex)
{
LogManager.Error($"提取Transform矩阵失败: {ex.Message}");
transformMatrix = null;
}
finally
{
// 立即释放COM对象避免内存泄露
Marshal.ReleaseComObject(transform);
}
}
// 使用实际的路径键而不是递增索引
fragmentList.Add(new FragmentInfo
{
Fragment = fragment,
TransformMatrix = transformMatrix,
PathKey = pathKey // 使用实际的路径键
});
}
catch (Exception ex)
{
LogManager.Error($"处理单个片段失败: {ex.Message}");
}
}
}
catch (Exception ex)
{
LogManager.Error($"遍历路径片段失败: {ex.Message}");
}
finally
{
// 释放路径COM对象
if (path != null)
{
Marshal.ReleaseComObject(path);
}
}
}
}
catch (Exception ex)
{
LogManager.Error($"获取所有片段失败: {ex.Message}");
}
return fragmentList;
}
/// <summary>
/// 从三角形生成轮廓
/// </summary>
/// <param name="triangles">三角形集合</param>
/// <param name="tolerance">容差</param>
/// <returns>轮廓点集合</returns>
private static List<Point3D> GenerateOutlineFromTriangles(List<Triangle3D> triangles, double tolerance)
{
var outlinePoints = new List<Point3D>();
try
{
if (triangles.Count == 0) return outlinePoints;
// 找到最高的 Z 坐标
var maxZ = triangles.SelectMany(t => new[] { t.Point1.Z, t.Point2.Z, t.Point3.Z }).Max();
// 筛选顶部表面的三角形
var topTriangles = triangles.Where(t =>
Math.Abs(t.Point1.Z - maxZ) < tolerance &&
Math.Abs(t.Point2.Z - maxZ) < tolerance &&
Math.Abs(t.Point3.Z - maxZ) < tolerance).ToList();
LogManager.Debug($"顶部三角形数量: {topTriangles.Count}");
if (topTriangles.Count == 0) return outlinePoints;
// 提取所有边
var edges = new List<Edge2D>();
foreach (var triangle in topTriangles)
{
edges.Add(new Edge2D(
new Point2D(triangle.Point1.X, triangle.Point1.Y),
new Point2D(triangle.Point2.X, triangle.Point2.Y)));
edges.Add(new Edge2D(
new Point2D(triangle.Point2.X, triangle.Point2.Y),
new Point2D(triangle.Point3.X, triangle.Point3.Y)));
edges.Add(new Edge2D(
new Point2D(triangle.Point3.X, triangle.Point3.Y),
new Point2D(triangle.Point1.X, triangle.Point1.Y)));
}
// 找到边界边(只出现一次的边)
var boundaryEdges = FindBoundaryEdges(edges);
LogManager.Debug($"边界边数量: {boundaryEdges.Count}");
if (boundaryEdges.Count > 0)
{
// 构建轮廓
var outlinePoints2D = BuildOutlineFromEdges(boundaryEdges);
// 转换为3D点使用最高Z坐标
outlinePoints = outlinePoints2D.Select(p => new Point3D(p.X, p.Y, maxZ)).ToList();
LogManager.Debug($"最终轮廓点数量: {outlinePoints.Count}");
}
}
catch (Exception ex)
{
LogManager.Error($"生成轮廓失败: {ex.Message}");
}
return outlinePoints;
}
/// <summary>
/// 查找边界边(在俯视图中只出现一次的边)
/// </summary>
/// <param name="edges">所有边的集合</param>
/// <returns>边界边集合</returns>
private static List<Edge2D> FindBoundaryEdges(List<Edge2D> edges)
{
var boundaryEdges = new List<Edge2D>();
var edgeCount = new Dictionary<string, int>();
var edgeMap = new Dictionary<string, Edge2D>();
try
{
// 统计每条边出现的次数
foreach (var edge in edges)
{
var key = edge.GetKey();
var reverseKey = edge.GetReverseKey();
// 使用规范化的边键(总是用较小的端点作为起点)
var normalizedKey = string.Compare(key, reverseKey) < 0 ? key : reverseKey;
var normalizedEdge = string.Compare(key, reverseKey) < 0 ? edge : new Edge2D(edge.End, edge.Start);
if (edgeCount.ContainsKey(normalizedKey))
{
edgeCount[normalizedKey]++;
}
else
{
edgeCount[normalizedKey] = 1;
edgeMap[normalizedKey] = normalizedEdge;
}
}
LogManager.Debug($"边计数完成,唯一边数: {edgeCount.Count}");
// 找出只出现一次的边(边界边)
int boundaryCount = 0;
int sharedCount = 0;
foreach (var kvp in edgeCount)
{
if (kvp.Value == 1)
{
boundaryEdges.Add(edgeMap[kvp.Key]);
boundaryCount++;
}
else
{
sharedCount++;
}
}
LogManager.Debug($"边界边分析完成:");
LogManager.Debug($" 边界边数量: {boundaryCount}");
LogManager.Debug($" 共享边数量: {sharedCount}");
LogManager.Debug($" 边界/总边比例: {(double)boundaryCount / edges.Count:P1}");
// 验证边界边的连通性
if (boundaryEdges.Count > 0)
{
var connectivity = AnalyzeBoundaryConnectivity(boundaryEdges);
LogManager.Debug($"边界边连通性分析: {connectivity}");
}
}
catch (Exception ex)
{
LogManager.Error($"查找边界边失败: {ex.Message}");
}
return boundaryEdges;
}
/// <summary>
/// 分析边界边的连通性
/// </summary>
/// <param name="boundaryEdges">边界边集合</param>
/// <returns>连通性分析字符串</returns>
private static string AnalyzeBoundaryConnectivity(List<Edge2D> boundaryEdges)
{
try
{
var vertexConnectionCount = new Dictionary<string, int>();
// 统计每个顶点的连接数
foreach (var edge in boundaryEdges)
{
var startKey = $"{edge.Start.X:F3},{edge.Start.Y:F3}";
var endKey = $"{edge.End.X:F3},{edge.End.Y:F3}";
vertexConnectionCount[startKey] = (vertexConnectionCount.ContainsKey(startKey) ? vertexConnectionCount[startKey] : 0) + 1;
vertexConnectionCount[endKey] = (vertexConnectionCount.ContainsKey(endKey) ? vertexConnectionCount[endKey] : 0) + 1;
}
// 分析连接模式
int isolatedVertices = 0; // 度数为1的顶点
int normalVertices = 0; // 度数为2的顶点
int junctionVertices = 0; // 度数>2的顶点
foreach (var count in vertexConnectionCount.Values)
{
if (count == 1) isolatedVertices++;
else if (count == 2) normalVertices++;
else junctionVertices++;
}
return $"顶点: {vertexConnectionCount.Count}, 孤立: {isolatedVertices}, 正常: {normalVertices}, 连接点: {junctionVertices}";
}
catch (Exception ex)
{
return $"连通性分析失败: {ex.Message}";
}
}
/// <summary>
/// 从边界边构建轮廓点集合(支持多轮廓)
/// </summary>
/// <param name="edges">边界边集合</param>
/// <returns>轮廓点集合</returns>
private static List<Point2D> BuildOutlineFromEdges(List<Edge2D> edges)
{
var allOutlinePoints = new List<Point2D>();
var remainingEdges = new List<Edge2D>(edges);
var contourCount = 0;
try
{
LogManager.Debug($"开始构建轮廓,总边数: {edges.Count}");
while (remainingEdges.Count > 0 && contourCount < 10) // 最多处理10个轮廓
{
contourCount++;
LogManager.Debug($"=== 开始构建第 {contourCount} 个轮廓 ===");
var currentContour = BuildSingleContour(remainingEdges);
if (currentContour.Count >= 3) // 至少3个点才能形成有效轮廓
{
LogManager.Debug($"第 {contourCount} 个轮廓包含 {currentContour.Count} 个点");
allOutlinePoints.AddRange(currentContour);
// 添加轮廓分隔符(使用特殊坐标标记)
if (contourCount > 1)
{
allOutlinePoints.Add(new Point2D(double.NaN, double.NaN)); // 轮廓分隔符
}
}
else
{
LogManager.Debug($"第 {contourCount} 个轮廓点数不足({currentContour.Count}),跳过");
break; // 如果轮廓太小,可能是噪声,停止处理
}
}
LogManager.Info($"轮廓构建完成,共 {contourCount} 个轮廓,总点数: {allOutlinePoints.Count}");
// 如果有多个轮廓,返回最大的外部轮廓
if (contourCount > 1)
{
LogManager.Debug("检测到多个轮廓,返回最大轮廓作为外部边界");
return GetLargestContour(allOutlinePoints);
}
}
catch (Exception ex)
{
LogManager.Error($"构建轮廓失败: {ex.Message}");
}
return allOutlinePoints;
}
/// <summary>
/// 构建单个连续轮廓
/// </summary>
/// <param name="remainingEdges">剩余边集合(会被修改)</param>
/// <returns>单个轮廓的点集合</returns>
private static List<Point2D> BuildSingleContour(List<Edge2D> remainingEdges)
{
var contour = new List<Point2D>();
try
{
if (remainingEdges.Count == 0) return contour;
// 从第一条边开始
var currentEdge = remainingEdges[0];
remainingEdges.RemoveAt(0);
contour.Add(currentEdge.Start);
var currentPoint = currentEdge.End;
var startPoint = currentEdge.Start;
LogManager.Debug($"轮廓起点: ({startPoint.X:F2}, {startPoint.Y:F2})");
// 连接后续的边
int maxIterations = remainingEdges.Count + 10; // 防止无限循环
int iteration = 0;
while (remainingEdges.Count > 0 && iteration < maxIterations)
{
iteration++;
var nextEdgeIndex = -1;
var tolerance = TOLERANCE_1CM_METERS; // 1cm容差稍微放宽
for (int i = 0; i < remainingEdges.Count; i++)
{
var edge = remainingEdges[i];
// 检查边的起点是否与当前点连接
if (Math.Abs(edge.Start.X - currentPoint.X) < tolerance &&
Math.Abs(edge.Start.Y - currentPoint.Y) < tolerance)
{
nextEdgeIndex = i;
currentPoint = edge.End;
break;
}
// 检查边的终点是否与当前点连接(反向)
else if (Math.Abs(edge.End.X - currentPoint.X) < tolerance &&
Math.Abs(edge.End.Y - currentPoint.Y) < tolerance)
{
nextEdgeIndex = i;
currentPoint = edge.Start;
break;
}
}
if (nextEdgeIndex >= 0)
{
contour.Add(currentPoint);
remainingEdges.RemoveAt(nextEdgeIndex);
// 检查是否回到起点(闭合轮廓)
if (Math.Abs(currentPoint.X - startPoint.X) < tolerance &&
Math.Abs(currentPoint.Y - startPoint.Y) < tolerance)
{
LogManager.Debug($"轮廓已闭合,点数: {contour.Count}");
break;
}
}
else
{
LogManager.Warning($"无法找到连接边,轮廓中断,点数: {contour.Count}");
break;
}
}
if (iteration >= maxIterations)
{
LogManager.Warning($"轮廓构建达到最大迭代次数,强制停止");
}
}
catch (Exception ex)
{
LogManager.Error($"构建单个轮廓失败: {ex.Message}");
}
return contour;
}
/// <summary>
/// 从多个轮廓中获取最大的轮廓(外部边界)
/// </summary>
/// <param name="allPoints">包含多个轮廓的点集合</param>
/// <returns>最大轮廓的点集合</returns>
private static List<Point2D> GetLargestContour(List<Point2D> allPoints)
{
var contours = new List<List<Point2D>>();
var currentContour = new List<Point2D>();
try
{
// 分离各个轮廓
foreach (var point in allPoints)
{
// 检查是否为分隔符
if (double.IsNaN(point.X) || double.IsNaN(point.Y))
{
if (currentContour.Count > 0)
{
contours.Add(new List<Point2D>(currentContour));
currentContour.Clear();
}
}
else
{
currentContour.Add(point);
}
}
// 添加最后一个轮廓
if (currentContour.Count > 0)
{
contours.Add(currentContour);
}
if (contours.Count == 0)
{
LogManager.Warning("未找到有效轮廓");
return allPoints;
}
// 找到最大的轮廓(按包围盒面积)
double maxArea = 0;
List<Point2D> largestContour = new List<Point2D>();
for (int i = 0; i < contours.Count; i++)
{
var contour = contours[i];
if (contour.Count >= 3)
{
var area = CalculateContourArea(contour);
LogManager.Debug($"轮廓 {i + 1} 面积: {area:F2}, 点数: {contour.Count}");
if (area > maxArea)
{
maxArea = area;
largestContour = contour;
}
}
}
LogManager.Debug($"选择最大轮廓,面积: {maxArea:F2}, 原始点数: {largestContour.Count}");
// 清理和排序最大轮廓
var cleanedContour = CleanAndSortContour(largestContour);
LogManager.Debug($"清理后轮廓点数: {cleanedContour.Count}");
return cleanedContour;
}
catch (Exception ex)
{
LogManager.Error($"获取最大轮廓失败: {ex.Message}");
return allPoints; // 返回原始点集合
}
}
/// <summary>
/// 清理和排序轮廓点
/// </summary>
/// <param name="contour">原始轮廓点</param>
/// <returns>清理和排序后的轮廓点</returns>
private static List<Point2D> CleanAndSortContour(List<Point2D> contour)
{
var result = new List<Point2D>();
try
{
if (contour.Count < 3)
{
LogManager.Warning($"轮廓点数不足: {contour.Count}");
return contour;
}
// 第1步移除重复点
var uniquePoints = new List<Point2D>();
const double tolerance = TOLERANCE_1CM_METERS; // 1cm容差
foreach (var point in contour)
{
bool isDuplicate = false;
foreach (var existing in uniquePoints)
{
if (Math.Abs(point.X - existing.X) < tolerance &&
Math.Abs(point.Y - existing.Y) < tolerance)
{
isDuplicate = true;
break;
}
}
if (!isDuplicate)
{
uniquePoints.Add(point);
}
}
LogManager.Debug($"去重后点数: {uniquePoints.Count}");
if (uniquePoints.Count < 3)
{
LogManager.Warning("去重后点数不足,返回原始轮廓");
return contour;
}
// 第2步排序点以形成正确的轮廓
result = SortPointsIntoContour(uniquePoints);
LogManager.Debug($"排序后轮廓点数: {result.Count}");
for (int i = 0; i < result.Count; i++)
{
LogManager.Debug($" 排序点 {i}: ({result[i].X:F2}, {result[i].Y:F2})");
}
}
catch (Exception ex)
{
LogManager.Error($"清理轮廓失败: {ex.Message}");
return contour;
}
return result.Count >= 3 ? result : contour;
}
/// <summary>
/// 将点排序成正确的轮廓顺序
/// </summary>
/// <param name="points">无序的点集合</param>
/// <returns>有序的轮廓点</returns>
private static List<Point2D> SortPointsIntoContour(List<Point2D> points)
{
var sortedPoints = new List<Point2D>();
try
{
if (points.Count < 3) return points;
// 找到最左下角的点作为起点(保证起点的唯一性)
var startPoint = points.OrderBy(p => p.X).ThenBy(p => p.Y).First();
sortedPoints.Add(startPoint);
var remainingPoints = new List<Point2D>(points);
remainingPoints.Remove(startPoint);
LogManager.Debug($"轮廓起始点: ({startPoint.X:F2}, {startPoint.Y:F2})");
// 使用最近邻算法按顺序连接点
var currentPoint = startPoint;
while (remainingPoints.Count > 0)
{
Point2D? nearestPoint = null;
double minDistance = double.MaxValue;
foreach (var point in remainingPoints)
{
var distance = Math.Sqrt(
Math.Pow(point.X - currentPoint.X, 2) +
Math.Pow(point.Y - currentPoint.Y, 2)
);
if (distance < minDistance)
{
minDistance = distance;
nearestPoint = point;
}
}
if (nearestPoint.HasValue)
{
sortedPoints.Add(nearestPoint.Value);
remainingPoints.Remove(nearestPoint.Value);
currentPoint = nearestPoint.Value;
LogManager.Debug($"下一个点: ({nearestPoint.Value.X:F2}, {nearestPoint.Value.Y:F2}), 距离: {minDistance:F2}");
}
else
{
LogManager.Warning("无法找到下一个最近点");
break;
}
}
// 验证轮廓是否闭合(可选)
if (sortedPoints.Count >= 3)
{
var firstPoint = sortedPoints[0];
var lastPoint = sortedPoints[sortedPoints.Count - 1];
var closingDistance = Math.Sqrt(
Math.Pow(lastPoint.X - firstPoint.X, 2) +
Math.Pow(lastPoint.Y - firstPoint.Y, 2)
);
LogManager.Debug($"轮廓闭合距离: {closingDistance:F2}");
}
}
catch (Exception ex)
{
LogManager.Error($"排序轮廓点失败: {ex.Message}");
return points;
}
return sortedPoints;
}
/// <summary>
/// 计算轮廓的包围盒面积
/// </summary>
/// <param name="contour">轮廓点集合</param>
/// <returns>包围盒面积</returns>
private static double CalculateContourArea(List<Point2D> contour)
{
if (contour.Count < 3) return 0;
try
{
var minX = contour.Min(p => p.X);
var maxX = contour.Max(p => p.X);
var minY = contour.Min(p => p.Y);
var maxY = contour.Max(p => p.Y);
return (maxX - minX) * (maxY - minY);
}
catch
{
return 0;
}
}
/// <summary>
/// 射线与三角形相交检测Möller-Trumbore算法
/// </summary>
/// <param name="rayOrigin">射线起点</param>
/// <param name="rayDirection">射线方向</param>
/// <param name="triangle">三角形</param>
/// <param name="intersectionZ">相交点的Z坐标</param>
/// <returns>是否相交</returns>
public static bool RayTriangleIntersect(Point3D rayOrigin, Point3D rayDirection, Triangle3D triangle, out double intersectionZ)
{
intersectionZ = 0.0;
const double EPSILON = 0.0000001;
try
{
// 计算三角形的两条边
var edge1 = SubtractPoints(triangle.Point2, triangle.Point1);
var edge2 = SubtractPoints(triangle.Point3, triangle.Point1);
// 计算射线方向与edge2的叉积
var h = CrossProduct(rayDirection, edge2);
var a = DotProduct(edge1, h);
// 如果a接近0射线与三角形平行
if (a > -EPSILON && a < EPSILON)
{
return false;
}
var f = 1.0 / a;
var s = SubtractPoints(rayOrigin, triangle.Point1);
var u = f * DotProduct(s, h);
if (u < 0.0 || u > 1.0)
{
return false;
}
var q = CrossProduct(s, edge1);
var v = f * DotProduct(rayDirection, q);
if (v < 0.0 || u + v > 1.0)
{
return false;
}
// 计算t值射线参数
var t = f * DotProduct(edge2, q);
if (t > EPSILON) // 射线相交
{
// 计算交点
intersectionZ = rayOrigin.Z + t * rayDirection.Z;
return true;
}
return false; // 线段相交但射线不相交
}
catch (Exception ex)
{
LogManager.Error($"射线-三角形相交计算出错: {ex.Message}");
return false;
}
}
/// <summary>
/// 点减法运算
/// </summary>
public static Point3D SubtractPoints(Point3D a, Point3D b)
{
return new Point3D(a.X - b.X, a.Y - b.Y, a.Z - b.Z);
}
/// <summary>
/// 向量叉积运算
/// </summary>
public static Point3D CrossProduct(Point3D a, Point3D b)
{
return new Point3D(
a.Y * b.Z - a.Z * b.Y,
a.Z * b.X - a.X * b.Z,
a.X * b.Y - a.Y * b.X
);
}
/// <summary>
/// Rodrigues旋转公式 - 绕任意轴旋转点
/// </summary>
/// <param name="point">要旋转的点</param>
/// <param name="center">旋转中心</param>
/// <param name="axis">旋转轴(单位向量)</param>
/// <param name="angle">旋转角度(弧度)</param>
/// <returns>旋转后的点</returns>
public static Point3D RotatePointAroundAxis(
Point3D point,
Point3D center,
Vector3D axis,
double angle)
{
// 将点转换为相对于中心的向量
Vector3D v = point - center;
// Rodrigues旋转公式
// v_rot = v * cos(θ) + (k × v) * sin(θ) + k * (k · v) * (1 - cos(θ))
Point3D kxv = CrossProduct(
new Point3D(axis.X, axis.Y, axis.Z),
new Point3D(v.X, v.Y, v.Z));
double kdv = DotProduct(
new Point3D(axis.X, axis.Y, axis.Z),
new Point3D(v.X, v.Y, v.Z));
Vector3D kxvVec = new Vector3D(kxv.X, kxv.Y, kxv.Z);
Vector3D vRot = v * Math.Cos(angle) + kxvVec * Math.Sin(angle) + axis * kdv * (1 - Math.Cos(angle));
return center + vRot;
}
/// <summary>
/// 向量点积运算
/// </summary>
public static double DotProduct(Point3D a, Point3D b)
{
return a.X * b.X + a.Y * b.Y + a.Z * b.Z;
}
/// <summary>
/// 检查2D点是否在三角形内部使用重心坐标法
/// </summary>
/// <param name="point">测试点</param>
/// <param name="triangle">三角形</param>
/// <returns>是否在内部</returns>
public static bool IsPointInTriangle2D(Point3D point, Triangle3D triangle)
{
// 使用重心坐标法判断点是否在三角形内
var v0x = triangle.Point3.X - triangle.Point1.X;
var v0y = triangle.Point3.Y - triangle.Point1.Y;
var v1x = triangle.Point2.X - triangle.Point1.X;
var v1y = triangle.Point2.Y - triangle.Point1.Y;
var v2x = point.X - triangle.Point1.X;
var v2y = point.Y - triangle.Point1.Y;
var dot00 = v0x * v0x + v0y * v0y;
var dot01 = v0x * v1x + v0y * v1y;
var dot02 = v0x * v2x + v0y * v2y;
var dot11 = v1x * v1x + v1y * v1y;
var dot12 = v1x * v2x + v1y * v2y;
var invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
var u = (dot11 * dot02 - dot01 * dot12) * invDenom;
var v = (dot00 * dot12 - dot01 * dot02) * invDenom;
return (u >= 0) && (v >= 0) && (u + v <= 1);
}
/// <summary>
/// 计算两个3D点之间的距离
/// </summary>
/// <param name="point1">第一个点</param>
/// <param name="point2">第二个点</param>
/// <returns>两点之间的欧几里得距离</returns>
public static double CalculatePointDistance(Point3D point1, Point3D point2)
{
var dx = point1.X - point2.X;
var dy = point1.Y - point2.Y;
var dz = point1.Z - point2.Z;
return Math.Sqrt(dx * dx + dy * dy + dz * dz);
}
}
/// <summary>
/// 片段信息类 - 优化版本预处理Transform矩阵避免COM对象泄露
/// </summary>
public class FragmentInfo
{
public ComApi.InwOaFragment3 Fragment { get; set; }
public double[] TransformMatrix { get; set; } // 预处理为普通数组避免COM对象泄露
public string PathKey { get; set; }
}
/// <summary>
/// 优化的几何回调处理器 - 使用预处理的矩阵数组避免COM对象泄露
/// </summary>
public class OptimizedGeometryCallback : ComApi.InwSimplePrimitivesCB
{
private List<Triangle3D> _triangles = new List<Triangle3D>();
private double[] _transformMatrix;
public OptimizedGeometryCallback(double[] transformMatrix)
{
_transformMatrix = transformMatrix;
}
public void Line(ComApi.InwSimpleVertex v1, ComApi.InwSimpleVertex v2)
{
// 线段处理(如果需要)
}
public void Point(ComApi.InwSimpleVertex v1)
{
// 点处理(如果需要)
}
public void SnapPoint(ComApi.InwSimpleVertex v1)
{
// 捕捉点处理(如果需要)
}
public void Triangle(ComApi.InwSimpleVertex v1, ComApi.InwSimpleVertex v2, ComApi.InwSimpleVertex v3)
{
try
{
// 使用优化的数组访问方法
var coords1 = ((Array)(object)v1.coord).ToArray<float>();
var coords2 = ((Array)(object)v2.coord).ToArray<float>();
var coords3 = ((Array)(object)v3.coord).ToArray<float>();
// 创建局部坐标点
var localPoint1 = new Point3D(coords1[0], coords1[1], coords1[2]);
var localPoint2 = new Point3D(coords2[0], coords2[1], coords2[2]);
var localPoint3 = new Point3D(coords3[0], coords3[1], coords3[2]);
// 应用变换矩阵转换为世界坐标
var worldPoint1 = TransformPoint(localPoint1);
var worldPoint2 = TransformPoint(localPoint2);
var worldPoint3 = TransformPoint(localPoint3);
// 创建三角形
var triangle = new Triangle3D(worldPoint1, worldPoint2, worldPoint3);
_triangles.Add(triangle);
}
catch (Exception ex)
{
LogManager.Error($"处理三角形失败: {ex.Message}");
}
}
/// <summary>
/// 将局部坐标点转换为世界坐标点 - 优化版本,直接使用预处理的矩阵数组
/// </summary>
/// <param name="localPoint">局部坐标点</param>
/// <returns>世界坐标点</returns>
private Point3D TransformPoint(Point3D localPoint)
{
if (_transformMatrix == null || _transformMatrix.Length != 16)
{
return localPoint; // 如果没有有效的变换矩阵,返回原始点
}
try
{
// 应用变换矩阵 (4x4 矩阵,列主序)
double x = localPoint.X;
double y = localPoint.Y;
double z = localPoint.Z;
double w = 1.0;
double newX = _transformMatrix[0] * x + _transformMatrix[4] * y + _transformMatrix[8] * z + _transformMatrix[12] * w;
double newY = _transformMatrix[1] * x + _transformMatrix[5] * y + _transformMatrix[9] * z + _transformMatrix[13] * w;
double newZ = _transformMatrix[2] * x + _transformMatrix[6] * y + _transformMatrix[10] * z + _transformMatrix[14] * w;
double newW = _transformMatrix[3] * x + _transformMatrix[7] * y + _transformMatrix[11] * z + _transformMatrix[15] * w;
// 透视除法(如果需要)
if (Math.Abs(newW) > 1e-10)
{
newX /= newW;
newY /= newW;
newZ /= newW;
}
return new Point3D(newX, newY, newZ);
}
catch (Exception ex)
{
LogManager.Error($"坐标变换失败: {ex.Message}");
return localPoint;
}
}
/// <summary>
/// 获取提取的三角形集合
/// </summary>
/// <returns>三角形集合</returns>
public List<Triangle3D> GetTriangles()
{
return _triangles;
}
}
/// <summary>
/// 数组扩展方法(性能优化)
/// </summary>
public static class ArrayExtensions
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T[] ToArray<T>(this Array arr) where T : struct
{
T[] result = new T[arr.Length];
Array.Copy(arr, result, result.Length);
return result;
}
}
/// <summary>
/// 三角形结构体
/// </summary>
public struct Triangle3D
{
public Point3D Point1 { get; }
public Point3D Point2 { get; }
public Point3D Point3 { get; }
public Triangle3D(Point3D point1, Point3D point2, Point3D point3)
{
Point1 = point1;
Point2 = point2;
Point3 = point3;
}
public override string ToString()
{
return $"Triangle[{Point1}, {Point2}, {Point3}]";
}
}
/// <summary>
/// 2D点结构体
/// </summary>
public struct Point2D
{
public double X { get; }
public double Y { get; }
public Point2D(double x, double y)
{
X = x;
Y = y;
}
public override string ToString()
{
return $"Point2D({X:F3}, {Y:F3})";
}
}
/// <summary>
/// 2D边结构体
/// </summary>
public struct Edge2D
{
public Point2D Start { get; }
public Point2D End { get; }
public Edge2D(Point2D start, Point2D end)
{
Start = start;
End = end;
}
public string GetKey()
{
return $"{Start.X:F6},{Start.Y:F6}-{End.X:F6},{End.Y:F6}";
}
public string GetReverseKey()
{
return $"{End.X:F6},{End.Y:F6}-{Start.X:F6},{Start.Y:F6}";
}
public override string ToString()
{
return $"Edge2D[{Start} -> {End}]";
}
}
}