using System; using System.Collections.Generic; using System.Linq; using Assets.Common; using Assets.Generators; using Assets.Map; using Assets.Voronoi; using UnityEditor; using UnityEngine; using Random = System.Random; namespace Assets { [Serializable] public class GraphGeneratorDebug { public bool enabled = true; public bool displayPoints = true; public bool displayBeachLine = true; public bool displayParabolas = false; public bool displayVertices = true; public bool displayNeighbours = false; public bool displayLabels = false; public bool displayHalfEdges = false; public bool displayEdges = true; public bool displayLocations = true; } [Serializable] public class Section { public string name; public Color color; } public enum GenerationStage { Start, Voronoi, Location, Done }; [RequireComponent(typeof(MeshRenderer), typeof(MeshFilter)), ExecuteInEditMode] public class GraphGenerator : MonoBehaviour { MeshFilter _filter; private PoissonDiskSampler _sampler; List _points = new List(); public List
sections = new List
(); private VoronoiGenerator _voronoiGenerator; private LocationGenerator _locationGenerator; public VoronoiGenerator VoronoiGenerator => _voronoiGenerator; public LocationGenerator LocationGenerator => _locationGenerator; public int seed = Environment.TickCount; public Vector2 size = new Vector2(); public GraphGeneratorDebug debug; public GenerationStage Stage { get; internal set; } = GenerationStage.Start; [Range(2.0f, 64.0f)] public float radius = 8; private double _directrix = 0.0f; private Random _random; private Graph _locations; void Start() { _filter = GetComponent(); Reset(); } public void Reset() { _random = new System.Random(seed); _sampler = new PoissonDiskSampler(radius); _sampler.Generator = _random; _points = new List(); _points.Add(new Vector3(0, -0.1f)); _points.Add(new Vector3(size.x, 0.1f)); _points.Add(new Vector3(size.x, size.y - 0.1f)); _points.Add(new Vector3(0, size.y + 0.1f)); _points.AddRange(_sampler.Generate(size.x, size.y)); Stage = GenerationStage.Voronoi; _voronoiGenerator = new VoronoiGenerator(_points.Select(x => new Point(x.x, x.y)).ToList()); _locationGenerator = null; } public void Generate() { while (Stage != GenerationStage.Done) { Step(); } } public void Step() { if (Stage == GenerationStage.Voronoi) { _voronoiGenerator.Step(); _directrix = _voronoiGenerator.Line.Directrix; if (_voronoiGenerator.Done) { Stage = GenerationStage.Location; _locationGenerator = new LocationGenerator(_voronoiGenerator.Delaunay.Morph(p => new Location(p)), _random); _locationGenerator.Init( sections.Select(section => (_random.Next(_voronoiGenerator.Delaunay.Vertices.Count), section.color)) .Concat(new List<(int, Color)> { (0, Color.white), (1, Color.white), (2, Color.white), (3, Color.white)}) .ToArray() ); } } else if (Stage == GenerationStage.Location) { _locationGenerator.Step(); if (_locationGenerator.Done) Stage = GenerationStage.Done; } } void OnDrawGizmos() { if (!debug.enabled || _voronoiGenerator == null) return; if (debug.displayPoints) { Gizmos.color = Color.white; DisplayGraphVertices(_voronoiGenerator.Delaunay); } if (debug.displayNeighbours) { Gizmos.color = Color.yellow; DisplayGraphEdges(_voronoiGenerator.Delaunay); } if (debug.displayVertices) { Gizmos.color = Color.blue; DisplayGraphVertices(_voronoiGenerator.Voronoi); } if (debug.displayHalfEdges) { Gizmos.color = Color.green; foreach (var edge in _voronoiGenerator.HalfEdges) { Gizmos.DrawLine(edge.Start.ToVector3(), edge.End.ToVector3()); } } if (debug.displayEdges) { Gizmos.color = Color.white; DisplayGraphEdges(_voronoiGenerator.Voronoi); } Gizmos.color = Color.red; Gizmos.DrawLine(new Vector3(0, (float)_voronoiGenerator.Line.Directrix), new Vector3(size.x, (float)_voronoiGenerator.Line.Directrix)); Gizmos.color = Color.blue; Vector3 start; if (debug.displayParabolas) { foreach (var parabola in _voronoiGenerator.Line.Parabolas) { start = new Vector3(-size.x, (float)_voronoiGenerator.Line.EvalParabola(parabola.Site.Point, -size.x)); for (var x = -size.x + 0.1f; x < size.x * 2; x += 0.1f) { var point = new Vector3(x, (float)_voronoiGenerator.Line.EvalParabola(parabola.Site.Point, x)); Gizmos.DrawLine(start, point); start = point; } } } if (debug.displayBeachLine) { Gizmos.color = Color.white; start = new Vector3(-size.x, (float)_voronoiGenerator.Line.Eval(-size.x)); for (var x = -size.x + 0.1f; x < size.x * 2; x += 0.1f) { var point = new Vector3(x, (float)_voronoiGenerator.Line.Eval(x)); Gizmos.DrawLine(start, point); start = point; } } if (debug.displayLocations && LocationGenerator != null) { foreach (var location in LocationGenerator.Result.Vertices.Where(l => l.Type != Color.white)) { Gizmos.color = location.Type; Gizmos.DrawSphere(location.Center.ToVector3(), 2); } } } public void Rewind(float y) { while (_voronoiGenerator.Line.Directrix < y && !_voronoiGenerator.Done) { _voronoiGenerator.Step(); } } private void DisplayGraphVertices(Graph graph) { var vertices = graph.Vertices.Select(p => p.ToVector3()); var offset = Vector3.right; foreach (var (v, i) in vertices.Select((x, i) => (x, i))) { Gizmos.DrawSphere(v, 1); if (debug.displayLabels) Handles.Label(v + offset, $"{i}"); } } private void DisplayGraphEdges(Graph graph) { var edges = graph.Edges .Select(edge => (graph.Vertices[edge.Item1], graph.Vertices[edge.Item2])) .Select(edge => (edge.Item1.ToVector3(), edge.Item2.ToVector3())); foreach (var (s, e) in edges) Gizmos.DrawLine(s, e); } } }