using System.Collections.Generic; using System.Linq; using Assets.Cities; using Assets.Common; using Assets.Voronoi; namespace Assets.AnnotationPass { public class CityFieldsPass : IAnnotationPass { private System.Random _random; private Graph<Point> _voronoiGraph; private Graph<Location> _locationGraph; private Graph<MapSite> _basicGraph; private List<int> verticesForRoads; private List<int> verticesForCitites; List<City> cities = new List<City>(); Graph<MapSite> roads = new Graph<MapSite>(); Graph<Point> mainRoads = new Graph<Point>(); double minimumAngle; List<int> CountProbablitityOfCity(List<int> vertices) { List<int> probabilities = new List<int>(); vertices.ForEach(v => probabilities.Add((v % 2 == 0 ? _random.Next(0, 50) : _random.Next(50, 100)))); return probabilities; } List<int> 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.Boundary.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<MapSite> { site }; var location = site.Metadata.GetProperty<Location>(LandmassPass.SiteLocationProperty); for (int i = 0; i < size - 1; i++) { var neighbours = _basicGraph.Neighbours(site.Index).Select(j => _basicGraph.Vertices[j]).Where(s => s.Metadata.GetProperty<Location>(LandmassPass.SiteLocationProperty) == 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); FixRoadsDensity(newCity, minimumAngle); CreateFieldBoundaries(newCity); return newCity; } List<int> ChoosePoints(int number) { var vertices = new List<int>(); for (int i = 0; i < number; i++) { var randomLocation = _locationGraph.Vertices[_random.Next(1, _locationGraph.Vertices.Count())]; var count = randomLocation.Sites.Count(); var 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<Point> roads = new Graph<Point>() { Vertices = city.roads.Vertices }; foreach (var edge in original.Edges) { roads.AddEdge(edge.Item1, edge.Item2); if (Graph<Point>.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); var deadEnds = city.fieldBoundaries.Vertices.Select((_, i) => i).Where(i => city.fieldBoundaries.Neighbours(i).Count() == 1); foreach (var deadEnd in deadEnds) { var neighbour = city.fieldBoundaries.Neighbours(deadEnd).First(); var closest = city.fieldBoundaries.Vertices .Select((_, i) => i) .OrderBy(i => Point.Dist(city.fieldBoundaries[i], city.fieldBoundaries[deadEnd])) .Skip(1) .First(c => c != neighbour); city.fieldBoundaries.AddEdge(deadEnd, closest); city.roads.AddEdge(deadEnd, closest); } } private void FixRoadsDensity(City city, double angle = 0.25f) { // dla każdego punktu w grafie dróg // chyba że ma 2 to pomiń? // wyznacz punkt 0 // oblicz odległości kątowe do wszystkich punktów // posortuj je // między wszystkimiu parami oblicz kąt // potem według jakiegoś parametru usuń te, które są za bliskie for (int i = 0; i < city.roads.Vertices.Count(); i++) { var p = city.roads[i]; if (city.roads.Neighbours(i).Count() <= 2) continue; var reference = new Point(p.x, p.y + 30); var orderedNeighours = city.roads.Neighbours(i) .Select(n => (Vertex: n, Point: city.roads[n])) .Select(x => (Vertex: x.Vertex, Angle: PointUtils.AngleBetween(p, reference, x.Point))) .OrderBy(x => x.Angle) .Select(x => x.Vertex); foreach (var (a, b) in orderedNeighours.RotateRight(1).Zip(orderedNeighours, (a, b) => (a, b))) { if( PointUtils.AngleBetween(p, city.roads[a], city.roads[b]) < angle) { city.roads.DeleteEdge(i, a); } } } } void CreateWorldRoadGraph() { roads.Vertices = verticesForCitites.Select(i => _basicGraph.Vertices[i]).ToList(); VoronoiGenerator generator = new VoronoiGenerator(roads.Vertices.Select(s => s.Center).ToList()); generator.Generate(); roads.Edges = generator.Delaunay.Edges; } void AddLanesToRoads() { // ergo dodaj randomowe połączenia xd } public void Annotate(Map map) { _basicGraph = map.Sites.Clone() as Graph<MapSite>; _voronoiGraph = map.Boundaries; _locationGraph = map.Metadata.GetProperty<Graph<Location>>(LandmassPass.MapLocationsProperty); verticesForCitites = LocateCities(); foreach (var index in verticesForCitites) { cities.Add(CreateCity(index, _random.Next(0, 10))); } CreateWorldRoadGraph(); mainRoads = roads.Morph(s => s.Center); } } }