public Triangle(Site a, Site b, Site c) { _sites = new List<Site>(); _sites.Add(a); _sites.Add(b); _sites.Add(c); }
public static int CompareByYThenX(Site s1, Vector2 s2) { if (s1.y < s2.y) return -1; if (s1.y > s2.y) return 1; if (s1.x < s2.x) return -1; if (s1.x > s2.x) return 1; return 0; }
/** * This is the only way to create a new Edge * @param site0 * @param site1 * @return * */ public static Edge CreateBisectingEdge (Site site0, Site site1) { float dx, dy, absdx, absdy; float a, b, c; dx = site1.x - site0.x; dy = site1.y - site0.y; absdx = dx > 0 ? dx : -dx; absdy = dy > 0 ? dy : -dy; c = site0.x * dx + site0.y * dy + (dx * dx + dy * dy) * 0.5f; if (absdx > absdy) { a = 1.0f; b = dy / dx; c /= dx; } else { b = 1.0f; a = dx / dy; c /= dy; } Edge edge = Edge.Create (); edge.leftSite = site0; edge.rightSite = site1; site0.AddEdge (edge); site1.AddEdge (edge); edge._leftVertex = null; edge._rightVertex = null; edge.a = a; edge.b = b; edge.c = c; //trace("createBisectingEdge: a ", edge.a, "b", edge.b, "c", edge.c); return edge; }
public int Add (Site site) { _sorted = false; _sites.Add (site); return _sites.Count; }
private void FortunesAlgorithm () { Site newSite, bottomSite, topSite, tempSite; Vertex v, vertex; Vector2 newintstar = Vector2.zero; //Because the compiler doesn't know that it will have a value - Julian Side leftRight; Halfedge lbnd, rbnd, llbnd, rrbnd, bisector; Edge edge; Rect dataBounds = _sites.GetSitesBounds (); int sqrt_nsites = (int)(Mathf.Sqrt (_sites.Count + 4)); HalfedgePriorityQueue heap = new HalfedgePriorityQueue (dataBounds.y, dataBounds.height, sqrt_nsites); EdgeList edgeList = new EdgeList (dataBounds.x, dataBounds.width, sqrt_nsites); List<Halfedge> halfEdges = new List<Halfedge> (); List<Vertex> vertices = new List<Vertex> (); fortunesAlgorithm_bottomMostSite = _sites.Next (); newSite = _sites.Next (); for (;;) { if (heap.Empty () == false) { newintstar = heap.Min (); } if (newSite != null && (heap.Empty () || CompareByYThenX (newSite, newintstar) < 0)) { /* new site is smallest */ //trace("smallest: new site " + newSite); // Step 8: lbnd = edgeList.EdgeListLeftNeighbor (newSite.Coord); // the Halfedge just to the left of newSite //trace("lbnd: " + lbnd); rbnd = lbnd.edgeListRightNeighbor; // the Halfedge just to the right //trace("rbnd: " + rbnd); bottomSite = FortunesAlgorithm_rightRegion (lbnd); // this is the same as leftRegion(rbnd) // this Site determines the region containing the new site //trace("new Site is in region of existing site: " + bottomSite); // Step 9: edge = Edge.CreateBisectingEdge (bottomSite, newSite); //trace("new edge: " + edge); _edges.Add (edge); bisector = Halfedge.Create (edge, Side.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, Side.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 () == false) { /* intersection is smallest */ lbnd = heap.ExtractMin (); llbnd = lbnd.edgeListLeftNeighbor; rbnd = lbnd.edgeListRightNeighbor; rrbnd = rbnd.edgeListRightNeighbor; bottomSite = FortunesAlgorithm_leftRegion (lbnd); topSite = FortunesAlgorithm_rightRegion (rbnd); // these three sites define a Delaunay triangle // (not actually using these for anything...) //_triangles.push(new Triangle(bottomSite, topSite, rightRegion(lbnd))); v = lbnd.vertex; v.SetIndex (); lbnd.edge.SetVertex ((Side)lbnd.leftRight, v); rbnd.edge.SetVertex ((Side)rbnd.leftRight, v); edgeList.Remove (lbnd); heap.Remove (rbnd); edgeList.Remove (rbnd); leftRight = Side.LEFT; if (bottomSite.y > topSite.y) { tempSite = bottomSite; bottomSite = topSite; topSite = tempSite; leftRight = Side.RIGHT; } edge = Edge.CreateBisectingEdge (bottomSite, topSite); _edges.Add (edge); bisector = Halfedge.Create (edge, leftRight); halfEdges.Add (bisector); edgeList.Insert (llbnd, bisector); edge.SetVertex (SideHelper.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 (); for (int hIndex = 0; hIndex<halfEdges.Count; hIndex++) { Halfedge halfEdge = halfEdges [hIndex]; halfEdge.ReallyDispose (); } halfEdges.Clear (); // we need the vertices to clip the edges for (int eIndex = 0; eIndex<_edges.Count; eIndex++) { edge = _edges [eIndex]; edge.ClipVertices (_plotBounds); } // but we don't actually ever use them again! for (int vIndex = 0; vIndex<vertices.Count; vIndex++) { vertex = vertices [vIndex]; vertex.Dispose (); } vertices.Clear (); }
/** * sort sites on y, then x, coord * also change each site's _siteIndex to match its new position in the list * so the _siteIndex can be used to identify the site for nearest-neighbor queries * * haha "also" - means more than one responsibility... * */ private static float Compare(Site s1, Site s2) { int returnValue = (int)Voronoi.CompareByYThenX(s1, s2); // swap _siteIndex values if necessary to match new ordering: int tempIndex; if (returnValue == -1) { if (s1._siteIndex > s2._siteIndex) { tempIndex = (int)s1._siteIndex; s1._siteIndex = s2._siteIndex; s2._siteIndex = (uint)tempIndex; } } else if (returnValue == 1) { if (s2._siteIndex > s1._siteIndex) { tempIndex = (int)s2._siteIndex; s2._siteIndex = s1._siteIndex; s1._siteIndex = (uint)tempIndex; } } return returnValue; }
public Triangle (Site a, Site b, Site c) { _sites = new List<Site> () { a, b, c }; }
public uint Push(Site site) { _sorted = false; return (uint)_sites.Push(site); }
/** Stupidly, Unity in 2D mode uses co-ords incompatible with Unity in 3D mode (xy instead of xz). So we have to provide a boolean to switch between the two modes! */ public static VoronoiDiagram CreateDiagramFromVoronoiOutput(Voronoi voronoiGenerator, bool useUnity2DCoordsNot3D ) { GameObject go = new GameObject("New VoronoiMap"); GameObject goCellHolder = new GameObject(_cellHolderName); goCellHolder.transform.parent = go.transform; GameObject goEdgeHolder = new GameObject(_edgeHolderName); goEdgeHolder.transform.parent = go.transform; GameObject goVertexHolder = new GameObject(_vertexHolderName); goVertexHolder.transform.parent = go.transform; VoronoiDiagram map = go.AddComponent<VoronoiDiagram>(); System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch(); watch.Reset(); watch.Start(); Dictionary<Site,VoronoiCell> generatedCells = new Dictionary<Site, VoronoiCell>(); Dictionary<Vector2,VoronoiCellVertex> generatedVertices = new Dictionary<Vector2, VoronoiCellVertex>(); int numEdgesCreated = 0; foreach (Edge edge in voronoiGenerator.Edges()) { GameObject goEdge = new GameObject("Edge-#" + (numEdgesCreated++)); goEdge.transform.parent = goEdgeHolder.transform; VoronoiCellEdge vEdge = goEdge.AddComponent<VoronoiCellEdge>(); Debug.Log("Processing edge = "+edge+" with clippedEnds = "+(edge.clippedEnds==null?"null":""+edge.clippedEnds.Count) ); if( ! edge.visible) { Debug.Log("...Voronoi algorithm generated a non-existent edge, skipping it..."); continue; } Vector2 clippedEndLeft = (Vector2)edge.clippedEnds [Side.LEFT]; Vector2 clippedEndRight = (Vector2)edge.clippedEnds [Side.RIGHT]; vEdge.AddVertex(FetchVertexOrAddToObject( clippedEndLeft, generatedVertices, goVertexHolder, useUnity2DCoordsNot3D)); vEdge.AddVertex(FetchVertexOrAddToObject( clippedEndRight, generatedVertices, goVertexHolder, useUnity2DCoordsNot3D)); goEdge.transform.localPosition = (vEdge.edgeVertexA.transform.localPosition + vEdge.edgeVertexB.transform.localPosition ) / 2.0f; Site[] bothSites = new Site[] { edge.leftSite, edge.rightSite }; foreach (Site site in bothSites) { /** Re-use or create the Cell */ VoronoiCell newCell = null; // C# is rubbish. Crashes if Dictionary lacks the key. Very bad design. if (generatedCells.ContainsKey(site)) newCell = generatedCells [site]; GameObject goCell; if (newCell == null) { goCell = new GameObject("Cell-#" + generatedCells.Count); goCell.transform.parent = goCellHolder.transform; goCell.transform.localPosition = new Vector3( site.Coord.x, useUnity2DCoordsNot3D? site.Coord.y : 0, useUnity2DCoordsNot3D? 0 : site.Coord.y ); newCell = goCell.AddComponent<VoronoiCell>(); generatedCells.Add(site, newCell); } else { goCell = newCell.gameObject; } /** Now that cell is created, attach it to BOTH Vertex's on the new Edge (so that later its easy for us to to find Cells-for-this-Vertex, and also: for a Cell to "trace" around its edges, picking "next edge" from each Vertex by looking at which edges from the vertex border this cell (by checking that BOTH Vertex-ends of the edge are attached to this cell) Also add the edge itself to the cell, and the cell to the edge. */ vEdge.AddCellWithSideEffects(newCell); } } watch.Stop(); return map; }
Site RightRegion(Halfedge he, Site bottomMostSite) { Edge edge = he.edge; if (edge == null) { return bottomMostSite; } return edge.GetSite(LR.Other(he.leftRight)); }
internal static float CompareByYThenX(Site s1, Vector2 s2) { if (s1.Y < s2.y) return -1; if (s1.Y > s2.y) return 1; if (s1.X < s2.x) return -1; if (s1.X > s2.x) return 1; return 0; }
internal static float CompareByYThenX(Site s1, Site s2) { if (s1.Y < s2.Y) return -1; if (s1.Y > s2.Y) return 1; if (s1.X < s2.X) return -1; if (s1.X > s2.X) return 1; return 0; }
private void FortunesAlgorithm() { Site newSite, bottomSite, topSite, tempSite; Vertex v, vertex; Vector2 newintstar = Vector2.zero; //Because the compiler doesn't know that it will have a value - Julian Side leftRight; Halfedge lbnd, rbnd, llbnd, rrbnd, bisector; Edge edge; Rect dataBounds = _sites.GetSitesBounds(); int sqrt_nsites = (int)(Mathf.Sqrt(_sites.Count + 4)); HalfedgePriorityQueue heap = new HalfedgePriorityQueue(dataBounds.y, dataBounds.height, sqrt_nsites); EdgeList edgeList = new EdgeList(dataBounds.x, dataBounds.width, sqrt_nsites); List <Halfedge> halfEdges = new List <Halfedge>(); List <Vertex> vertices = new List <Vertex>(); fortunesAlgorithm_bottomMostSite = _sites.Next(); newSite = _sites.Next(); for (;;) { if (heap.Empty() == false) { newintstar = heap.Min(); } if (newSite != null && (heap.Empty() || CompareByYThenX(newSite, newintstar) < 0)) { /* new site is smallest */ //trace("smallest: new site " + newSite); // Step 8: lbnd = edgeList.EdgeListLeftNeighbor(newSite.Coord); // the Halfedge just to the left of newSite //trace("lbnd: " + lbnd); rbnd = lbnd.edgeListRightNeighbor; // the Halfedge just to the right //trace("rbnd: " + rbnd); bottomSite = FortunesAlgorithm_rightRegion(lbnd); // this is the same as leftRegion(rbnd) // this Site determines the region containing the new site //trace("new Site is in region of existing site: " + bottomSite); // Step 9: edge = Edge.CreateBisectingEdge(bottomSite, newSite); //trace("new edge: " + edge); _edges.Add(edge); bisector = Halfedge.Create(edge, Side.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, Side.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() == false) { /* intersection is smallest */ lbnd = heap.ExtractMin(); llbnd = lbnd.edgeListLeftNeighbor; rbnd = lbnd.edgeListRightNeighbor; rrbnd = rbnd.edgeListRightNeighbor; bottomSite = FortunesAlgorithm_leftRegion(lbnd); topSite = FortunesAlgorithm_rightRegion(rbnd); // these three sites define a Delaunay triangle // (not actually using these for anything...) _triangles.Add(new Triangle(bottomSite, topSite, FortunesAlgorithm_rightRegion(lbnd))); //_triangles.push(new Triangle(bottomSite, topSite, rightRegion(lbnd))); v = lbnd.vertex; v.SetIndex(); lbnd.edge.SetVertex((Side)lbnd.leftRight, v); rbnd.edge.SetVertex((Side)rbnd.leftRight, v); edgeList.Remove(lbnd); heap.Remove(rbnd); edgeList.Remove(rbnd); leftRight = Side.LEFT; if (bottomSite.y > topSite.y) { tempSite = bottomSite; bottomSite = topSite; topSite = tempSite; leftRight = Side.RIGHT; } edge = Edge.CreateBisectingEdge(bottomSite, topSite); _edges.Add(edge); bisector = Halfedge.Create(edge, leftRight); halfEdges.Add(bisector); edgeList.Insert(llbnd, bisector); edge.SetVertex(SideHelper.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(); for (int hIndex = 0; hIndex < halfEdges.Count; hIndex++) { Halfedge halfEdge = halfEdges[hIndex]; halfEdge.ReallyDispose(); } halfEdges.Clear(); // we need the vertices to clip the edges for (int eIndex = 0; eIndex < _edges.Count; eIndex++) { edge = _edges[eIndex]; edge.ClipVertices(_plotBounds); } // but we don't actually ever use them again! for (int vIndex = 0; vIndex < vertices.Count; vIndex++) { vertex = vertices[vIndex]; vertex.Dispose(); } vertices.Clear(); }