135 lines
4.3 KiB
C#
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();
|
|
}
|
|
}
|
|
} |