using System; using System.Collections.Generic; using System.Linq; using Assets.Common; namespace Assets.AnnotationPass { public class LocationPass : IAnnotationPass { public const string MapLocationsProperty = "Locations"; public const string SiteLocationProperty = "Location"; private IList _points; private Graph _sites; private readonly Graph _locations; private readonly List<(int, Location)> _queue = new List<(int, Location)>(); private Random _random; public LocationPass(IEnumerable locations, Random random = null) { _locations = new Graph { Vertices = new List(locations) }; _random = random ?? new Random(); } private void Process(int vertex, Location location) { location.AddSite(_sites.Vertices[vertex]); _sites.Vertices[vertex].Metadata.SetProperty(SiteLocationProperty, location); foreach (var neighbour in _sites.Neighbours(vertex)) { var site = _sites[neighbour]; if (!site.Metadata.HasProperty(SiteLocationProperty)) _queue.Add((neighbour, location)); else if (location != site.Metadata.GetProperty(SiteLocationProperty)) _locations.AddEdge( _locations.Vertices.IndexOf(location), _locations.Vertices.IndexOf(site.Metadata.GetProperty(SiteLocationProperty)) ); } } private (int, Location) Dequeue() { while (_queue.Count > 0) { var index = _random.Next(_queue.Count); var pair = _queue[index]; _queue.RemoveAt(index); if (!_sites[pair.Item1].Metadata.HasProperty(SiteLocationProperty)) return pair; } throw new Exception("No more elements."); } private void Step() { try { var (vertex, location) = Dequeue(); Process(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; } public void Annotate(Map map) { _random = new Random(map.Seed); _sites = map.Sites; _points = map.Boundaries.Vertices; map.Metadata.SetProperty(MapLocationsProperty, _locations); var initial = from location in _locations.Vertices.Skip(1) select (_random.Next(map.Sites.Vertices.Count), location); initial = initial.Concat( map.Sites.Vertices .Select((s, i) => (Site: s, Vertex: i)) .Where(t => t.Site.IsOuter) .Select(t => (t.Vertex, _locations[0])) ); foreach (var (vertex, location) in initial) Process(vertex, location); while (_queue.Count != 0) Step(); } } }