using System; using System.Collections.Generic; using System.Linq; using Assets.Common; using Assets.Voronoi; using UnityEngine.UIElements; namespace Assets.AnnotationPass { public class LandmassPass : 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 LandmassPass(IEnumerable locations, Random random = null) { _locations = new Graph { Vertices = new List(locations) }; _random = random ?? new Random(); } private void Process(int vertex, Location location) { SetLocation(_sites.Vertices[vertex], location); foreach (var neighbour in _sites.Neighbours(vertex)) { var site = _sites[neighbour]; if (!site.HasProperty(SiteLocationProperty)) { if (location == _locations[0] || !site.Tags.Contains(CommonTags.Edge)) _queue.Add((neighbour, location)); } else if (location != site.GetProperty(SiteLocationProperty)) _locations.AddEdge( _locations.Vertices.IndexOf(location), _locations.Vertices.IndexOf(site.Metadata.GetProperty(SiteLocationProperty)) ); } } private void SetLocation(MapSite site, Location location) { location.AddSite(site); if (location != _locations[0]) site.Tags.Add(CommonTags.Land); site.SetProperty(SiteLocationProperty, location); site.SetProperty(CommonSiteProperties.Height, (double)location.Type.height); } 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].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.SetProperty(MapLocationsProperty, _locations); var ocean = _locations[0]; var possible = new Queue(map.Sites .Vertices .Select((site, i) => (Vertex: i, Site: site)) .Where(tuple => !tuple.Site.Tags.Contains(CommonTags.Edge)) .Select(tuple => tuple.Vertex) .OrderBy(tuple => _random.Next())); var initial = from location in _locations.Vertices.Skip(1) select (possible.Dequeue(), location); initial = initial.Concat( map.Sites.Vertices .Select((s, i) => (Site: s, Vertex: i)) .Where(t => t.Site.Tags.Contains(CommonTags.Outer)) .Select(t => (t.Vertex, ocean)) ); foreach (var (vertex, location) in initial) Process(vertex, location); while (_queue.Count != 0) Step(); } } }