You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							855 lines
						
					
					
						
							36 KiB
						
					
					
				
			
		
		
	
	
							855 lines
						
					
					
						
							36 KiB
						
					
					
				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 | 
						|
{ | 
						|
    /// <summary> | 
						|
    /// 尖锐角检查与处理逻辑 | 
						|
    /// </summary> | 
						|
    public class SharpAngle | 
						|
    { | 
						|
        /// <summary> | 
						|
        /// 处理几何图形中的尖锐角(小于指定角度阈值) | 
						|
        /// </summary> | 
						|
        /// <param name="geometry"></param> | 
						|
        /// <param name="distance"></param> | 
						|
        /// <param name="angleThreshold">默认小于10度为尖锐角</param> | 
						|
        /// <returns></returns> | 
						|
        public static IGeometry ProcessSharpAngles(IGeometry geometry, double distance, double angleThreshold = 10, int recursionCount = 0) | 
						|
        { | 
						|
            // 预处理:移除几何图形中的重复顶点,确保顶点唯一性 | 
						|
            //geometry = RemoveDuplicatePoints(geometry); | 
						|
            // 存储构成最小角度顶点集合的列表(通常包含三个顶点:构成尖角的三个点) | 
						|
            List<IPoint> pointList = new List<IPoint>(); | 
						|
            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<IPoint> pointlist) | 
						|
        { | 
						|
            double result = -1; | 
						|
            IPolygon4 poly4 = null; | 
						|
            ITopologicalOperator topo = null; | 
						|
            GeometryBag geoBag = null; | 
						|
            IGeometryCollection geoCollection = null; | 
						|
            pointlist = new List<IPoint>(); | 
						|
            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<IGeometry> rings = new List<IGeometry>(); | 
						|
                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; | 
						|
        } | 
						|
        /// <summary> | 
						|
        /// 沿多边形边计算距离终点指定长度的点坐标 | 
						|
        /// </summary> | 
						|
        /// <param name="fromPoint">边的起点坐标</param> | 
						|
        /// <param name="toPoint">边的终点坐标</param> | 
						|
        /// <param name="distanceFromEnd">从终点向起点方向移动的距离(非负数)</param> | 
						|
        /// <returns>计算得到的新坐标点</returns> | 
						|
        /// <exception cref="ArgumentException">当距离参数为负数时抛出</exception> | 
						|
        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 | 
						|
            ); | 
						|
        } | 
						|
 | 
						|
        /// <summary> | 
						|
        /// 创建新点对象(封装对象创建逻辑) | 
						|
        /// </summary> | 
						|
        private static IPoint CreateNewPoint(double x, double y) | 
						|
            => new PointClass() { X = x, Y = y }; | 
						|
        /// <summary> | 
						|
        /// 复制点对象(避免引用关联) | 
						|
        /// </summary> | 
						|
        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<IPoint> 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<IPoint> pointlist) | 
						|
        { | 
						|
            const double NoAngleFound = double.MaxValue; | 
						|
            double minAngle = NoAngleFound; | 
						|
            pointlist = new List<IPoint>(); | 
						|
            if (pGeo == null || pGeo.IsEmpty) | 
						|
                return -1; | 
						|
            if (!(pGeo is IPolygon4 poly4)) | 
						|
                return -1; | 
						|
            // 简化几何拓扑 | 
						|
            (poly4 as ITopologicalOperator)?.Simplify(); | 
						|
            // 收集所有环(外环 + 内环) | 
						|
            var rings = new List<IRing>(); | 
						|
            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<List<IPoint>>(); | 
						|
            // 提前提取所有环的点坐标 | 
						|
            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<IPoint>(); | 
						|
                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<IPoint> pointlist, bool skipSimplify = false) | 
						|
        { | 
						|
            const double NoAngleFound = double.MaxValue; | 
						|
            double minAngle = NoAngleFound; | 
						|
            pointlist = new List<IPoint>(); | 
						|
 | 
						|
            // 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<List<PointD>> ExtractRingsAsPointD(IPolygon4 poly4) | 
						|
        { | 
						|
            var allRingsPoints = new List<List<PointD>>(); | 
						|
 | 
						|
            // 提取外环 | 
						|
            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<PointD> ExtractRingPointsAsPointD(IRing ring) | 
						|
        { | 
						|
            var points = new List<PointD>(); | 
						|
            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<PointD> 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<PointD> | 
						|
    { | 
						|
        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<IPoint> pointlist) | 
						|
        { | 
						|
            // 初始化返回值和常量 | 
						|
            const double NoAngleFound = double.MaxValue; | 
						|
            pointlist = new List<IPoint>(); | 
						|
 | 
						|
            // 输入参数校验 | 
						|
            if (pGeo == null || pGeo.IsEmpty || !(pGeo is IPolygon4 poly4)) | 
						|
                return -1; | 
						|
 | 
						|
            // 简化几何拓扑(修复自相交、冗余顶点等) | 
						|
            (poly4 as ITopologicalOperator)?.Simplify(); | 
						|
 | 
						|
            // 收集所有环的顶点(直接提取,不暂存环对象) | 
						|
            var ringPoints = new List<List<PointD>>(); | 
						|
            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<List<PointD>> 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<List<PointD>> 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<PointD>(); | 
						|
            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; | 
						|
        } | 
						|
    } | 
						|
} |