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;
}
}
}