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 namespace Assets.Map
{ {
[Serializable] [Serializable]
public class Graph<T> public class Graph<T> : ICloneable
{ {
public List<T> Vertices { get; internal set; } = new List<T>(); public List<T> Vertices { get; internal set; } = new List<T>();
private Dictionary<int, List<int>> _edges = new Dictionary<int, List<int>>(); private Dictionary<int, List<int>> _edges = new Dictionary<int, List<int>>();
@ -114,6 +114,11 @@ namespace Assets.Map
return _edges[vertex]; return _edges[vertex];
} }
virtual public object Clone()
{
return Morph(l => l);
}
} }
public class Graph<T, E> : Graph<T> public class Graph<T, E> : Graph<T>
@ -153,5 +158,10 @@ namespace Assets.Map
return result; 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) public int GetHashCode(Point obj)
{ {
return 0; return obj.GetHashCode();
} }
} }
public class CityField
{
public List<Point> boundary;
}
public class City public class City
{ {
public Graph<Voronoi.Site> startGraph { get; set; } public Graph<Voronoi.Site> startGraph;
public List<Voronoi.Site> sitesList { get; set; } public List<Voronoi.Site> sites;
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<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) public City(Graph<Site> startGraph)
{ {
this.startGraph = 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) public City(Graph<Site> startGraph, List<Site> sitesList) : this(startGraph)
{ {
this.sitesList = sitesList; this.sites = sitesList;
} }
public void AddSite(Site site) public void AddSite(Site site)
{ {
sitesList.Add(site); sites.Add(site);
FixBoundaryEdges(site); FixBoundaryEdges(site);
} }
private void FixBoundaryEdges(Site 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)); 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) 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(); 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 displayEdges = false;
public bool displayCities = true; public bool displayCities = true;
public bool displayCityRoads = true; public bool displayCityRoads = true;
public bool displayCityMainRoads = true; public bool displayFieldBoundaries = true;
public bool displayWorldRoads = false; public bool displayWorldRoads = false;
} }
@ -95,9 +110,9 @@ namespace Assets.Scripts
private List<int> verticesForRoads; private List<int> verticesForRoads;
private List<int> verticesForCitites; private List<int> verticesForCitites;
public GraphGeneratorDebug debug; public GraphGeneratorDebug debug;
public City city;
public List<City> cities = new List<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>(); Graph<Point> mainRoads = new Graph<Point>();
List<int> CountProbablitityOfCity(List<int> vertices) List<int> CountProbablitityOfCity(List<int> vertices)
@ -115,6 +130,11 @@ namespace Assets.Scripts
return ChoosePoints(20); 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 CreateCity(int vertex, int size)
{ {
City newCity = new City(_basicGraph); City newCity = new City(_basicGraph);
@ -136,7 +156,20 @@ namespace Assets.Scripts
} }
newCity.CreateRoadGraph(_voronoiGraph.Vertices); 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; return newCity;
} }
@ -154,28 +187,35 @@ namespace Assets.Scripts
return vertices; 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. //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. //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. //3.Repeat step#2 until there are (V-1) edges in the spanning tree.
graph.Edges = graph.Edges.OrderBy(e => graph.GetEdgeData(e)); var edges = original.Edges.OrderByDescending(e => original.GetEdgeData(e));
graph._edgeData = graph._edgeData.OrderByDescending(e => e.Value).ToDictionary(e => e.Key, k => k.Value);
Graph<Point> spanningTree = new Graph<Point>(); Graph<Point> roads = new Graph<Point>() { Vertices = city.roads.Vertices };
spanningTree.Vertices = graph.Vertices;
foreach (var edge in graph.Edges) foreach (var edge in original.Edges)
{ {
spanningTree.AddEdge(edge.Item1, edge.Item2); roads.AddEdge(edge.Item1, edge.Item2);
if (Graph<Point>.HasCycle(spanningTree))
spanningTree.DeleteEdge(edge.Item1, edge.Item2); if (Graph<Point>.HasCycle(roads))
if (spanningTree.Edges.Count() == spanningTree.Vertices.Count() - 1) roads.DeleteEdge(edge.Item1, edge.Item2);
if (roads.Edges.Count() == roads.Vertices.Count() - 1)
break; 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() void CreateWorldRoadGraph()
@ -250,10 +290,10 @@ namespace Assets.Scripts
private void DisplayGraphCrossroads(Graph<Point> graph, List<int> vertices) private void DisplayGraphCrossroads(Graph<Point> graph, List<int> vertices)
{ {
foreach (var v in vertices.Select(i => graph.Vertices[i].ToVector3())) //foreach (var v in vertices.Select(i => graph.Vertices[i].ToVector3()))
{ //{
Gizmos.DrawSphere(v, 1); // Gizmos.DrawSphere(v, 1);
} //}
} }
@ -262,12 +302,12 @@ namespace Assets.Scripts
foreach (City city in cities) foreach (City city in cities)
{ {
Gizmos.color = Color.magenta; 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()); 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); Gizmos.DrawSphere(a.Point.ToVector3(), 1);
} }
} }
@ -281,6 +321,7 @@ namespace Assets.Scripts
foreach (var (s, e) in edges) Gizmos.DrawLine(s, e); foreach (var (s, e) in edges) Gizmos.DrawLine(s, e);
} }
private void OnDrawGizmos() private void OnDrawGizmos()
{ {
@ -308,9 +349,18 @@ namespace Assets.Scripts
DisplayGraphCities(cities); DisplayGraphCities(cities);
} }
if (debug.displayFieldBoundaries)
{
Gizmos.color = Color.white;
foreach (var city in cities)
DisplayGraphEdges(city.fieldBoundaries);
}
if (debug.displayCityRoads) if (debug.displayCityRoads)
{ {
Gizmos.color = Color.blue; Gizmos.color = Color.green;
foreach (var city in cities) foreach (var city in cities)
{ {
DisplayGraphEdges(city.roads); 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) if (debug.displayWorldRoads)
{ {
// Gizmos.color = new Color(0.254f, 0.127f, 0.156f); // Gizmos.color = new Color(0.254f, 0.127f, 0.156f);

View File

@ -21,7 +21,7 @@ namespace Assets
public GameObject forest; public GameObject forest;
private Random _random = new Random(); private Random _random = new Random();
private List<LocationSite> _sites; private List<Site> _sites;
private GraphGenerator Generator => GetComponent<GraphGenerator>(); private GraphGenerator Generator => GetComponent<GraphGenerator>();
@ -39,17 +39,17 @@ namespace Assets
private void PlaceTrees() private void PlaceTrees()
{ {
foreach (LocationSite site in _sites) foreach (Site site in _sites)
{ {
var sampler = new PoissonDiskSampler(radius) { Generator = _random }; var sampler = new PoissonDiskSampler(radius) { Generator = _random };
Bounding bounding = 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 offset = Vector3.up * site.Location.Type.height;
var points = sampler.Generate((float)bounding.Width, (float)bounding.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); PlaceRandomTree(point.ToVector3() + offset);
} }
@ -70,7 +70,7 @@ namespace Assets
private void FindForestSites() private void FindForestSites()
{ {
_sites = new List<LocationSite>(); _sites = new List<Site>();
foreach (var location in Generator.LocationGraph.Vertices.Skip(1)) 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) 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; int start = _vertices.Count;

View File

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