コード例 #1
0
        public override Graph GenerateGraph(int vertexCount, int edgePercent, int size = 500)
        {
            var graph = new Graph();

            var random = new Random();

            graph.Vertices = Enumerable.Range(0, vertexCount)
                             .Select(i => new Vertex(i, new Point(random.NextDouble() * size, random.NextDouble() * size)))
                             .ToDictionary(_ => _.Id, _ => _);
            if (graph.Vertices.Count < 2)
            {
                return(graph);
            }
            if (graph.Vertices.Count == 2)
            {
                graph.AddEdge(graph.Vertices[0], graph.Vertices[1]);
                return(graph);
            }

            // соединяем точки гранями, строим триангуляцию Делоне
            // чтобы ускорить процесс, найдем самую левую вверхнюю точку, отсортируем все точки по расстоянию от нее
            // и соеденим с ближайшей к ней точке, оброзовав первую грань графа.

            var first = graph.Vertices[0];

            foreach (var v in graph.Vertices.Values)
            {
                if (v.X < first.X ||
                    v.X == first.X && v.Y < first.Y)
                {
                    first = v;
                }
            }

            var range = new double[graph.Vertices.Count];

            for (var i = 0; i < graph.Vertices.Count; i++)
            {
                var x = graph.Vertices[i].X - first.X;
                var y = graph.Vertices[i].Y - first.Y;
                range[i] = x * x + y * y;
            }

            var sortedVertices = graph.Vertices.Values.ToArray();

            Array.Sort(range, sortedVertices);

            var vertex = new TrisVertex[sortedVertices.Length];

            for (var i = 0; i < sortedVertices.Length; i++)
            {
                vertex[i] = new TrisVertex(sortedVertices[i]);
            }

            // создаем первую грань

            var tris = new Tris(vertex[0], vertex[1], vertex[2]);

            tris.MakeClockwise();

            var resultTriangles = new List <Tris>(sortedVertices.Length * 2);

            tris[0].Trises.Add(tris);
            tris[1].Trises.Add(tris);
            tris[2].Trises.Add(tris);
            resultTriangles.Add(tris);

            var hull = new CircularList <TrisEdge>();

            hull.AddItem(new TrisEdge(tris, tris[0], tris[1]));
            hull.AddItem(new TrisEdge(tris, tris[1], tris[2]));
            hull.AddItem(new TrisEdge(tris, tris[2], tris[0]));

            var seekStart = hull.Last;

            for (var i = 3; i < vertex.Length; i++)
            {
                var nextPoint = vertex[i];

                CircularItem <TrisEdge> visiblePoint = null;
                if (seekStart.Value.EdgeVisibleFrom(nextPoint.Position))
                {
                    visiblePoint = seekStart;
                }
                else
                {
                    visiblePoint = hull.FindNext(seekStart,
                                                 t => t.Value.EdgeVisibleFrom(nextPoint.Position));
                }

                if (visiblePoint == null)
                {
                    continue;
                }
                var notVisibleLeft = hull.FindPrevious(visiblePoint,
                                                       t => !t.Value.EdgeVisibleFrom(nextPoint.Position));

                var notVisibleRight = hull.FindNext(visiblePoint,
                                                    t => !t.Value.EdgeVisibleFrom(nextPoint.Position));

                if (notVisibleLeft == null || notVisibleRight == null)
                {
                    continue;
                }
                var visibleLeft = notVisibleLeft.Next;
                var edge        = visibleLeft.Value;
                var hullItem    = visibleLeft;

                Tris firstEdgeTris = null;
                Tris lastEdgeTris  = null;

                while (hullItem != notVisibleRight)
                {
                    var nextTris = new Tris(edge.A, nextPoint, edge.B);

                    nextTris[0].Trises.Add(nextTris);
                    nextTris[1].Trises.Add(nextTris);
                    nextTris[2].Trises.Add(nextTris);
                    resultTriangles.Add(nextTris);

                    // разворачиваем треугольники, чтобы удовлетворяли критерию Делоне
                    if (FlipIfNeeded(edge.tris, nextTris, nextPoint, out var returnLinkTris))
                    {
                        if (firstEdgeTris == null)
                        {
                            firstEdgeTris = returnLinkTris;
                        }
                    }


                    if (firstEdgeTris == null)
                    {
                        firstEdgeTris = nextTris;
                    }

                    lastEdgeTris = nextTris;

                    hullItem = hullItem.Next;
                    edge     = hullItem.Value;
                }

                // закрываем дырку
                hull.LinkTwoItem(notVisibleLeft, notVisibleRight);
                seekStart       = hull.AddItemAfter(notVisibleLeft);
                seekStart.Value = new TrisEdge(firstEdgeTris, notVisibleLeft.Value.B, nextPoint);


                seekStart       = hull.AddItemAfter(seekStart);
                seekStart.Value = new TrisEdge(lastEdgeTris, nextPoint, notVisibleRight.Value.A);
            }

            // формируем связи на основе триангуляции
            graph.Edges = new List <Edge>(resultTriangles.Count * 2);
            foreach (var tr in resultTriangles)
            {
                graph.AddEdge(tr[0].Original, tr[1].Original);
                graph.AddEdge(tr[1].Original, tr[2].Original);
                graph.AddEdge(tr[2].Original, tr[0].Original);
            }


            // пытаемся оставить только нужное кол-во связей, при этом проверяя что все узлы соеденены

            var needEdges = (int)(graph.Edges.Count / 100f * edgePercent);

            var rnd = new Random();

            var testEdges = new List <Edge>(graph.Edges);

            while (testEdges.Count > 0 && needEdges < graph.Edges.Count)
            {
                var removeIndex = rnd.Next(testEdges.Count);
                var testEdge    = testEdges[removeIndex];

                if (graph.HasWayBetweenWithoutEdge(testEdge.A, testEdge.B))
                {
                    graph.RemoveEdge(testEdge.A, testEdge.B);
                }

                testEdges.RemoveAt(removeIndex);
            }

            return(graph);
        }