diff --git a/src/CAD/IFox.CAD.Shared/ExtensionMethod/GeometryEx.cs b/src/CAD/IFox.CAD.Shared/ExtensionMethod/GeometryEx.cs index d5e213e3c38bc3acf66c803ce99ada70914c837c..02af063da51cb92570217014ec60d4251ba9fcfd 100644 --- a/src/CAD/IFox.CAD.Shared/ExtensionMethod/GeometryEx.cs +++ b/src/CAD/IFox.CAD.Shared/ExtensionMethod/GeometryEx.cs @@ -401,6 +401,18 @@ public static OrientationType IsClockWise(this IEnumerable pnts) return ca2d; } + /// + /// 叉积,二维叉乘计算 + /// + /// 原点 + /// oa向量 + /// ob向量,此为判断点 + /// 返回值有正负,表示绕原点四象限的位置变换,也就是有向面积 + private static double Cross(Point2d o, Point2d a, Point2d b) + { + return (a.X - o.X) * (b.Y - o.Y) - (a.Y - o.Y) * (b.X - o.X); + } + /// /// 获取点集的凸包 /// @@ -408,34 +420,32 @@ public static OrientationType IsClockWise(this IEnumerable pnts) /// 凸包 public static List? ConvexHull(this List points) { - if (points is null) - return null; + if (points.Count < 3) return null; - if (points.Count <= 1) - return points; + //坐标排序 + points = points.OrderBy(p => p.X).ThenBy(p => p.Y).ToList(); - int n = points.Count, k = 0; - List H = new(new Point2d[2 * n]); + var hullPts = new List(); - points.Sort((a, b) => - a.X == b.X ? a.Y.CompareTo(b.Y) : a.X.CompareTo(b.X)); - - // Build lower hull - for (int i = 0; i < n; ++i) - { - while (k >= 2 && IsClockWise(H[k - 2], H[k - 1], points[i]) == OrientationType.CounterClockWise) - k--; - H[k++] = points[i]; + //构建下凸包 + foreach (var pt in points) { + while (hullPts.Count >= 2 && Cross(hullPts[^2], hullPts[^1], pt) <= 0) + hullPts.RemoveAt(hullPts.Count - 1); + hullPts.Add(pt); } - // Build upper hull - for (int i = n - 2, t = k + 1; i >= 0; i--) - { - while (k >= t && IsClockWise(H[k - 2], H[k - 1], points[i]) == OrientationType.CounterClockWise) - k--; - H[k++] = points[i]; + //构建上凸包 + var lowerHullCount = hullPts.Count + 1; + for (var i = points.Count - 2; i >= 0; i--) { + while (hullPts.Count >= lowerHullCount && Cross(hullPts[^2], hullPts[^1], points[i]) <= 0) + hullPts.RemoveAt(hullPts.Count - 1); + hullPts.Add(points[i]); } - return H.Take(k - 1).ToList(); + + //移除与起点重复的尾点 + hullPts.RemoveAt(hullPts.Count - 1); + + return hullPts.Count >= 3 ? hullPts : null; }