From 56690bbb75394f8595eeaa19e495ec77cf99c084 Mon Sep 17 00:00:00 2001 From: veniti Date: Mon, 9 Sep 2019 18:51:56 +0200 Subject: [PATCH] generating main roads in cities --- Assets/Common/Point.cs | 9 ++ Assets/Editor/CityGeneratorUI.cs | 1 + Assets/Map/Graph.cs | 106 +++++++++++++- Assets/Scripts/CityGenerator.cs | 244 +++++++++++++++++++------------ 4 files changed, 266 insertions(+), 94 deletions(-) diff --git a/Assets/Common/Point.cs b/Assets/Common/Point.cs index 661eebf..6f439f9 100644 --- a/Assets/Common/Point.cs +++ b/Assets/Common/Point.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; namespace Assets.Common { @@ -17,5 +18,13 @@ namespace Assets.Common { return Math.Sqrt(Math.Pow(a.x - b.x, 2) + Math.Pow(a.y - b.y, 2)); } + + public override int GetHashCode() + { + var hashCode = 1502939027; + hashCode = hashCode * -1521134295 + x.GetHashCode(); + hashCode = hashCode * -1521134295 + y.GetHashCode(); + return hashCode; + } } } \ No newline at end of file diff --git a/Assets/Editor/CityGeneratorUI.cs b/Assets/Editor/CityGeneratorUI.cs index dbceb94..b5f2e71 100644 --- a/Assets/Editor/CityGeneratorUI.cs +++ b/Assets/Editor/CityGeneratorUI.cs @@ -34,6 +34,7 @@ namespace Assets.Editor generator.NewCity(); } + } } } diff --git a/Assets/Map/Graph.cs b/Assets/Map/Graph.cs index 4b84597..35f7646 100644 --- a/Assets/Map/Graph.cs +++ b/Assets/Map/Graph.cs @@ -1,3 +1,4 @@ +using Assets.Common; using System; using System.Collections.Generic; using System.Linq; @@ -8,7 +9,7 @@ namespace Assets.Map public class Graph { public List Vertices { get; internal set; } = new List(); - public Dictionary> _edges = new Dictionary>(); + private Dictionary> _edges = new Dictionary>(); public IEnumerable<(int, int)> Edges { @@ -38,11 +39,69 @@ namespace Assets.Map _edges[b].Add(a); } - public Graph Morph(Func morph) + public void DeleteEdge(int a, int b) + { + if (!_edges.ContainsKey(a)) + return; + + if (!_edges.ContainsKey(b)) + return; + + _edges[a].Remove(b); + _edges[b].Remove(a); + } + + public static bool HasCycle(Graph graph) + { + int Find(int[] parent, int i) + { + if (parent[i] == -1) + return i; + + return Find(parent, parent[i]); + } + + void Union(int[] parent, int x, int y) + { + int xset = Find(parent, x); + int yset = Find(parent, y); + parent[xset] = yset; + } + + int[] trueParent = new int[graph.Vertices.Count()]; + + for (int i = 0; i < graph.Vertices.Count(); ++i) + trueParent[i] = -1; + + for (int i = 0; i < graph.Edges.Count(); ++i) + { + int x = Find(trueParent, graph.Edges.ElementAt(i).Item1); + int y = Find(trueParent, graph.Edges.ElementAt(i).Item2); + + if (x == y) + return true; + + Union(trueParent, x, y); + } + + return false; + } + + public Graph Morph(Func vertices) { var result = new Graph() { Edges = Edges }; - - result.Vertices.AddRange(Vertices.Select(morph)); + + result.Vertices.AddRange(Vertices.Select(vertices)); + + return result; + } + + public Graph Morph(Func vertices, Func<(int, int), E> edges) + { + var result = new Graph() { Edges = Edges }; + + result._edgeData = Edges.ToDictionary(k => k, v => edges(v)); + result.Vertices.AddRange(Vertices.Select(vertices)); return result; } @@ -54,4 +113,43 @@ namespace Assets.Map return _edges[vertex]; } } + + public class Graph : Graph + { + internal Dictionary<(int, int), E> _edgeData = new Dictionary<(int, int), E>(); + + public void AddEdge(int a, int b, E data) + { + AddEdge(a, b); + SetEdgeData((a, b), data); + } + + public void SetEdgeData((int, int) edge, E data) + { + var (a, b) = edge; + + if (a > b) (a, b) = (b, a); + + _edgeData[(a, b)] = data; + } + + public E GetEdgeData((int, int) edge) + { + var (a, b) = edge; + + if (a > b) (a, b) = (b, a); + + return _edgeData[(a, b)]; + } + + public Graph Morph(Func vertices, Func edges) + { + var result = new Graph() { Edges = Edges }; + + result._edgeData = _edgeData.ToDictionary(e => e.Key, e => edges(e.Value, e.Key)); + result.Vertices.AddRange(Vertices.Select(vertices)); + + return result; + } + } } \ No newline at end of file diff --git a/Assets/Scripts/CityGenerator.cs b/Assets/Scripts/CityGenerator.cs index 8fcdb78..ee444d5 100644 --- a/Assets/Scripts/CityGenerator.cs +++ b/Assets/Scripts/CityGenerator.cs @@ -10,12 +10,33 @@ using UnityEngine; namespace Assets.Scripts { + public class PointProximityComparer : IEqualityComparer + { + double _threshold; + + public PointProximityComparer(double threshold = 0.2) + { + _threshold = threshold; + } + + public bool Equals(Point x, Point y) + { + return Point.Dist(x, y) < _threshold; + } + + public int GetHashCode(Point obj) + { + return obj.GetHashCode(); + } + } + public class City { public Graph startGraph { get; set; } public List sitesList { get; set; } - public Graph roads { get; set; } = new Graph(); + public Graph roads { get; set; } = new Graph(); public List<(int, int)> boundariesEdges { get; set; } + public Graph mainRoad { get; set; } = new Graph(); public City(Graph startGraph) { @@ -38,19 +59,20 @@ namespace Assets.Scripts private void FixBoundaryEdges(Site site) { var a = boundariesEdges; - var b = site.Edges.Select(x => x.Item1 < x.Item2 ? x : (x.Item2, x.Item1)); + var b = site.Edges.Select(x => x.Item1 < x.Item2 ? x : (x.Item2, x.Item1)); boundariesEdges = a.Union(b).Except(a.Intersect(b)).ToList(); } public void CreateRoadGraph(IList points) { - roads = new Graph - { - Vertices = sitesList.SelectMany(site => site.Vertices.Select(i => points[i]).Append(site.Point)).ToList(), - }; + VoronoiGenerator generator = new VoronoiGenerator(sitesList.SelectMany(site => site.Vertices.Select(i => points[i]).Append(site.Point)).Distinct(new PointProximityComparer(4)).ToList()); + generator.Generate(); + + roads = generator.Delaunay.Morph(s => s.Point, e => Point.Dist(generator.Delaunay.Vertices[e.Item1].Point, generator.Delaunay.Vertices[e.Item2].Point)); } } + [Serializable] public class GraphGeneratorDebug { @@ -59,20 +81,125 @@ namespace Assets.Scripts public bool displayEdges = false; public bool displayCities = true; public bool displayCityRoads = true; + public bool displayCityMainRoads = true; + public bool displayWorldRoads = false; } + [RequireComponent(typeof(GraphGenerator))] public class CityGenerator : MonoBehaviour { private System.Random _random; private Graph _voronoiGraph; private Graph _locationGraph; - private Graph _basicGraph; + private Graph _basicGraph; private List verticesForRoads; private List verticesForCitites; public GraphGeneratorDebug debug; public City city; public List cities = new List(); - + Graph roads = new Graph(); + Graph mainRoads = new Graph(); + + List CountProbablitityOfCity(List vertices) + { + List probabilities = new List(); + vertices.ForEach(v => probabilities.Add((v % 2 == 0 ? _random.Next(0, 50) : _random.Next(50, 100)))); + return probabilities; + } + + List LocateCities() + { + // var verticesForCitites = CountProbablitityOfCity(ChoosePoints(20)); + // verticesForCitites.Sort(); + // return verticesForCitites.GetRange(0, 10); + return ChoosePoints(20); + } + + City CreateCity(int vertex, int size) + { + City newCity = new City(_basicGraph); + var site = _basicGraph.Vertices[vertex]; + var sites = new List() { site }; + var location = site.Location; + + for (int i = 0; i < size - 1; i++) + { + location = site.Location; + var neighbours = _basicGraph.Neighbours(site.Index).Select(j => _basicGraph.Vertices[j]).Where(s => s.Location == location).ToList(); + site = neighbours[_random.Next(neighbours.Count)]; + sites.Add(site); + } + + foreach (var s in sites.Distinct()) + { + newCity.AddSite(s); + } + + newCity.CreateRoadGraph(_voronoiGraph.Vertices); + newCity.mainRoad = ConnectPointsIntoRoads(newCity.roads); + return newCity; + } + + List ChoosePoints(int number) + { + var vertices = new List(); + for (int i = 0; i < number; i++) + { + var randomLocation = _locationGraph.Vertices[_random.Next(1, _locationGraph.Vertices.Count())]; + var count = randomLocation.Sites.Count(); + Site randomPoint; + randomPoint = randomLocation.Sites[_random.Next(0, count)]; + vertices.Add(randomPoint.Index); + } + return vertices; + } + + Graph ConnectPointsIntoRoads(Graph graph) + { + + //1.Sort all the edges in non - decreasing order of their weight. + //2.Pick the smallest edge.Check if it forms a cycle with the spanning tree formed so far. If cycle is not formed, include this edge.Else, discard it. + //3.Repeat step#2 until there are (V-1) edges in the spanning tree. + + graph.Edges = graph.Edges.OrderBy(e => graph.GetEdgeData(e)); + graph._edgeData = graph._edgeData.OrderBy(e => e.Value).ToDictionary(e => e.Key, k => k.Value); + + Graph spanningTree = new Graph(); + spanningTree.Vertices = graph.Vertices; + + foreach (var edge in graph.Edges) + { + spanningTree.AddEdge(edge.Item1, edge.Item2); + if (Graph.HasCycle(spanningTree)) + spanningTree.DeleteEdge(edge.Item1, edge.Item2); + if (spanningTree.Edges.Count() == spanningTree.Vertices.Count() - 1) + break; + } + return spanningTree; + } + + void CreateWorldRoadGraph() + { + roads.Vertices = verticesForCitites.Select(i => _basicGraph.Vertices[i]).ToList(); + + VoronoiGenerator generator = new VoronoiGenerator(roads.Vertices.Select(s => s.Point).ToList()); + generator.Generate(); + + roads.Edges = generator.Delaunay.Edges; + } + + + void AddLanesToRoads() + { + // ergo dodaj randomowe połączenia xd + } + + + + + // UI Methods + + public void Start() { Reset(); @@ -89,10 +216,8 @@ namespace Assets.Scripts public void NewCity() { cities.Add(CreateCity(LocateCities()[_random.Next(0, 10)], _random.Next(0, 10))); - } - public void Generate() { var graphGenerator = GetComponent(); @@ -106,7 +231,12 @@ namespace Assets.Scripts { cities.Add(CreateCity(index, _random.Next(0, 10))); } + + CreateWorldRoadGraph(); + mainRoads = roads.Morph(s => s.Point); + } + private void DisplayGraphVertices(Graph graph) { var vertices = graph.Vertices.Select(p => p.ToVector3()); @@ -115,7 +245,6 @@ namespace Assets.Scripts foreach (var (v, i) in vertices.Select((x, i) => (x, i))) { Gizmos.DrawSphere(v, 1); - } } @@ -183,92 +312,27 @@ namespace Assets.Scripts { Gizmos.color = Color.blue; foreach (var city in cities) + { + DisplayGraphEdges(city.roads); DisplayGraphVertices(city.roads); + } } - } - List CountProbablitityOfCity(List vertices) - { - List probabilities = new List(); - vertices.ForEach(v => probabilities.Add((v % 2 == 0 ? _random.Next(0, 50) : _random.Next(50, 100)))); - return probabilities; - } - - List LocateCities() - { - // var verticesForCitites = CountProbablitityOfCity(ChoosePoints(20)); - // verticesForCitites.Sort(); - // return verticesForCitites.GetRange(0, 10); - return ChoosePoints(20); - } - - City CreateCity(int vertex, int size) - { - City newCity = new City(_basicGraph); - var site = _basicGraph.Vertices[vertex]; - var sites = new List(); - var location = site.Location; - - for (int i = 0; i < size - 1; i++) + if (debug.displayCityMainRoads) { - location = site.Location; - var neighbours = _basicGraph.Neighbours(site.Index).Select(j => _basicGraph.Vertices[j]).Where(s => s.Location == location).ToList(); - site = neighbours[_random.Next(neighbours.Count)]; - sites.Add(site); + Gizmos.color = new Color(2.54f, 0.127f, 1.56f); + //Gizmos.color = Color.green; + foreach (var city in cities) + { + DisplayGraphEdges(city.mainRoad); + } } - - foreach (var s in sites.Distinct()) + if (debug.displayWorldRoads) { - newCity.AddSite(s); + // Gizmos.color = new Color(0.254f, 0.127f, 0.156f); + Gizmos.color = Color.blue; + DisplayGraphEdges(mainRoads); } - - newCity.CreateRoadGraph(_voronoiGraph.Vertices); - - return newCity; - } - - List ChoosePoints(int number) - { - var vertices = new List(); - for (int i = 0; i < number; i++) - { - var randomLocation = _locationGraph.Vertices[_random.Next(1, _locationGraph.Vertices.Count())]; - var count = randomLocation.Sites.Count(); - Site randomPoint; - randomPoint = randomLocation.Sites[_random.Next(0, count)]; - vertices.Add(randomPoint.Index); - } - return vertices; - } - - - List ChoosePoints(int number, Graph graph) - { - - var vertices = new List(); - for (int i = 0; i < number; i++) - { - var randomLocation = _locationGraph.Vertices[_random.Next(1, _locationGraph.Vertices.Count())]; - var count = randomLocation.Sites.Count(); - Site randomPoint; - randomPoint = randomLocation.Sites[_random.Next(0, count)]; - vertices.Add(randomPoint.Index); - } - return vertices; - } - - - - void ConnectPointIntoRoads(/*punkty*/) - { - // spanning tree - - - } - void AddLanesToRoads() - { - // ergo dodaj randomowe połączenia xd } } - } \ No newline at end of file