Merge remote-tracking branch 'origin/city_generator'

This commit is contained in:
Kacper Donat 2019-11-09 14:23:12 +01:00
commit 7827f6cc21
19 changed files with 36536 additions and 60030 deletions

3
.idea/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@

# Default ignored files
/.idea.inz-00/.idea/workspace.xml

View File

@ -1,3 +1,4 @@
using Assets.Common;
using System;
using System.Collections.Generic;
using System.Linq;
@ -5,10 +6,10 @@ using System.Linq;
namespace Assets.Common
{
[Serializable]
public class Graph<T>
public class Graph<T> : ICloneable
{
public List<T> Vertices { get; internal set; } = new List<T>();
public Dictionary<int, List<int>> _edges = new Dictionary<int, List<int>>();
private Dictionary<int, List<int>> _edges = new Dictionary<int, List<int>>();
public T this[int i] => Vertices[i];
@ -40,11 +41,69 @@ namespace Assets.Common
_edges[b].Add(a);
}
public Graph<U> Morph<U>(Func<T, U> morph)
public void DeleteEdge(int a, int b)
{
if (!_edges.ContainsKey(a))
return;
if (!_edges.ContainsKey(b))
return;
_edges[a].Remove(b);
_edges[b].Remove(a);
}
public static bool HasCycle(Graph<T> graph)
{
int Find(int[] parent, int i)
{
if (parent[i] == -1)
return i;
return Find(parent, parent[i]);
}
void Union(int[] parent, int x, int y)
{
int xset = Find(parent, x);
int yset = Find(parent, y);
parent[xset] = yset;
}
int[] trueParent = new int[graph.Vertices.Count()];
for (int i = 0; i < graph.Vertices.Count(); ++i)
trueParent[i] = -1;
for (int i = 0; i < graph.Edges.Count(); ++i)
{
int x = Find(trueParent, graph.Edges.ElementAt(i).Item1);
int y = Find(trueParent, graph.Edges.ElementAt(i).Item2);
if (x == y)
return true;
Union(trueParent, x, y);
}
return false;
}
public Graph<U> Morph<U>(Func<T, U> vertices)
{
var result = new Graph<U>() { Edges = Edges };
result.Vertices.AddRange(Vertices.Select(morph));
result.Vertices.AddRange(Vertices.Select(vertices));
return result;
}
public Graph<U, E> Morph<U, E>(Func<T, U> vertices, Func<(int, int), E> edges)
{
var result = new Graph<U, E>() { Edges = Edges };
result._edgeData = Edges.ToDictionary(k => k, v => edges(v));
result.Vertices.AddRange(Vertices.Select(vertices));
return result;
}
@ -55,5 +114,54 @@ namespace Assets.Common
return _edges[vertex];
}
virtual public object Clone()
{
return Morph(l => l);
}
}
public class Graph<T, E> : Graph<T>
{
internal Dictionary<(int, int), E> _edgeData = new Dictionary<(int, int), E>();
public void AddEdge(int a, int b, E data)
{
AddEdge(a, b);
SetEdgeData((a, b), data);
}
public void SetEdgeData((int, int) edge, E data)
{
var (a, b) = edge;
if (a > b) (a, b) = (b, a);
_edgeData[(a, b)] = data;
}
public E GetEdgeData((int, int) edge)
{
var (a, b) = edge;
if (a > b) (a, b) = (b, a);
return _edgeData[(a, b)];
}
public Graph<U, E2> Morph<U, E2>(Func<T, U> vertices, Func<E, (int, int), E2> edges)
{
var result = new Graph<U, E2>() { Edges = Edges };
result._edgeData = _edgeData.ToDictionary(e => e.Key, e => edges(e.Value, e.Key));
result.Vertices.AddRange(Vertices.Select(vertices));
return result;
}
public override object Clone()
{
return Morph(l => l, l => l);
}
}
}

View File

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
namespace Assets.Common
{
@ -6,6 +7,7 @@ namespace Assets.Common
{
public double x;
public double y;
public double Length => Math.Sqrt(x*x+y*y);
public Point(double x, double y)
{
@ -18,11 +20,22 @@ namespace Assets.Common
return Math.Sqrt(Math.Pow(a.x - b.x, 2) + Math.Pow(a.y - b.y, 2));
}
public override int GetHashCode()
{
var hashCode = 1502939027;
hashCode = hashCode * -1521134295 + x.GetHashCode();
hashCode = hashCode * -1521134295 + y.GetHashCode();
return hashCode;
}
public static Point operator +(Point a, Point b) => new Point(a.x + b.x, a.y + b.y);
public static Point operator +(Point a) => a;
public static Point operator -(Point a, Point b) => new Point(a.x - b.x, a.y - b.y);
public static Point operator -(Point a) => new Point(-a.x, -a.y);
public static Point operator *(Point a, double b) => new Point(a.x * b, a.y * b);
public static Point operator /(Point a, double b) => new Point(a.x / b, a.y / b);
}
}

View File

