// Get area data graph that contains the given area segments, with the respective center and polygon, and neighborhood information public Graph <AreaData> GetAreaDataGraph(IEnumerable <int> areaSegments) { if (!AreNeighbours(areaSegments)) { return(null); } // Build polygons and gather roads for each site Dictionary <int, Vector2[]> areaSegmentPolygonMap = new Dictionary <int, Vector2[]>(); Dictionary <int, List <Vector2[]> > areaSegmentRoadMap = new Dictionary <int, List <Vector2[]> >(); foreach (int areaSegment in areaSegments) { var site = new Vector2f(_areaSegmentCenterMap[areaSegment]); areaSegmentPolygonMap.Add(areaSegment, GetSitePolygon(site)); areaSegmentRoadMap.Add(areaSegment, GetSitePaths(site)); } // Build graph Dictionary <int, int> graphMap = new Dictionary <int, int>(); Graph <AreaData> areaDataGraph = new Graph <AreaData>(); foreach (int areaSegment in areaSegments) { AreaData data = new AreaData { Center = _areaSegmentCenterMap[areaSegment], Polygon = areaSegmentPolygonMap[areaSegment], Segment = AreaSegmentGraph.GetNodeData(areaSegment), Paths = areaSegmentRoadMap[areaSegment] }; int newID = areaDataGraph.AddNode(data); graphMap.Add(areaSegment, newID); } foreach (int areaSegment in areaSegments) { foreach (int neighbour in AreaSegmentGraph.GetNeighbours(areaSegment)) { if (areaSegments.Contains(neighbour)) { areaDataGraph.AddEdge(graphMap[areaSegment], graphMap[neighbour], 0); } } } return(areaDataGraph); }
// Get closest AreaSegment to specified pos private AreaSegment GetClosestAreaSegment(Vector2 pos) { return(AreaSegmentGraph.GetNodeData(GetClosestAreaSegmentID(pos))); }
// Create border blocker polygon private void CreateBorderBlockerLines(float borderInlandOffset) { var segments = new List <KeyValuePair <Edge, Vector2> >(); foreach (var edge in VoronoiDiagram.Edges) { //Check if this edge is visible before continuing if (!edge.Visible()) { continue; } int leftAreaSegmentID = _siteAreaSegmentMap[edge.RightSite.Coord]; int rightAreaSegmentID = _siteAreaSegmentMap[edge.LeftSite.Coord]; AreaSegment leftAreaSegment = AreaSegmentGraph.GetNodeData(leftAreaSegmentID); AreaSegment rightAreaSegment = AreaSegmentGraph.GetNodeData(rightAreaSegmentID); // Either one or the other must be a border type if (leftAreaSegment.Type != rightAreaSegment.Type && (leftAreaSegment.Type == AreaSegment.EAreaSegmentType.Border || rightAreaSegment.Type == AreaSegment.EAreaSegmentType.Border)) { // Add border edge with the biome center to scale inwards later segments.Add(new KeyValuePair <Edge, Vector2>(edge, leftAreaSegment.Type == AreaSegment.EAreaSegmentType.Border ? edge.LeftSite.Coord.ToUnityVector2() : edge.RightSite.Coord.ToUnityVector2())); } } // Group connected segments var edgeGroups = new List <List <KeyValuePair <Edge, Vector2> > >(); while (segments.Count > 0) { var edges = new List <KeyValuePair <Edge, Vector2> >(); var startEdge = segments[0]; segments.Remove(startEdge); Vertex headPoint = startEdge.Key.RightVertex; Vertex tailPoint = startEdge.Key.LeftVertex; edges.Add(startEdge); // Find a polygon var polygonClosed = false; while (!polygonClosed && segments.Count > 0) { for (int i = 0; i < segments.Count; i++) { var currentElement = segments[i]; Vertex leftPoint = currentElement.Key.LeftVertex; Vertex rightPoint = currentElement.Key.RightVertex; if (leftPoint == headPoint) { edges.Add(currentElement); segments.Remove(currentElement); headPoint = rightPoint; } else if (rightPoint == headPoint) { edges.Add(currentElement); segments.Remove(currentElement); headPoint = leftPoint; } // Polygon has been closed if (headPoint == tailPoint) { polygonClosed = true; break; } } } edgeGroups.Add(edges); } // Iterate over each polygon found previously foreach (var edges in edgeGroups) { var polygon = new List <Vector2>(); var borderLines = edges.Select(pair => pair.Key).ToList().EdgesToSortedLines(); foreach (var line in borderLines) { // Offset borders towards biome center var left = line[0]; var right = line[1]; var center = Vector2.zero; edges.ForEach(e => { var l = e.Key.ClippedEnds[LR.LEFT].ToUnityVector2(); var r = e.Key.ClippedEnds[LR.RIGHT].ToUnityVector2(); if ((l == left || l == right) && (r == left || r == right)) { center = e.Value; } }); left += (center - left).normalized * borderInlandOffset; right += (center - right).normalized * borderInlandOffset; // Offsetting can give duplicated points if (!polygon.Contains(left)) { polygon.Add(left); } if (!polygon.Contains(right)) { polygon.Add(right); } } // Create border blocker lines BorderBlockerLines.AddRange(polygon.ToArray().PolygonToLines()); } }
// Create a graph containing all connected empty areas private void CreateBaseGraph(int lloydIterations) { // Create uniform random point distribution and Voronoi Diagram var centers = new List <Vector2f>(); for (int i = 0; i < _voronoiSamples; i++) { var x = Random.Range(0f, MapSize); var y = Random.Range(0f, MapSize); centers.Add(new Vector2f(x, y)); } VoronoiDiagram = new Voronoi(centers, new Rectf(0, 0, MapSize, MapSize)); // Apply Lloyd Relaxation VoronoiDiagram.LloydRelaxation(lloydIterations); // Assign area segments to initial areas foreach (var site in VoronoiDiagram.SiteCoords()) { bool isOnBorder = false; var segments = VoronoiDiagram.VoronoiBoundaryForSite(site); foreach (var segment in segments) { if (!(segment.p0.x <= VoronoiDiagram.PlotBounds.left) && !(segment.p0.x >= VoronoiDiagram.PlotBounds.right) && !(segment.p0.y <= VoronoiDiagram.PlotBounds.top) && !(segment.p0.y >= VoronoiDiagram.PlotBounds.bottom) && !(segment.p1.x <= VoronoiDiagram.PlotBounds.left) && !(segment.p1.x >= VoronoiDiagram.PlotBounds.right) && !(segment.p1.y <= VoronoiDiagram.PlotBounds.top) && !(segment.p1.y >= VoronoiDiagram.PlotBounds.bottom)) { continue; } isOnBorder = true; break; } // Assign areaSegment to site and corresponding area var areaSegment = new AreaSegment(isOnBorder ? AreaSegment.EAreaSegmentType.Border : AreaSegment.EAreaSegmentType.Empty); var nodeID = AreaSegmentGraph.AddNode(areaSegment); _siteAreaSegmentMap.Add(site, nodeID); _areaSegmentCenterMap.Add(nodeID, site.ToUnityVector2()); } // Create navigation graph - for each area segment that is not a border, add reachable neighbors foreach (var id in _siteAreaSegmentMap) { var areaSegment = AreaSegmentGraph.GetNodeData(id.Value); if (areaSegment.Type == AreaSegment.EAreaSegmentType.Border) { continue; } Vector2 center = _areaSegmentCenterMap[id.Value]; foreach (var neighbor in VoronoiDiagram.NeighborSitesForSite(new Vector2f(center.x, center.y))) { var neighborSegment = AreaSegmentGraph.GetNodeData(_siteAreaSegmentMap[neighbor]); if (neighborSegment.Type != AreaSegment.EAreaSegmentType.Border) { AreaSegmentGraph.AddEdge(_siteAreaSegmentMap[neighbor], id.Value, (int)AreaSegment.EAreaSegmentEdgeType.NonNavigable); } } } }