/// <summary>
 /// Static function to convert a list of points into a polygon without having a local object instance
 /// </summary>
 public static Polygon Get(List<Point2D> data)
 {
     Polygon p = new Polygon();
     p.Import(data);
     return p;
 }
        /// <summary>
        /// Merges two convex hulls into one convex hull
        /// Finds upper and lower tangents and removes the points in between
        /// </summary>
        private static Polygon Merge(Polygon polygonA, Polygon polygonB, Boolean stepThrough)
        {
            //Since a lot of functions require polygons of type List<Point2D>
            //Convert them here to save time converting lots later on
            var newPolygonA = PolygonManipulation.SortLexographically(polygonA.Convert());
            var newPolygonB = PolygonManipulation.SortLexographically(polygonB.Convert());

            //Get the rightmost point of polygon A and the leftmost point of polygon B
            var tangentPointA = GetRightmostPoint(newPolygonA);
            var tangentPointB = GetLeftmostPoint(newPolygonB);

            //Find lower tangent
            //While Tangent = tangentPointA -> tangentPointB is not a lower tangent to both A and B
            while (!(IsLowerTangent(newPolygonA, tangentPointA, tangentPointB) && IsLowerTangent(newPolygonB, tangentPointB,tangentPointA)))
            {
                //While Tangent = tangentPointA -> tangentPointB is not a lower tangent to A
                while (!IsLowerTangent(newPolygonA, tangentPointA,tangentPointB))
                {
                    //Move counter clockwise
                    tangentPointA = PolygonManipulation.GetNextPoint(newPolygonA, tangentPointA);
                }

                //While Tangent = tangentPointA -> tangentPointB is not a lower tangent to B
                while (!IsLowerTangent(newPolygonB, tangentPointB,tangentPointA))
                {
                    //Move clockwise
                    tangentPointB = PolygonManipulation.GetPreviousPoint(newPolygonB, tangentPointB);
                }
            }

            //Get the lower tangent
            Vector2D lowerTangent = new Vector2D(tangentPointA, tangentPointB);

            //Reset the tangent points
            tangentPointA = GetRightmostPoint(newPolygonA);
            tangentPointB = GetLeftmostPoint(newPolygonB);

            //While Tangent = tangentPointA -> tangentPointB is not an upper tangent to both A and B
            while (!IsUpperTangent(newPolygonA, tangentPointA, tangentPointB) || !(IsUpperTangent(newPolygonB, tangentPointB, tangentPointA)))
            {
                //While Tangent = tangentPointA -> tangentPointB is not an upper tangent to A
                while (!IsUpperTangent(newPolygonA, tangentPointA, tangentPointB))
                {
                    //Move counter clockwise
                    tangentPointA = PolygonManipulation.GetPreviousPoint(newPolygonA, tangentPointA);
                }

                //While Tangent = tangentPointA -> tangentPointB is not an upper tangent to B
                while (!IsUpperTangent(newPolygonB, tangentPointB, tangentPointA))
                {
                    //Move clockwise
                    tangentPointB = PolygonManipulation.GetNextPoint(newPolygonB, tangentPointB);
                }
            }

            //Get the upper tangent
            Vector2D upperTangent = new Vector2D(tangentPointA, tangentPointB);

            //Draw tangents if in step through mode
            if (stepThrough)
            {
                //Convert the polygons into a format for drawing
                var listPolygonA = polygonA.Convert();
                var listPolygonB = polygonB.Convert();

                //If the polygons have more than two points, fill them in
                if (listPolygonA.Count > 2)
                {
                    GraphicalUserInterface.DrawPolygon(listPolygonA, true, XOffset, YOffset, new System.Drawing.SolidBrush(System.Drawing.Color.Coral));
                    GraphicalUserInterface.DrawPolygon(polygonB.Convert(), true, XOffset, YOffset);
                }

                GraphicalUserInterface.DrawPolygon(listPolygonA, false, XOffset, YOffset, new System.Drawing.SolidBrush(System.Drawing.Color.Coral));
                GraphicalUserInterface.DrawPolygon(polygonB.Convert(), false, XOffset, YOffset);
                GraphicalUserInterface.GraphicsObject.DrawLine(new System.Drawing.Pen(System.Drawing.Color.Blue) { Width = 4 },
                                                               lowerTangent.Start.Convert(XOffset, YOffset, GraphicalUserInterface.SizeMultiplier),
                                                               lowerTangent.Target.Convert(XOffset, YOffset, GraphicalUserInterface.SizeMultiplier));
                GraphicalUserInterface.GraphicsObject.DrawLine(new System.Drawing.Pen(System.Drawing.Color.Black) { Width = 4 },
                                                               upperTangent.Start.Convert(XOffset, YOffset, GraphicalUserInterface.SizeMultiplier),
                                                               upperTangent.Target.Convert(XOffset, YOffset, GraphicalUserInterface.SizeMultiplier));

                //Wait for the user to enter a key into the console to continue
                Console.ReadKey();
            }

            //Wire together the polygons by tangent
            return Wire(polygonA, polygonB, lowerTangent, upperTangent);
        }
        /// <summary>
        /// This method uses a different method to the Monotone Chain convex hull
        /// Instead of storing the vertices of the polygon in lists
        /// The vertices are stored as doubly linked lists
        /// </summary>
        private static Polygon RecursiveSolve(Polygon points, bool stepThrough = false)
        {
            //If there are few enough points to warrant the Graham Scan algorithm
            if (points.Count() <= MinimumSize)
            {
                return Polygon.Get(ConvexHull.Solve(points.Convert()));
            }

            //Sort the points by x value and then by y value
            var sortedPoints = PolygonManipulation.SortLexographically(points.Convert());

            //Split points into two sets
            var a = new List<Point2D>();
            var b = new List<Point2D>();

            a.AddRange(sortedPoints.Take(sortedPoints.Count()/2));
            b.AddRange(sortedPoints.Skip(sortedPoints.Count()/2).Take(sortedPoints.Count() - sortedPoints.Count()/2));

            //Compute the convex hulls of a and b recursively
            var polyA = RecursiveSolve(Polygon.Get(a), stepThrough);
            var polyB = RecursiveSolve(Polygon.Get(b), stepThrough);

            //Merge the convex hulls together
            return Merge(polyA, polyB, stepThrough);
        }
        /// <summary>
        /// Given two polygons, and two tangent which will join together
        /// This function will create a polygon which merge the two polygons together using the tangents to bridge between them
        /// </summary>
        public static Polygon Wire(Polygon polygonA, Polygon polygonB, Vector2D lowerTangent, Vector2D upperTangent)
        {
            var wiredPolygon = new List<Point2D>();

            //If UpperTangent.Start is equal to LowerTangent.Start, we need to end the loop early
            Boolean endEarly = PolygonManipulation.Equals(upperTangent.Start, lowerTangent.Start);

            //Add the start point to the wired polygon
            wiredPolygon.Add(upperTangent.Start);

            //Jump across the tangent at set the target at the current vertex
            var currentVertex = polygonB.GetVertex(upperTangent.Target);

            //Since we need to traverse each polygon in a different direction, this marks what polygon we are on
            Boolean leftPolygon = false;

            //While we aren't back at start
            do
            {
                //Add the current vertex to the wired polygon
                wiredPolygon.Add(currentVertex.Point);

                //If we have hit the lower tangent target
                if (PolygonManipulation.Equals(currentVertex.Point, lowerTangent.Target))
                {
                    //Jump across the lower tangent onto the other polygon
                    leftPolygon = true;
                    currentVertex = polygonA.GetVertex(lowerTangent.Start);
                    wiredPolygon.Add(currentVertex.Point);

                    //If we need to end early, break the loop
                    if (endEarly)
                        break;
                }

                //While we haven't find a suitable current vertex
                do
                {
                    if (leftPolygon)
                    {
                        //Traverse Clockwise
                        if (currentVertex == null || currentVertex.Point == null)
                            currentVertex = polygonA.Tail.Prev;
                        else
                            currentVertex = currentVertex.Prev;
                    }
                    else
                    {
                        //Traverse Counterclockwise
                        if (currentVertex == null || currentVertex.Point == null)
                            currentVertex = polygonB.Tail.Prev;
                        else
                            currentVertex = currentVertex.Prev;
                    }
                } while (currentVertex == null || currentVertex.Point == null);

            } while (!PolygonManipulation.Equals(currentVertex.Point, upperTangent.Start));

            wiredPolygon = wiredPolygon.Distinct().ToList();

            //Since the wired polygon is in clockwise order, we need to reverse it
            wiredPolygon.Reverse();
            return Polygon.Get(wiredPolygon);
        }