public void Insert(VoronoiDiagramHalfEdge <T> halfEdge)
        {
            VoronoiDiagramHalfEdge <T> previous, next;
            int insertionBucket = GetBucket(halfEdge);

            if (insertionBucket < _minimumBucket)
            {
                _minimumBucket = insertionBucket;
            }

            // Start at the beginning of the bucket and find where the half edge should go
            previous = _hash[insertionBucket];
            next     = previous.NextInPriorityQueue;
            while (
                next != null &&
                (
                    halfEdge.StarY > next.StarY ||
                    (halfEdge.StarY.IsAlmostEqualTo(next.StarY) &&
                     halfEdge.Vertex.Coordinate.x > next.Vertex.Coordinate.x)
                )
                )
            {
                previous = next;
                next     = previous.NextInPriorityQueue;
            }

            halfEdge.NextInPriorityQueue = previous.NextInPriorityQueue;
            previous.NextInPriorityQueue = halfEdge;
            _count++;
        }
 public void Insert(VoronoiDiagramHalfEdge <T> leftBound, VoronoiDiagramHalfEdge <T> newHalfEdge)
 {
     newHalfEdge.EdgeListLeft             = leftBound;
     newHalfEdge.EdgeListRight            = leftBound.EdgeListRight;
     leftBound.EdgeListRight.EdgeListLeft = newHalfEdge;
     leftBound.EdgeListRight = newHalfEdge;
 }
        /// <summary>
        /// Creates a vertex at the intersection of two edges.
        /// </summary>
        /// <param name="halfEdgeA">The first edge</param>
        /// <param name="halfEdgeB">The second edge</param>
        /// <returns>The vertex of the intersection. null if the two edges do not intersect</returns>
        public static VoronoiDiagramVertex <T> Intersect(VoronoiDiagramHalfEdge <T> halfEdgeA, VoronoiDiagramHalfEdge <T> halfEdgeB)
        {
            VoronoiDiagramEdge <T>     edgeA, edgeB, edge;
            VoronoiDiagramHalfEdge <T> halfEdge;
            float determinant, intersectionX, intersectionY;
            bool  isRightOfSite;

            edgeA = halfEdgeA.Edge;
            edgeB = halfEdgeB.Edge;

            if (edgeA == null || edgeB == null)
            {
                return(null);
            }

            if (edgeA.RightSite == edgeB.RightSite)
            {
                return(null);
            }

            determinant = (edgeA.A * edgeB.B) - (edgeA.B * edgeB.A);

            if (determinant.IsAlmostZero())
            {
                // The edges are parallel
                return(null);
            }

            intersectionX = (edgeA.C * edgeB.B - edgeB.C * edgeA.B) / determinant;
            intersectionY = (edgeB.C * edgeA.A - edgeA.C * edgeB.A) / determinant;

            if (
                edgeA.RightSite.Coordinate.y < edgeB.RightSite.Coordinate.y ||
                (
                    edgeA.RightSite.Coordinate.y.IsAlmostEqualTo(edgeB.RightSite.Coordinate.y) &&
                    edgeA.RightSite.Coordinate.x < edgeB.RightSite.Coordinate.x
                )
                )
            {
                halfEdge = halfEdgeA;
                edge     = edgeA;
            }
            else
            {
                halfEdge = halfEdgeB;
                edge     = edgeB;
            }

            isRightOfSite = intersectionX >= edge.RightSite.Coordinate.x;
            if (
                (isRightOfSite && halfEdge.EdgeType == VoronoiDiagramEdgeType.Left) ||
                (!isRightOfSite && halfEdge.EdgeType == VoronoiDiagramEdgeType.Right)
                )
            {
                return(null);
            }

            return(new VoronoiDiagramVertex <T>(-1, new Vector2(intersectionX, intersectionY)));
        }
 public void Delete(VoronoiDiagramHalfEdge <T> halfEdge)
 {
     halfEdge.EdgeListLeft.EdgeListRight = halfEdge.EdgeListRight;
     halfEdge.EdgeListRight.EdgeListLeft = halfEdge.EdgeListLeft;
     halfEdge.Edge          = VoronoiDiagramEdge <T> .Deleted;
     halfEdge.EdgeListLeft  = null;
     halfEdge.EdgeListRight = null;
 }
Esempio n. 5
0
 /// <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;
 }
        /// <summary>
        /// Returns the right region in relation to a half edge
        /// </summary>
        /// <param name="halfEdge">The half edge to calculate from</param>
        /// <returns>The right region</returns>
        private VoronoiDiagramSite <T> GetRightRegion(VoronoiDiagramHalfEdge <T> halfEdge)
        {
            if (halfEdge.Edge == null)
            {
                return(_bottomMostSite);
            }

            if (halfEdge.EdgeType == VoronoiDiagramEdgeType.Left)
            {
                return(halfEdge.Edge.RightSite);
            }
            else
            {
                return(halfEdge.Edge.LeftSite);
            }
        }
        public int GetBucket(VoronoiDiagramHalfEdge <T> halfEdge)
        {
            int bucket;

            bucket = Mathf.RoundToInt((halfEdge.StarY - _minimumValues.y) / _deltaValues.y * _hash.Count);
            if (bucket < 0)
            {
                bucket = 0;
            }

            if (bucket >= _hash.Count)
            {
                bucket = _hash.Count - 1;
            }

            return(bucket);
        }
        public void Delete(VoronoiDiagramHalfEdge <T> halfEdge)
        {
            int removalBucket = GetBucket(halfEdge);

            if (halfEdge.Vertex != null)
            {
                VoronoiDiagramHalfEdge <T> previous = _hash[removalBucket];
                while (previous.NextInPriorityQueue != halfEdge)
                {
                    previous = previous.NextInPriorityQueue;
                }

                previous.NextInPriorityQueue = halfEdge.NextInPriorityQueue;
                _count--;

                halfEdge.Vertex = null;
                halfEdge.NextInPriorityQueue = null;
            }
        }
        public VoronoiDiagramEdgeList(int numberOfSites, Vector2 minimumValues, Vector2 deltaValues)
        {
            MinimumValues = minimumValues;
            DeltaValues   = deltaValues;

            Hash = new List <VoronoiDiagramHalfEdge <T> >();
            for (int i = 0; i < 2 * Mathf.Sqrt(numberOfSites); i++)
            {
                Hash.Add(null);
            }

            LeftEnd  = new VoronoiDiagramHalfEdge <T>(null, VoronoiDiagramEdgeType.None);
            RightEnd = new VoronoiDiagramHalfEdge <T>(null, VoronoiDiagramEdgeType.None);

            LeftEnd.EdgeListLeft  = null;
            LeftEnd.EdgeListRight = RightEnd;

            RightEnd.EdgeListLeft  = LeftEnd;
            RightEnd.EdgeListRight = null;

            Hash[0] = LeftEnd;
            Hash[Hash.Count - 1] = RightEnd;
        }
        /// <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();
            }
        }