偶遇需要拆解凹多边形
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Media; namespace DrawPolygon { public static class Settings { public const float Epsilon = 1.192092896e-07f; /// <summary> /// The maximum number of vertices on a convex polygon. /// </summary> public static int MaxPolygonVertices = 7; } public class Vertices : List<Point> { public Vertices() { } public Vertices(int capacity) { Capacity = capacity; } public Vertices(Point[] Point) { for (int i = 0; i < Point.Length; i++) { Add(Point[i]); } } public Vertices(IList<Point> vertices) { for (int i = 0; i < vertices.Count; i++) { Add(vertices[i]); } } public int NextIndex(int index) { if (index == Count - 1) { return 0; } return index + 1; } public Point NextVertex(int index) { return this[NextIndex(index)]; } public int PreviousIndex(int index) { if (index == 0) { return Count - 1; } return index - 1; } public Point PreviousVertex(int index) { return this[PreviousIndex(index)]; } public float GetSignedArea() { int i; float area = 0; for (i = 0; i < Count; i++) { int j = (i + 1) % Count; area += (float)(this[i].X * this[j].Y); area -= (float)(this[i].Y * this[j].X); } area /= 2.0f; return area; } public const float Pi = 3.14159f; public bool IsCounterClockWise() { return Count < 3 ? true : (GetSignedArea() > 0.0f); } public void ForceCounterClockWise() { if (!IsCounterClockWise()) { Reverse(); } } public override string ToString() { StringBuilder builder = new StringBuilder(); for (int i = 0; i < Count; i++) { builder.Append(this[i].ToString()); if (i < Count - 1) { builder.Append(" "); } } return builder.ToString(); } } public static class BayazitDecomposer { private static int num = 1; private static int stop = 20; private static int stopTemp = 12; public static List<Vertices> GetConvexPartition(PointCollection points) { return GetConvexPartition(new Vertices(points)); } public static List<Vertices> GetConvexPartition(Vertices vertices) { stop = 50; stopTemp = 20; return ConvexPartition(vertices, 1); } private static List<Vertices> ConvexPartition(Vertices vertices, int verticesTemp) { stopTemp++; num = verticesTemp; //verticesTemp为1时正常分解,为2时代表遇到不能分解的轮廓,这时返回null,并放弃这个轮廓的更新 if (stop == 1) { stop = vertices.Count; } if (stopTemp >= stop) { num = 2; } if (num == 1) { if (vertices == null) return null; vertices.ForceCounterClockWise(); List<Vertices> list = new List<Vertices>(); float d, lowerDist, upperDist; Point p; Point lowerInt = new Point(); Point upperInt = new Point(); int lowerIndex = 0, upperIndex = 0; Vertices lowerPoly, upperPoly; for (int i = 0; i < vertices.Count; ++i) { try { Reflex(i, vertices); } catch { num = 2; return null; } if (Reflex(i, vertices)) { lowerDist = upperDist = float.MaxValue; for (int j = 0; j < vertices.Count; ++j) { // if line intersects with an edge if (Left(At(i - 1, vertices), At(i, vertices), At(j, vertices)) && RightOn(At(i - 1, vertices), At(i, vertices), At(j - 1, vertices))) { // find the point of intersection p = LineTools.LineIntersect(At(i - 1, vertices), At(i, vertices), At(j, vertices), At(j - 1, vertices)); if (Right(At(i + 1, vertices), At(i, vertices), p)) { // make sure it's inside the poly d = SquareDist(At(i, vertices), p); if (d < lowerDist) { // keep only the closest intersection lowerDist = d; lowerInt = p; lowerIndex = j; } } } if (Left(At(i + 1, vertices), At(i, vertices), At(j + 1, vertices)) && RightOn(At(i + 1, vertices), At(i, vertices), At(j, vertices))) { p = LineTools.LineIntersect(At(i + 1, vertices), At(i, vertices), At(j, vertices), At(j + 1, vertices)); if (Left(At(i - 1, vertices), At(i, vertices), p)) { d = SquareDist(At(i, vertices), p); if (d < upperDist) { upperDist = d; upperIndex = j; upperInt = p; } } } } // if there are no vertices to connect to, choose a point in the middle if (lowerIndex == (upperIndex + 1) % vertices.Count) { Point sp = new Point((lowerInt.X + upperInt.X) / 2, (lowerInt.Y + upperInt.Y) / 2); lowerPoly = Copy(i, upperIndex, vertices); lowerPoly.Add(sp); upperPoly = Copy(lowerIndex, i, vertices); upperPoly.Add(sp); } else { double highestScore = 0, bestIndex = lowerIndex; while (upperIndex < lowerIndex) upperIndex += vertices.Count; for (int j = lowerIndex; j <= upperIndex; ++j) { if (CanSee(i, j, vertices)) { double score = 1 / (SquareDist(At(i, vertices), At(j, vertices)) + 1); if (Reflex(j, vertices)) { if (RightOn(At(j - 1, vertices), At(j, vertices), At(i, vertices)) && LeftOn(At(j + 1, vertices), At(j, vertices), At(i, vertices))) { score += 3; } else { score += 2; } } else { score += 1; } if (score > highestScore) { bestIndex = j; highestScore = score; } } } lowerPoly = Copy(i, (int)bestIndex, vertices); upperPoly = Copy((int)bestIndex, i, vertices); } //在每次递归时都进行检测,遇到不能分解的就开始跳出循环 var lslowerPoly = ConvexPartition(lowerPoly, num); var lsupperPoly = ConvexPartition(upperPoly, num); if (lslowerPoly == null || lsupperPoly == null) { return null; } list.AddRange(lslowerPoly); list.AddRange(lsupperPoly); return list; } } // polygon is already convex if (vertices.Count > Settings.MaxPolygonVertices) { lowerPoly = Copy(0, vertices.Count / 2, vertices); upperPoly = Copy(vertices.Count / 2, 0, vertices); //当轮廓的点数目大于Settings.MaxPolygonVertices时分解轮廓的带你数目 var lslowerPoly = ConvexPartition(lowerPoly, num); var lsupperPoly = ConvexPartition(upperPoly, num); if (lslowerPoly == null || lsupperPoly == null) { return null; } list.AddRange(lslowerPoly); list.AddRange(lsupperPoly); } else { list.Add(vertices); } //The polygons are not guaranteed to be without collinear points. We remove them to be sure. for (int i = 0; i < list.Count; i++) { list[i] = SimplifyTools.CollinearSimplify(list[i], 0); } //Remove empty vertice collections for (int i = list.Count - 1; i >= 0; i--) { if (list[i].Count == 0) { list.RemoveAt(i); } } return list; } return null; } private static bool Reflex(int i, Vertices vertices) { return Right(i, vertices); } private static bool Right(int i, Vertices vertices) { return Right(At(i - 1, vertices), At(i, vertices), At(i + 1, vertices)); } private static bool Left(Point a, Point b, Point c) { return MathUtils.Area(ref a, ref b, ref c) > 0; } private static bool LeftOn(Point a, Point b, Point c) { return MathUtils.Area(ref a, ref b, ref c) >= 0; } private static bool Right(Point a, Point b, Point c) { return MathUtils.Area(ref a, ref b, ref c) < 0; } private static bool RightOn(Point a, Point b, Point c) { return MathUtils.Area(ref a, ref b, ref c) <= 0; } private static float SquareDist(Point a, Point b) { float dx = (float)(b.X - a.X); float dy = (float)(b.Y - a.Y); return dx * dx + dy * dy; } private static Point At(int i, Vertices vertices) { int s = vertices.Count; return vertices[i < 0 ? s - (-i % s) : i % s]; } private static Vertices Copy(int i, int j, Vertices vertices) { Vertices p = new Vertices(); while (j < i) j += vertices.Count; for (; i <= j; ++i) { p.Add(At(i, vertices)); } return p; } private static bool CanSee(int i, int j, Vertices vertices) { if (Reflex(i, vertices)) { if (LeftOn(At(i, vertices), At(i - 1, vertices), At(j, vertices)) && RightOn(At(i, vertices), At(i + 1, vertices), At(j, vertices))) return false; } else { if (RightOn(At(i, vertices), At(i + 1, vertices), At(j, vertices)) || LeftOn(At(i, vertices), At(i - 1, vertices), At(j, vertices))) return false; } if (Reflex(j, vertices)) { if (LeftOn(At(j, vertices), At(j - 1, vertices), At(i, vertices)) && RightOn(At(j, vertices), At(j + 1, vertices), At(i, vertices))) return false; } else { if (RightOn(At(j, vertices), At(j + 1, vertices), At(i, vertices)) || LeftOn(At(j, vertices), At(j - 1, vertices), At(i, vertices))) return false; } for (int k = 0; k < vertices.Count; ++k) { if ((k + 1) % vertices.Count == i || k == i || (k + 1) % vertices.Count == j || k == j) { continue; } Point intersectionPoint; if (LineTools.LineIntersect(At(i, vertices), At(j, vertices), At(k, vertices), At(k + 1, vertices), out intersectionPoint)) { return false; } } return true; } } public static class MathUtils { public static bool IsValid(double x) { return double.IsNaN(x) ? false : !double.IsInfinity(x); } public static void Cross(ref Point a, ref Point b, out float c) { c = (float)(a.X * b.Y - a.Y * b.X); } public static float Area(ref Point a, ref Point b, ref Point c) { return (float)(a.X * (b.Y - c.Y) + b.X * (c.Y - a.Y) + c.X * (a.Y - b.Y)); } public static bool Collinear(ref Point a, ref Point b, ref Point c, float tolerance) { return FloatInRange(Area(ref a, ref b, ref c), -tolerance, tolerance); } public static bool FloatEquals(float value1, float value2) { return Math.Abs(value1 - value2) <= Settings.Epsilon; } public static bool FloatInRange(float value, float min, float max) { return (value >= min && value <= max); } } public static class SimplifyTools { public static Vertices CollinearSimplify(Vertices vertices, float collinearityTolerance) { if (vertices.Count < 3) return vertices; Vertices simplified = new Vertices(); for (int i = 0; i < vertices.Count; i++) { int prevId = vertices.PreviousIndex(i); int nextId = vertices.NextIndex(i); Point prev = vertices[prevId]; Point current = vertices[i]; Point next = vertices[nextId]; if (MathUtils.Collinear(ref prev, ref current, ref next, collinearityTolerance)) continue; simplified.Add(current); } return simplified; } } public static class LineTools { public static Point LineIntersect(Point p1, Point p2, Point q1, Point q2) { Point i = new Point(0, 0); float a1 = (float)(p2.Y - p1.Y); float b1 = (float)(p1.X - p2.X); float c1 = (float)(a1 * p1.X + b1 * p1.Y); float a2 = (float)(q2.Y - q1.Y); float b2 = (float)(q1.X - q2.X); float c2 = (float)(a2 * q1.X + b2 * q1.Y); float det = a1 * b2 - a2 * b1; if (!MathUtils.FloatEquals(det, 0)) { i.X = (b2 * c1 - b1 * c2) / det; i.Y = (a1 * c2 - a2 * c1) / det; } return i; } public static bool LineIntersect(ref Point point1, ref Point point2, ref Point point3, ref Point point4, bool firstIsSegment, bool secondIsSegment, out Point point) { point = new Point(); float a = (float)(point4.Y - point3.Y); float b = (float)(point2.X - point1.X); float c = (float)(point4.X - point3.X); float d = (float)(point2.Y - point1.Y); float denom = (a * b) - (c * d); if (!(denom >= -Settings.Epsilon && denom <= Settings.Epsilon)) { float e = (float)(point1.Y - point3.Y); float f = (float)(point1.X - point3.X); float oneOverDenom = 1.0f / denom; float ua = (c * e) - (a * f); ua *= oneOverDenom; if (!firstIsSegment || ua >= 0.0f && ua <= 1.0f) { float ub = (b * e) - (d * f); ub *= oneOverDenom; if (!secondIsSegment || ub >= 0.0f && ub <= 1.0f) { if (ua != 0f || ub != 0f) { point.X = point1.X + ua * b; point.Y = point1.Y + ua * d; return true; } } } } return false; } public static bool LineIntersect(Point point1, Point point2, Point point3, Point point4, out Point intersectionPoint) { return LineIntersect(ref point1, ref point2, ref point3, ref point4, true, true, out intersectionPoint); } } }