using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using ESRI.ArcGIS.Geometry; using KGIS.Framework.AE; namespace Kingo.PluginServiceInterface.Helper { /// /// 尖锐角检查与处理逻辑 /// public class SharpAngle { /// /// 处理几何图形中的尖锐角(小于指定角度阈值) /// /// /// /// 默认小于10度为尖锐角 /// public static IGeometry ProcessSharpAngles(IGeometry geometry, double distance, double angleThreshold = 10, int recursionCount = 0) { // 预处理:移除几何图形中的重复顶点,确保顶点唯一性 //geometry = RemoveDuplicatePoints(geometry); // 存储构成最小角度顶点集合的列表(通常包含三个顶点:构成尖角的三个点) List pointList = new List(); double angle = 0; // 获取当前几何图形中的最小内角,并获取对应的三个顶点(尖角顶点及其相邻顶点) double internalAngle = GetMinAngle1(geometry, ref pointList); // 仅当找到有效三角形顶点时处理(三点构成一个尖角) if (pointList.Count == 3) { // 获取构成尖角的三个顶点 // a - 前驱顶点,b - 尖角顶点,c - 后继顶点 IPoint a = pointList[0]; IPoint b = pointList[1]; IPoint c = pointList[2]; IPolygon triangle = CreateTriangle(a, b, c); if (triangle != null) { var area = triangle as IArea; if (area != null && area.Area < 0.1) { if (!FeatureAPI.IsInterSect(triangle, geometry)) { geometry = FeatureAPI.Union(geometry, triangle); } else { // 从原始几何体中减去三角形区域,实现尖角切割 geometry = FeatureAPI.Difference(geometry, triangle); } } else { // 在ab边上距离顶点b指定距离的位置生成新点d IPoint d = GetPointAlongEdge(a, b, distance); // 在cb边上距离顶点b指定距离的位置生成新点e //IPoint e = GetPointAlongEdge(c, b, distance); // 计算向量db的分量 double dbX = b.X - d.X; double dbY = b.Y - d.Y; // 计算分子和分母 double numerator = dbX * (b.X - d.X) + dbY * (b.Y - d.Y); double denominator = dbX * (c.X - b.X) + dbY * (c.Y - b.Y); // 避免分母为0(若分母为0,说明bc边与db向量平行,无法形成直角) if (Math.Abs(denominator) < 1e-9) { // 处理边界:无法在bc边上找到e点,可返回原逻辑或抛出异常 return CreateTriangle(d, b, GetPointAlongEdge(c, b, distance)); // 原逻辑生成e点 } double t = -numerator / denominator; // 限制t在[0,1]范围内(确保e点在bc边上) t = Math.Max(0, Math.Min(1, t)); // 生成e点(位于bc边上) IPoint e = new PointClass() { X = b.X + t * (c.X - b.X), Y = b.Y + t * (c.Y - b.Y) }; // 用d-b-e三点创建三角形几何体(用于切割尖角区域) triangle = CreateTriangle(d, b, e); // 从原始几何体中减去三角形区域,实现尖角切割 //geometry = FeatureAPI.Difference(geometry, triangle); if (triangle != null) { if (!FeatureAPI.IsInterSect(triangle, geometry)) { geometry = FeatureAPI.Union(geometry, triangle); } else { // 从原始几何体中减去三角形区域,实现尖角切割 geometry = FeatureAPI.Difference(geometry, triangle); } } } } // 检测处理后的几何体是否仍存在需要处理的尖角 if (JudgmentSharpAngle(geometry, ref pointList, ref angle)) { if (recursionCount < 10) { // 递归调用时,计数器加1 geometry = ProcessSharpAngles(geometry, distance, angleThreshold, recursionCount + 1); } else { triangle = CreateTriangle(a, b, c); if (!FeatureAPI.IsInterSect(triangle, geometry)) { geometry = FeatureAPI.Union(geometry, triangle); } else { // 从原始几何体中减去三角形区域,实现尖角切割 geometry = FeatureAPI.Difference(geometry, triangle); } } } } // 返回处理后的几何体 return geometry; } private static double GetMinAngle(IGeometry pGeo, ref List pointlist) { double result = -1; IPolygon4 poly4 = null; ITopologicalOperator topo = null; GeometryBag geoBag = null; IGeometryCollection geoCollection = null; pointlist = new List(); try { if (pGeo == null || pGeo.IsEmpty) return result; poly4 = pGeo as IPolygon4; topo = poly4 as ITopologicalOperator; topo?.Simplify(); geoBag = poly4.ExteriorRingBag as GeometryBag; if (geoBag == null) return result; geoCollection = geoBag as IGeometryCollection; List rings = new List(); for (int j = 0; j < geoCollection.GeometryCount; j++) { IGeometry geo = geoCollection.get_Geometry(j); rings.Add(geo); //内环图形 IGeometryBag InteriorBag = (pGeo as IPolygon4).get_InteriorRingBag(geo as IRing); if (InteriorBag != null) { IGeometryCollection InteriorRingGeometryCollection = InteriorBag as IGeometryCollection; for (int IR = 0; IR < InteriorRingGeometryCollection.GeometryCount; IR++) { rings.Add(InteriorRingGeometryCollection.get_Geometry(IR)); } } } foreach (IGeometry ring in rings) { if (ring.IsEmpty) continue; IPointCollection points = ring as IPointCollection; int num = points.PointCount - 1; for (int i = 0; i < num; i++) { IPoint p1 = null; IPoint p2 = points.get_Point(i); IPoint p3 = null; if (i == 0) { p1 = points.get_Point(num - 1); p3 = points.get_Point(i + 1); } else if (i == num - 1) { p1 = points.get_Point(i - 1); p3 = points.get_Point(0); } else { p1 = points.get_Point(i - 1); p3 = points.get_Point(i + 1); } if ((p2.X == p1.X && p2.Y == p1.Y) || (p2.X == p3.X && p2.Y == p3.Y) || (p1.X == p3.X && p1.Y == p3.Y)) continue; double angle = GetAngle(p2, p1, p3); if (double.IsNaN(angle)) continue; if (result == -1) { result = angle; pointlist.Add(p1); pointlist.Add(p2); pointlist.Add(p3); } else { if (double.IsNaN(result)) { result = angle; } if (result > angle) { result = angle; if (pointlist.Count > 0) pointlist.Clear(); pointlist.Add(p1); pointlist.Add(p2); pointlist.Add(p3); } } //Marshal.ReleaseComObject(p1); //Marshal.ReleaseComObject(p2); //Marshal.ReleaseComObject(p3); } //Marshal.ReleaseComObject(ring); } } catch (Exception ex) { throw ex; } finally { //if (poly4 != null) // Marshal.ReleaseComObject(poly4); //if (topo != null) // Marshal.ReleaseComObject(topo); //if (geoBag != null) // Marshal.ReleaseComObject(geoBag); //if (geoCollection != null) // Marshal.ReleaseComObject(geoCollection); } return result; } private static double GetAngle(IPoint cenPoint, IPoint firstPoint, IPoint secondPoint) { double ma_x = firstPoint.X - cenPoint.X; double ma_y = firstPoint.Y - cenPoint.Y; double mb_x = secondPoint.X - cenPoint.X; double mb_y = secondPoint.Y - cenPoint.Y; double v1 = (ma_x * mb_x) + (ma_y * mb_y); double ma_val = Math.Sqrt(ma_x * ma_x + ma_y * ma_y); double mb_val = Math.Sqrt(mb_x * mb_x + mb_y * mb_y); if (ma_val * mb_val == 0) { return -1; } double cosM = v1 / (ma_val * mb_val); double angleAMB = Math.Acos(cosM) * 180 / Math.PI; return angleAMB; } private static IGeometry RemoveDuplicatePoints(IGeometry geometry) { IPointCollection srcPoints = geometry as IPointCollection; IPointCollection dstPoints = new PolygonClass(); IPoint lastPoint = null; for (int i = 0; i < srcPoints.PointCount; i++) { IPoint current = srcPoints.get_Point(i); if (lastPoint == null || !PointsEqual(current, lastPoint)) { dstPoints.AddPoint(current); lastPoint = current; } } // 处理闭合环特殊情况 if (dstPoints.PointCount > 1 && PointsEqual(dstPoints.get_Point(0), dstPoints.get_Point(dstPoints.PointCount - 1))) { dstPoints.RemovePoints(dstPoints.PointCount - 1, 1); } return dstPoints as IPolygon; } private static bool PointsEqual(IPoint p1, IPoint p2) { const double tolerance = 1e-6; return Math.Abs(p1.X - p2.X) < tolerance && Math.Abs(p1.Y - p2.Y) < tolerance; } /// /// 沿多边形边计算距离终点指定长度的点坐标 /// /// 边的起点坐标 /// 边的终点坐标 /// 从终点向起点方向移动的距离(非负数) /// 计算得到的新坐标点 /// 当距离参数为负数时抛出 private static IPoint GetPointAlongEdge(IPoint fromPoint, IPoint toPoint, double distanceFromEnd) { // 参数有效性验证 if (distanceFromEnd < 0) throw new ArgumentException("移动距离必须为非负值", nameof(distanceFromEnd)); const double precisionEpsilon = 1e-9; // 浮点计算精度阈值 // 计算边向量分量 double deltaX = toPoint.X - fromPoint.X; double deltaY = toPoint.Y - fromPoint.Y; // 计算边长的平方(避免开根号提升性能) double squaredEdgeLength = deltaX * deltaX + deltaY * deltaY; /* 处理特殊边界情况: * 1. 起点终点重合(边长接近0) * 2. 移动距离接近0 * 以上情况直接返回终点坐标 */ if (squaredEdgeLength < precisionEpsilon * precisionEpsilon || distanceFromEnd < precisionEpsilon) { return ClonePoint(toPoint); } // 计算实际边长和标准化比例 double edgeLength = Math.Sqrt(squaredEdgeLength); double normalizedRatio = distanceFromEnd / edgeLength; // 当移动距离超过边长时返回起点 if (normalizedRatio >= 1.0) { return ClonePoint(fromPoint); } /* 坐标计算逻辑: * 从终点(toPoint)向起点(fromPoint)方向移动指定距离 * 新坐标 = 终点坐标 - 边向量 * 距离比例 */ return CreateNewPoint( x: toPoint.X - deltaX * normalizedRatio, y: toPoint.Y - deltaY * normalizedRatio ); } /// /// 创建新点对象(封装对象创建逻辑) /// private static IPoint CreateNewPoint(double x, double y) => new PointClass() { X = x, Y = y }; /// /// 复制点对象(避免引用关联) /// private static IPoint ClonePoint(IPoint original) => CreateNewPoint(original.X, original.Y); private static IPolygon CreateTriangle(IPoint d, IPoint b, IPoint e) { IPolygon triangle = new PolygonClass(); IPointCollection trianglePoints = triangle as IPointCollection; trianglePoints.AddPoint(d); trianglePoints.AddPoint(b); trianglePoints.AddPoint(e); trianglePoints.AddPoint(d); // 闭合多边形 // 确保几何有效 ITopologicalOperator topo = triangle as ITopologicalOperator; topo.Simplify(); return triangle; } #region 判断尖锐角与修复尖锐角 public static bool JudgmentSharpAngle(IGeometry geometry, ref List pointList, ref double angle) { bool isSharpAngle = false; try { if (geometry == null) return isSharpAngle; angle = GetMinAngle1(geometry, ref pointList, false); if (angle < 10) isSharpAngle = true; } catch (Exception) { } return isSharpAngle; } #endregion public static double GetMinAngle1(IGeometry pGeo, ref List pointlist) { const double NoAngleFound = double.MaxValue; double minAngle = NoAngleFound; pointlist = new List(); if (pGeo == null || pGeo.IsEmpty) return -1; if (!(pGeo is IPolygon4 poly4)) return -1; // 简化几何拓扑 (poly4 as ITopologicalOperator)?.Simplify(); // 收集所有环(外环 + 内环) var rings = new List(); IGeometryCollection exteriorRings = poly4.ExteriorRingBag as IGeometryCollection; if (exteriorRings != null) { for (int i = 0; i < exteriorRings.GeometryCount; i++) { if (exteriorRings.get_Geometry(i) is IRing ring) { rings.Add(ring); // 获取当前外环的内环 IGeometryBag interiorBag = poly4.get_InteriorRingBag(ring); if (interiorBag != null) { IGeometryCollection interiorRings = interiorBag as IGeometryCollection; for (int j = 0; j < interiorRings.GeometryCount; j++) { if (interiorRings.get_Geometry(j) is IRing interiorRing) { rings.Add(interiorRing); } } } } } } // 存储所有环的点集合(每个环单独存储) var ringPoints = new List>(); // 提前提取所有环的点坐标 foreach (IRing ring in rings) { if (ring.IsEmpty) continue; IPointCollection pColl = ring as IPointCollection; int totalPoints = pColl.PointCount; // 检查是否闭合(首尾点相同) bool isClosed = totalPoints > 0 && PointEqual(pColl.get_Point(0), pColl.get_Point(totalPoints - 1)); // 提取点坐标(如果是闭合环则忽略最后一个点) var points = new List(); int pointsToTake = isClosed ? totalPoints - 1 : totalPoints; for (int i = 0; i < pointsToTake; i++) { points.Add(pColl.get_Point(i)); } ringPoints.Add(points); } // 使用线程安全的集合存储结果 var minAngles = new ConcurrentBag<(double angle, IPoint p1, IPoint p2, IPoint p3)>(); // 并行处理每个环 Parallel.ForEach(ringPoints, (points) => { int count = points.Count; // 单环内并行处理每个点组合 Parallel.For(0, count, (i) => { int prevIdx = (i - 1 + count) % count; int nextIdx = (i + 1) % count; IPoint p1 = points[prevIdx]; IPoint p2 = points[i]; IPoint p3 = points[nextIdx]; if (PointEqual(p1, p2) || PointEqual(p2, p3) || PointEqual(p1, p3)) return; double angle = GetAngle(p2, p1, p3); if (double.IsNaN(angle)) return; // 添加到线程安全集合 minAngles.Add((angle, p1, p2, p3)); }); }); // 在所有结果中找到最小角度 foreach (var item in minAngles) { if (item.angle < minAngle) { minAngle = item.angle; pointlist.Clear(); pointlist.Add(item.p1); pointlist.Add(item.p2); pointlist.Add(item.p3); } } return minAngle == NoAngleFound ? -1 : minAngle; } // 辅助方法:判断两点是否重合 private static bool PointEqual(IPoint a, IPoint b) => a.X == b.X && a.Y == b.Y; #region MyRegion public struct PointD { public double X { get; } public double Y { get; } public PointD(double x, double y) { X = x; Y = y; } } // 优化后的最小角度计算方法(针对大型图形) public static double GetMinAngle1(IGeometry pGeo, ref List pointlist, bool skipSimplify = false) { const double NoAngleFound = double.MaxValue; double minAngle = NoAngleFound; pointlist = new List(); // 1. 输入校验(提前返回) if (pGeo == null || pGeo.IsEmpty) return -1; if (!(pGeo is IPolygon4 poly4)) return -1; // 2. 拓扑简化(可选跳过,适用于外部已简化的几何) if (!skipSimplify) { (poly4 as ITopologicalOperator)?.Simplify(); // 仅执行一次简化 } // 3. 提取所有环(外环 + 内环)并转换为值类型点集合(减少 COM 交互) var allRingsPoints = ExtractRingsAsPointD(poly4); if (allRingsPoints.Count == 0) return -1; // 4. 并行/串行处理每个环(根据环大小动态选择) var minAngles = new ConcurrentBag<(double angle, PointD p1, PointD p2, PointD p3)>(); foreach (var ringPoints in allRingsPoints) { int pointCount = ringPoints.Count; if (pointCount < 3) continue; // 环至少需要3个点才能构成角 // 动态选择处理方式:点数量大时并行,否则串行(避免小任务并行开销) if (pointCount > 1000) // 阈值可根据实际场景调整 { Parallel.For(0, pointCount, i => ProcessPointInRing(ringPoints, i, minAngles)); } else { for (int i = 0; i < pointCount; i++) { ProcessPointInRing(ringPoints, i, minAngles); } } } // 5. 查找全局最小角度(并转换回 IPoint) foreach (var item in minAngles) { if (item.angle < minAngle) { minAngle = item.angle; pointlist.Clear(); // 将值类型点转换为 IPoint(仅最后需要时创建 COM 对象) pointlist.Add(CreateIPoint(item.p1.X, item.p1.Y)); pointlist.Add(CreateIPoint(item.p2.X, item.p2.Y)); pointlist.Add(CreateIPoint(item.p3.X, item.p3.Y)); } // 优化:若已找到小于10度的角,可提前终止(适用于仅需判断尖锐角的场景) if (minAngle < 10) break; } return minAngle == NoAngleFound ? -1 : minAngle; } // 辅助方法:提取所有环的点坐标(转换为值类型 PointD) private static List> ExtractRingsAsPointD(IPolygon4 poly4) { var allRingsPoints = new List>(); // 提取外环 IGeometryCollection exteriorRings = poly4.ExteriorRingBag as IGeometryCollection; if (exteriorRings != null) { for (int i = 0; i < exteriorRings.GeometryCount; i++) { if (exteriorRings.get_Geometry(i) is IRing ring) { var ringPoints = ExtractRingPointsAsPointD(ring); allRingsPoints.Add(ringPoints); // 提取当前外环的内环 IGeometryBag interiorBag = poly4.get_InteriorRingBag(ring); if (interiorBag != null) { IGeometryCollection interiorRings = interiorBag as IGeometryCollection; for (int j = 0; j < interiorRings.GeometryCount; j++) { if (interiorRings.get_Geometry(j) is IRing interiorRing) { var interiorRingPoints = ExtractRingPointsAsPointD(interiorRing); allRingsPoints.Add(interiorRingPoints); } } } } } } return allRingsPoints; } // 辅助方法:提取单个环的点坐标(转换为 PointD) private static List ExtractRingPointsAsPointD(IRing ring) { var points = new List(); if (ring.IsEmpty) return points; IPointCollection pColl = ring as IPointCollection; int totalPoints = pColl.PointCount; bool isClosed = totalPoints > 0 && PointEqual2(pColl.get_Point(0), pColl.get_Point(totalPoints - 1)); int pointsToTake = isClosed ? totalPoints - 1 : totalPoints; // 仅遍历一次 COM 对象,转换为值类型存储 for (int i = 0; i < pointsToTake; i++) { IPoint comPoint = pColl.get_Point(i); points.Add(new PointD(comPoint.X, comPoint.Y)); } return points; } // 辅助方法:处理环中的单个点(计算角度) private static void ProcessPointInRing(List ringPoints, int i, ConcurrentBag<(double angle, PointD p1, PointD p2, PointD p3)> minAngles) { int count = ringPoints.Count; int prevIdx = (i - 1 + count) % count; int nextIdx = (i + 1) % count; PointD p1 = ringPoints[prevIdx]; PointD p2 = ringPoints[i]; PointD p3 = ringPoints[nextIdx]; // 跳过重合点(提前判断,减少无效计算) if (PointEqual(p1, p2) || PointEqual(p2, p3) || PointEqual(p1, p3)) return; // 计算角度(预计算向量差值和模长,避免重复计算) double maX = p1.X - p2.X; double maY = p1.Y - p2.Y; double mbX = p3.X - p2.X; double mbY = p3.Y - p2.Y; double dotProduct = maX * mbX + maY * mbY; double maLength = Math.Sqrt(maX * maX + maY * maY); double mbLength = Math.Sqrt(mbX * mbX + mbY * mbY); if (maLength < 1e-9 || mbLength < 1e-9) // 避免除零错误 return; double cosTheta = dotProduct / (maLength * mbLength); cosTheta = Math.Max(Math.Min(cosTheta, 1), -1); // 防止浮点误差导致超出 [-1,1] double angle = Math.Acos(cosTheta) * 180 / Math.PI; minAngles.Add((angle, p1, p2, p3)); } // 辅助方法:判断两个 PointD 是否重合(值类型比较更快) private static bool PointEqual(PointD a, PointD b) => Math.Abs(a.X - b.X) < 1e-6 && Math.Abs(a.Y - b.Y) < 1e-6; // 辅助方法:将值类型点转换为 IPoint(仅最后需要时创建) private static IPoint CreateIPoint(double x, double y) => new PointClass { X = x, Y = y }; // 辅助方法:兼容原 COM 对象的点比较(仅用于初始环提取) private static bool PointEqual2(IPoint a, IPoint b) => Math.Abs(a.X - b.X) < 1e-6 && Math.Abs(a.Y - b.Y) < 1e-6; #endregion } // 自定义值类型:存储坐标(替代IPoint引用类型,减少GC) public struct PointD : IEquatable { public double X { get; } public double Y { get; } public PointD(double x, double y) { X = x; Y = y; } public bool Equals(PointD other) { return X.Equals(other.X) && Y.Equals(other.Y); } public override bool Equals(object obj) { return obj is PointD other && Equals(other); } public override int GetHashCode() { return 0; //HashCode.Combine(X, Y); } public static bool operator ==(PointD left, PointD right) { return left.Equals(right); } public static bool operator !=(PointD left, PointD right) { return !left.Equals(right); } } public static class GeometryOptimizer { // 线程安全的最小值容器(存储角度及对应三点) private class ThreadSafeMinAngle { private double _minAngle = double.MaxValue; private PointD _p1, _p2, _p3; private readonly object _lock = new object(); public void Update(double angle, PointD p1, PointD p2, PointD p3) { lock (_lock) { if (angle < _minAngle) { _minAngle = angle; _p1 = p1; _p2 = p2; _p3 = p3; } } } public (double angle, PointD p1, PointD p2, PointD p3) GetResult() { lock (_lock) { return (_minAngle == double.MaxValue ? -1 : _minAngle, _p1, _p2, _p3); } } } public static double GetMinAngle1(IGeometry pGeo, ref List pointlist) { // 初始化返回值和常量 const double NoAngleFound = double.MaxValue; pointlist = new List(); // 输入参数校验 if (pGeo == null || pGeo.IsEmpty || !(pGeo is IPolygon4 poly4)) return -1; // 简化几何拓扑(修复自相交、冗余顶点等) (poly4 as ITopologicalOperator)?.Simplify(); // 收集所有环的顶点(直接提取,不暂存环对象) var ringPoints = new List>(); CollectRingPoints(poly4, ringPoints); // 过滤无效环(顶点数 < 3 的环无法构成角度) var validRingPoints = ringPoints.Where(points => points.Count >= 3).ToList(); if (!validRingPoints.Any()) return -1; // 并行计算角度并实时跟踪最小值 var minAngleContainer = new ThreadSafeMinAngle(); Parallel.ForEach(validRingPoints, points => { int count = points.Count; for (int i = 0; i < count; i++) { // 获取当前点及相邻点(循环索引) int prevIdx = (i - 1 + count) % count; int nextIdx = (i + 1) % count; PointD pPrev = points[prevIdx]; PointD pCurr = points[i]; PointD pNext = points[nextIdx]; // 跳过重复点(避免计算无效角度) if (pPrev == pCurr || pCurr == pNext || pPrev == pNext) continue; // 计算角度(使用值类型坐标提升效率) double angle = CalculateAngle(pCurr, pPrev, pNext); if (!double.IsNaN(angle)) minAngleContainer.Update(angle, pPrev, pCurr, pNext); } }); // 处理最终结果 var result = minAngleContainer.GetResult(); if (result.angle <= 0) return -1; // 转换值类型坐标为IPoint并返回 pointlist.Add(CreateIPoint(result.p1)); pointlist.Add(CreateIPoint(result.p2)); pointlist.Add(CreateIPoint(result.p3)); return result.angle; } // 辅助方法:收集多边形所有环的顶点(外环+内环) private static void CollectRingPoints(IPolygon4 poly4, List> ringPoints) { // 处理外环 if (poly4.ExteriorRingBag is IGeometryCollection exteriorRings) { for (int i = 0; i < exteriorRings.GeometryCount; i++) { if (exteriorRings.get_Geometry(i) is IRing exteriorRing) { AddRingPointsToCollection(exteriorRing, ringPoints); // 处理当前外环的内环 if (poly4.get_InteriorRingBag(exteriorRing) is IGeometryCollection interiorRings) { for (int j = 0; j < interiorRings.GeometryCount; j++) { if (interiorRings.get_Geometry(j) is IRing interiorRing) AddRingPointsToCollection(interiorRing, ringPoints); } } } } } } // 辅助方法:提取单个环的顶点并添加到集合 private static void AddRingPointsToCollection(IRing ring, List> ringPoints) { if (ring.IsEmpty || !(ring is IPointCollection pointColl)) return; // 提取顶点(闭合环忽略最后一个点,避免重复) int totalPoints = pointColl.PointCount; bool isClosed = totalPoints > 0 && PointEqual(pointColl.get_Point(0), pointColl.get_Point(totalPoints - 1)); int pointsToTake = isClosed ? totalPoints - 1 : totalPoints; var points = new List(); for (int i = 0; i < pointsToTake; i++) { IPoint p = pointColl.get_Point(i); points.Add(new PointD(p.X, p.Y)); // 转换为值类型坐标 } ringPoints.Add(points); } // 辅助方法:计算三点构成的夹角(pCurr为顶点) private static double CalculateAngle(PointD pCurr, PointD pPrev, PointD pNext) { // 构建向量:pCurr->pPrev 和 pCurr->pNext double v1X = pPrev.X - pCurr.X; double v1Y = pPrev.Y - pCurr.Y; double v2X = pNext.X - pCurr.X; double v2Y = pNext.Y - pCurr.Y; // 向量模长(避免除以零) double len1 = Math.Sqrt(v1X * v1X + v1Y * v1Y); double len2 = Math.Sqrt(v2X * v2X + v2Y * v2Y); if (len1 < 1e-9 || len2 < 1e-9) return double.NaN; // 向量点积计算夹角(弧度转角度) double dotProduct = v1X * v2X + v1Y * v2Y; double cosTheta = Math.Max(-1, Math.Min(1, dotProduct / (len1 * len2))); // 修正精度误差 return Math.Acos(cosTheta) * (180 / Math.PI); } // 辅助方法:比较两个IPoint是否相等(坐标精度:1e-6) private static bool PointEqual(IPoint p1, IPoint p2) { return Math.Abs(p1.X - p2.X) < 1e-6 && Math.Abs(p1.Y - p2.Y) < 1e-6; } // 辅助方法:将PointD转换为IPoint(假设IPoint可实例化) private static IPoint CreateIPoint(PointD point) { var iPoint = new PointClass(); iPoint.X = point.X; iPoint.Y = point.Y; return iPoint; } } }