using System; using System.Collections.Generic; using System.Linq; using Assets.Common; using Assets.Voronoi; using Random = System.Random; namespace Assets.Map { public class LocationGenerator { private readonly IList _points; private readonly Graph _sites; private readonly Graph _locations; private readonly List<(int, Location)> _queue = new List<(int, Location)>(); private readonly Random _random; public Graph Sites => _sites; public Graph Result => _locations; public bool Done => _queue.Count == 0; public LocationGenerator(List locations, Graph sites, IList points, Random random = null) { _points = points; _sites = sites.Morph(s => s); _locations = new Graph { Vertices = new List(locations) }; _random = random ?? new Random(); } public void SetLocation(int vertex, Location location) { location.AddSite(_sites.Vertices[vertex]); foreach (var neighbour in _sites.Neighbours(vertex)) if (_sites.Vertices[neighbour].Location == null) _queue.Add((neighbour, location)); else if (location != _sites.Vertices[neighbour].Location) _locations.AddEdge( _locations.Vertices.IndexOf(location), _locations.Vertices.IndexOf(_sites.Vertices[neighbour].Location) ); } private (int, Location) Dequeue() { while (_queue.Count > 0) { var index = _random.Next(_queue.Count); var pair = _queue[index]; _queue.RemoveAt(index); if (_sites.Vertices[pair.Item1].Location == null) return pair; } throw new Exception("No more elements."); } public void Step() { try { var (vertex, location) = Dequeue(); SetLocation(vertex, location); } catch (Exception ex) { foreach (var location in _locations.Vertices.Skip(1)) FixLocation(location); } } private void FixLocation(Location location) { var center = location.Center; var left = location.BoundaryEdges; location.BoundaryEdges = new List<(int, int)>() { EnsureClockwise(center, left[0]) }; (int, int) last = location.BoundaryEdges[0]; left.RemoveAt(0); while (left.Count > 0) { var index = left.FindIndex(x => x.Item1 == last.Item2 || x.Item2 == last.Item2); if (index == -1) break; var item = left[index]; item = item.Item1 == last.Item2 ? item : (item.Item2, item.Item1); left.RemoveAt(index); location.BoundaryEdges.Add(item); last = item; } } private (int, int) EnsureClockwise(Point center, (int, int) edge) { return PointUtils.IsClockwise(center, _points[edge.Item1], _points[edge.Item2]) ? (edge.Item2, edge.Item1) : edge; } } }