From 9b606119c4f683fb5f31e87bcb33d48d2478130d Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Sun, 10 Nov 2019 23:20:24 +0100 Subject: [PATCH] refaktoryzacja rozmieszczenia miast --- Assets/Editor/CityGeneratorUI.cs | 36 -- Assets/Editor/CityGeneratorUI.cs.meta | 11 - .../Scripts/AnnotationPass/CityFieldsPass.cs | 208 +++++++++ .../AnnotationPass/CityFieldsPass.cs.meta | 3 + .../{LocationPass.cs => LandmassPass.cs} | 4 +- ...ationPass.cs.meta => LandmassPass.cs.meta} | 0 .../AnnotationPass/LocateCitiesPass.cs | 84 ++++ .../AnnotationPass/LocateCitiesPass.cs.meta | 3 + Assets/Scripts/Cities.meta | 8 + Assets/Scripts/Cities/City.cs | 60 +++ Assets/Scripts/Cities/City.cs.meta | 3 + Assets/Scripts/Cities/CityField.cs | 10 + Assets/Scripts/Cities/CityField.cs.meta | 3 + Assets/Scripts/CityGenerator.cs | 437 ------------------ Assets/Scripts/CityGenerator.cs.meta | 11 - Assets/Scripts/Common/LocationSite.cs | 15 - Assets/Scripts/Common/LocationSite.cs.meta | 3 - Assets/Scripts/Common/Map.cs | 4 + Assets/Scripts/Common/MapSite.cs | 1 + Assets/Scripts/LandmassGenerator.cs | 40 +- .../Scripts/Utils/PointProximityComparer.cs | 25 + .../Utils/PointProximityComparer.cs.meta | 3 + 22 files changed, 449 insertions(+), 523 deletions(-) delete mode 100644 Assets/Editor/CityGeneratorUI.cs delete mode 100644 Assets/Editor/CityGeneratorUI.cs.meta create mode 100644 Assets/Scripts/AnnotationPass/CityFieldsPass.cs create mode 100644 Assets/Scripts/AnnotationPass/CityFieldsPass.cs.meta rename Assets/Scripts/AnnotationPass/{LocationPass.cs => LandmassPass.cs} (97%) rename Assets/Scripts/AnnotationPass/{LocationPass.cs.meta => LandmassPass.cs.meta} (100%) create mode 100644 Assets/Scripts/AnnotationPass/LocateCitiesPass.cs create mode 100644 Assets/Scripts/AnnotationPass/LocateCitiesPass.cs.meta create mode 100644 Assets/Scripts/Cities.meta create mode 100644 Assets/Scripts/Cities/City.cs create mode 100644 Assets/Scripts/Cities/City.cs.meta create mode 100644 Assets/Scripts/Cities/CityField.cs create mode 100644 Assets/Scripts/Cities/CityField.cs.meta delete mode 100644 Assets/Scripts/CityGenerator.cs delete mode 100644 Assets/Scripts/CityGenerator.cs.meta delete mode 100644 Assets/Scripts/Common/LocationSite.cs delete mode 100644 Assets/Scripts/Common/LocationSite.cs.meta create mode 100644 Assets/Scripts/Utils/PointProximityComparer.cs create mode 100644 Assets/Scripts/Utils/PointProximityComparer.cs.meta diff --git a/Assets/Editor/CityGeneratorUI.cs b/Assets/Editor/CityGeneratorUI.cs deleted file mode 100644 index 2ea0189..0000000 --- a/Assets/Editor/CityGeneratorUI.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using Assets; -using UnityEditor; -using UnityEngine; -using Random = UnityEngine.Random; - -namespace Assets.Editor -{ - [CustomEditor(typeof(CityGenerator))] - public class CityGeneratorUI : UnityEditor.Editor - { - public override void OnInspectorGUI() - { - CityGenerator generator = (CityGenerator)target; - - DrawDefaultInspector(); - -// if (GUILayout.Button("Reset")) -// { -// generator.Reset(); -// } -// -// if (GUILayout.Button("Generate")) -// { -// generator.Reset(); -// generator.Generate(); -// } -// -// if (GUILayout.Button("New City")) -// { -// generator.NewCity(); -// } - } - } -} - diff --git a/Assets/Editor/CityGeneratorUI.cs.meta b/Assets/Editor/CityGeneratorUI.cs.meta deleted file mode 100644 index e63bec3..0000000 --- a/Assets/Editor/CityGeneratorUI.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 86bc882017a8bcb459fc88d1ef89f4b1 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Scripts/AnnotationPass/CityFieldsPass.cs b/Assets/Scripts/AnnotationPass/CityFieldsPass.cs new file mode 100644 index 0000000..d822cb0 --- /dev/null +++ b/Assets/Scripts/AnnotationPass/CityFieldsPass.cs @@ -0,0 +1,208 @@ +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 _voronoiGraph; + private Graph _locationGraph; + private Graph _basicGraph; + private List verticesForRoads; + private List verticesForCitites; + + List cities = new List(); + Graph roads = new Graph(); + Graph mainRoads = new Graph(); + double minimumAngle; + + List CountProbablitityOfCity(List vertices) + { + List probabilities = new List(); + vertices.ForEach(v => probabilities.Add((v % 2 == 0 ? _random.Next(0, 50) : _random.Next(50, 100)))); + return probabilities; + } + + List 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 { 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); + } + + 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 ChoosePoints(int number) + { + var vertices = new List(); + 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 roads = new Graph() { Vertices = city.roads.Vertices }; + + foreach (var edge in original.Edges) + { + roads.AddEdge(edge.Item1, edge.Item2); + + if (Graph.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; + _voronoiGraph = map.Boundaries; + _locationGraph = map.Metadata.GetProperty>(LandmassPass.MapLocationsProperty); + + verticesForCitites = LocateCities(); + foreach (var index in verticesForCitites) + { + cities.Add(CreateCity(index, _random.Next(0, 10))); + } + + CreateWorldRoadGraph(); + mainRoads = roads.Morph(s => s.Center); + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/AnnotationPass/CityFieldsPass.cs.meta b/Assets/Scripts/AnnotationPass/CityFieldsPass.cs.meta new file mode 100644 index 0000000..bf140ab --- /dev/null +++ b/Assets/Scripts/AnnotationPass/CityFieldsPass.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: d612532edd124c75a746b1d74faa42f8 +timeCreated: 1573422504 \ No newline at end of file diff --git a/Assets/Scripts/AnnotationPass/LocationPass.cs b/Assets/Scripts/AnnotationPass/LandmassPass.cs similarity index 97% rename from Assets/Scripts/AnnotationPass/LocationPass.cs rename to Assets/Scripts/AnnotationPass/LandmassPass.cs index 336bce8..05bd984 100644 --- a/Assets/Scripts/AnnotationPass/LocationPass.cs +++ b/Assets/Scripts/AnnotationPass/LandmassPass.cs @@ -5,7 +5,7 @@ using Assets.Common; namespace Assets.AnnotationPass { - public class LocationPass : IAnnotationPass + public class LandmassPass : IAnnotationPass { public const string MapLocationsProperty = "Locations"; public const string SiteLocationProperty = "Location"; @@ -19,7 +19,7 @@ namespace Assets.AnnotationPass private Random _random; - public LocationPass(IEnumerable locations, Random random = null) + public LandmassPass(IEnumerable locations, Random random = null) { _locations = new Graph { Vertices = new List(locations) }; _random = random ?? new Random(); diff --git a/Assets/Scripts/AnnotationPass/LocationPass.cs.meta b/Assets/Scripts/AnnotationPass/LandmassPass.cs.meta similarity index 100% rename from Assets/Scripts/AnnotationPass/LocationPass.cs.meta rename to Assets/Scripts/AnnotationPass/LandmassPass.cs.meta diff --git a/Assets/Scripts/AnnotationPass/LocateCitiesPass.cs b/Assets/Scripts/AnnotationPass/LocateCitiesPass.cs new file mode 100644 index 0000000..20d335f --- /dev/null +++ b/Assets/Scripts/AnnotationPass/LocateCitiesPass.cs @@ -0,0 +1,84 @@ +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"; + + private Graph _locationGraph; + private Graph _basicGraph; + private Random _random; + + public LocateCitiesPass(Random random = null) + { + _random = random ?? new Random(); + } + + IEnumerable 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 }; + 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); + } + + return newCity; + } + + List ChoosePoints(int number) + { + var vertices = new List(); + + 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; + } + + public void Annotate(Map map) + { + _basicGraph = map.Sites.Clone() as Graph; + _locationGraph = map.Metadata.GetProperty>(LandmassPass.MapLocationsProperty); + + var cities = new List(); + var locations = LocateCities(); + + foreach (var index in locations) + cities.Add(CreateCity(index, _random.Next(0, 10))); + + map.Metadata.SetProperty(CitiesProperty, cities); + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/AnnotationPass/LocateCitiesPass.cs.meta b/Assets/Scripts/AnnotationPass/LocateCitiesPass.cs.meta new file mode 100644 index 0000000..8115f76 --- /dev/null +++ b/Assets/Scripts/AnnotationPass/LocateCitiesPass.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a5fc9d4f539a45d9af0661c60fbc1bb8 +timeCreated: 1573418593 \ No newline at end of file diff --git a/Assets/Scripts/Cities.meta b/Assets/Scripts/Cities.meta new file mode 100644 index 0000000..50c4e6f --- /dev/null +++ b/Assets/Scripts/Cities.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3890d890d2b20cd08920cac7a9706272 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Cities/City.cs b/Assets/Scripts/Cities/City.cs new file mode 100644 index 0000000..62e76db --- /dev/null +++ b/Assets/Scripts/Cities/City.cs @@ -0,0 +1,60 @@ +using System.Collections.Generic; +using System.Linq; +using Assets.Common; +using Assets.Voronoi; + +namespace Assets.Cities +{ + public class City + { + public Graph startGraph; + public List sites; + + public Graph roads = new Graph(); + public Graph fieldBoundaries = new Graph(); + + public List fields = new List(); + + public List<(int, int)> edges; + + public City(Graph startGraph) + { + this.startGraph = startGraph; + + this.sites = new List(); + this.edges = new List<(int, int)>(); + } + + public City(Graph startGraph, List sitesList) : this(startGraph) + { + this.sites = sitesList; + } + + public void AddSite(MapSite site) + { + sites.Add(site); + FixBoundaryEdges(site); + } + + private void FixBoundaryEdges(MapSite site) + { + var a = edges; + var b = site.Edges.Select(x => x.Item1 < x.Item2 ? x : (x.Item2, x.Item1)); + + edges = a.Union(b).Except(a.Intersect(b)).ToList(); + } + + public void CreateRoadGraph(IList points) + { + var pointsForRoads = sites.SelectMany(site => site.Boundary.Select(i => (i, points[i])).Append((-1, site.Center))).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); + + fieldBoundaries = new Graph { Vertices = roads.Vertices, Edges = edges.Select(e => (mapping[e.Item1], mapping[e.Item2])) }; + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Cities/City.cs.meta b/Assets/Scripts/Cities/City.cs.meta new file mode 100644 index 0000000..398d605 --- /dev/null +++ b/Assets/Scripts/Cities/City.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: ea278043ef9644c6992b3269f26b1f61 +timeCreated: 1573418494 \ No newline at end of file diff --git a/Assets/Scripts/Cities/CityField.cs b/Assets/Scripts/Cities/CityField.cs new file mode 100644 index 0000000..27b4f7f --- /dev/null +++ b/Assets/Scripts/Cities/CityField.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using Assets.Common; + +namespace Assets.Cities +{ + public class CityField + { + public List boundary; + } +} \ No newline at end of file diff --git a/Assets/Scripts/Cities/CityField.cs.meta b/Assets/Scripts/Cities/CityField.cs.meta new file mode 100644 index 0000000..2069915 --- /dev/null +++ b/Assets/Scripts/Cities/CityField.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: d5245590d0aa431a880e2b98e127d5f0 +timeCreated: 1573418463 \ No newline at end of file diff --git a/Assets/Scripts/CityGenerator.cs b/Assets/Scripts/CityGenerator.cs deleted file mode 100644 index 8cfb560..0000000 --- a/Assets/Scripts/CityGenerator.cs +++ /dev/null @@ -1,437 +0,0 @@ -using Assets.Common; -using Assets.Voronoi; -using System; -using System.Collections.Generic; -using System.Linq; -using UnityEngine; - -//namespace Assets.Scripts -//{ -// public class PointProximityComparer : IEqualityComparer -// { -// 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 CityField -// { -// public List boundary; -// } -// -// public class City -// { -// public Graph startGraph; -// public List sites; -// -// public Graph roads = new Graph(); -// public Graph fieldBoundaries = new Graph(); -// -// public List fields = new List(); -// -// public List<(int, int)> edges; -// -// public City(Graph startGraph) -// { -// this.startGraph = startGraph; -// -// this.sites = new List(); -// this.edges = new List<(int, int)>(); -// } -// -// public City(Graph startGraph, List sitesList) : this(startGraph) -// { -// this.sites = sitesList; -// } -// -// public void AddSite(Site site) -// { -// sites.Add(site); -// FixBoundaryEdges(site); -// } -// -// private void FixBoundaryEdges(Site site) -// { -// var a = edges; -// var b = site.Edges.Select(x => x.Item1 < x.Item2 ? x : (x.Item2, x.Item1)); -// -// edges = a.Union(b).Except(a.Intersect(b)).ToList(); -// } -// -// public void CreateRoadGraph(IList points) -// { -// 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); -// -// fieldBoundaries = new Graph { Vertices = roads.Vertices, Edges = edges.Select(e => (mapping[e.Item1], mapping[e.Item2])) }; -// } -// } -// -// [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 displayFieldBoundaries = true; -// public bool displayWorldRoads = false; -// } -// -// [RequireComponent(typeof(LandmassGenerator))] - public class CityGenerator : MonoBehaviour - { -// private System.Random _random; -// private Graph _voronoiGraph; -// private Graph _locationGraph; -// private Graph _basicGraph; -// private List verticesForRoads; -// private List verticesForCitites; -// -// public GraphGeneratorDebug debug; -// public double minimumAngle = 0.25; -// -// public List cities = new List(); -// -// Graph roads = new Graph(); -// -// Graph mainRoads = new Graph(); -// -// List CountProbablitityOfCity(List vertices) -// { -// List probabilities = new List(); -// vertices.ForEach(v => probabilities.Add((v % 2 == 0 ? _random.Next(0, 50) : _random.Next(50, 100)))); -// return probabilities; -// } -// -// List 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.Vertices.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() { 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); -// -// 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 ChoosePoints(int number) -// { -// var vertices = new List(); -// 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 roads = new Graph() { Vertices = city.roads.Vertices }; -// -// foreach (var edge in original.Edges) -// { -// roads.AddEdge(edge.Item1, edge.Item2); -// -// if (Graph.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.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(); -// _random = new System.Random(graphGenerator.seed); -// cities = new List(); -// } -// -// public void NewCity() -// { -// cities.Add(CreateCity(LocateCities()[_random.Next(0, 10)], _random.Next(0, 10))); -// } -// -// public void Generate() -// { -// var graphGenerator = GetComponent(); -// 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 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 graph, List vertices) -// { -// //foreach (var v in vertices.Select(i => graph.Vertices[i].ToVector3())) -// //{ -// // Gizmos.DrawSphere(v, 1); -// //} -// } -// -// -// private void DisplayGraphCities(List cities) -// { -// foreach (City city in cities) -// { -// Gizmos.color = Color.magenta; -// foreach (var (a, b) in city.edges) -// { -// Gizmos.DrawLine(_voronoiGraph.Vertices[a].ToVector3(), _voronoiGraph.Vertices[b].ToVector3()); -// } -// -// foreach (var a in city.sites) -// Gizmos.DrawSphere(a.Point.ToVector3(), 1); -// } -// } -// -// -// private void DisplayGraphEdges(Graph 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.displayFieldBoundaries) -// { -// Gizmos.color = Color.white; -// -// foreach (var city in cities) -// DisplayGraphEdges(city.fieldBoundaries); -// } -// -// if (debug.displayCityRoads) -// { -// Gizmos.color = Color.green; -// -// foreach (var city in cities) -// { -// DisplayGraphEdges(city.roads); -// DisplayGraphVertices(city.roads); -// } -// } -// -// -// if (debug.displayWorldRoads) -// { -// // Gizmos.color = new Color(0.254f, 0.127f, 0.156f); -// Gizmos.color = Color.blue; -// DisplayGraphEdges(mainRoads); -// } -// } - } -//} \ No newline at end of file diff --git a/Assets/Scripts/CityGenerator.cs.meta b/Assets/Scripts/CityGenerator.cs.meta deleted file mode 100644 index ca714ea..0000000 --- a/Assets/Scripts/CityGenerator.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: ec8bb5e8662a03f4bb9224a34ef8db2b -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Scripts/Common/LocationSite.cs b/Assets/Scripts/Common/LocationSite.cs deleted file mode 100644 index 23bfd72..0000000 --- a/Assets/Scripts/Common/LocationSite.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Assets.Voronoi; - -namespace Assets.Common -{ - public class LocationSite - { - public readonly Site Site; - public Location Location; - - public LocationSite(Site site) - { - Site = site; - } - } -} \ No newline at end of file diff --git a/Assets/Scripts/Common/LocationSite.cs.meta b/Assets/Scripts/Common/LocationSite.cs.meta deleted file mode 100644 index cd682ba..0000000 --- a/Assets/Scripts/Common/LocationSite.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: ae1606ff46e64c2a9dc080c61bbd74b8 -timeCreated: 1573304795 \ No newline at end of file diff --git a/Assets/Scripts/Common/Map.cs b/Assets/Scripts/Common/Map.cs index fd2ce1d..a7d356c 100644 --- a/Assets/Scripts/Common/Map.cs +++ b/Assets/Scripts/Common/Map.cs @@ -17,8 +17,12 @@ namespace Assets.Common set { _sites = value; + int i = 0; foreach (var site in _sites.Vertices) + { site.Map = this; + site.Index = i++; + } } } diff --git a/Assets/Scripts/Common/MapSite.cs b/Assets/Scripts/Common/MapSite.cs index 19ec58f..9b85641 100644 --- a/Assets/Scripts/Common/MapSite.cs +++ b/Assets/Scripts/Common/MapSite.cs @@ -16,6 +16,7 @@ namespace Assets.Common Boundary = boundary; } + public int Index { get; internal set; } public Point Center { get; set; } public bool IsOuter { get; set; } = false; public IEnumerable Boundary { get; set; } diff --git a/Assets/Scripts/LandmassGenerator.cs b/Assets/Scripts/LandmassGenerator.cs index a38a117..1d85ac2 100644 --- a/Assets/Scripts/LandmassGenerator.cs +++ b/Assets/Scripts/LandmassGenerator.cs @@ -1,9 +1,11 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using Assets.AnnotationPass; +using Assets.Cities; using Assets.Common; using Assets.GridGenerator; using Assets.PointDistribution; @@ -28,6 +30,7 @@ namespace Assets public bool displayLocations = true; public bool displayLocationCells = true; public bool displayLocationEdges = true; + public bool displayCities = true; public bool displayLocationPoints = true; [NonSerialized] public float generationTime = 0.0f; @@ -58,7 +61,7 @@ namespace Assets public Graph SitesGraph => Map?.Sites; public Graph BoundariesGraph => Map?.Boundaries; - public Graph LocationGraph => Map?.Metadata.GetProperty>(LocationPass.MapLocationsProperty); + public Graph LocationGraph => Map?.Metadata.GetProperty>(LandmassPass.MapLocationsProperty); public void Generate() { @@ -86,7 +89,8 @@ namespace Assets { var generator = new MapGenerator(new VoronoiGridGenerator(seed, new PoissonDiskSampler(radius) { Generator = new Random(seed) })); - generator.AddAnnotationPass(new LocationPass(types.Prepend(new LocationType { name = "Ocean", height = -1 }).Select(t => new Location { Type = t }), new Random(seed))); + generator.AddAnnotationPass(new LandmassPass(types.Prepend(new LocationType { name = "Ocean", height = -1 }).Select(t => new Location { Type = t }), new Random(seed))); + generator.AddAnnotationPass(new LocateCitiesPass(new Random(seed))); return generator; } @@ -95,6 +99,20 @@ namespace Assets { GenerationThread?.Abort(); } + + private void DisplayGraphCities(IEnumerable cities) + { + foreach (City city in cities) + { + foreach (var (a, b) in city.edges) + { + Gizmos.DrawLine(Map.Boundaries[a].ToVector3(), Map.Boundaries[b].ToVector3()); + } + + foreach (var a in city.sites) + Gizmos.DrawSphere(a.Center.ToVector3(), 1); + } + } void OnDrawGizmos() { @@ -136,19 +154,19 @@ namespace Assets Gizmos.color = Color.blue; - if (!Map.Metadata.HasProperty(LocationPass.MapLocationsProperty)) return; + if (!Map.Metadata.HasProperty(LandmassPass.MapLocationsProperty)) return; - var locations = Map.Metadata.GetProperty>(LocationPass.MapLocationsProperty); + var locations = Map.Metadata.GetProperty>(LandmassPass.MapLocationsProperty); if (debug.displayLocationCells) { var cells = Map.Sites.Vertices - .Where(s => s.Metadata.HasProperty(LocationPass.SiteLocationProperty)) - .Where(s => s.Metadata.GetProperty(LocationPass.SiteLocationProperty).Type.name != "Ocean"); + .Where(s => s.Metadata.HasProperty(LandmassPass.SiteLocationProperty)) + .Where(s => s.Metadata.GetProperty(LandmassPass.SiteLocationProperty).Type.name != "Ocean"); foreach (var site in cells) { - var location = site.Metadata.GetProperty(LocationPass.SiteLocationProperty); + var location = site.Metadata.GetProperty(LandmassPass.SiteLocationProperty); Gizmos.color = location.Type.color; Gizmos.DrawSphere(site.Center.ToVector3(), 2); @@ -177,7 +195,7 @@ namespace Assets // if (debug.displayLocationEdges) { - foreach (var location in Map.Metadata.GetProperty>(LocationPass.MapLocationsProperty).Vertices) + foreach (var location in Map.Metadata.GetProperty>(LandmassPass.MapLocationsProperty).Vertices) { Gizmos.color = location.Type.color; foreach (var (a, b) in location.BoundaryEdges) @@ -186,6 +204,12 @@ namespace Assets } } } + + if (debug.displayCities && Map.Metadata.HasProperty(LocateCitiesPass.CitiesProperty)) + { + Gizmos.color = Color.magenta; + DisplayGraphCities(Map.Metadata.GetProperty>(LocateCitiesPass.CitiesProperty)); + } } } } \ No newline at end of file diff --git a/Assets/Scripts/Utils/PointProximityComparer.cs b/Assets/Scripts/Utils/PointProximityComparer.cs new file mode 100644 index 0000000..805abae --- /dev/null +++ b/Assets/Scripts/Utils/PointProximityComparer.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; +using Assets.Common; + +namespace Assets.Utils +{ + public class PointProximityComparer : IEqualityComparer + { + 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(); + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Utils/PointProximityComparer.cs.meta b/Assets/Scripts/Utils/PointProximityComparer.cs.meta new file mode 100644 index 0000000..f7bd1a6 --- /dev/null +++ b/Assets/Scripts/Utils/PointProximityComparer.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 765daf76a463490ba0a1e186f5c82fdf +timeCreated: 1573418560 \ No newline at end of file