inz-00/Assets/Scripts/AnnotationPass/LocationPass.cs

135 lines
4.3 KiB
C#

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<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 LocationPass(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)
{
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<Location>(SiteLocationProperty))
_locations.AddEdge(
_locations.Vertices.IndexOf(location),
_locations.Vertices.IndexOf(site.Metadata.GetProperty<Location>(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();
}
}
}