generating city fields

This commit is contained in:
veniti 2019-09-10 20:55:37 +02:00
parent 0e0c591cf3
commit ceb20fcba1
12 changed files with 161 additions and 53 deletions

View File

@ -6,7 +6,7 @@ using System.Linq;
namespace Assets.Map
{
[Serializable]
public class Graph<T>
public class Graph<T> : ICloneable
{
public List<T> Vertices { get; internal set; } = new List<T>();
private Dictionary<int, List<int>> _edges = new Dictionary<int, List<int>>();
@ -114,6 +114,11 @@ namespace Assets.Map
return _edges[vertex];
}
virtual public object Clone()
{
return Morph(l => l);
}
}
public class Graph<T, E> : Graph<T>
@ -153,5 +158,10 @@ namespace Assets.Map
return result;
}
public override object Clone()
{
return Morph(l => l, l => l);
}
}
}

8
Assets/SampleScenes.meta Normal file
View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 3d8829ac873af27488b1412b8db301ab
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 35e5b60069b1d4945af0cbaefb902d02
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 92a62c583a2e8734d9a6018eec4d27fb
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 7ff1b560cd403d249b3359b432be2796
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4014c75d332963b40a9724fb931da32e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 26c0bbb8e6792b546bf28bbe08d7c96d
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c3ed31e3b158a9e4994bd4c23c947afb
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -26,50 +26,65 @@ namespace Assets.Scripts
public int GetHashCode(Point obj)
{
return 0;
return obj.GetHashCode();
}
}
public class CityField
{
public List<Point> boundary;
}
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 Graph<Voronoi.Site> startGraph;
public List<Voronoi.Site> sites;
public Graph<Point> roads = new Graph<Point>();
public Graph<Point> fieldBoundaries = new Graph<Point>();
public List<CityField> fields = new List<CityField>();
public List<(int, int)> edges;
public City(Graph<Site> startGraph)
{
this.startGraph = startGraph;
this.sitesList = new List<Voronoi.Site>();
this.boundariesEdges = new List<(int, int)>();
this.sites = new List<Voronoi.Site>();
this.edges = new List<(int, int)>();
}
public City(Graph<Site> startGraph, List<Site> sitesList) : this(startGraph)
{
this.sitesList = sitesList;
this.sites = sitesList;
}
public void AddSite(Site site)
{
sitesList.Add(site);
sites.Add(site);
FixBoundaryEdges(site);
}
private void FixBoundaryEdges(Site site)
{
var a = boundariesEdges;
var a = edges;
var b = site.Edges.Select(x => x.Item1 < x.Item2 ? x : (x.Item2, x.Item1));
boundariesEdges = a.Union(b).Except(a.Intersect(b)).ToList();
edges = 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(2)).ToList());
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, e => Point.Dist(generator.Delaunay.Vertices[e.Item1].Point, generator.Delaunay.Vertices[e.Item2].Point));
roads = generator.Delaunay.Morph(s => s.Point);
fieldBoundaries = new Graph<Point> { Vertices = roads.Vertices, Edges = edges.Select(e => (mapping[e.Item1], mapping[e.Item2])) };
}
}
@ -81,7 +96,7 @@ namespace Assets.Scripts
public bool displayEdges = false;
public bool displayCities = true;
public bool displayCityRoads = true;
public bool displayCityMainRoads = true;
public bool displayFieldBoundaries = true;
public bool displayWorldRoads = false;
}
@ -95,9 +110,9 @@ namespace Assets.Scripts
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<Site> roads = new Graph<Site>();
Graph<Point> mainRoads = new Graph<Point>();
List<int> CountProbablitityOfCity(List<int> vertices)
@ -115,6 +130,11 @@ namespace Assets.Scripts
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);
@ -136,7 +156,20 @@ namespace Assets.Scripts
}
newCity.CreateRoadGraph(_voronoiGraph.Vertices);
newCity.mainRoad = ConnectPointsIntoRoads(newCity.roads);
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;
}
@ -154,28 +187,35 @@ namespace Assets.Scripts
return vertices;
}
Graph<Point> ConnectPointsIntoRoads(Graph<Point, double> graph)
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.
graph.Edges = graph.Edges.OrderBy(e => graph.GetEdgeData(e));
graph._edgeData = graph._edgeData.OrderByDescending(e => e.Value).ToDictionary(e => e.Key, k => k.Value);
var edges = original.Edges.OrderByDescending(e => original.GetEdgeData(e));
Graph<Point> spanningTree = new Graph<Point>();
spanningTree.Vertices = graph.Vertices;
Graph<Point> roads = new Graph<Point>() { Vertices = city.roads.Vertices };
foreach (var edge in graph.Edges)
foreach (var edge in original.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)
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;
}
return spanningTree;
city.roads = roads;
}
private void CreateFieldBoundaries(City city)
{
foreach (var (a, b) in city.roads.Edges)
city.fieldBoundaries.AddEdge(a, b);
}
void CreateWorldRoadGraph()
@ -250,10 +290,10 @@ namespace Assets.Scripts
private void DisplayGraphCrossroads(Graph<Point> graph, List<int> vertices)
{
foreach (var v in vertices.Select(i => graph.Vertices[i].ToVector3()))
{
Gizmos.DrawSphere(v, 1);
}
//foreach (var v in vertices.Select(i => graph.Vertices[i].ToVector3()))
//{
// Gizmos.DrawSphere(v, 1);
//}
}
@ -262,12 +302,12 @@ namespace Assets.Scripts
foreach (City city in cities)
{
Gizmos.color = Color.magenta;
foreach (var (a, b) in city.boundariesEdges)
foreach (var (a, b) in city.edges)
{
Gizmos.DrawLine(_voronoiGraph.Vertices[a].ToVector3(), _voronoiGraph.Vertices[b].ToVector3());
}
foreach (var a in city.sitesList)
foreach (var a in city.sites)
Gizmos.DrawSphere(a.Point.ToVector3(), 1);
}
}
@ -281,6 +321,7 @@ namespace Assets.Scripts
foreach (var (s, e) in edges) Gizmos.DrawLine(s, e);
}
private void OnDrawGizmos()
{
@ -308,9 +349,18 @@ namespace Assets.Scripts
DisplayGraphCities(cities);
}
if (debug.displayFieldBoundaries)
{
Gizmos.color = Color.white;
foreach (var city in cities)
DisplayGraphEdges(city.fieldBoundaries);
}
if (debug.displayCityRoads)
{
Gizmos.color = Color.blue;
Gizmos.color = Color.green;
foreach (var city in cities)
{
DisplayGraphEdges(city.roads);
@ -318,15 +368,7 @@ namespace Assets.Scripts
}
}
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);

