zoukankan      html  css  js  c++  java
  • 拆解凹多边形

    偶遇需要拆解凹多边形

    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);
            }
        }
    }
    View Code
  • 相关阅读:
    Linux命令未找到(command not found),误删Linux path原始路径
    Linux安装JDK1.8
    Python批量修改文件夹内所有json文件中部分内容
    [转]jQuery插件开发精品教程,让你的jQuery提升一个台阶
    使用apache的ab对页面进行压力测试
    http_load压力测试
    php json_encode数据格式化2种格式[]和{}
    PHP接收JSON格式的数据
    新浪短链接API接口示例
    一个提示样式
  • 原文地址:https://www.cnblogs.com/jiailiuyan/p/3392275.html
Copyright © 2011-2022 走看看