/// <summary> /// Returns the number of polygons that have to be traversed (through water or land neighbour) to get from p1 to p2 (neighbouring polygons have distance = 1) /// </summary> public static int GetRegionDistance(GraphPolygon p1, GraphPolygon p2) { int range = 0; List <GraphPolygon> rangePolygons = new List <GraphPolygon>() { p1 }; while (!rangePolygons.Contains(p2) && range < 50) { range++; List <GraphPolygon> polygonsToAdd = new List <GraphPolygon>(); foreach (GraphPolygon poly in rangePolygons) { foreach (GraphPolygon neighbour in poly.Neighbours) { if (!rangePolygons.Contains(neighbour) && !polygonsToAdd.Contains(neighbour)) { polygonsToAdd.Add(neighbour); } } } rangePolygons.AddRange(polygonsToAdd); } return(range); }
/// <summary> /// Takes a list of regions as an input and splits them into clusters. A cluster is defined as each region is reachable from each other region in the cluster through direct land connections. /// </summary> public static List <List <GraphPolygon> > FindClusters(List <GraphPolygon> polygons, bool landConnectionsOnly = true) { List <List <GraphPolygon> > clusters = new List <List <GraphPolygon> >(); List <GraphPolygon> polygonsWithoutCluster = new List <GraphPolygon>(); polygonsWithoutCluster.AddRange(polygons); while (polygonsWithoutCluster.Count > 0) { List <GraphPolygon> cluster = new List <GraphPolygon>(); Queue <GraphPolygon> polygonsToAdd = new Queue <GraphPolygon>(); polygonsToAdd.Enqueue(polygonsWithoutCluster[0]); while (polygonsToAdd.Count > 0) { GraphPolygon polygonToAdd = polygonsToAdd.Dequeue(); cluster.Add(polygonToAdd); List <GraphPolygon> neighbouringPolygons = landConnectionsOnly ? polygonToAdd.LandNeighbours.Where(x => polygons.Contains(x)).ToList() : polygonToAdd.Neighbours.Where(x => polygons.Contains(x)).ToList(); foreach (GraphPolygon neighbourPolygon in neighbouringPolygons) { if (!cluster.Contains(neighbourPolygon) && !polygonsToAdd.Contains(neighbourPolygon)) { polygonsToAdd.Enqueue(neighbourPolygon); } } } clusters.Add(cluster); foreach (GraphPolygon p in cluster) { polygonsWithoutCluster.Remove(p); } } return(clusters); }
private void Form1_Load(object sender, EventArgs e) { B = new Bitmap(pictureBox1.Width, pictureBox1.Height); pictureBox1.Image = B; G = Graphics.FromImage(B); Polygons = new List <GraphPolygon>(); ClipPolygon = new GraphPolygon(B, G, new Pen(pnlCuttingRect.BackColor)); Polygons.Add(new GraphPolygon(B, G, new Pen(pnlLine.BackColor))); }
public void Init(GraphPolygon p) { Id = p.Id; Polygon = p; BorderPoints = p.Nodes.Select(x => x.BorderPoint).ToList(); Borders = p.Connections.Select(x => x.Border).ToList(); AdjacentRegions = p.AdjacentPolygons.Select(x => x.Region).ToList(); LandNeighbours = p.LandNeighbours.Select(x => x.Region).ToList(); WaterNeighbours = p.WaterNeighbours.Select(x => x.Region).ToList(); Neighbours = LandNeighbours.Concat(WaterNeighbours).ToList(); SetRegionBorders(); Width = p.Width; Height = p.Height; XPos = p.Nodes.Min(x => x.Vertex.x); YPos = p.Nodes.Min(x => x.Vertex.y); Centroid = p.Centroid; CenterPoi = p.CenterPoi; Area = p.Area; Jaggedness = p.Jaggedness; TotalBorderLength = p.TotalBorderLength; InlandBorderLength = p.InlandBorderLength; InlandBorderRatio = InlandBorderLength / TotalBorderLength; CoastLength = p.Coastline; CoastRatio = CoastLength / TotalBorderLength; Temperature = p.Temperature; Precipitation = p.Precipitation; Biome = p.Biome; IsWater = p.IsWater; IsNextToWater = p.IsNextToWater; IsEdgeRegion = p.IsEdgePolygon; IsOuterOcean = p.IsOuterPolygon; DistanceFromNearestWater = p.DistanceFromNearestWater; GetComponent <Renderer>().material = MapDisplayResources.Singleton.DefaultMaterial; // Coast OceanCoastLength = RegionBorders.Where(x => x.Key.WaterBody != null && !x.Key.WaterBody.IsLake).Sum(x => x.Value.Sum(y => y.Length)); OceanCoastRatio = OceanCoastLength / TotalBorderLength; LakeCoastLength = RegionBorders.Where(x => x.Key.WaterBody != null && x.Key.WaterBody.IsLake).Sum(x => x.Value.Sum(y => y.Length)); LakeCoastRatio = LakeCoastLength / TotalBorderLength; // Border surrounding the region Border = MeshGenerator.CreateSinglePolygonBorder(p.Nodes, PolygonMapGenerator.DefaultRegionBorderWidth, Color.black, layer: PolygonMapGenerator.LAYER_REGION_BORDER); Border.transform.SetParent(transform); // Connection overlays (lines to neighbouring regions) ConnectionOverlays = InitConnectionOverlays(); ShowConnectionOverlays(false); }
private void ClipAndDraw() { if (curstep == 0) { Result = new GraphPolygon(B, G, new Pen(pnlCuttingLine.BackColor, 2)); } for (int i = 0; i < Polygons.Count - 1; i++) { if (PolygonClip(Polygons[i], ref Result, ClipPolygon)) { Result.Draw(); } } }
/// <summary> /// Returns the shortest distance between two polygons (distance between two closest points) /// </summary> public static float GetPolygonDistance(GraphPolygon p1, GraphPolygon p2) { float shortestDistance = float.MaxValue; foreach (GraphNode fromNode in p1.Nodes) { foreach (GraphNode toNode in p2.Nodes) { float distance = Vector2.Distance(fromNode.Vertex, toNode.Vertex); if (distance < shortestDistance) { shortestDistance = distance; } } } return(shortestDistance); }
/// <summary> /// Returns a list containing the two graphnodes that are the closest to each other from p1 and p2. The first element is a node from p1, the second element a node from p2. /// if ignoreMultiNodes is true, only nodes that have exactly 2 polygons are considered /// if shoreOnly is true, only nodes that have at least one water polygon will be considered /// </summary> public static List <GraphNode> GetClosestPolygonNodes(GraphPolygon p1, GraphPolygon p2, bool ignoreMultiNodes, bool shoreOnly) { float shortestDistance = float.MaxValue; GraphNode n1 = null; GraphNode n2 = null; List <GraphNode> p1Nodes = p1.Nodes; List <GraphNode> p2Nodes = p2.Nodes; if (ignoreMultiNodes) { p1Nodes = p1Nodes.Where(x => x.Polygons.Count == 2).ToList(); p2Nodes = p2Nodes.Where(x => x.Polygons.Count == 2).ToList(); } if (shoreOnly) { p1Nodes = p1Nodes.Where(x => x.Type == BorderPointType.Shore).ToList(); p2Nodes = p2Nodes.Where(x => x.Type == BorderPointType.Shore).ToList(); } foreach (GraphNode fromNode in p1Nodes) { foreach (GraphNode toNode in p2Nodes) { float distance = Vector2.Distance(fromNode.Vertex, toNode.Vertex); if (distance < shortestDistance) { shortestDistance = distance; n1 = fromNode; n2 = toNode; } } } return(new List <GraphNode>() { n1, n2 }); }
public static Map LoadMapFromHash(PolygonMapGenerator PMG, string mapHash) { string[] lines = mapHash.Split(new string[] { Environment.NewLine }, StringSplitOptions.None); int lineIndex = 0; // Line 0: MapGenerationSettings: seed;width;height;minPolygonArea;maxPolygonArea;minContinentSize;maxContinentSize;mapType;continentSizeScale int debugI = 0; Debug.Log(debugI++); string[] mgsLine = lines[lineIndex++].Split(';'); int seed = int.Parse(mgsLine[0]); int width = int.Parse(mgsLine[1]); int height = int.Parse(mgsLine[2]); float minPolygonArea = float.Parse(mgsLine[3]); float maxPolygonArea = float.Parse(mgsLine[4]); int minContinentSize = int.Parse(mgsLine[5]); int maxContinentSize = int.Parse(mgsLine[6]); MapType mapType = (MapType)int.Parse(mgsLine[7]); float continentSizeScale = float.Parse(mgsLine[8]); PMG.GenerationSettings = new MapGenerationSettings(seed, width, height, minPolygonArea, maxPolygonArea, minContinentSize, maxContinentSize, mapType, continentSizeScale); Debug.Log(debugI++); // Line 1: n_nodes;n_ingraphconnections;n_egdeconnections;n_polygons;n_paths;n_landmasses;n_waterbodies;n_continents string[] line1 = lines[lineIndex++].Split(';'); int n_nodes = int.Parse(line1[0]); int n_inGraphConnections = int.Parse(line1[1]); int n_edgeConnections = int.Parse(line1[2]); int n_polygons = int.Parse(line1[3]); int n_riverPaths = int.Parse(line1[4]); int n_landmasses = int.Parse(line1[5]); int n_waterBodies = int.Parse(line1[6]); int n_continents = int.Parse(line1[7]); Debug.Log(debugI++); // Node Lines: id;positionX;positionY;riverWidth;distanceFromNearestOcean PMG.Nodes.Clear(); Dictionary <int, GraphNode> nodeMap = new Dictionary <int, GraphNode>(); for (int i = 0; i < n_nodes; i++) { string[] attributes = lines[lineIndex++].Split(';'); int id = int.Parse(attributes[0]); float x = float.Parse(attributes[1]); float y = float.Parse(attributes[2]); float riverWidth = float.Parse(attributes[3]); int distanceFromNearestOcean = int.Parse(attributes[4]); GraphNode node = new GraphNode(id, x, y, riverWidth, distanceFromNearestOcean); nodeMap.Add(id, node); PMG.Nodes.Add(node); } Debug.Log(debugI++); // InGraphConnection Lines: id;fromNodeId;toNodeId;riverWidth PMG.InGraphConnections.Clear(); Dictionary <int, GraphConnection> connectionMap = new Dictionary <int, GraphConnection>(); for (int i = 0; i < n_inGraphConnections; i++) { string[] attributes = lines[lineIndex++].Split(';'); int id = int.Parse(attributes[0]); int startNodeId = int.Parse(attributes[1]); int endNodeId = int.Parse(attributes[2]); float riverWidth = float.Parse(attributes[3]); GraphConnection connection = new GraphConnection(id, nodeMap[startNodeId], nodeMap[endNodeId], riverWidth); connectionMap.Add(id, connection); PMG.InGraphConnections.Add(connection); } Debug.Log(debugI++); // EdgeConnection Lines: id;fromNodeId;toNodeId;riverWidth PMG.EdgeConnections.Clear(); for (int i = 0; i < n_edgeConnections; i++) { string[] attributes = lines[lineIndex++].Split(';'); int id = int.Parse(attributes[0]); int startNodeId = int.Parse(attributes[1]); int endNodeId = int.Parse(attributes[2]); float riverWidth = float.Parse(attributes[3]); GraphConnection connection = new GraphConnection(id, nodeMap[startNodeId], nodeMap[endNodeId], riverWidth); connectionMap.Add(id, connection); PMG.EdgeConnections.Add(connection); } Debug.Log(debugI++); // Polygon Lines: id;[nodeIds];[connectionIds] PMG.Polygons.Clear(); Dictionary <int, GraphPolygon> polygonMap = new Dictionary <int, GraphPolygon>(); for (int i = 0; i < n_polygons; i++) { string[] attributes = lines[lineIndex++].Split(';'); int id = int.Parse(attributes[0]); List <GraphNode> nodes = new List <GraphNode>(); string[] nodeIds = attributes[1].Split(','); foreach (string s in nodeIds) { nodes.Add(nodeMap[int.Parse(s)]); } List <GraphConnection> connections = new List <GraphConnection>(); string[] connectionIds = attributes[2].Split(','); foreach (string s in connectionIds) { connections.Add(connectionMap[int.Parse(s)]); } GraphPolygon polygon = new GraphPolygon(id, nodes, connections); polygonMap.Add(id, polygon); PMG.Polygons.Add(polygon); } Debug.Log(debugI++); // River Path Lines: id;[nodeIds];[connectionIds];[polygonIds] PMG.RiverPaths.Clear(); for (int i = 0; i < n_riverPaths; i++) { string[] attributes = lines[lineIndex++].Split(';'); int id = int.Parse(attributes[0]); List <GraphNode> nodes = new List <GraphNode>(); string[] nodeIds = attributes[1].Split(','); foreach (string s in nodeIds) { nodes.Add(nodeMap[int.Parse(s)]); } List <GraphConnection> connections = new List <GraphConnection>(); string[] connectionIds = attributes[2].Split(','); foreach (string s in connectionIds) { connections.Add(connectionMap[int.Parse(s)]); } List <GraphPolygon> polygons = new List <GraphPolygon>(); string[] polygonIds = attributes[3].Split(','); foreach (string s in polygonIds) { polygons.Add(polygonMap[int.Parse(s)]); } GraphPath path = new GraphPath(id, nodes, connections, polygons); PMG.RiverPaths.Add(path); } Debug.Log(debugI++); // Landmass Lines: [polygonIds] PMG.Landmasses.Clear(); for (int i = 0; i < n_landmasses; i++) { string[] attributes = lines[lineIndex++].Split(';'); List <GraphPolygon> polygons = new List <GraphPolygon>(); string[] polygonIds = attributes[0].Split(','); foreach (string s in polygonIds) { polygons.Add(polygonMap[int.Parse(s)]); } PMG.Landmasses.Add(polygons); } Debug.Log(debugI++); // WaterBody Lines: [polygonIds] PMG.WaterBodies.Clear(); for (int i = 0; i < n_waterBodies; i++) { string[] attributes = lines[lineIndex++].Split(';'); List <GraphPolygon> polygons = new List <GraphPolygon>(); string[] polygonIds = attributes[0].Split(','); foreach (string s in polygonIds) { polygons.Add(polygonMap[int.Parse(s)]); } PMG.WaterBodies.Add(polygons); } Debug.Log(debugI++); // Continent Lines: [polygonIds] PMG.Continents.Clear(); for (int i = 0; i < n_continents; i++) { string[] attributes = lines[lineIndex++].Split(';'); List <GraphPolygon> polygons = new List <GraphPolygon>(); string[] polygonIds = attributes[0].Split(','); foreach (string s in polygonIds) { polygons.Add(polygonMap[int.Parse(s)]); } PMG.Continents.Add(polygons); } return(PMG.DrawMap()); }
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(); } }
public bool PolygonClip(GraphPolygon Source, ref GraphPolygon Result, GraphPolygon Clipper) { if ((Clipper.Count < 3) || (Clipper.Convexity == -1)) { MessageBox.Show("Polygon is not convex"); return(false); } int i, j; PointD R; PointD S = new PointD(), F = new PointD(); List <PointD> TempSource = new List <PointD>(); if (curstep == 0) { Result.Vertexes = new List <PointD>(Source.Vertexes); } Result.Closed = true; int t; if (Clipper.Square > 0) { t = 1; } else { t = -1; } Clipper.Add(Clipper[0]); for (i = curstep; i < Clipper.Count - 1; i++) { TempSource.Clear(); F = Result.Vertexes[0]; // end vertex S = Result.Vertexes[0]; // begin vertex if (Geom2d.Square(S, Clipper.Vertexes[i], Clipper.Vertexes[i + 1]) * t >= 0) { TempSource.Add(S); } for (j = 1; j < Result.Vertexes.Count; j++) { if (Geom2d.SegmentLineIntersect(S, Result.Vertexes[j], Clipper.Vertexes[i], Clipper.Vertexes[i + 1], out R) == 1) { TempSource.Add(R); } S = Result.Vertexes[j]; if (Geom2d.Square(S, Clipper.Vertexes[i], Clipper.Vertexes[i + 1]) * t > 0) { TempSource.Add(S); } } if ((TempSource.Count != 0) && (Geom2d.SegmentLineIntersect(S, F, Clipper.Vertexes[i], Clipper.Vertexes[i + 1], out R) == 1)) { TempSource.Add(R); } Result.Clear(); Result.Vertexes.AddRange(TempSource); if (step) { curstep = i + 1; step = false; Clipper.RemoveLast(); return(true); } } Clipper.RemoveLast(); return(true); }
public KargerGraphVertex(GraphPolygon poly) { ContainedPolygons.Add(poly); }