/* * Set clippedVertices to contain the two ends of the portion of the Voronoi edge that is visible * within the bounds. If no part of the Edge falls within the bounds, leave clippedVertices null * @param bounds */ public void ClipVertices(Rectf bounds) { float xmin = bounds.x; float ymin = bounds.y; float xmax = bounds.right; float ymax = bounds.bottom; Vertex vertex0, vertex1; float x0, x1, y0, y1; if (a == 1 && b >= 0) { vertex0 = rightVertex; vertex1 = leftVertex; } else { vertex0 = leftVertex; vertex1 = rightVertex; } if (a == 1) { y0 = ymin; if (vertex0 != null && vertex0.y > ymin) { y0 = vertex0.y; } if (y0 > ymax) { return; } x0 = c - b * y0; y1 = ymax; if (vertex1 != null && vertex1.y < ymax) { y1 = vertex1.y; } if (y1 < ymin) { return; } x1 = c - b * y1; if ((x0 > xmax && x1 > xmax) || (x0 < xmin && x1 < xmin)) { return; } if (x0 > xmax) { x0 = xmax; y0 = (c - x0) / b; } else if (x0 < xmin) { x0 = xmin; y0 = (c - x0) / b; } if (x1 > xmax) { x1 = xmax; y1 = (c - x1) / b; } else if (x1 < xmin) { x1 = xmin; y1 = (c - x1) / b; } } else { x0 = xmin; if (vertex0 != null && vertex0.x > xmin) { x0 = vertex0.x; } if (x0 > xmax) { return; } y0 = c - a * x0; x1 = xmax; if (vertex1 != null && vertex1.x < xmax) { x1 = vertex1.x; } if (x1 < xmin) { return; } y1 = c - a * x1; if ((y0 > ymax && y1 > ymax) || (y0 < ymin && y1 < ymin)) { return; } if (y0 > ymax) { y0 = ymax; x0 = (c - y0) / a; } else if (y0 < ymin) { y0 = ymin; x0 = (c - y0) / a; } if (y1 > ymax) { y1 = ymax; x1 = (c - y1) / a; } else if (y1 < ymin) { y1 = ymin; x1 = (c - y1) / a; } } clippedVertices = new Dictionary <LR, Vector2f>(); if (vertex0 == leftVertex) { clippedVertices[LR.LEFT] = new Vector2f(x0, y0); clippedVertices[LR.RIGHT] = new Vector2f(x1, y1); } else { clippedVertices[LR.RIGHT] = new Vector2f(x0, y0); clippedVertices[LR.LEFT] = new Vector2f(x1, y1); } }
public Voronoi(List <Vector2f> points, Rectf plotBounds) { weigthDistributor = new Random(); Init(points, plotBounds, new List <Vector2f>()); }
private void Connect(ref List <Vector2f> points, int j, Rectf bounds, bool closingUp) { Vector2f rightPoint = points[points.Count - 1]; Edge newEdge = edges[j]; LR newOrientation = edgeOrientations[j]; // The point that must be conected to rightPoint: Vector2f newPoint = newEdge.ClippedEnds[newOrientation]; if (!CloseEnough(rightPoint, newPoint)) { // The points do not coincide, so they must have been clipped at the bounds; // see if they are on the same border of the bounds: if (rightPoint.x != newPoint.x && rightPoint.y != newPoint.y) { // They are on different borders of the bounds; // insert one or two corners of bounds as needed to hook them up: // (NOTE this will not be correct if the region should take up more than // half of the bounds rect, for then we will have gone the wrong way // around the bounds and included the smaller part rather than the larger) int rightCheck = BoundsCheck.Check(rightPoint, bounds); int newCheck = BoundsCheck.Check(newPoint, bounds); float px, py; if ((rightCheck & BoundsCheck.RIGHT) != 0) { px = bounds.right; if ((newCheck & BoundsCheck.BOTTOM) != 0) { py = bounds.bottom; points.Add(new Vector2f(px, py)); } else if ((newCheck & BoundsCheck.TOP) != 0) { py = bounds.top; points.Add(new Vector2f(px, py)); } else if ((newCheck & BoundsCheck.LEFT) != 0) { if (rightPoint.y - bounds.y + newPoint.y - bounds.y < bounds.height) { py = bounds.top; } else { py = bounds.bottom; } points.Add(new Vector2f(px, py)); points.Add(new Vector2f(bounds.left, py)); } } else if ((rightCheck & BoundsCheck.LEFT) != 0) { px = bounds.left; if ((newCheck & BoundsCheck.BOTTOM) != 0) { py = bounds.bottom; points.Add(new Vector2f(px, py)); } else if ((newCheck & BoundsCheck.TOP) != 0) { py = bounds.top; points.Add(new Vector2f(px, py)); } else if ((newCheck & BoundsCheck.RIGHT) != 0) { if (rightPoint.y - bounds.y + newPoint.y - bounds.y < bounds.height) { py = bounds.top; } else { py = bounds.bottom; } points.Add(new Vector2f(px, py)); points.Add(new Vector2f(bounds.right, py)); } } else if ((rightCheck & BoundsCheck.TOP) != 0) { py = bounds.top; if ((newCheck & BoundsCheck.RIGHT) != 0) { px = bounds.right; points.Add(new Vector2f(px, py)); } else if ((newCheck & BoundsCheck.LEFT) != 0) { px = bounds.left; points.Add(new Vector2f(px, py)); } else if ((newCheck & BoundsCheck.BOTTOM) != 0) { if (rightPoint.x - bounds.x + newPoint.x - bounds.x < bounds.width) { px = bounds.left; } else { px = bounds.right; } points.Add(new Vector2f(px, py)); points.Add(new Vector2f(px, bounds.bottom)); } } else if ((rightCheck & BoundsCheck.BOTTOM) != 0) { py = bounds.bottom; if ((newCheck & BoundsCheck.RIGHT) != 0) { px = bounds.right; points.Add(new Vector2f(px, py)); } else if ((newCheck & BoundsCheck.LEFT) != 0) { px = bounds.left; points.Add(new Vector2f(px, py)); } else if ((newCheck & BoundsCheck.TOP) != 0) { if (rightPoint.x - bounds.x + newPoint.x - bounds.x < bounds.width) { px = bounds.left; } else { px = bounds.right; } points.Add(new Vector2f(px, py)); points.Add(new Vector2f(px, bounds.top)); } } } if (closingUp) { // newEdge's ends have already been added return; } points.Add(newPoint); } Vector2f newRightPoint = newEdge.ClippedEnds[LR.Other(newOrientation)]; if (!CloseEnough(points[0], newRightPoint)) { points.Add(newRightPoint); } }
private void FortunesAlgorithm() { Site newSite, bottomSite, topSite, tempSite; Vertex v, vertex; Vector2f newIntStar = Vector2f.zero; LR leftRight; Halfedge lbnd, rbnd, llbnd, rrbnd, bisector; Edge edge; Rectf dataBounds = sites.GetSitesBounds(); int sqrtSitesNb = (int)Math.Sqrt(sites.Count() + 4); HalfedgePriorityQueue heap = new HalfedgePriorityQueue(dataBounds.y, dataBounds.height, sqrtSitesNb); EdgeList edgeList = new EdgeList(dataBounds.x, dataBounds.width, sqrtSitesNb); List <Halfedge> halfEdges = new List <Halfedge>(); List <Vertex> vertices = new List <Vertex>(); Site bottomMostSite = sites.Next(); newSite = sites.Next(); while (true) { if (!heap.Empty()) { newIntStar = heap.Min(); } if (newSite != null && (heap.Empty() || CompareByYThenX(newSite, newIntStar) < 0)) { // New site is smallest //Debug.Log("smallest: new site " + newSite); // Step 8: lbnd = edgeList.EdgeListLeftNeighbor(newSite.Coord); // The halfedge just to the left of newSite //UnityEngine.Debug.Log("lbnd: " + lbnd); rbnd = lbnd.edgeListRightNeighbor; // The halfedge just to the right //UnityEngine.Debug.Log("rbnd: " + rbnd); bottomSite = RightRegion(lbnd, bottomMostSite); // This is the same as leftRegion(rbnd) // This Site determines the region containing the new site //UnityEngine.Debug.Log("new Site is in region of existing site: " + bottomSite); // Step 9 edge = Edge.CreateBisectingEdge(bottomSite, newSite); //UnityEngine.Debug.Log("new edge: " + edge); edges.Add(edge); bisector = Halfedge.Create(edge, LR.LEFT); halfEdges.Add(bisector); // Inserting two halfedges into edgelist constitutes Step 10: // Insert bisector to the right of lbnd: edgeList.Insert(lbnd, bisector); // First half of Step 11: if ((vertex = Vertex.Intersect(lbnd, bisector)) != null) { vertices.Add(vertex); heap.Remove(lbnd); lbnd.vertex = vertex; lbnd.ystar = vertex.y + newSite.Dist(vertex); heap.Insert(lbnd); } lbnd = bisector; bisector = Halfedge.Create(edge, LR.RIGHT); halfEdges.Add(bisector); // Second halfedge for Step 10:: // Insert bisector to the right of lbnd: edgeList.Insert(lbnd, bisector); // Second half of Step 11: if ((vertex = Vertex.Intersect(bisector, rbnd)) != null) { vertices.Add(vertex); bisector.vertex = vertex; bisector.ystar = vertex.y + newSite.Dist(vertex); heap.Insert(bisector); } newSite = sites.Next(); } else if (!heap.Empty()) { // Intersection is smallest lbnd = heap.ExtractMin(); llbnd = lbnd.edgeListLeftNeighbor; rbnd = lbnd.edgeListRightNeighbor; rrbnd = rbnd.edgeListRightNeighbor; bottomSite = LeftRegion(lbnd, bottomMostSite); topSite = RightRegion(rbnd, bottomMostSite); // These three sites define a Delaunay triangle // (not actually using these for anything...) // triangles.Add(new SiteTriangle(bottomSite, topSite, RightRegion(lbnd, bottomMostSite))); v = lbnd.vertex; v.SetIndex(); lbnd.edge.SetVertex(lbnd.leftRight, v); rbnd.edge.SetVertex(rbnd.leftRight, v); edgeList.Remove(lbnd); heap.Remove(rbnd); edgeList.Remove(rbnd); leftRight = LR.LEFT; if (bottomSite.y > topSite.y) { tempSite = bottomSite; bottomSite = topSite; topSite = tempSite; leftRight = LR.RIGHT; } edge = Edge.CreateBisectingEdge(bottomSite, topSite); edges.Add(edge); bisector = Halfedge.Create(edge, leftRight); halfEdges.Add(bisector); edgeList.Insert(llbnd, bisector); edge.SetVertex(LR.Other(leftRight), v); if ((vertex = Vertex.Intersect(llbnd, bisector)) != null) { vertices.Add(vertex); heap.Remove(llbnd); llbnd.vertex = vertex; llbnd.ystar = vertex.y + bottomSite.Dist(vertex); heap.Insert(llbnd); } if ((vertex = Vertex.Intersect(bisector, rrbnd)) != null) { vertices.Add(vertex); bisector.vertex = vertex; bisector.ystar = vertex.y + bottomSite.Dist(vertex); heap.Insert(bisector); } } else { break; } } // Heap should be empty now heap.Dispose(); edgeList.Dispose(); foreach (Halfedge halfedge in halfEdges) { halfedge.ReallyDispose(); } halfEdges.Clear(); // we need the vertices to clip the edges foreach (Edge e in edges) { e.ClipVertices(plotBounds); } // But we don't actually ever use them again! foreach (Vertex ve in vertices) { ve.Dispose(); } vertices.Clear(); }
public void LloydRelaxation(int nbIterations, Vector2f[] ignores) { // Reapeat the whole process for the number of iterations asked for (int i = 0; i < nbIterations; i++) { List <Vector2f> newPoints = new List <Vector2f>(); // Go thourgh all sites sites.ResetListIndex(); Site site = sites.Next(); while (site != null) { if (ignores.Contains(site.Coord)) { site = sites.Next(); continue; } // Loop all corners of the site to calculate the centroid List <Vector2f> region = site.Region(plotBounds); if (region.Count < 1) { site = sites.Next(); continue; } Vector2f centroid = Vector2f.zero; float signedArea = 0; float x0 = 0; float y0 = 0; float x1 = 0; float y1 = 0; float a = 0; // For all vertices except last for (int j = 0; j < region.Count - 1; j++) { x0 = region[j].x; y0 = region[j].y; x1 = region[j + 1].x; y1 = region[j + 1].y; a = x0 * y1 - x1 * y0; signedArea += a; centroid.x += (x0 + x1) * a; centroid.y += (y0 + y1) * a; } // Do last vertex x0 = region[region.Count - 1].x; y0 = region[region.Count - 1].y; x1 = region[0].x; y1 = region[0].y; a = x0 * y1 - x1 * y0; signedArea += a; centroid.x += (x0 + x1) * a; centroid.y += (y0 + y1) * a; signedArea *= 0.5f; centroid.x /= (6 * signedArea); centroid.y /= (6 * signedArea); // Move site to the centroid of its Voronoi cell newPoints.Add(centroid); site = sites.Next(); } // Between each replacement of the cendroid of the cell, // we need to recompute Voronoi diagram: Rectf origPlotBounds = this.plotBounds; Dispose(); Init(newPoints, origPlotBounds, ignores.ToList()); } }
public Voronoi(List <Vector2> points, Rectf plotBounds) { weigthDistributor = new System.Random(); Init(points, plotBounds); }
public Voronoi(List <Vector2> points, Rectf plotBounds, int lloydIterations) { weigthDistributor = new System.Random(); Init(points, plotBounds); LloydRelaxation(lloydIterations); }
/* * Set clipped vertices to contain the two ends of the portion of the Voronoi edge that is * visible within the bounds. If no part of the edge falls within the bounds, leave clipped * vertices null. * @param bounds */ public void ClipVertices(Rectf bounds) { float xmin = bounds.X; float ymin = bounds.Y; float xmax = bounds.Right; float ymax = bounds.Bottom; Vertex vertex0, vertex1; float x0, x1, y0, y1; if (a == 1 && b >= 0) { vertex0 = this.RightVertex; vertex1 = this.LeftVertex; } else { vertex0 = this.LeftVertex; vertex1 = this.RightVertex; } if (a == 1) { y0 = ymin; if (vertex0 != null && vertex0.Y > ymin) { y0 = vertex0.Y; } if (y0 > ymax) { return; } x0 = c - b * y0; y1 = ymax; if (vertex1 != null && vertex1.Y < ymax) { y1 = vertex1.Y; } if (y1 < ymin) { return; } x1 = c - b * y1; if ((x0 > xmax && x1 > xmax) || (x0 < xmin && x1 < xmin)) { return; } if (x0 > xmax) { x0 = xmax; y0 = (c - x0) / b; } else if (x0 < xmin) { x0 = xmin; y0 = (c - x0) / b; } if (x1 > xmax) { x1 = xmax; y1 = (c - x1) / b; } else if (x1 < xmin) { x1 = xmin; y1 = (c - x1) / b; } } else { x0 = xmin; if (vertex0 != null && vertex0.X > xmin) { x0 = vertex0.X; } if (x0 > xmax) { return; } y0 = c - a * x0; x1 = xmax; if (vertex1 != null && vertex1.X < xmax) { x1 = vertex1.X; } if (x1 < xmin) { return; } y1 = c - a * x1; if ((y0 > ymax && y1 > ymax) || (y0 < ymin && y1 < ymin)) { return; } if (y0 > ymax) { y0 = ymax; x0 = (c - y0) / a; } else if (y0 < ymin) { y0 = ymin; x0 = (c - y0) / a; } if (y1 > ymax) { y1 = ymax; x1 = (c - y1) / a; } else if (y1 < ymin) { y1 = ymin; x1 = (c - y1) / a; } } this.ClippedVertices = new Dictionary <LR, Vector2f>(); if (vertex0 == this.LeftVertex) { this.ClippedVertices[LR.LEFT] = new Vector2f(x0, y0); this.ClippedVertices[LR.RIGHT] = new Vector2f(x1, y1); } else { this.ClippedVertices[LR.RIGHT] = new Vector2f(x0, y0); this.ClippedVertices[LR.LEFT] = new Vector2f(x1, y1); } }
public Voronoi(List <Vector2f> points, Rectf plotBounds, int lloydIterations) : this(new VoronoiManager(points.Count), points, plotBounds, lloydIterations) { }
public Voronoi(List <Vector2f> points, Rectf plotBounds) : this(new VoronoiManager(points.Count), points, plotBounds, 0) { }
private void FortunesAlgorithm() { Profiler.BeginSample("DAll HEs"); Halfedge.DisposeAll(); Edge.DisposeAll(); Profiler.EndSample(); //UnityEngine.Debug.Log("HE pool: " + Halfedge.unusedPool.Count); currentSiteIndex = 0; nVertices = 0; // vars Profiler.BeginSample("Fortunes: initing"); Site newSite, bottomSite, topSite, tempSite; Vertex v, vertex; Vector2f newIntStar = Vector2f.zero; bool leftRight; Halfedge lbnd = null; Halfedge rbnd = null; Halfedge llbnd = null; Halfedge rrbnd = null; Halfedge bisector = null; Edge edge; Profiler.EndSample(); // Data bounds Profiler.BeginSample("Fortunes: Getting data bounds"); Rectf dataBounds = Site.GetSitesBounds(sites); Profiler.EndSample(); int sqrtSitesNb = (int)Math.Sqrt(sites.Count + 4); // WTF Profiler.BeginSample("Fortunes: Init heap"); if (heap == null) { heap = new HalfedgePriorityQueue(dataBounds.y, dataBounds.height, sqrtSitesNb); } else { heap.ReinitNoSizeChange(dataBounds.y, dataBounds.height); } Profiler.EndSample(); Profiler.BeginSample("Fortunes: Init EdgeList"); if (edgeList == null) { edgeList = new EdgeList(dataBounds.x, dataBounds.width, sqrtSitesNb); } else { edgeList.ClearNoResize(dataBounds.x, dataBounds.width); } //edgeList = new EdgeList(dataBounds.x, dataBounds.width, sqrtSitesNb); Profiler.EndSample(); Profiler.BeginSample("Fortunes: Init HEs and vertices"); if (halfEdges == null) // TODO: Move to init { halfEdges = new List <Halfedge>(); vertices = new List <Vertex>(); } else { halfEdges.Clear(); vertices.Clear(); } Profiler.EndSample(); Site bottomMostSite = GetNextSite(); newSite = GetNextSite(); Profiler.BeginSample("Fortunes: Main Loop"); while (true) { if (heap.count > 0) { newIntStar = heap.Min(); } if (newSite != null && (heap.count == 0 || CompareByYThenX(newSite, newIntStar) < 0)) { // New site is smallest //Debug.Log("smallest: new site " + newSite); // Step 8: // The halfedge just to the left of newSite //UnityEngine.Debug.Log("lbnd: " + lbnd); lbnd = edgeList.EdgeListLeftNeighbor(newSite.Coord); // The halfedge just to the right rbnd = lbnd.edgeListRightNeighbor; //UnityEngine.Debug.Log("rbnd: " + rbnd); // This is the same as leftRegion(rbnd) // This Site determines the region containing the new site bottomSite = RightRegion(lbnd, bottomMostSite); //UnityEngine.Debug.Log("new Site is in region of existing site: " + bottomSite); // Step 9 Profiler.BeginSample("CreateBisectingEdge"); edge = Edge.CreateBisectingEdge(bottomSite, newSite); Profiler.EndSample(); //UnityEngine.Debug.Log("new edge: " + edge); Edges.Add(edge); bisector = Halfedge.Create(edge, false); halfEdges.Add(bisector); // Inserting two halfedges into edgelist constitutes Step 10: // Insert bisector to the right of lbnd: edgeList.Insert(lbnd, bisector); // First half of Step 11: if ((vertex = Vertex.Intersect(lbnd, bisector)) != null) { vertices.Add(vertex); heap.Remove(lbnd); lbnd.vertex = vertex; lbnd.ystar = vertex.y + newSite.Dist(vertex); heap.Insert(lbnd); } lbnd = bisector; bisector = Halfedge.Create(edge, true); halfEdges.Add(bisector); // Second halfedge for Step 10:: // Insert bisector to the right of lbnd: edgeList.Insert(lbnd, bisector); // Second half of Step 11: if ((vertex = Vertex.Intersect(bisector, rbnd)) != null) { vertices.Add(vertex); bisector.vertex = vertex; bisector.ystar = vertex.y + newSite.Dist(vertex); heap.Insert(bisector); } newSite = GetNextSite(); } else if (heap.count > 0) { // Intersection is smallest lbnd = heap.ExtractMin(); llbnd = lbnd.edgeListLeftNeighbor; rbnd = lbnd.edgeListRightNeighbor; rrbnd = rbnd.edgeListRightNeighbor; bottomSite = LeftRegion(lbnd, bottomMostSite); topSite = RightRegion(rbnd, bottomMostSite); // These three sites define a Delaunay triangle // (not actually using these for anything...) #if TRIANGLES triangles.Add(new Triangle(bottomSite, topSite, RightRegion(lbnd, bottomMostSite))); #endif v = lbnd.vertex; v.VertexIndex = nVertices++; lbnd.edge.SetVertex(lbnd.leftRight, v); rbnd.edge.SetVertex(rbnd.leftRight, v); edgeList.Remove(lbnd); heap.Remove(rbnd); edgeList.Remove(rbnd); leftRight = false; if (bottomSite.y > topSite.y) { tempSite = bottomSite; bottomSite = topSite; topSite = tempSite; leftRight = true; } edge = Edge.CreateBisectingEdge(bottomSite, topSite); Profiler.BeginSample("addedge"); Edges.Add(edge); Profiler.EndSample(); bisector = Halfedge.Create(edge, leftRight); halfEdges.Add(bisector); edgeList.Insert(llbnd, bisector); edge.SetVertex(!leftRight, v); if ((vertex = Vertex.Intersect(llbnd, bisector)) != null) { vertices.Add(vertex); heap.Remove(llbnd); llbnd.vertex = vertex; llbnd.ystar = vertex.y + bottomSite.Dist(vertex); heap.Insert(llbnd); } if ((vertex = Vertex.Intersect(bisector, rrbnd)) != null) { vertices.Add(vertex); bisector.vertex = vertex; bisector.ystar = vertex.y + bottomSite.Dist(vertex); heap.Insert(bisector); } } else { break; } } Profiler.EndSample(); // DISPOSE // Heap should be empty now Profiler.BeginSample("Fortunes: Heap dispose"); heap.Dispose(); Profiler.EndSample(); Profiler.BeginSample("Fortunes: Edgelist dispose"); edgeList.Dispose(); Profiler.EndSample(); Profiler.BeginSample("Fortunes: Halfedges REALLY dispose"); for (int i = 0; i < halfEdges.Count; i++) { halfEdges[i].ReallyDispose(); } halfEdges.Clear(); Profiler.EndSample(); Profiler.BeginSample("Fortunes: ClipVertices"); // we need the vertices to clip the edges for (int i = 0; i < Edges.Count; i++) { Edges[i].ClipVertices(PlotBounds, true); } Profiler.EndSample(); // But we don't actually ever use them again! if (!disposeVerticesManually) { Profiler.BeginSample("Vertices dispose"); DisposeVertices(); Profiler.EndSample(); UnityEngine.Debug.Log("Disposing vertices!"); } /* * UnityEngine.Debug.Assert(Halfedge.unusedPool.Contains(lbnd), "lbnd"); * UnityEngine.Debug.Assert(Halfedge.unusedPool.Contains(rbnd), "rbnd"); * UnityEngine.Debug.Assert(Halfedge.unusedPool.Contains(llbnd), "llbnd"); * UnityEngine.Debug.Assert(Halfedge.unusedPool.Contains(rrbnd), "rrbnd"); * UnityEngine.Debug.Assert(Halfedge.unusedPool.Contains(bisector), "bisector"); */ }
public void Redo(List <Vector2f> points, Rectf plotBounds) { Clear(); Init(points, plotBounds); }