Пример #1
0
        /// <summary>
        /// Return an edge that is the bisection between two sites
        /// </summary>
        /// <param name="siteA">The first site</param>
        /// <param name="siteB">The second site</param>
        /// <returns>The new edge</returns>
        public static VoronoiDiagramEdge <T> Bisect(VoronoiDiagramSite <T> siteA, VoronoiDiagramSite <T> siteB)
        {
            float dx, dy;
            var   newEdge = new VoronoiDiagramEdge <T>
            {
                LeftSite      = siteA,
                RightSite     = siteB,
                LeftEndPoint  = null,
                RightEndPoint = null
            };

            dx = siteB.Coordinate.x - siteA.Coordinate.x;
            dy = siteB.Coordinate.y - siteA.Coordinate.y;

            newEdge.C = siteA.Coordinate.x * dx + siteA.Coordinate.y * dy + (dx * dx + dy * dy) * 0.5f;
            if (Mathf.Abs(dx) > Mathf.Abs(dy))
            {
                newEdge.A  = 1f;
                newEdge.B  = dy / dx;
                newEdge.C /= dx;
            }
            else
            {
                newEdge.B  = 1f;
                newEdge.A  = dx / dy;
                newEdge.C /= dy;
            }

            siteA.Edges.Add(newEdge);
            siteB.Edges.Add(newEdge);

            return(newEdge);
        }
 /// <summary>
 /// Creates a new half edge
 /// </summary>
 /// <param name="edge">The edge of the new half edge</param>
 /// <param name="edgeType">The edge type that this half edge represents (left or right).  The only half edges that should be "None" are the buckets in the priority queue.</param>
 public VoronoiDiagramHalfEdge(VoronoiDiagramEdge <T> edge, VoronoiDiagramEdgeType edgeType)
 {
     Edge                = edge;
     EdgeType            = edgeType;
     Vertex              = null;
     StarY               = 0f;
     EdgeListLeft        = null;
     EdgeListRight       = null;
     NextInPriorityQueue = null;
 }