View File

@ -21,7 +21,7 @@ namespace Assets
public GameObject forest;
private Random _random = new Random();
private List<LocationSite> _sites;
private List<Site> _sites;
private GraphGenerator Generator => GetComponent<GraphGenerator>();
@ -39,17 +39,17 @@ namespace Assets
private void PlaceTrees()
{
foreach (LocationSite site in _sites)
foreach (Site site in _sites)
{
var sampler = new PoissonDiskSampler(radius) { Generator = _random };
Bounding bounding =
BoundingTools.GetBounding(site.Site.Vertices.Select(i => Generator.BoundariesGraph.Vertices[i]));
BoundingTools.GetBounding(site.Vertices.Select(i => Generator.BoundariesGraph.Vertices[i]));
var offset = Vector3.up * site.Location.Type.height;
var points = sampler.Generate((float)bounding.Width, (float)bounding.Height);
foreach (var point in points.Select(point => point + bounding.Min).Where(point => IsPointInSite(site.Site, point)))
foreach (var point in points.Select(point => point + bounding.Min).Where(point => IsPointInSite(site, point)))
{
PlaceRandomTree(point.ToVector3() + offset);
}
@ -70,7 +70,7 @@ namespace Assets
private void FindForestSites()
{
_sites = new List<LocationSite>();
_sites = new List<Site>();
foreach (var location in Generator.LocationGraph.Vertices.Skip(1))
{

View File

@ -104,7 +104,7 @@ namespace Assets
private void GenerateLocationMesh(Location location, IList<Point> points)
{
foreach (var vertices in location.Sites.Select(site => site.Edges.Select(x => points[x.Item1]).Reverse()))
foreach (var vertices in location.Sites.Select(site => site.Edges.Select(x => x.Item1).Reverse()))
{
int start = _vertices.Count;

View File

@ -59,7 +59,7 @@ namespace Assets.Voronoi
public bool Done => Queue.Count == 0;
public VoronoiGenerator(IList<Point> sites)
public VoronoiGenerator(IEnumerable<Point> sites)
{
int i = 0;
Sites = sites.Select(x => new Site(x, i++)).ToList();