private void BuildGraph(IEnumerable <Vector2> points, Delaunay.Voronoi voronoi) { // Build graph data structure in 'edges', 'centers', 'corners', // based on information in the Voronoi results: point.neighbors // will be a list of neighboring points of the same type (corner // or center); point.edges will be a list of edges that include // that point. Each edge connects to four points: the Voronoi edge // edge.{v0,v1} and its dual Delaunay triangle edge edge.{d0,d1}. // For boundary polygons, the Delaunay edge will have one null // point, and the Voronoi edge may be null. var libedges = voronoi.Edges(); var centerLookup = new Dictionary <Vector2?, Center>(); // Build Center objects for each of the points, and a lookup map // to find those Center objects again as we build the graph foreach (var point in points) { var p = new Center { index = centers.Count, point = point }; centers.Add(p); centerLookup[point] = p; } // Workaround for Voronoi lib bug: we need to call region() // before Edges or neighboringSites are available foreach (var p in centers) { voronoi.Region(p.point); } foreach (var libedge in libedges) { var dedge = libedge.DelaunayLine(); var vedge = libedge.VoronoiEdge(); // Fill the graph data. Make an Edge object corresponding to // the edge from the voronoi library. var edge = new Edge { index = edges.Count, river = 0, // Edges point to corners. Edges point to centers. v0 = MakeCorner(vedge.p0), v1 = MakeCorner(vedge.p1), d0 = centerLookup[dedge.p0], d1 = centerLookup[dedge.p1] }; if (vedge.p0.HasValue && vedge.p1.HasValue) { edge.midpoint = Vector2Extensions.Interpolate(vedge.p0.Value, vedge.p1.Value, 0.5f); } edges.Add(edge); // Centers point to edges. Corners point to edges. if (edge.d0 != null) { edge.d0.borders.Add(edge); } if (edge.d1 != null) { edge.d1.borders.Add(edge); } if (edge.v0 != null) { edge.v0.protrudes.Add(edge); } if (edge.v1 != null) { edge.v1.protrudes.Add(edge); } // Centers point to centers. if (edge.d0 != null && edge.d1 != null) { AddToCenterList(edge.d0.neighbors, edge.d1); AddToCenterList(edge.d1.neighbors, edge.d0); } // Corners point to corners if (edge.v0 != null && edge.v1 != null) { AddToCornerList(edge.v0.adjacent, edge.v1); AddToCornerList(edge.v1.adjacent, edge.v0); } // Centers point to corners if (edge.d0 != null) { AddToCornerList(edge.d0.corners, edge.v0); AddToCornerList(edge.d0.corners, edge.v1); } if (edge.d1 != null) { AddToCornerList(edge.d1.corners, edge.v0); AddToCornerList(edge.d1.corners, edge.v1); } // Corners point to centers if (edge.v0 != null) { AddToCenterList(edge.v0.touches, edge.d0); AddToCenterList(edge.v0.touches, edge.d1); } if (edge.v1 != null) { AddToCenterList(edge.v1.touches, edge.d0); AddToCenterList(edge.v1.touches, edge.d1); } } // TODO: use edges to determine these var topLeft = centers.OrderBy(p => p.point.x + p.point.y).First(); AddCorner(topLeft, 0, 0); var bottomRight = centers.OrderByDescending(p => p.point.x + p.point.y).First(); AddCorner(bottomRight, Width, Height); var topRight = centers.OrderByDescending(p => Width - p.point.x + p.point.y).First(); AddCorner(topRight, 0, Height); var bottomLeft = centers.OrderByDescending(p => p.point.x + Height - p.point.y).First(); AddCorner(bottomLeft, Width, 0); // required for polygon fill foreach (var center in centers) { center.corners.Sort(ClockwiseComparison(center)); } }
/** 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; }
private void CreateDataStructure(Map map, Voronoi v, KDTree <Center> kd, DataFactory factory) { foreach (var voronEdge in v.Edges()) { if (voronEdge.leftSite == null || voronEdge.rightSite == null || voronEdge.leftVertex == null || voronEdge.rightVertex == null) { continue; } var centerLeft = factory.CenterFactory(voronEdge.leftSite.Coord.ToVector3xz()); var centerRight = factory.CenterFactory(voronEdge.rightSite.Coord.ToVector3xz()); var cornerLeft = factory.CornerFactory(voronEdge.leftVertex.Coord.ToVector3xz()); var cornerRight = factory.CornerFactory(voronEdge.rightVertex.Coord.ToVector3xz()); var ed = factory.EdgeFactory(cornerLeft, cornerRight, centerLeft, centerRight); } foreach (var edge in map.Edges.Values) { edge.VoronoiStart.Protrudes[edge.Midpoint] = edge; edge.VoronoiEnd.Protrudes[edge.Midpoint] = edge; edge.DelaunayStart.Borders[edge.Midpoint] = edge; edge.DelaunayEnd.Borders[edge.Midpoint] = edge; } foreach (var corner in map.Corners.Values) { foreach (var edge in corner.Protrudes.Values) { if (edge.VoronoiStart != corner) { corner.Adjacents[edge.VoronoiStart.Point] = edge.VoronoiStart; } if (edge.VoronoiEnd != corner) { corner.Adjacents[edge.VoronoiEnd.Point] = edge.VoronoiEnd; } // Adding one side of every protruder will make it all if (!corner.Touches.ContainsKey(edge.DelaunayStart.Point)) { corner.Touches[edge.DelaunayStart.Point] = edge.DelaunayStart; } if (!corner.Touches.ContainsKey(edge.DelaunayEnd.Point)) { corner.Touches[edge.DelaunayEnd.Point] = edge.DelaunayEnd; } } } foreach (var center in map.Centers.Values) { foreach (var edge in center.Borders.Values) { if (edge.DelaunayStart != center) { center.Neighbours[edge.DelaunayStart.Point] = edge.DelaunayStart; } if (edge.DelaunayStart != center && !center.Neighbours.ContainsKey(edge.DelaunayStart.Point)) { center.Neighbours[edge.DelaunayStart.Point] = edge.DelaunayStart; } // Adding one side of every border will make it all if (!center.Corners.ContainsKey(edge.VoronoiStart.Point)) { center.Corners[edge.VoronoiStart.Point] = edge.VoronoiStart; } if (!center.Corners.ContainsKey(edge.VoronoiEnd.Point)) { center.Corners[edge.VoronoiEnd.Point] = edge.VoronoiEnd; } } } }