/// <summary>
        /// Split an edge with <paramref name="vertexIndex"/>.
        /// For each half edge two new triangles replace old one.
        /// </summary>
        /// <param name="vertexIndex">Vertex index.</param>
        /// <param name="edge">Half edge.</param>
        private void SplitEdge(int vertexIndex, HalfEdge edge)
        {
            var twin         = edge.Twin;
            var edgesToCheck = new List <HalfEdge>(12);

            SplitHalfEdge(edge);
            if (twin != null)
            {
                SplitHalfEdge(twin);
                var twinSplitNext = twin.Next.Twin.Next;
                twin.TwinWith(edge.Next.Twin.Next);
                twinSplitNext.TwinWith(edge);
            }
            edge.Constrained = edge.Constrained;
            edge.Next.Twin.Next.Constrained = edge.Constrained;
            RestoreDelaunayProperty(edgesToCheck);
            void SplitHalfEdge(HalfEdge e)
            {
                var next = e.Next;
                var prev = next.Next;
                // Form first triangle
                var split = new HalfEdge(vertexIndex)
                {
                    Next = prev,
                };

                e.Next = split;
                // Form second triangle
                var splitTwin     = new HalfEdge(prev.Origin);
                var splitTwinNext = new HalfEdge(vertexIndex)
                {
                    Next = next,
                };

                splitTwin.Next = splitTwinNext;
                next.Next      = splitTwin;
                splitTwin.TwinWith(split);
                edgesToCheck.Add(e);
                edgesToCheck.Add(split);
                edgesToCheck.Add(prev);
                edgesToCheck.Add(splitTwin);
                edgesToCheck.Add(splitTwinNext);
                edgesToCheck.Add(next);
            }
        }
        private HalfEdge Flip(HalfEdge edge)
        {
            var twin     = edge.Twin;
            var edgeNext = edge.Next;
            var twinNext = twin.Next;

            edge.Prev.Next = twinNext;
            twin.Prev.Next = edgeNext;
            edge.Detach();
            twin.Detach();
            edge = new HalfEdge(edgeNext.Next.Origin)
            {
                Next = twinNext.Next
            };
            twin = new HalfEdge(twinNext.Next.Origin)
            {
                Next = edgeNext.Next
            };
            edge.TwinWith(twin);
            edgeNext.Next = edge;
            twinNext.Next = twin;
            return(edge);
        }
        private void ToConvexHull(HalfEdge borderEdge = null)
        {
            var current = borderEdge;

            if (current == null)
            {
                foreach (var halfEdge in HalfEdges)
                {
                    if (halfEdge.Twin == null)
                    {
                        current = halfEdge;
                        break;
                    }
                }
            }
            var edgesToCheck = new List <HalfEdge>();
            var prev         = PrevBorderEdge(current);
            var start        = current;

            // Same as Ear clipping method but for 'outside' ears.
            do
            {
                var o1 = prev.Origin;
                var o2 = current.Origin;
                var o3 = current.Next.Origin;
                var p1 = InnerVertices[o1].Pos;
                var p2 = InnerVertices[o2].Pos;
                var p3 = InnerVertices[o3].Pos;
                if (!AreClockwiseOrderedOrCollinear(p1, p2, p3))
                {
                    var c            = start;
                    var intersectAny = false;
                    do
                    {
                        if (c.Origin == o1 || c.Origin == o2 || c.Origin == o3 || c.Next.Origin == o1)
                        {
                            c = NextBorderEdge(c);
                            continue;
                        }
                        var s1 = InnerVertices[c.Origin].Pos;
                        var s2 = InnerVertices[c.Next.Origin].Pos;
                        if (RobustSegmentSegmentIntersection(p1, p3, s1, s2))
                        {
                            intersectAny = true;
                            break;
                        }
                        c = NextBorderEdge(c);
                    } while (start != c);
                    if (!intersectAny)
                    {
                        var t = new HalfEdge(current.Next.Origin)
                        {
                            Next = new HalfEdge(current.Origin)
                            {
                                Next = new HalfEdge(prev.Origin),
                            },
                        };
                        edgesToCheck.Add(t.Prev);
                        t.Prev.Next = t;
                        t.TwinWith(current);
                        t.Next.TwinWith(prev);
                        start = current = t.Prev;
                        prev  = PrevBorderEdge(start);
                        continue;
                    }
                }
                prev    = current;
                current = NextBorderEdge(current);
                if (current == start)
                {
                    break;
                }
            } while (true);
            RestoreDelaunayProperty(edgesToCheck);
        }
        private bool InnerInsertConstrainedEdge(int index0, int index1, bool destroyConstrained = false)
        {
            var location = LocateClosestTriangle(index0, out var start);

            System.Diagnostics.Debug.Assert(location == LocationResult.SameVertex);
            System.Diagnostics.Debug.Assert(start.Origin == index0);
            // Check test cases for explanation
            var upperUsed             = new HashSet <int>();
            var lowerUsed             = new HashSet <int>();
            var shouldBeReinserted    = new HashSet <int>();
            var shouldBeReconstrained = new List <(int, int)>();
            var a      = InnerVertices[index0].Pos;
            var b      = InnerVertices[index1].Pos;
            var ab     = b - a;
            var signab = new IntVector2(Mathf.Sign(ab.X), Mathf.Sign(ab.Y));
            // In order to insert a constrain edge we have to delete all
            // triangles that (index0, index1) crosses. Thereafter we
            // have 2 polygonal holes that should be triangulated.
            List <HalfEdge> upperPolygon = null, lowerPolygon = null;
            var             current = start;

            foreach (var incidentEdge in IncidentEdges(start))
            {
                var next = incidentEdge.Next;
                var prev = next.Next;
                // Special case: requested edge already exists.
                if (next.Origin == index1)
                {
                    return(incidentEdge.Constrained = true);
                }
                if (prev.Origin == index1)
                {
                    return(prev.Constrained = true);
                }
                var c = InnerVertices[next.Origin].Pos;
                var d = InnerVertices[prev.Origin].Pos;
                var e = InnerVertices[incidentEdge.Origin].Pos;
                // Special case: requested edge lies on the same line as [e, c] or [e, d].
                // If true then mark edge as constrained and insert edge [next.Origin, index1] or [prev.Origin, index1].
                if (IsVertexOnLine(a, e, c) && IsVertexOnLine(b, e, c))
                {
                    var ec = c - e;
                    // Check for co-directionality in order to prevent looping
                    if (Mathf.Sign(ec.X) == signab.X && Mathf.Sign(ec.Y) == signab.Y)
                    {
                        return(incidentEdge.Constrained = InnerInsertConstrainedEdge(next.Origin, index1, destroyConstrained));
                    }
                }
                else if (IsVertexOnLine(a, e, d) && IsVertexOnLine(b, e, d))
                {
                    var ed = d - e;
                    // Check for co-directionality in order to prevent looping
                    if (Mathf.Sign(ed.X) == signab.X && Mathf.Sign(ed.Y) == signab.Y)
                    {
                        return(prev.Constrained = InnerInsertConstrainedEdge(prev.Origin, index1, destroyConstrained));
                    }
                }
                else if (RobustSegmentSegmentIntersection(a, b, c, d))
                {
                    // Should not delete existing constrain edges.
                    if (next.Constrained && !destroyConstrained)
                    {
                        return(false);
                    }
                    // Then we should start traversing
                    upperPolygon = new List <HalfEdge> {
                        incidentEdge
                    };
                    lowerPolygon = new List <HalfEdge> {
                        prev
                    };
                    lowerUsed.Add(prev.Origin);
                    upperUsed.Add(incidentEdge.Origin);
                    current = next;
                    break;
                }
            }
            while (true)
            {
                if (current.Twin == null)
                {
                    // Something horrible has happened..
                    // Something like trying to connect two vertices trough concavity.
                    // Should not happen at all in current implementation.
                    return(false);
                }
                current = current.Twin;
                var next = current.Next;
                var prev = next.Next;
                if (prev.Origin == index1)
                {
                    // We found the end of the requested edge.
                    // Stop traversing and triangulate both polygons.
                    AddLower(prev);
                    AddUpper(next);
                    Finish(index0, index1);
                    return(true);
                }
                var c = InnerVertices[next.Origin].Pos;
                var d = InnerVertices[prev.Origin].Pos;
                var e = InnerVertices[current.Origin].Pos;
                if (IsVertexOnLine(d, a, b))
                {
                    // Special case: [a, b] intersects triangle exactly in the
                    // vertex that is opposite to basis (`current` edge).
                    // Should finish traversing and insert constrain edge
                    // between prev.Origin and index1.
                    AddLower(prev);
                    AddUpper(next);
                    var edge = Finish(index0, prev.Origin);
                    edge.Constrained = InnerInsertConstrainedEdge(prev.Origin, index1, destroyConstrained);
                    RestoreDelaunayProperty(upperPolygon);
                    RestoreDelaunayProperty(lowerPolygon);
                    return(edge.Constrained);
                }
                if (RobustSegmentSegmentIntersection(a, b, c, d))
                {
                    if (next.Constrained && !destroyConstrained)
                    {
                        return(false);
                    }
                    AddLower(prev);
                    current = next;
                }
                else if (RobustSegmentSegmentIntersection(a, b, d, e))
                {
                    if (prev.Constrained && !destroyConstrained)
                    {
                        return(false);
                    }
                    AddUpper(next);
                    current = prev;
                }
                else
                {
                    System.Diagnostics.Debug.Fail("This is impossible case.");
                }
            }
            void AddUpper(HalfEdge e)
            {
                if (upperUsed.Add(e.Origin))
                {
                    upperPolygon.Add(e);
                }
                else
                {
                    // It means that triangles from i to polygon.Count should be removed.
                    // Removing those triangles makes some vertices isolated and
                    // also may remove previously inserted constrained edges.
                    // So we keep that in mind in order to reinsert missing elements later.
                    var i = upperPolygon.FindIndex(edge => edge.Origin == e.Origin);
                    for (int j = upperPolygon.Count - 1; j >= i; j--)
                    {
                        var t = upperPolygon[j];
                        shouldBeReinserted.Add(t.Origin);
                        shouldBeReinserted.Add(t.Next.Origin);
                        if (t.Constrained)
                        {
                            shouldBeReconstrained.Add((t.Origin, t.Next.Origin));
                        }
                        upperPolygon.RemoveAt(j);
                    }
                    upperPolygon.Add(e);
                }
            }

            void AddLower(HalfEdge e)
            {
                if (lowerUsed.Add(e.Origin))
                {
                    lowerPolygon.Add(e);
                }
                else
                {
                    // Same as for upper polygon except that it is filled in reverse order.
                    var i = lowerPolygon.FindIndex(edge => edge.Origin == e.Origin);
                    for (int j = lowerPolygon.Count - 1; j > i; j--)
                    {
                        var t = lowerPolygon[j];
                        shouldBeReinserted.Add(t.Origin);
                        shouldBeReinserted.Add(t.Next.Origin);
                        if (t.Constrained)
                        {
                            shouldBeReconstrained.Add((t.Origin, t.Next.Origin));
                        }
                        lowerPolygon.RemoveAt(j);
                    }
                    shouldBeReinserted.Add(e.Next.Origin);
                }
            }

            HalfEdge Finish(int i0, int i1)
            {
                // Create (i0, i1) half edges.
                var e1 = new HalfEdge(i1);
                var e2 = new HalfEdge(i0);

                e1.TwinWith(e2);
                e1.Constrained = true;
                AddUpper(e1);
                AddLower(e2);
                lowerPolygon.Reverse();
                // Triangulate both polygonal holes.
                TriangulatePolygonByEarClipping(upperPolygon, false);
                TriangulatePolygonByEarClipping(lowerPolygon, false);
                RestoreDelaunayProperty(upperPolygon);
                // Reinsert missing elements.
                foreach (var vertexIndex in shouldBeReinserted)
                {
                    AddVertex(vertexIndex);
                }
                foreach (var edge in shouldBeReconstrained)
                {
                    InnerInsertConstrainedEdge(edge.Item1, edge.Item2);
                }
                return(e2);
            }
        }