using System; using System.Collections.Generic; using System.Linq; using Assets.Cities; using Assets.Common; namespace Assets.AnnotationPass { public class LocateCitiesPass : IAnnotationPass { public const string CitiesProperty = "Cities"; public const string CityProperty = "City"; public int MaxCitiesCount { get; set; } = 20; public int MaxPlacementTries { get; set; } = 30; public float RangeOfInfluence { get; set; } = .5f; public float CitiesSize { get; set; } = .5f; private Graph _locationGraph; private Graph _basicGraph; private Random _random; public LocateCitiesPass(Random random = null) { _random = random ?? new Random(); } City CreateCity(int vertex, int size) { City newCity = new City(); var site = _basicGraph.Vertices[vertex]; newCity.Center = site.Center.Clone() as Point; site.Tags.Add("City.Center"); var sites = new List { site }; var location = site.Metadata.GetProperty(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(LandmassPass.SiteLocationProperty) == location).ToList(); site = neighbours[_random.Next(neighbours.Count)]; sites.Add(site); } foreach (var s in sites.Distinct()) { newCity.AddSite(s); s.Metadata.SetProperty(CityProperty, newCity); } newCity.RangeOfInfluence = GetCityRangeOfInfluence(newCity); return newCity; } List CreateCities() { List cities = new List(); var vertices = new List(); int guard = MaxPlacementTries; int createdCitiesCount = 0; while (createdCitiesCount < MaxCitiesCount && guard-- > 0) { var randomLocation = _locationGraph.Vertices.Skip(1).RandomElement(_random); var randomSite = randomLocation.Sites.RandomElement(_random); if (cities.Any(c => Point.Dist(PointUtils.Mean(c.Sites.Select(s => s.Center)), randomSite.Center) < c.RangeOfInfluence)) continue; cities.Add(CreateCity(randomSite.Index, (int)Math.Ceiling(CitiesSize * _random.Next(0, 30)))); guard = MaxPlacementTries; createdCitiesCount++; } return cities; } public float GetCityRangeOfInfluence(City city) { return RangeOfInfluence*city.Sites.Count; } public void Annotate(Map map) { _basicGraph = map.Sites.Clone() as Graph; _locationGraph = map.Metadata.GetProperty>(LandmassPass.MapLocationsProperty); var cities = CreateCities(); map.Metadata.SetProperty(CitiesProperty, cities); } } }