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 { /// /// 几何提取器 - 使用改进的算法和性能优化 /// public class GeometryExtractor { /// /// 提取模型项的顶视图轮廓 /// /// 模型项 /// 容差 /// 轮廓点集合 public static List ExtractTopViewOutline(ModelItem modelItem, double tolerance = 0.5) { var points = new List(); 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; } /// /// 优化的三角形提取方法 /// /// 模型项 /// 三角形集合 private static List ExtractTrianglesOptimized(ModelItem modelItem) { var triangles = new List(); 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; } /// /// 获取唯一的片段(解决多实例问题) /// /// COM 选择 /// 唯一片段信息集合 private static List GetUniqueFragments(ComApi.InwOpSelection selection) { var fragmentMap = new Dictionary(); try { foreach (ComApi.InwOaPath3 path in selection.Paths()) { try { foreach (ComApi.InwOaFragment3 fragment in path.Fragments()) { try { // 获取片段的唯一标识 var pathArray = ((Array)fragment.path.ArrayData).ToArray(); 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(); } /// /// 从三角形生成轮廓 /// /// 三角形集合 /// 容差 /// 轮廓点集合 private static List GenerateOutlineFromTriangles(List triangles, double tolerance) { var outlinePoints = new List(); 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(); 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; } /// /// 查找边界边(在俯视图中只出现一次的边) /// /// 所有边的集合 /// 边界边集合 private static List FindBoundaryEdges(List edges) { var boundaryEdges = new List(); var edgeCount = new Dictionary(); var edgeMap = new Dictionary(); 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; } /// /// 分析边界边的连通性 /// /// 边界边集合 /// 连通性分析字符串 private static string AnalyzeBoundaryConnectivity(List boundaryEdges) { try { var vertexConnectionCount = new Dictionary(); // 统计每个顶点的连接数 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}"; } } /// /// 从边界边构建轮廓点集合(支持多轮廓) /// /// 边界边集合 /// 轮廓点集合 private static List BuildOutlineFromEdges(List edges) { var allOutlinePoints = new List(); var remainingEdges = new List(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; } /// /// 构建单个连续轮廓 /// /// 剩余边集合(会被修改) /// 单个轮廓的点集合 private static List BuildSingleContour(List remainingEdges) { var contour = new List(); 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; } /// /// 从多个轮廓中获取最大的轮廓(外部边界) /// /// 包含多个轮廓的点集合 /// 最大轮廓的点集合 private static List GetLargestContour(List allPoints) { var contours = new List>(); var currentContour = new List(); try { // 分离各个轮廓 foreach (var point in allPoints) { // 检查是否为分隔符 if (double.IsNaN(point.X) || double.IsNaN(point.Y)) { if (currentContour.Count > 0) { contours.Add(new List(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 largestContour = new List(); 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; // 返回原始点集合 } } /// /// 清理和排序轮廓点 /// /// 原始轮廓点 /// 清理和排序后的轮廓点 private static List CleanAndSortContour(List contour) { var result = new List(); try { if (contour.Count < 3) { LogManager.WriteLog($"轮廓点数不足: {contour.Count}"); return contour; } // 第1步:移除重复点 var uniquePoints = new List(); 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; } /// /// 将点排序成正确的轮廓顺序 /// /// 无序的点集合 /// 有序的轮廓点 private static List SortPointsIntoContour(List points) { var sortedPoints = new List(); 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(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; } /// /// 计算轮廓的包围盒面积 /// /// 轮廓点集合 /// 包围盒面积 private static double CalculateContourArea(List 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; } } } /// /// 片段信息类 /// public class FragmentInfo { public ComApi.InwOaFragment3 Fragment { get; set; } public ComApi.InwLTransform3f3 Transform { get; set; } public string PathKey { get; set; } } /// /// 优化的几何回调处理器 /// public class OptimizedGeometryCallback : ComApi.InwSimplePrimitivesCB { private List _triangles = new List(); 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(); } 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(); var coords2 = ((Array)(object)v2.coord).ToArray(); var coords3 = ((Array)(object)v3.coord).ToArray(); // 创建局部坐标点 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}"); } } /// /// 将局部坐标点转换为世界坐标点 /// /// 局部坐标点 /// 世界坐标点 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; } } /// /// 获取提取的三角形集合 /// /// 三角形集合 public List GetTriangles() { return _triangles; } } /// /// 数组扩展方法(性能优化) /// public static class ArrayExtensions { [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T[] ToArray(this Array arr) where T : struct { T[] result = new T[arr.Length]; Array.Copy(arr, result, result.Length); return result; } } /// /// 三角形结构体 /// 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}]"; } } /// /// 2D点结构体 /// 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})"; } } /// /// 2D边结构体 /// 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}]"; } } }