102 lines
3.4 KiB
C#
102 lines
3.4 KiB
C#
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, new Size(width, height));
|
|
|
|
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<Point>;
|
|
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<Point> GeneratePoints(double width, double height)
|
|
{
|
|
var points = new List<Point>
|
|
{
|
|
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<int> ClockwiseVertices(Graph<Point> graph, IEnumerable<int> 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);
|
|
}
|
|
}
|
|
} |