using Assets.Common; using Assets.Map; using Assets.Voronoi; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; 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 CityField { public List boundary; } public class City { public Graph startGraph; public List sites; public Graph roads = new Graph(); public Graph fieldBoundaries = new Graph(); public List fields = new List(); public List<(int, int)> edges; public City(Graph startGraph) { this.startGraph = startGraph; this.sites = new List(); this.edges = new List<(int, int)>(); } public City(Graph startGraph, List sitesList) : this(startGraph) { this.sites = sitesList; } public void AddSite(Site site) { sites.Add(site); FixBoundaryEdges(site); } private void FixBoundaryEdges(Site site) { var a = edges; var b = site.Edges.Select(x => x.Item1 < x.Item2 ? x : (x.Item2, x.Item1)); edges = a.Union(b).Except(a.Intersect(b)).ToList(); } public void CreateRoadGraph(IList points) { var pointsForRoads = sites.SelectMany(site => site.Vertices.Select(i => (i, points[i])).Append((-1, site.Point))).Distinct().ToList(); var mapping = pointsForRoads.Select((x, i) => x.Item1 == -1 ? (-1, -1) : (i, x.Item1)).Where(x => x.Item1 != -1).ToDictionary(x => x.Item2, x => x.Item1); VoronoiGenerator generator = new VoronoiGenerator(pointsForRoads.Select(x => x.Item2)); generator.Generate(); roads = generator.Delaunay.Morph(s => s.Point); fieldBoundaries = new Graph { Vertices = roads.Vertices, Edges = edges.Select(e => (mapping[e.Item1], mapping[e.Item2])) }; } } [Serializable] public class GraphGeneratorDebug { public bool displayVertices = false; public bool displayCrossroads = true; public bool displayEdges = false; public bool displayCities = true; public bool displayCityRoads = true; public bool displayFieldBoundaries = 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 List verticesForRoads; private List verticesForCitites; public GraphGeneratorDebug debug; 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); } public bool IsPointInsideCity(City city, Point point) { return city.sites.Any(s => PointUtils.IsPointInside(point, s.Vertices.Select(i => _voronoiGraph[i]).ToArray())); } 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); var edges = newCity.roads.Edges.ToList(); foreach (var (a, b) in edges) { var (va, vb) = (newCity.roads[a], newCity.roads[b]); var center = (va + vb) / 2; if (!IsPointInsideCity(newCity, center)) newCity.roads.DeleteEdge(a, b); } ConnectPointsIntoRoads(newCity); CreateFieldBoundaries(newCity); 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; } void ConnectPointsIntoRoads(City city) { var original = city.roads.Morph(s => s, e => Point.Dist(city.roads[e.Item1], city.roads[e.Item2])); //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. var edges = original.Edges.OrderByDescending(e => original.GetEdgeData(e)); Graph roads = new Graph() { Vertices = city.roads.Vertices }; foreach (var edge in original.Edges) { roads.AddEdge(edge.Item1, edge.Item2); if (Graph.HasCycle(roads)) roads.DeleteEdge(edge.Item1, edge.Item2); if (roads.Edges.Count() == roads.Vertices.Count() - 1) break; } city.roads = roads; } private void CreateFieldBoundaries(City city) { foreach (var (a, b) in city.roads.Edges) city.fieldBoundaries.AddEdge(a, b); } 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(); } public void Reset() { var graphGenerator = GetComponent(); _random = new System.Random(graphGenerator.seed); cities = new List(); } public void NewCity() { cities.Add(CreateCity(LocateCities()[_random.Next(0, 10)], _random.Next(0, 10))); } public void Generate() { var graphGenerator = GetComponent(); graphGenerator.Generate(); _basicGraph = graphGenerator.VoronoiGenerator.Delaunay.Morph(l => l); _voronoiGraph = graphGenerator.VoronoiGenerator.Voronoi; _locationGraph = graphGenerator.LocationGenerator.Result; verticesForCitites = LocateCities(); foreach (var index in verticesForCitites) { 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()); var offset = Vector3.right; foreach (var (v, i) in vertices.Select((x, i) => (x, i))) { Gizmos.DrawSphere(v, 1); } } private void DisplayGraphCrossroads(Graph graph, List vertices) { //foreach (var v in vertices.Select(i => graph.Vertices[i].ToVector3())) //{ // Gizmos.DrawSphere(v, 1); //} } private void DisplayGraphCities(List cities) { foreach (City city in cities) { Gizmos.color = Color.magenta; foreach (var (a, b) in city.edges) { Gizmos.DrawLine(_voronoiGraph.Vertices[a].ToVector3(), _voronoiGraph.Vertices[b].ToVector3()); } foreach (var a in city.sites) Gizmos.DrawSphere(a.Point.ToVector3(), 1); } } private void DisplayGraphEdges(Graph graph) { var edges = graph.Edges .Select(edge => (graph.Vertices[edge.Item1], graph.Vertices[edge.Item2])) .Select(edge => (edge.Item1.ToVector3(), edge.Item2.ToVector3())); foreach (var (s, e) in edges) Gizmos.DrawLine(s, e); } private void OnDrawGizmos() { if (debug.displayVertices) { Gizmos.color = Color.white; DisplayGraphVertices(_basicGraph.Morph(l => l.Point)); } if (debug.displayEdges) { Gizmos.color = Color.white; DisplayGraphEdges(_basicGraph.Morph(l => l.Point)); } if (debug.displayCrossroads) { Gizmos.color = Color.magenta; DisplayGraphCrossroads(_basicGraph.Morph(l => l.Point), verticesForRoads); } if (debug.displayCities) { Gizmos.color = Color.magenta; DisplayGraphCities(cities); } if (debug.displayFieldBoundaries) { Gizmos.color = Color.white; foreach (var city in cities) DisplayGraphEdges(city.fieldBoundaries); } if (debug.displayCityRoads) { Gizmos.color = Color.green; foreach (var city in cities) { DisplayGraphEdges(city.roads); DisplayGraphVertices(city.roads); } } if (debug.displayWorldRoads) { // Gizmos.color = new Color(0.254f, 0.127f, 0.156f); Gizmos.color = Color.blue; DisplayGraphEdges(mainRoads); } } } }