Пример #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);
        }
Пример #2
0
 /// <summary>
 /// Creates a site
 /// </summary>
 /// <param name="index">Index of the new site</param>
 /// <param name="site">new site</param>
 internal VoronoiDiagramSite(int index, VoronoiDiagramSite <T> site)
 {
     Index      = index;
     Coordinate = site.Coordinate;
     IsCorner   = false;
     IsEdge     = false;
     Vertices   = new List <Vector2>();
     Edges      = new List <VoronoiDiagramEdge <T> >();
     SiteData   = site.SiteData;
 }
Пример #3
0
 private VoronoiDiagramEdge()
 {
     Index         = -1;
     A             = 0f;
     B             = 0f;
     C             = 0f;
     LeftEndPoint  = null;
     RightEndPoint = null;
     LeftSite      = null;
     RightSite     = null;
 }
Пример #4
0
        /// <summary>
        /// Returns the next site and increments _currentSiteIndex
        /// </summary>
        /// <returns>The next site</returns>
        private VoronoiDiagramSite <T> GetNextSite()
        {
            if (_currentSiteIndex < _sites.Count)
            {
                VoronoiDiagramSite <T> nextSite = _sites[_currentSiteIndex];
                _currentSiteIndex++;
                return(nextSite);
            }

            return(null);
        }
Пример #5
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();
            }
        }
        /// <summary>
        /// Is the edge left of the passed in point?
        /// </summary>
        /// <param name="point">Point to check against</param>
        /// <returns>True if left, false if right</returns>
        public bool IsLeftOf(Vector2 point)
        {
            VoronoiDiagramSite <T> topSite = Edge.RightSite;
            bool isRightOfSite             = point.x > topSite.Coordinate.x;
            bool isAbove;

            if (isRightOfSite && EdgeType == VoronoiDiagramEdgeType.Left)
            {
                return(true);
            }

            if (!isRightOfSite && EdgeType == VoronoiDiagramEdgeType.Right)
            {
                return(false);
            }

            if (Edge.A.IsAlmostEqualTo(1f))
            {
                var dyp = point.y - topSite.Coordinate.y;
                var dxp = point.x - topSite.Coordinate.x;

                var isFast = false;
                if ((!isRightOfSite && Edge.B < 0.0f) ||
                    (isRightOfSite && Edge.B >= 0.0f))
                {
                    isAbove = dyp >= Edge.B * dxp;
                    isFast  = isAbove;
                }
                else
                {
                    isAbove = point.x + point.y * Edge.B > Edge.C;
                    if (Edge.B < 0.0f)
                    {
                        isAbove = !isAbove;
                    }
                    if (!isAbove)
                    {
                        isFast = true;
                    }
                }

                if (!isFast)
                {
                    var dxs = topSite.Coordinate.x - Edge.LeftSite.Coordinate.x;
                    isAbove = Edge.B * (dxp * dxp - dyp * dyp) < dxs * dyp * (1f + 2f * dxp / dxs + Edge.B * Edge.B);
                    if (Edge.B < 0f)
                    {
                        isAbove = !isAbove;
                    }
                }
            }
            else // edge.b == 1.0
            {
                float t1, t2, t3, yl;
                yl      = Edge.C - Edge.A * point.x;
                t1      = point.y - yl;
                t2      = point.x - topSite.Coordinate.x;
                t3      = yl - topSite.Coordinate.y;
                isAbove = t1 * t1 > t2 * t2 + t3 * t3;
            }
            return(EdgeType == VoronoiDiagramEdgeType.Left ? isAbove : !isAbove);
        }