/// <summary> /// Returns the middle point and angle of a border to an adjacent region. The angle always points to the direction of this region. /// </summary> public Tuple <Vector2, float> GetBorderCenterPositionTo(Region otherRegion) { if (!AdjacentRegions.Contains(otherRegion)) { throw new Exception("Other region is not adjacent to this region"); } // Find all borders between the two regions List <Border> borders = Borders.Where(x => x.Regions.Contains(otherRegion)).ToList(); // Split borders into clusters and take longest cluster List <List <Border> > clusters = PolygonMapFunctions.FindBorderClusters(borders); List <Border> longestCluster = clusters.OrderByDescending(x => x.Sum(y => y.Length)).First(); // Find center of longest cluster Tuple <Vector2, float> center = PolygonMapFunctions.FindBorderCenter(longestCluster); // Swap angle if the border was reversed Vector2 testPoint = center.Item1 + new Vector2(Mathf.Sin(Mathf.Deg2Rad * center.Item2) * 0.01f, Mathf.Cos(Mathf.Deg2Rad * center.Item2) * 0.01f); if (!GeometryFunctions.IsPointInPolygon4(Polygon.Nodes.Select(x => x.Vertex).ToList(), testPoint)) { center = new Tuple <Vector2, float>(center.Item1, center.Item2 + 180); } return(center); }
public void Label() { foreach (List <Region> cluster in Clusters) { List <List <GraphNode> > clusterBorders = PolygonMapFunctions.FindOutsideNodes(cluster.Select(x => x.Polygon).ToList()); GameObject clusterLabel = CenterlineLabler.LabelPolygon(clusterBorders, Name, SecondaryColor); RegionLabels.Add(clusterLabel); } }
public void Init(List <Region> regions) { Name = "Continent XYZ"; Regions = regions; Size = regions.Count; Area = regions.Sum(x => x.Area); List <List <Region> > clusters = PolygonMapFunctions.FindClusters(regions); foreach (List <Region> cluster in clusters) { List <GameObject> clusterBorders = MeshGenerator.CreatePolygonGroupBorder(cluster.Select(x => x.Polygon).ToList(), PolygonMapGenerator.DefaulContinentBorderWidth, Color.black, onOutside: false, yPos: PolygonMapGenerator.LAYER_CONTINENT); foreach (GameObject clusterBorder in clusterBorders) { Borders.Add(clusterBorder); } } foreach (GameObject border in Borders) { border.transform.SetParent(transform); } }
public void UpdateProperties() { DestroyAllObjects(); if (Regions.Count == 0) { return; } // Border Area = Regions.Sum(x => x.Area); Clusters = PolygonMapFunctions.FindClusters(Regions); foreach (List <Region> cluster in Clusters) { List <GameObject> clusterBorders = MeshGenerator.CreatePolygonGroupBorder(cluster.Select(x => x.Polygon).ToList(), PolygonMapGenerator.DefaultShorelineBorderWidth, SecondaryColor, onOutside: false, yPos: 0.0002f); foreach (GameObject clusterBorder in clusterBorders) { Borders.Add(clusterBorder); } } // Label Label(); }
/// <summary> /// Creates a GameObject with a mesh that represents the bounds of multiple polygons. onOutside means the border will be drawn on the outside of the polygons. /// All given polygons must be connected by land for this function to work! If they are not, first split it into clusters with PolygonMapFunctions.FindClusters() /// </summary> public static List <GameObject> CreatePolygonGroupBorder(List <GraphPolygon> polygons, float width, Color c, bool onOutside, float yPos) { List <GameObject> borders = new List <GameObject>(); // Find outer mesh vertices List <List <GraphNode> > outerNodes = PolygonMapFunctions.FindOutsideNodes(polygons); List <GraphNode> outsideBorder = outerNodes.First(x => x.Count == outerNodes.Max(y => y.Count)); foreach (List <GraphNode> border in outerNodes) { List <Vector2> outerVertices = border.Select(x => x.Vertex).ToList(); bool isClockwise = GeometryFunctions.IsClockwise(outerVertices); if (border == outsideBorder) { borders.Add(CreateSinglePolygonBorder(border, width, c, yPos, onOutside ? !isClockwise : isClockwise)); } else { borders.Add(CreateSinglePolygonBorder(border, width, c, yPos, onOutside ? isClockwise : !isClockwise)); } } return(borders); }
public static void CreateContinents(PolygonMapGenerator PMG) { PMG.Continents = new List<List<GraphPolygon>>(); // 1. Create one continent per landmass foreach (List<GraphPolygon> landmass in PMG.Landmasses) { List<GraphPolygon> continentPolygons = new List<GraphPolygon>(); continentPolygons.AddRange(landmass); foreach (GraphPolygon landmassPoly in landmass) landmassPoly.Continent = continentPolygons; PMG.Continents.Add(continentPolygons); } // 2. Split big landmasses into mutliple continents List<GraphPolygon> biggestContinent = PMG.Continents.OrderByDescending(x => x.Count).First(); while(biggestContinent.Count > PMG.GenerationSettings.MaxContinentSize) { int minCuts = int.MaxValue; int minCutMinRegions = int.MaxValue; KargerGraph bestGraph = null; int cyclesWithoutImprovement = 0; while(cyclesWithoutImprovement < 50 || bestGraph == null) { cyclesWithoutImprovement++; KargerGraph graph = SplitClusterOnce(biggestContinent); if(graph.Vertices[0].ContainedPolygons.Count >= PMG.GenerationSettings.MinContinentSize && graph.Vertices[1].ContainedPolygons.Count >= PMG.GenerationSettings.MinContinentSize) { int graphMinCutMinRegions = Mathf.Min(graph.Vertices[0].ContainedPolygons.Count, graph.Vertices[1].ContainedPolygons.Count); if (graph.CutSize < minCuts || (graph.CutSize == minCuts && graphMinCutMinRegions > minCutMinRegions)) { minCuts = graph.CutSize; minCutMinRegions = graphMinCutMinRegions; bestGraph = graph; cyclesWithoutImprovement = 0; } } } List<GraphPolygon> newContinent = new List<GraphPolygon>(); foreach (GraphPolygon splittedPolygon in bestGraph.Vertices[0].ContainedPolygons) { newContinent.Add(splittedPolygon); biggestContinent.Remove(splittedPolygon); splittedPolygon.Continent = newContinent; } PMG.Continents.Add(newContinent); biggestContinent = PMG.Continents.OrderByDescending(x => x.Count).First(); } // 3. Assign islands that are too small to the nearest continent List<GraphPolygon> smallestContinent = PMG.Continents.OrderBy(x => x.Count).First(); while(smallestContinent.Count < PMG.GenerationSettings.MinContinentSize) { float shortestDistance = float.MaxValue; GraphPolygon shortestDistancePolygon = null; foreach(GraphPolygon continentPoly in smallestContinent) { foreach(GraphPolygon waterNeighbour in continentPoly.WaterNeighbours) { if(!smallestContinent.Contains(waterNeighbour)) { float distance = PolygonMapFunctions.GetPolygonDistance(continentPoly, waterNeighbour); if (distance < shortestDistance) { shortestDistance = distance; shortestDistancePolygon = waterNeighbour; } } } } foreach (GraphPolygon continentPoly in smallestContinent) { continentPoly.Continent = shortestDistancePolygon.Continent; shortestDistancePolygon.Continent.Add(continentPoly); } smallestContinent.Clear(); PMG.Continents.Remove(smallestContinent); smallestContinent = PMG.Continents.OrderBy(x => x.Count).First(); } }