inz-00/Assets/Scripts/AnnotationPass/LandmassPass.cs
2019-11-17 20:33:32 +01:00

156 lines
5.0 KiB
C#

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<Point> _points;
private Graph<MapSite> _sites;
private readonly Graph<Location> _locations;
private readonly List<(int, Location)> _queue = new List<(int, Location)>();
private Random _random;
public LandmassPass(IEnumerable<Location> locations, Random random = null)
{
_locations = new Graph<Location> { Vertices = new List<Location>(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.Metadata.HasProperty(SiteLocationProperty))
{
if (location == _locations[0] || !site.Tags.Contains(CommonTags.Edge))
_queue.Add((neighbour, location));
}
else if (location != site.Metadata.GetProperty<Location>(SiteLocationProperty))
_locations.AddEdge(
_locations.Vertices.IndexOf(location),
_locations.Vertices.IndexOf(site.Metadata.GetProperty<Location>(SiteLocationProperty))
);
}
}
private void SetLocation(MapSite site, Location location)
{
location.AddSite(site);
if (location != _locations[0])
site.Tags.Add(CommonTags.Land);
site.Metadata.SetProperty(SiteLocationProperty, location);
}
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 ocean = _locations[0];
var possible = new Queue<int>(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();
}
}
}