private void generateGraph(Voronoi voronoi) { points = new HashSet <Vector3>(); nodes = new HashSet <Node>(); edges = new List <NodeHalfEdge>(); var edgesByStartPosition = new Dictionary <Vector3, List <NodeHalfEdge> >(); var siteEdges = new Dictionary <Vector2, List <LineSegment> >(); foreach (var edge in voronoi.Edges()) { if (edge.visible) { var p1 = edge.clippedEnds[Delaunay.LR.Side.LEFT]; var p2 = edge.clippedEnds[Delaunay.LR.Side.RIGHT]; var segment = new LineSegment(p1, p2); if (Vector2.Distance(p1.Value, p2.Value) < 0.001f) { continue; } if (edge.leftSite != null) { if (!siteEdges.ContainsKey(edge.leftSite.Coord)) { siteEdges.Add(edge.leftSite.Coord, new List <LineSegment>()); } siteEdges[edge.leftSite.Coord].Add(segment); } if (edge.rightSite != null) { if (!siteEdges.ContainsKey(edge.rightSite.Coord)) { siteEdges.Add(edge.rightSite.Coord, new List <LineSegment>()); } siteEdges[edge.rightSite.Coord].Add(segment); } } } foreach (var site in voronoi.SiteCoords()) { var boundries = getBoundriesForSite(siteEdges, site); var center = getPointWithElevation(site); var currentNode = new Node { centerPoint = center }; nodes.Add(currentNode); NodeHalfEdge firstEdge = null; NodeHalfEdge previousEdge = null; for (var i = 0; i < boundries.Count; i++) { var edge = boundries[i]; var start = getPointWithElevation(edge.p0.Value); var end = getPointWithElevation(edge.p1.Value); if (start == end) { continue; } previousEdge = addEdge(edgesByStartPosition, previousEdge, start, end, currentNode); if (firstEdge == null) { firstEdge = previousEdge; } if (currentNode.startEdge == null) { currentNode.startEdge = previousEdge; } // figure out if the two edges meet, and if not then insert some more edges to close the polygon if (i < boundries.Count - 1) { start = getPointWithElevation(edge.p1.Value); end = getPointWithElevation(boundries[i + 1].p0.Value); } else if (i == boundries.Count - 1) { start = getPointWithElevation(edge.p1.Value); end = getPointWithElevation(boundries[0].p0.Value); } if (start != end) { previousEdge = addEdge(edgesByStartPosition, previousEdge, previousEdge.destination, end, currentNode); } } // Connect up the end of the loop previousEdge.next = firstEdge; firstEdge.previous = previousEdge; } connectOpposites(edgesByStartPosition); }
private void CreateFromVoronoi(Voronoi voronoi) { vertices = new Dictionary <Vector3, Vertex>(); nodesByCenterPosition = new Dictionary <Vector3, Node>(); var edgesByStartPosition = new Dictionary <Vector3, List <Edge> >(); edges = new List <Edge>(); plotBounds = voronoi.plotBounds; var bottomLeftSite = voronoi.NearestSitePoint(plotBounds.xMin, plotBounds.yMin); var bottomRightSite = voronoi.NearestSitePoint(plotBounds.xMax, plotBounds.yMin); var topLeftSite = voronoi.NearestSitePoint(plotBounds.xMin, plotBounds.yMax); var topRightSite = voronoi.NearestSitePoint(plotBounds.xMax, plotBounds.yMax); var topLeft = new Vector3(plotBounds.xMin, 0, plotBounds.yMax); var topRight = new Vector3(plotBounds.xMax, 0, plotBounds.yMax); var bottomLeft = new Vector3(plotBounds.xMin, 0, plotBounds.yMin); var bottomRight = new Vector3(plotBounds.xMax, 0, plotBounds.yMin); var siteEdges = new Dictionary <Vector2, List <LineSegment> >(); var edgePointsRemoved = 0; foreach (var edge in voronoi.Edges()) { if (!edge.visible) { continue; } var p1 = edge.clippedEnds[Delaunay.LR.Side.LEFT]; var p2 = edge.clippedEnds[Delaunay.LR.Side.RIGHT]; var segment = new LineSegment(p1, p2); if (Vector2.Distance(p1.Value, p2.Value) < 0.001f) { edgePointsRemoved++; continue; } if (edge.leftSite != null) { if (!siteEdges.ContainsKey(edge.leftSite.Coord)) { siteEdges.Add(edge.leftSite.Coord, new List <LineSegment>()); } siteEdges[edge.leftSite.Coord].Add(segment); } if (edge.rightSite != null) { if (!siteEdges.ContainsKey(edge.rightSite.Coord)) { siteEdges.Add(edge.rightSite.Coord, new List <LineSegment>()); } siteEdges[edge.rightSite.Coord].Add(segment); } } Debug.Assert(edgePointsRemoved == 0, string.Format("{0} edge points too close and have been removed", edgePointsRemoved)); foreach (var site in voronoi.SiteCoords()) { var boundries = GetBoundriesForSite(siteEdges, site); var center = ToVector3(site); var currentNode = new Node { centerPoint = center }; nodesByCenterPosition.Add(center, currentNode); Edge firstEdge = null; Edge previousEdge = null; for (var i = 0; i < boundries.Count; i++) { var edge = boundries[i]; var start = ToVector3(edge.p0.Value); var end = ToVector3(edge.p1.Value); if (start == end) { continue; } previousEdge = AddEdge(edgesByStartPosition, previousEdge, start, end, currentNode); if (firstEdge == null) { firstEdge = previousEdge; } if (currentNode.startEdge == null) { currentNode.startEdge = previousEdge; } // We need to figure out if the two edges meet, and if not then // insert some more edges to close the polygon var insertEdges = false; if (i < boundries.Count - 1) { start = ToVector3(boundries[i + 0].p1.Value); end = ToVector3(boundries[i + 1].p0.Value); insertEdges = start != end; } else if (i == boundries.Count - 1) { start = ToVector3(boundries[i].p1.Value); end = ToVector3(boundries[0].p0.Value); insertEdges = start != end; } if (insertEdges) { // Check which corners are within this node var startIsTop = start.z == voronoi.plotBounds.yMax; var startIsBottom = start.z == voronoi.plotBounds.yMin; var startIsLeft = start.x == voronoi.plotBounds.xMin; var startIsRight = start.x == voronoi.plotBounds.xMax; var hasTopLeft = site == topLeftSite && !(startIsTop && startIsLeft); var hasTopRight = site == topRightSite && !(startIsTop && startIsRight); var hasBottomLeft = site == bottomLeftSite && !(startIsBottom && startIsLeft); var hasBottomRight = site == bottomRightSite && !(startIsBottom && startIsRight); if (startIsTop) { if (hasTopRight) { previousEdge = AddEdge(edgesByStartPosition, previousEdge, start, topRight, currentNode); } if (hasBottomRight) { previousEdge = AddEdge(edgesByStartPosition, previousEdge, previousEdge.destination.position, bottomRight, currentNode); } if (hasBottomLeft) { previousEdge = AddEdge(edgesByStartPosition, previousEdge, previousEdge.destination.position, bottomLeft, currentNode); } if (hasTopLeft) { previousEdge = AddEdge(edgesByStartPosition, previousEdge, previousEdge.destination.position, topLeft, currentNode); } } else if (startIsRight) { if (hasBottomRight) { previousEdge = AddEdge(edgesByStartPosition, previousEdge, start, bottomRight, currentNode); } if (hasBottomLeft) { previousEdge = AddEdge(edgesByStartPosition, previousEdge, previousEdge.destination.position, bottomLeft, currentNode); } if (hasTopLeft) { previousEdge = AddEdge(edgesByStartPosition, previousEdge, previousEdge.destination.position, topLeft, currentNode); } if (hasTopRight) { previousEdge = AddEdge(edgesByStartPosition, previousEdge, previousEdge.destination.position, topRight, currentNode); } } else if (startIsBottom) { if (hasBottomLeft) { previousEdge = AddEdge(edgesByStartPosition, previousEdge, start, bottomLeft, currentNode); } if (hasTopLeft) { previousEdge = AddEdge(edgesByStartPosition, previousEdge, previousEdge.destination.position, topLeft, currentNode); } if (hasTopRight) { previousEdge = AddEdge(edgesByStartPosition, previousEdge, previousEdge.destination.position, topRight, currentNode); } if (hasBottomRight) { previousEdge = AddEdge(edgesByStartPosition, previousEdge, previousEdge.destination.position, bottomRight, currentNode); } } else if (startIsLeft) { if (hasTopLeft) { previousEdge = AddEdge(edgesByStartPosition, previousEdge, start, topLeft, currentNode); } if (hasTopRight) { previousEdge = AddEdge(edgesByStartPosition, previousEdge, previousEdge.destination.position, topRight, currentNode); } if (hasBottomRight) { previousEdge = AddEdge(edgesByStartPosition, previousEdge, previousEdge.destination.position, bottomRight, currentNode); } if (hasBottomLeft) { previousEdge = AddEdge(edgesByStartPosition, previousEdge, previousEdge.destination.position, bottomLeft, currentNode); } } previousEdge = AddEdge(edgesByStartPosition, previousEdge, previousEdge.destination.position, end, currentNode); } } // Connect up the end of the loop previousEdge.next = firstEdge; firstEdge.previous = previousEdge; AddLeavingEdge(firstEdge); } ConnectOpposites(edgesByStartPosition); }
/** * 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); }
// 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. private void BuildGraph() { Voronoi voronoi = new Voronoi(pointsCount, new Rect(0, 0, width, height), NUM_LLOYD_ITERATIONS); points = voronoi.GetPoints(); // Workaround for Voronoi lib bug: we need to call region() // before Edges or neighboringSites are available foreach (var center in _centers) { voronoi.Region(center.point); } List <Delaunay.Edge> libedges = voronoi.Edges(); Dictionary <Vector2, Center> 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) { Center p = new Center(); p.index = _centers.Count; p.point = point; p.neighbors = new List <Center>(); p.borders = new List <DoubleEdge>(); p.corners = new List <Corner>(); _centers.Add(p); centerLookup[point] = p; } Dictionary <int, List <Corner> > _cornerMap = new Dictionary <int, List <Corner> >(); foreach (Delaunay.Edge libedge in libedges) { LineSegment dedge = libedge.DelaunayLine(); LineSegment vedge = libedge.VoronoiEdge(); // Fill the graph data. Make an Edge object corresponding to // the edge from the voronoi library. DoubleEdge edge = new DoubleEdge(); edge.index = _edges.Count; edge.river = 0; if (vedge.p0 != null && vedge.p1 != null) { edge.midpoint = Vector2.Lerp((Vector2)vedge.p0, (Vector2)vedge.p1, 0.5f); } // Edges point to corners. Edges point to centers. edge.v0 = MakeCorner(vedge.p0, _cornerMap); edge.v1 = MakeCorner(vedge.p1, _cornerMap); Center findCenter = null; if (centerLookup.TryGetValue((Vector2)dedge.p0, out findCenter)) { edge.d0 = findCenter; } else { edge.d0 = null; } if (centerLookup.TryGetValue((Vector2)dedge.p1, out findCenter)) { edge.d1 = centerLookup[(Vector2)dedge.p1]; } else { edge.d1 = null; } // 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) { AddToGenericList(edge.d0.neighbors, edge.d1); AddToGenericList(edge.d1.neighbors, edge.d0); } // Corners point to corners if (edge.v0 != null && edge.v1 != null) { AddToGenericList(edge.v0.adjacent, edge.v1); AddToGenericList(edge.v1.adjacent, edge.v0); } // Centers point to corners if (edge.d0 != null) { AddToGenericList(edge.d0.corners, edge.v0); AddToGenericList(edge.d0.corners, edge.v1); } if (edge.d1 != null) { AddToGenericList(edge.d1.corners, edge.v0); AddToGenericList(edge.d1.corners, edge.v1); } // Corners point to centers if (edge.v0 != null) { AddToGenericList(edge.v0.touches, edge.d0); AddToGenericList(edge.v0.touches, edge.d1); } if (edge.v1 != null) { AddToGenericList(edge.v1.touches, edge.d0); AddToGenericList(edge.v1.touches, edge.d1); } _edges.Add(edge); } }
public void buildGraph(List<Vector2> points, Voronoi voronoi) { Center p; Corner q; Vector2 point; Vector2 other; List<Delaunay.Edge> libedges= voronoi.Edges(); Dictionary<System.Nullable<Vector2>,Center> centerLookup = new Dictionary<System.Nullable<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 ( Vector2 ppp in points) { System.Nullable<Vector2> pp = (System.Nullable<Vector2>) ppp; p = new Center(); p.index = centers.Count; p.point = (Vector2) pp; p.neighbors = new List<Center>(); p.borders = new List<Edge>(); p.corners = new List<Corner>(); centers.Add(p); centerLookup[pp] = p; } foreach ( Center po in centers) { voronoi.Region(po.point); } foreach (Delaunay.Edge libedge in libedges) { LineSegment dedge = libedge.DelaunayLine(); LineSegment vedge = libedge.VoronoiEdge(); // Fill the graph data. Make an Edge object corresponding to // the edge from the voronoi library. Edge edge = new Edge(); edge.index = edges.Count; edge.river = 0; edges.Add(edge); edge.midpoint = null; if (vedge.p0!= null && vedge.p1 != null) edge.midpoint = Vector2.Lerp( (Vector2) vedge.p0, (Vector2) vedge.p1, 0.5f); // Edges point to corners. Edges point to centers. edge.v0 = makeCorner(vedge.p0); edge.v1 = makeCorner(vedge.p1); edge.d0 = centerLookup[dedge.p0]; edge.d1 = centerLookup[dedge.p1]; // 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); } } }