inz-00/Assets/Map/LocationGenerator.cs
2019-09-09 19:25:49 +02:00

109 lines
3.4 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using Assets.Common;
using Assets.Voronoi;
using Random = System.Random;
namespace Assets.Map
{
public class LocationGenerator
{
private readonly IList<Point> _points;
private readonly Graph<Site> _sites;
private readonly Graph<Location> _locations;
private readonly List<(int, Location)> _queue = new List<(int, Location)>();
private readonly Random _random;
public Graph<Site> Sites => _sites;
public Graph<Location> Result => _locations;
public bool Done => _queue.Count == 0;
public LocationGenerator(List<Location> locations, Graph<Site> sites, IList<Point> points, Random random = null)
{
_points = points;
_sites = sites.Morph(s => s);
_locations = new Graph<Location> { Vertices = new List<Location>(locations) };
_random = random ?? new Random();
}
public void SetLocation(int vertex, Location location)
{
location.AddSite(_sites.Vertices[vertex]);
foreach (var neighbour in _sites.Neighbours(vertex))
if (_sites.Vertices[neighbour].Location == null)
_queue.Add((neighbour, location));
else if (location != _sites.Vertices[neighbour].Location)
_locations.AddEdge(
_locations.Vertices.IndexOf(location),
_locations.Vertices.IndexOf(_sites.Vertices[neighbour].Location)
);
}
private (int, Location) Dequeue()
{
while (_queue.Count > 0)
{
var index = _random.Next(_queue.Count);
var pair = _queue[index];
_queue.RemoveAt(index);
if (_sites.Vertices[pair.Item1].Location == null) return pair;
}
throw new Exception("No more elements.");
}
public void Step()
{
try
{
var (vertex, location) = Dequeue();
SetLocation(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;
}
}
}