1029 lines
38 KiB
C#
1029 lines
38 KiB
C#
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 GeometryExtractor
|
||
{
|
||
/// <summary>
|
||
/// 提取模型项的顶视图轮廓
|
||
/// </summary>
|
||
/// <param name="modelItem">模型项</param>
|
||
/// <param name="tolerance">容差</param>
|
||
/// <returns>轮廓点集合</returns>
|
||
public static List<Point3D> ExtractTopViewOutline(ModelItem modelItem, double tolerance = 0.5)
|
||
{
|
||
var points = new List<Point3D>();
|
||
|
||
try
|
||
{
|
||
LogManager.WriteLog($"开始提取模型 {modelItem.DisplayName} 的几何数据");
|
||
LogManager.WriteLog($"模型项类型: {modelItem.GetType().Name}");
|
||
LogManager.WriteLog($"模型项GUID: {modelItem.InstanceGuid}");
|
||
|
||
// 详细检查模型是否有几何数据
|
||
bool hasGeometry = modelItem.HasGeometry;
|
||
LogManager.WriteLog($"[关键检查] ModelItem.HasGeometry = {hasGeometry}");
|
||
|
||
if (!hasGeometry)
|
||
{
|
||
LogManager.WriteLog("[关键发现] 模型项没有几何数据 - HasGeometry 返回 false");
|
||
LogManager.WriteLog($"模型项详细信息:");
|
||
LogManager.WriteLog($" - 显示名称: {modelItem.DisplayName}");
|
||
LogManager.WriteLog($" - 实例GUID: {modelItem.InstanceGuid}");
|
||
|
||
// 检查子项数量来判断是否为叶节点
|
||
int childrenCount = modelItem.Children.Count();
|
||
LogManager.WriteLog($" - 子项数量: {childrenCount}");
|
||
LogManager.WriteLog($" - 是否为叶节点: {childrenCount == 0}");
|
||
|
||
// 尝试检查包围盒
|
||
try
|
||
{
|
||
var bbox = modelItem.BoundingBox();
|
||
if (bbox != null)
|
||
{
|
||
LogManager.WriteLog($" - 包围盒存在: {bbox.Min} - {bbox.Max}");
|
||
}
|
||
else
|
||
{
|
||
LogManager.WriteLog($" - 包围盒不存在");
|
||
}
|
||
}
|
||
catch (Exception bboxEx)
|
||
{
|
||
LogManager.WriteLog($" - 获取包围盒失败: {bboxEx.Message}");
|
||
}
|
||
|
||
return points;
|
||
}
|
||
|
||
LogManager.WriteLog("[几何检查通过] 模型项有几何数据,继续处理");
|
||
|
||
// 处理多实例问题 - 确保只获取当前实例的几何
|
||
var targetItem = modelItem;
|
||
int instanceCount = targetItem.Instances.Count();
|
||
LogManager.WriteLog($"原始模型实例数: {instanceCount}");
|
||
|
||
while (targetItem.Instances.Count() > 1)
|
||
{
|
||
targetItem = targetItem.Parent;
|
||
if (targetItem == null) break;
|
||
LogManager.WriteLog($"向上查找父项: {targetItem?.DisplayName}, 实例数: {targetItem?.Instances.Count()}");
|
||
}
|
||
|
||
if (targetItem == null)
|
||
{
|
||
LogManager.WriteLog("无法找到合适的几何节点");
|
||
return points;
|
||
}
|
||
|
||
// 再次检查目标项的几何状态
|
||
bool targetHasGeometry = targetItem.HasGeometry;
|
||
LogManager.WriteLog($"[目标项检查] 使用几何节点: {targetItem.DisplayName}");
|
||
LogManager.WriteLog($"[目标项检查] 实例数: {targetItem.Instances.Count()}");
|
||
LogManager.WriteLog($"[目标项检查] HasGeometry = {targetHasGeometry}");
|
||
|
||
if (!targetHasGeometry)
|
||
{
|
||
LogManager.WriteLog("[目标项问题] 目标节点也没有几何数据");
|
||
return points;
|
||
}
|
||
|
||
// 使用优化的几何提取方法
|
||
var triangles = ExtractTrianglesOptimized(targetItem);
|
||
LogManager.WriteLog($"提取到 {triangles.Count} 个三角形");
|
||
|
||
if (triangles.Count > 0)
|
||
{
|
||
// 生成轮廓
|
||
points = GenerateOutlineFromTriangles(triangles, tolerance);
|
||
LogManager.WriteLog($"生成轮廓包含 {points.Count} 个点");
|
||
}
|
||
else
|
||
{
|
||
LogManager.WriteLog("未提取到三角形数据");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.WriteLog($"提取几何数据失败: {ex.Message}");
|
||
LogManager.WriteLog($"堆栈跟踪: {ex.StackTrace}");
|
||
}
|
||
|
||
return points;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 优化的三角形提取方法
|
||
/// </summary>
|
||
/// <param name="modelItem">模型项</param>
|
||
/// <returns>三角形集合</returns>
|
||
private static List<Triangle3D> ExtractTrianglesOptimized(ModelItem modelItem)
|
||
{
|
||
var triangles = new List<Triangle3D>();
|
||
|
||
try
|
||
{
|
||
// 转换为 COM 选择
|
||
var modelCollection = new ModelItemCollection();
|
||
modelCollection.Add(modelItem);
|
||
|
||
var comState = ComApiBridge.State;
|
||
var comSelection = ComApiBridge.ToInwOpSelection(modelCollection);
|
||
|
||
LogManager.WriteLog($"COM 选择创建成功,路径数: {comSelection.Paths().Count}");
|
||
|
||
// 使用优化的片段去重方法
|
||
var uniqueFragments = GetUniqueFragments(comSelection);
|
||
LogManager.WriteLog($"获取到 {uniqueFragments.Count} 个唯一片段");
|
||
|
||
foreach (var fragmentInfo in uniqueFragments)
|
||
{
|
||
try
|
||
{
|
||
var callback = new OptimizedGeometryCallback(fragmentInfo.Transform);
|
||
fragmentInfo.Fragment.GenerateSimplePrimitives(
|
||
ComApi.nwEVertexProperty.eNORMAL,
|
||
callback);
|
||
|
||
var fragmentTriangles = callback.GetTriangles();
|
||
triangles.AddRange(fragmentTriangles);
|
||
|
||
LogManager.WriteLog($"片段生成了 {fragmentTriangles.Count} 个三角形");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.WriteLog($"处理片段失败: {ex.Message}");
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.WriteLog($"提取三角形失败: {ex.Message}");
|
||
}
|
||
|
||
return triangles;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取唯一的片段(解决多实例问题)
|
||
/// </summary>
|
||
/// <param name="selection">COM 选择</param>
|
||
/// <returns>唯一片段信息集合</returns>
|
||
private static List<FragmentInfo> GetUniqueFragments(ComApi.InwOpSelection selection)
|
||
{
|
||
var fragmentMap = new Dictionary<string, FragmentInfo>();
|
||
|
||
try
|
||
{
|
||
foreach (ComApi.InwOaPath3 path in selection.Paths())
|
||
{
|
||
try
|
||
{
|
||
foreach (ComApi.InwOaFragment3 fragment in path.Fragments())
|
||
{
|
||
try
|
||
{
|
||
// 获取片段的唯一标识
|
||
var pathArray = ((Array)fragment.path.ArrayData).ToArray<int>();
|
||
var pathKey = string.Join(",", pathArray);
|
||
|
||
if (!fragmentMap.ContainsKey(pathKey))
|
||
{
|
||
// 获取变换矩阵
|
||
var transform = (ComApi.InwLTransform3f3)(object)fragment.GetLocalToWorldMatrix();
|
||
|
||
fragmentMap[pathKey] = new FragmentInfo
|
||
{
|
||
Fragment = fragment,
|
||
Transform = transform,
|
||
PathKey = pathKey
|
||
};
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.WriteLog($"处理单个片段失败: {ex.Message}");
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.WriteLog($"遍历路径片段失败: {ex.Message}");
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.WriteLog($"获取唯一片段失败: {ex.Message}");
|
||
}
|
||
|
||
return fragmentMap.Values.ToList();
|
||
}
|
||
|
||
/// <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();
|
||
LogManager.WriteLog($"最高 Z 坐标: {maxZ}");
|
||
|
||
// 筛选顶部表面的三角形
|
||
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.WriteLog($"顶部三角形数量: {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.WriteLog($"边界边数量: {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.WriteLog($"最终轮廓点数量: {outlinePoints.Count}");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.WriteLog($"生成轮廓失败: {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
|
||
{
|
||
LogManager.WriteLog($"开始查找边界边,总边数: {edges.Count}");
|
||
|
||
// 统计每条边出现的次数
|
||
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.WriteLog($"边计数完成,唯一边数: {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.WriteLog($"边界边分析完成:");
|
||
LogManager.WriteLog($" 边界边数量: {boundaryCount}");
|
||
LogManager.WriteLog($" 共享边数量: {sharedCount}");
|
||
LogManager.WriteLog($" 边界/总边比例: {(double)boundaryCount / edges.Count:P1}");
|
||
|
||
// 验证边界边的连通性
|
||
if (boundaryEdges.Count > 0)
|
||
{
|
||
var connectivity = AnalyzeBoundaryConnectivity(boundaryEdges);
|
||
LogManager.WriteLog($"边界边连通性分析: {connectivity}");
|
||
}
|
||
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.WriteLog($"查找边界边失败: {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.WriteLog($"开始构建轮廓,总边数: {edges.Count}");
|
||
|
||
while (remainingEdges.Count > 0 && contourCount < 10) // 最多处理10个轮廓
|
||
{
|
||
contourCount++;
|
||
LogManager.WriteLog($"=== 开始构建第 {contourCount} 个轮廓 ===");
|
||
|
||
var currentContour = BuildSingleContour(remainingEdges);
|
||
|
||
if (currentContour.Count >= 3) // 至少3个点才能形成有效轮廓
|
||
{
|
||
LogManager.WriteLog($"第 {contourCount} 个轮廓包含 {currentContour.Count} 个点");
|
||
allOutlinePoints.AddRange(currentContour);
|
||
|
||
// 添加轮廓分隔符(使用特殊坐标标记)
|
||
if (contourCount > 1)
|
||
{
|
||
allOutlinePoints.Add(new Point2D(double.NaN, double.NaN)); // 轮廓分隔符
|
||
}
|
||
}
|
||
else
|
||
{
|
||
LogManager.WriteLog($"第 {contourCount} 个轮廓点数不足({currentContour.Count}),跳过");
|
||
break; // 如果轮廓太小,可能是噪声,停止处理
|
||
}
|
||
}
|
||
|
||
LogManager.WriteLog($"轮廓构建完成,共 {contourCount} 个轮廓,总点数: {allOutlinePoints.Count}");
|
||
|
||
// 如果有多个轮廓,返回最大的外部轮廓
|
||
if (contourCount > 1)
|
||
{
|
||
LogManager.WriteLog("检测到多个轮廓,返回最大轮廓作为外部边界");
|
||
return GetLargestContour(allOutlinePoints);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.WriteLog($"构建轮廓失败: {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.WriteLog($"轮廓起点: ({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 = 0.01; // 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.WriteLog($"轮廓已闭合,点数: {contour.Count}");
|
||
break;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
LogManager.WriteLog($"无法找到连接边,轮廓中断,点数: {contour.Count}");
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (iteration >= maxIterations)
|
||
{
|
||
LogManager.WriteLog($"轮廓构建达到最大迭代次数,强制停止");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.WriteLog($"构建单个轮廓失败: {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.WriteLog("未找到有效轮廓");
|
||
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.WriteLog($"轮廓 {i+1} 面积: {area:F2}, 点数: {contour.Count}");
|
||
|
||
if (area > maxArea)
|
||
{
|
||
maxArea = area;
|
||
largestContour = contour;
|
||
}
|
||
}
|
||
}
|
||
|
||
LogManager.WriteLog($"选择最大轮廓,面积: {maxArea:F2}, 原始点数: {largestContour.Count}");
|
||
|
||
// 清理和排序最大轮廓
|
||
var cleanedContour = CleanAndSortContour(largestContour);
|
||
LogManager.WriteLog($"清理后轮廓点数: {cleanedContour.Count}");
|
||
|
||
return cleanedContour;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.WriteLog($"获取最大轮廓失败: {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.WriteLog($"轮廓点数不足: {contour.Count}");
|
||
return contour;
|
||
}
|
||
|
||
// 第1步:移除重复点
|
||
var uniquePoints = new List<Point2D>();
|
||
const double tolerance = 0.01; // 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.WriteLog($"去重后点数: {uniquePoints.Count}");
|
||
|
||
if (uniquePoints.Count < 3)
|
||
{
|
||
LogManager.WriteLog("去重后点数不足,返回原始轮廓");
|
||
return contour;
|
||
}
|
||
|
||
// 第2步:排序点以形成正确的轮廓
|
||
result = SortPointsIntoContour(uniquePoints);
|
||
|
||
LogManager.WriteLog($"排序后轮廓点数: {result.Count}");
|
||
for (int i = 0; i < result.Count; i++)
|
||
{
|
||
LogManager.WriteLog($" 排序点 {i}: ({result[i].X:F2}, {result[i].Y:F2})");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.WriteLog($"清理轮廓失败: {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.WriteLog($"轮廓起始点: ({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.WriteLog($"下一个点: ({nearestPoint.Value.X:F2}, {nearestPoint.Value.Y:F2}), 距离: {minDistance:F2}");
|
||
}
|
||
else
|
||
{
|
||
LogManager.WriteLog("无法找到下一个最近点");
|
||
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.WriteLog($"轮廓闭合距离: {closingDistance:F2}");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.WriteLog($"排序轮廓点失败: {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>
|
||
/// 片段信息类
|
||
/// </summary>
|
||
public class FragmentInfo
|
||
{
|
||
public ComApi.InwOaFragment3 Fragment { get; set; }
|
||
public ComApi.InwLTransform3f3 Transform { get; set; }
|
||
public string PathKey { get; set; }
|
||
}
|
||
|
||
/// <summary>
|
||
/// 优化的几何回调处理器
|
||
/// </summary>
|
||
public class OptimizedGeometryCallback : ComApi.InwSimplePrimitivesCB
|
||
{
|
||
private List<Triangle3D> _triangles = new List<Triangle3D>();
|
||
private ComApi.InwLTransform3f3 _transform;
|
||
private double[] _transformMatrix;
|
||
|
||
public OptimizedGeometryCallback(ComApi.InwLTransform3f3 transform)
|
||
{
|
||
_transform = transform;
|
||
|
||
// 预先转换变换矩阵以提高性能
|
||
if (_transform != null)
|
||
{
|
||
try
|
||
{
|
||
var matrixArray = (Array)(object)_transform.Matrix;
|
||
_transformMatrix = matrixArray.ToArray<double>();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.WriteLog($"转换变换矩阵失败: {ex.Message}");
|
||
_transformMatrix = null;
|
||
}
|
||
}
|
||
}
|
||
|
||
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.WriteLog($"处理三角形失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 将局部坐标点转换为世界坐标点
|
||
/// </summary>
|
||
/// <param name="localPoint">局部坐标点</param>
|
||
/// <returns>世界坐标点</returns>
|
||
private Point3D TransformPoint(Point3D localPoint)
|
||
{
|
||
if (_transformMatrix == null)
|
||
{
|
||
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.WriteLog($"坐标变换失败: {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}]";
|
||
}
|
||
}
|
||
} |