inz-00/Assets/Scripts/CityGenerator.cs
2019-09-09 18:51:56 +02:00

338 lines
11 KiB
C#

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<Point>
{
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<Voronoi.Site> startGraph { get; set; }
public List<Voronoi.Site> sitesList { get; set; }
public Graph<Point, double> roads { get; set; } = new Graph<Point, double>();
public List<(int, int)> boundariesEdges { get; set; }
public Graph<Point> mainRoad { get; set; } = new Graph<Point>();
public City(Graph<Site> startGraph)
{
this.startGraph = startGraph;
this.sitesList = new List<Voronoi.Site>();
this.boundariesEdges = new List<(int, int)>();
}
public City(Graph<Site> startGraph, List<Site> sitesList) : this(startGraph)
{
this.sitesList = sitesList;
}
public void AddSite(Site site)
{
sitesList.Add(site);
FixBoundaryEdges(site);
}
private void FixBoundaryEdges(Site site)
{
var a = boundariesEdges;
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<Point> points)
{
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
{
public bool displayVertices = false;
public bool displayCrossroads = true;
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<Point> _voronoiGraph;
private Graph<Location> _locationGraph;
private Graph<Site> _basicGraph;
private List<int> verticesForRoads;
private List<int> verticesForCitites;
public GraphGeneratorDebug debug;
public City city;
public List<City> cities = new List<City>();
Graph<Site, double> roads = new Graph<Site, double>();
Graph<Point> mainRoads = new Graph<Point>();
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);
}
City CreateCity(int vertex, int size)
{
City newCity = new City(_basicGraph);
var site = _basicGraph.Vertices[vertex];
var sites = new List<Site>() { 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<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();
Site randomPoint;
randomPoint = randomLocation.Sites[_random.Next(0, count)];
vertices.Add(randomPoint.Index);
}
return vertices;
}
Graph<Point> ConnectPointsIntoRoads(Graph<Point, double> 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<Point> spanningTree = new Graph<Point>();
spanningTree.Vertices = graph.Vertices;
foreach (var edge in graph.Edges)
{
spanningTree.AddEdge(edge.Item1, edge.Item2);
if (Graph<Point>.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();
}
public void Reset()
{
var graphGenerator = GetComponent<GraphGenerator>();
_random = new System.Random(graphGenerator.seed);
cities = new List<City>();
}
public void NewCity()
{
cities.Add(CreateCity(LocateCities()[_random.Next(0, 10)], _random.Next(0, 10)));
}
public void Generate()
{
var graphGenerator = GetComponent<GraphGenerator>();
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<Point> 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<Point> graph, List<int> vertices)
{
foreach (var v in vertices.Select(i => graph.Vertices[i].ToVector3()))
{
Gizmos.DrawSphere(v, 1);
}
}
private void DisplayGraphCities(List<City> cities)
{
foreach (City city in cities)
{
Gizmos.color = Color.magenta;
foreach (var (a, b) in city.boundariesEdges)
{
Gizmos.DrawLine(_voronoiGraph.Vertices[a].ToVector3(), _voronoiGraph.Vertices[b].ToVector3());
}
foreach (var a in city.sitesList)
Gizmos.DrawSphere(a.Point.ToVector3(), 1);
}
}
private void DisplayGraphEdges(Graph<Point> 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.displayCityRoads)
{
Gizmos.color = Color.blue;
foreach (var city in cities)
{
DisplayGraphEdges(city.roads);
DisplayGraphVertices(city.roads);
}
}
if (debug.displayCityMainRoads)
{
Gizmos.color = new Color(2.54f, 0.127f, 1.56f);
//Gizmos.color = Color.green;
foreach (var city in cities)
{
DisplayGraphEdges(city.mainRoad);
}
}
if (debug.displayWorldRoads)
{
// Gizmos.color = new Color(0.254f, 0.127f, 0.156f);
Gizmos.color = Color.blue;
DisplayGraphEdges(mainRoads);
}
}
}
}