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(); result.Boundaries = (Graph)generator.Voronoi.Clone(); result.Sites = generator.Delaunay.Morph(s => { var site = new MapSite(s.Point, ClockwiseVertices(generator.Voronoi, s.Vertices)) { Edges = s.Edges }; site.Metadata.SetProperty(VoronoiSiteProperty, s); return site; }); foreach (var site in result.Sites.Vertices.Take(4)) site.IsOuter = true; return result; } 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, 1); 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); } } }