コード例 #1
0
        private bool FlipIfNeeded(Tris trisA, Tris trisB, TrisVertex newPoint, out Tris rt)
        {
            var indexB     = trisB.IndexOfPoint(newPoint);
            var indexBNext = indexB < 2 ? indexB + 1 : 0;
            var indexBPrev = indexB > 0 ? indexB - 1 : 2;

            var indexA    = trisA.IndexOfThirdPoint(trisB[indexBNext], trisB[indexBPrev]);
            var downPoint = trisA[indexA];

            var indexANext = indexA < 2 ? indexA + 1 : 0;
            var indexAPrev = indexA > 0 ? indexA - 1 : 2;

            var needAnotherCut = trisA.IsPointBrokeDelaunay(newPoint);

            if (!needAnotherCut)
            {
                needAnotherCut = trisB.IsPointBrokeDelaunay(downPoint);
            }

            if (needAnotherCut)
            {
                // запоминаем треугольники, "ниже"

                var trA = downPoint.FindAnotherTrisWith(trisA, trisA[indexANext], downPoint);
                var trB = downPoint.FindAnotherTrisWith(trisA, trisA[indexAPrev], downPoint);

                // рубим по другому образуемый этими треугольниками четырехугольник.
                trisA[indexAPrev].Trises.Remove(trisA);
                trisB[indexBPrev].Trises.Remove(trisB);

                trisA.Points[indexAPrev] = newPoint;
                trisB.Points[indexBPrev] = downPoint;

                newPoint.Trises.Add(trisA);
                downPoint.Trises.Add(trisB);

                // вызываем рекурсивно для внутренних
                rt = trisA;
                Tris tmp;

                if (trA != null)
                {
                    if (FlipIfNeeded(trA, trisA, newPoint, out tmp))
                    {
                        rt = tmp;
                    }
                }

                if (trB != null)
                {
                    FlipIfNeeded(trB, trisB, newPoint, out tmp);
                }
                return(true);
            }

            rt = null;
            return(false);
        }
コード例 #2
0
ファイル: Tris.cs プロジェクト: dmi3dmi3/Percolation-02
 public int IndexOfThirdPoint(TrisVertex a, TrisVertex b)
 {
     if (Points[0] != a && Points[0] != b)
     {
         return(0);
     }
     if (Points[1] != a && Points[1] != b)
     {
         return(1);
     }
     return(2);
 }
コード例 #3
0
ファイル: Tris.cs プロジェクト: dmi3dmi3/Percolation-02
        /// <summary>
        ///     Находим позиции и квадрат радиуса описаной окружности, через 3 точки
        /// </summary>
        public bool IsPointBrokeDelaunay(TrisVertex p)
        {
            // Используем относительные координаты до точки 'a'
            double xba = Points[1].X - Points[0].X;
            double yba = Points[1].Y - Points[0].Y;
            double xca = Points[2].X - Points[0].X;
            double yca = Points[2].Y - Points[0].Y;

            // Квадраты растояний граней принадлежащих 'a'
            var baLength = xba * xba + yba * yba;
            var caLength = xca * xca + yca * yca;

            // Считаем деноминатор формулы.
            var d = xba * yca - yba * xca;

            if (d == 0)
            {
                return(false);
            }

            var denominator = 0.5 / d;

            // Рассчитываем смещение (от 'pa') центра описанной окружности
            var xC = (yca * baLength - yba * caLength) * denominator;
            var yC = (xba * caLength - xca * baLength) * denominator;

            var radius2 = xC * xC + yC * yC;

            if (radius2 > 1e10 * baLength || radius2 > 1e10 * caLength)
            {
                return(false);
            }

            var px           = Points[0].X + xC - p.X;
            var py           = Points[0].Y + yC - p.Y;
            var pointRadius2 = px * px + py * py;

            return(pointRadius2 < radius2);
        }
コード例 #4
0
ファイル: Tris.cs プロジェクト: dmi3dmi3/Percolation-02
 public Tris(TrisVertex a, TrisVertex b, TrisVertex c)
 {
     Points[0] = a;
     Points[1] = b;
     Points[2] = c;
 }
コード例 #5
0
ファイル: Tris.cs プロジェクト: dmi3dmi3/Percolation-02
 public int IndexOfPoint(TrisVertex a)
 {
     return(Points[0] == a ? 0 : Points[1] == a ? 1 : 2);
 }
コード例 #6
0
ファイル: Tris.cs プロジェクト: dmi3dmi3/Percolation-02
 public bool HasEdge(TrisVertex a, TrisVertex b)
 {
     return((a == Points[0] || a == Points[1] || a == Points[2]) &&
            (b == Points[0] || b == Points[1] || b == Points[2]));
 }
コード例 #7
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);
        }