@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
namespace Assets.Common
@ -20,6 +21,14 @@ namespace Assets.Common
return result;
}
public static double AngleBetween(Point center, Point a, Point b)
{
a = a - center;
b = b - center;
return Math.Acos((a.x*b.x+a.y*b.y)/(a.Length * b.Length));
}
public static bool IsClockwise(Point center, Point a, Point b)
{
var xa = a.x - center.x;

View File

@ -1,3 +1,5 @@
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace Assets.Common
@ -9,4 +11,23 @@ namespace Assets.Common
return new Vector3((float)p.x, 0, (float)p.y);
}
}
public static class IEnumerableExtensions
{
public static T RandomElement<T>(this IEnumerable<T> collection, System.Random generator)
{
var count = collection.Count();
return collection.ElementAt(generator.Next(count));
}
public static IEnumerable<T> RotateRight<T>(this IEnumerable<T> collection, int count = 1)
{
var diff = collection.Count() - count;
var head = collection.Take(diff);
var tail = collection.Skip(diff).Take(count);
return tail.Concat(head);
}
}
}

View File

@ -0,0 +1,41 @@
using System;
using Assets;
using Assets.Scripts;
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();
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 86bc882017a8bcb459fc88d1ef89f4b1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -7,21 +7,21 @@ namespace Assets.Map
{
public class Location
{
public List<LocationSite> Sites = new List<LocationSite>();
public List<Site> Sites = new List<Site>();
public IEnumerable<int> Points => Sites.SelectMany(site => site.Site.Vertices);
public IEnumerable<int> Points => Sites.SelectMany(site => site.Vertices);
public IEnumerable<int> BoundaryPoints => BoundaryEdges.SelectMany(edge => new [] { edge.Item1, edge.Item2 }).Distinct();
public IEnumerable<int> InsidePoints => Points.Except(BoundaryPoints);
public List<(int, int)> BoundaryEdges = new List<(int, int)>();
public Point Center => PointUtils.Mean(Sites.Select(s => s.Site.Point));
public Point Center => PointUtils.Mean(Sites.Select(s => s.Point));
public LocationType Type;
public List<Point> DetailedEdge = new List<Point>();
public void AddSite(LocationSite site)
public void AddSite(Site site)
{
site.Location = this;
Sites.Add(site);
@ -29,10 +29,10 @@ namespace Assets.Map
FixBoundaryEdges(site);
}
private void FixBoundaryEdges(LocationSite site)
private void FixBoundaryEdges(Site site)
{
var a = BoundaryEdges;
var b = site.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));
BoundaryEdges = a.Union(b).Except(a.Intersect(b)).ToList();
}

View File

@ -10,14 +10,14 @@ namespace Assets.Map
public class LocationGenerator
{
private readonly IList<Point> _points;
private readonly Graph<LocationSite> _sites;
private readonly Graph<Site> _sites;
private readonly Graph<Location> _locations;
private readonly List<(int, Location)> _queue = new List<(int, Location)>();
private readonly Random _random;
public Graph<LocationSite> Sites => _sites;
public Graph<Site> Sites => _sites;
public Graph<Location> Result => _locations;
public bool Done => _queue.Count == 0;
@ -25,7 +25,7 @@ namespace Assets.Map
public LocationGenerator(List<Location> locations, Graph<Site> sites, IList<Point> points, Random random = null)
{
_points = points;
_sites = sites.Morph(s => new LocationSite(s));
_sites = sites.Morph(s => s);
_locations = new Graph<Location> { Vertices = new List<Location>(locations) };
_random = random ?? new Random();

8
Assets/SampleScenes.meta Normal file
View File

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

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,441 @@
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 CityField
{
public List<Point> boundary;
}
public class City
{
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.sites = new List<Voronoi.Site>();
this.edges = new List<(int, int)>();
}
public City(Graph<Site> startGraph, List<Site> 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<Point> 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<Point> { 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(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 double minimumAngle = 0.25;
public List<City> cities = new List<City>();
Graph<Site> roads = new Graph<Site>();
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);
}
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>() { 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<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;
}
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.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.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<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.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);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ec8bb5e8662a03f4bb9224a34ef8db2b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -20,7 +20,7 @@ namespace Assets
public GameObject forest;
private Random _random = new Random();
private List<LocationSite> _sites;
private List<Site> _sites;
private GraphGenerator Generator => GetComponent<GraphGenerator>();
@ -38,17 +38,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);
}
}
@ -67,7 +67,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

@ -258,7 +258,7 @@ namespace Assets
foreach (var location in LocationGenerator.Sites.Vertices.Where(l => l.Location != null && l.Location != _locations[0]))
{
Gizmos.color = location.Location.Type.color;
Gizmos.DrawSphere(location.Site.Point.ToVector3(), 2);
Gizmos.DrawSphere(location.Point.ToVector3(), 2);
}
}
@ -322,7 +322,7 @@ namespace Assets
foreach (var (s, e) in edges) Gizmos.DrawLine(s, e);
}
private void RecalculateLocationGraph()
{
_locationGraph = LocationGenerator.Result.Morph(location => (location.Center, location));

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.Site.Edges.Select(x => x.Item1).Reverse()))
foreach (var vertices in location.Sites.Select(site => site.Edges.Select(x => x.Item1).Reverse()))
{
int start = _vertices.Count;

View File

@ -32,6 +32,7 @@ namespace Assets.Voronoi
public class Site
{
public Point Point { get; internal set; }
public Location Location { get; set; }
public int Index { get; internal set; }
public List<int> Vertices { get; internal set; } = new List<int>();
public List<(int, int)> Edges { get; internal set; } = new List<(int, int)>();
@ -58,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();

View File

@ -1,2 +1,2 @@
m_EditorVersion: 2019.3.0a8
m_EditorVersionWithRevision: 2019.3.0a8 (8ea4afdbfa47)
m_EditorVersion: 2019.2.0f1
m_EditorVersionWithRevision: 2019.2.0f1 (20c1667945cf)