/// <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 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; }
private VoronoiDiagramEdge() { Index = -1; A = 0f; B = 0f; C = 0f; LeftEndPoint = null; RightEndPoint = null; LeftSite = null; RightSite = null; }
/// <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); }
/// <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); }