109 lines
3.4 KiB
C#
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;
|
|
}
|
|
}
|
|
} |