inz-00/Assets/Scripts/GraphGenerator.cs
2019-08-15 20:22:18 +02:00

246 lines
7.8 KiB
C#

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<Vector3> _points = new List<Vector3>();
public List<Section> sections = new List<Section>();
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<Location> _locations;
void Start()
{
_filter = GetComponent<MeshFilter>();
Reset();
}
public void Reset()
{
_random = new System.Random(seed);
_sampler = new PoissonDiskSampler(radius);
_sampler.Generator = _random;
_points = new List<Vector3>();
_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<Point> 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<Point> 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);
}
}
}