208 lines
7.5 KiB
C#
208 lines
7.5 KiB
C#
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);
|
|
}
|
|
}
|
|
} |