using System.Collections.Generic; using System.Linq; using Assets.Common; using Assets.PointDistribution; using Assets.Voronoi; namespace Assets.GridGenerator { public class VoronoiGridGenerator : IGridGenerator { public const string VoronoiSiteProperty = "VoronoiSite"; private readonly int _seed; private readonly IPointDistribution _sampler; public VoronoiGridGenerator(int seed, IPointDistribution sampler) { _seed = seed; _sampler = sampler; } public Map CreateGrid(double width, double height) { Map result = new Map(_seed); var points = GeneratePoints(width, height); var generator = new VoronoiGenerator(points); generator.Generate(); bool IsOutside(Point point) => !(point.x > 0 && point.x < width && point.y > 0 && point.y < height); result.Boundaries = generator.Voronoi.Clone() as Graph; result.Sites = generator.Delaunay.Morph(s => { var site = new MapSite(s.Point, ClockwiseVertices(generator.Voronoi, s.Vertices)); site.Metadata.SetProperty(VoronoiSiteProperty, s); if (site.Boundary.Select(n => result.Boundaries[n]).Any(IsOutside)) site.Tags.Add(CommonTags.Edge); return site; }); foreach (var site in result.Sites.Vertices.Take(4)) site.Tags.Add(CommonTags.Outer); FixTooLongEdges(result, width, height); return result; } private void FixTooLongEdges(Map map, double width, double height) { List<(int, int)> remove = new List<(int, int)>(); bool IsValid(Point point) => point.x > 0 && point.x < width && point.y > 0 && point.y < height; foreach (var (a, b) in map.Boundaries.Edges) { var pointA = map.Boundaries[a]; var pointB = map.Boundaries[b]; if (!IsValid(pointA) || !IsValid(pointB)) remove.Add((a, b)); } foreach (var (a, b) in remove) map.Boundaries.DeleteEdge(a, b); } private List GeneratePoints(double width, double height) { var points = new List { new Point(-width / 8, height / 2 + 0.1f), new Point(width * 1.125f, height / 2 - 0.1f), new Point(width / 2 - 0.1f, -height / 8), new Point(width / 2 + 0.1f, height * 1.125f) }; points.AddRange(_sampler.Generate(width, height)); return points; } private IEnumerable ClockwiseVertices(Graph graph, IEnumerable vertices) { var enumerated = vertices as int[] ?? vertices.ToArray(); var center = PointUtils.Mean(enumerated.Select(n => graph[n])); var reference = center + new Point(0, -100); return enumerated .Select(n => (Vertex: n, Point: graph[n])) .Select(x => (Vertex: x.Vertex, Angle: PointUtils.AngleBetween(center, reference, x.Point))) .OrderBy(x => x.Angle) .Select(x => x.Vertex); } } }