Пример #3
0
        /// <summary>
        /// Runs Fortune's Algorithm to generate sites with edges for the diagram
        /// </summary>
        /// <param name="relaxationCycles">Number of relaxation cycles to run</param>
        public void GenerateSites(int relaxationCycles)
        {
            if (_originalSites.Count == 0)
            {
                Debug.LogError("No points added to the diagram.  Sites cannot be generated");
                return;
            }

            _sites.Clear();
            foreach (VoronoiDiagramSite <T> site in _originalSites)
            {
                _sites.Add(new VoronoiDiagramSite <T>(_sites.Count, site));
            }
            SortSitesAndSetValues();

            // Cycles related to Lloyd's algorithm
            for (int cycles = 0; cycles < relaxationCycles; cycles++)
            {
                // Fortune's Algorithm
                int numGeneratedEdges    = 0;
                int numGeneratedVertices = 0;
                _currentSiteIndex = 0;

                var priorityQueue = new VoronoiDiagramPriorityQueue <T>(_sites.Count, _minValues, _deltaValues);
                var edgeList      = new VoronoiDiagramEdgeList <T>(_sites.Count, _minValues, _deltaValues);

                Vector2 currentIntersectionStar = Vector2.zero;
                VoronoiDiagramSite <T> currentSite;

                var generatedEdges = new List <VoronoiDiagramEdge <T> >();

                bool done = false;
                _bottomMostSite = GetNextSite();
                currentSite     = GetNextSite();
                while (!done)
                {
                    if (!priorityQueue.IsEmpty())
                    {
                        currentIntersectionStar = priorityQueue.GetMinimumBucketFirstPoint();
                    }

                    VoronoiDiagramSite <T>     bottomSite;
                    VoronoiDiagramHalfEdge <T> bisector;
                    VoronoiDiagramHalfEdge <T> rightBound;
                    VoronoiDiagramHalfEdge <T> leftBound;
                    VoronoiDiagramVertex <T>   vertex;
                    VoronoiDiagramEdge <T>     edge;
                    if (
                        currentSite != null &&
                        (
                            priorityQueue.IsEmpty() ||
                            currentSite.Coordinate.y < currentIntersectionStar.y ||
                            (
                                currentSite.Coordinate.y.IsAlmostEqualTo(currentIntersectionStar.y) &&
                                currentSite.Coordinate.x < currentIntersectionStar.x
                            )
                        )
                        )
                    {
                        // Current processed site is the smallest
                        leftBound  = edgeList.GetLeftBoundFrom(currentSite.Coordinate);
                        rightBound = leftBound.EdgeListRight;
                        bottomSite = GetRightRegion(leftBound);

                        edge = VoronoiDiagramEdge <T> .Bisect(bottomSite, currentSite);

                        edge.Index = numGeneratedEdges;
                        numGeneratedEdges++;

                        generatedEdges.Add(edge);

                        bisector = new VoronoiDiagramHalfEdge <T>(edge, VoronoiDiagramEdgeType.Left);
                        edgeList.Insert(leftBound, bisector);

                        vertex = VoronoiDiagramVertex <T> .Intersect(leftBound, bisector);

                        if (vertex != null)
                        {
                            priorityQueue.Delete(leftBound);

                            leftBound.Vertex = vertex;
                            leftBound.StarY  = vertex.Coordinate.y + currentSite.GetDistanceFrom(vertex);

                            priorityQueue.Insert(leftBound);
                        }

                        leftBound = bisector;
                        bisector  = new VoronoiDiagramHalfEdge <T>(edge, VoronoiDiagramEdgeType.Right);

                        edgeList.Insert(leftBound, bisector);

                        vertex = VoronoiDiagramVertex <T> .Intersect(bisector, rightBound);

                        if (vertex != null)
                        {
                            bisector.Vertex = vertex;
                            bisector.StarY  = vertex.Coordinate.y + currentSite.GetDistanceFrom(vertex);

                            priorityQueue.Insert(bisector);
                        }

                        currentSite = GetNextSite();
                    }
                    else if (priorityQueue.IsEmpty() == false)
                    {
                        // Current intersection is the smallest
                        leftBound = priorityQueue.RemoveAndReturnMinimum();
                        VoronoiDiagramHalfEdge <T> leftLeftBound = leftBound.EdgeListLeft;
                        rightBound = leftBound.EdgeListRight;
                        VoronoiDiagramHalfEdge <T> rightRightBound = rightBound.EdgeListRight;
                        bottomSite = GetLeftRegion(leftBound);
                        VoronoiDiagramSite <T> topSite = GetRightRegion(rightBound);

                        // These three sites define a Delaunay triangle
                        // Bottom, Top, EdgeList.GetRightRegion(rightBound);
                        // Debug.Log(string.Format("Delaunay triagnle: ({0}, {1}), ({2}, {3}), ({4}, {5})"),
                        //      bottomSite.Coordinate.x, bottomSite.Coordinate.y,
                        //      topSite.Coordinate.x, topSite.Coordinate.y,
                        //      edgeList.GetRightRegion(leftBound).Coordinate.x,
                        //      edgeList.GetRightRegion(leftBound).Coordinate.y);

                        var v = leftBound.Vertex;
                        v.Index = numGeneratedVertices;
                        numGeneratedVertices++;

                        leftBound.Edge.SetEndpoint(v, leftBound.EdgeType);
                        rightBound.Edge.SetEndpoint(v, rightBound.EdgeType);

                        edgeList.Delete(leftBound);

                        priorityQueue.Delete(rightBound);
                        edgeList.Delete(rightBound);

                        var edgeType = VoronoiDiagramEdgeType.Left;
                        if (bottomSite.Coordinate.y > topSite.Coordinate.y)
                        {
                            var tempSite = bottomSite;
                            bottomSite = topSite;
                            topSite    = tempSite;
                            edgeType   = VoronoiDiagramEdgeType.Right;
                        }

                        edge = VoronoiDiagramEdge <T> .Bisect(bottomSite, topSite);

                        edge.Index = numGeneratedEdges;
                        numGeneratedEdges++;

                        generatedEdges.Add(edge);

                        bisector = new VoronoiDiagramHalfEdge <T>(edge, edgeType);
                        edgeList.Insert(leftLeftBound, bisector);

                        edge.SetEndpoint(v,
                                         edgeType == VoronoiDiagramEdgeType.Left
                                ? VoronoiDiagramEdgeType.Right
                                : VoronoiDiagramEdgeType.Left);

                        vertex = VoronoiDiagramVertex <T> .Intersect(leftLeftBound, bisector);

                        if (vertex != null)
                        {
                            priorityQueue.Delete(leftLeftBound);

                            leftLeftBound.Vertex = vertex;
                            leftLeftBound.StarY  = vertex.Coordinate.y + bottomSite.GetDistanceFrom(vertex);

                            priorityQueue.Insert(leftLeftBound);
                        }

                        vertex = VoronoiDiagramVertex <T> .Intersect(bisector, rightRightBound);

                        if (vertex != null)
                        {
                            bisector.Vertex = vertex;
                            bisector.StarY  = vertex.Coordinate.y + bottomSite.GetDistanceFrom(vertex);

                            priorityQueue.Insert(bisector);
                        }
                    }
                    else
                    {
                        done = true;
                    }
                }

                GeneratedSites.Clear();
                // Bound the edges of the diagram
                foreach (VoronoiDiagramEdge <T> currentGeneratedEdge in generatedEdges)
                {
                    currentGeneratedEdge.GenerateClippedEndPoints(Bounds);
                }

                foreach (VoronoiDiagramSite <T> site in _sites)
                {
                    try
                    {
                        site.GenerateCentroid(Bounds);
                    }
                    catch (Exception)
                    {
                        Debug.Log("Coordinate");
                        Debug.Log(site.Coordinate);
                        Debug.Log("End points:");
                        foreach (var edge in site.Edges)
                        {
                            Debug.Log(edge.LeftClippedEndPoint + " , " + edge.RightClippedEndPoint);
                        }
                        throw;
                    }
                }

                foreach (VoronoiDiagramSite <T> site in _sites)
                {
                    var generatedSite = new VoronoiDiagramGeneratedSite <T>(site.Index, site.Coordinate, site.Centroid, new T(), site.IsCorner, site.IsEdge);
                    generatedSite.Vertices.AddRange(site.Vertices);
                    generatedSite.SiteData = site.SiteData;

                    foreach (VoronoiDiagramEdge <T> siteEdge in site.Edges)
                    {
                        // Only add edges that are visible
                        // Don't need to check the Right because they will both be float.MinValue
                        if (siteEdge.LeftClippedEndPoint == new Vector2(float.MinValue, float.MinValue))
                        {
                            continue;
                        }

                        generatedSite.Edges.Add(new VoronoiDiagramGeneratedEdge(siteEdge.Index,
                                                                                siteEdge.LeftClippedEndPoint, siteEdge.RightClippedEndPoint));

                        if (siteEdge.LeftSite != null && !generatedSite.NeighborSites.Contains(siteEdge.LeftSite.Index))
                        {
                            generatedSite.NeighborSites.Add(siteEdge.LeftSite.Index);
                        }

                        if (siteEdge.RightSite != null && !generatedSite.NeighborSites.Contains(siteEdge.RightSite.Index))
                        {
                            generatedSite.NeighborSites.Add(siteEdge.RightSite.Index);
                        }
                    }

                    GeneratedSites.Add(generatedSite.Index, generatedSite);

                    // Finished with the edges, remove the references so they can be removed at the end of the method
                    site.Edges.Clear();
                }

                // Clean up
                _bottomMostSite = null;
                _sites.Clear();

                // Lloyd's Algorithm
                foreach (KeyValuePair <int, VoronoiDiagramGeneratedSite <T> > generatedSite in GeneratedSites)
                {
                    var centroidPoint =
                        new Vector2(
                            Mathf.Clamp(generatedSite.Value.Centroid.x, 0, Bounds.width),
                            Mathf.Clamp(generatedSite.Value.Centroid.y, 0, Bounds.height));
                    var newSite = new VoronoiDiagramSite <T>(new Vector2(centroidPoint.x, centroidPoint.y), generatedSite.Value.SiteData);

                    if (!_sites.Any(item => item.Coordinate.x.IsAlmostEqualTo(newSite.Coordinate.x) && item.Coordinate.y.IsAlmostEqualTo(newSite.Coordinate.y)))
                    {
                        _sites.Add(new VoronoiDiagramSite <T>(_sites.Count, newSite));
                    }
                }
                SortSitesAndSetValues();
            }
        }