public IEnumerable <int> Build(IEnumerable <Vector2> vertices, IReadOnlyCollection <IEnumerable <int> > holeVertices)
        {
            var sortedVertices = vertices.Select((v, index) => new SortedVertex(index, v))
                                 .OrderBy(v => v.Vertex.x).ThenBy(v => v.Vertex.y).ToList();

            if (sortedVertices.Count < 3)
            {
                throw new ArgumentException($"Minimum vertices count for triangulation is 3, but was {sortedVertices.Count}");
            }

            var convexHull = new HullEdgesList(sortedVertices.Count);
            var graph      = new TriangulationGraph();

            convexHull.Add(new HullEdge(1, 1));
            convexHull.Add(new HullEdge(0, 0));
            graph[new Edge(0, 1)].Insert(2);

            for (var i = 2; i < sortedVertices.Count; i++)
            {
                AddVertexToTriangulation(i, sortedVertices, convexHull, graph);
            }

            return(DelaunayTriangulationConverter.ConvertToTriangulation(graph, sortedVertices, holeVertices));
        }
        private static int FixTriangulationIteratively(int vertexId, List <SortedVertex> vertices, HullEdgesList convexHull, HullEdge.IterationDirections direction,
                                                       Func <Vector2, Vector2, bool> condition, Action <int, int, int> fixTriangulation)
        {
            var       iterationsCount    = 0;
            const int maxIterationsCount = 100;

            var hullVertex = vertexId - 1;

            var lastVector     = vertices[hullVertex].Vertex - vertices[vertexId].Vertex;
            var nextHullVertex = convexHull[hullVertex].Get(direction);
            var newVector      = vertices[nextHullVertex].Vertex - vertices[vertexId].Vertex;

            while (condition(lastVector, newVector) && iterationsCount < maxIterationsCount)
            {
                fixTriangulation(hullVertex, nextHullVertex, vertexId);

                hullVertex     = nextHullVertex;
                lastVector     = newVector;
                nextHullVertex = convexHull[hullVertex].Get(direction);
                newVector      = vertices[nextHullVertex].Vertex - vertices[vertexId].Vertex;
                iterationsCount++;
            }
            convexHull[vertexId].Set(direction, hullVertex);

            return(hullVertex);
        }
        private static void AddVertexToTriangulation(int vertexId, List <SortedVertex> vertices, HullEdgesList convexHull,
                                                     TriangulationGraph graph)
        {
            FixTriangulationIteratively(vertexId, vertices, convexHull, HullEdge.IterationDirections.Right,
                                        (v1, v2) => TriangulationHelper.CrossProduct(v1, v2) > -DelaunayTriangulationValidator.Eps,
                                        (left, right, outer) => FixTriangulation(left, right, outer, vertices, graph));

            var hullVertex = FixTriangulationIteratively(vertexId, vertices, convexHull, HullEdge.IterationDirections.Left,
                                                         (v1, v2) => TriangulationHelper.CrossProduct(v1, v2) < DelaunayTriangulationValidator.Eps,
                                                         (left, right, outer) => FixTriangulation(right, left, outer, vertices, graph));

            convexHull[convexHull[vertexId].Right].Set(HullEdge.IterationDirections.Left, vertexId);
            convexHull[hullVertex].Set(HullEdge.IterationDirections.Right, vertexId);
        }