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; }
/// <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(); } }