inz-00/Assets/Generators/PoissonDiskSampler.cs
2019-07-27 22:40:42 +02:00

103 lines
2.9 KiB
C#

using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace Assets.Generators
{
public class PoissonDiskSampler
{
public System.Random Generator { get; set; } = new System.Random();
private int k;
private float r;
private float Random(float max, float min = 0)
{
return (float)Generator.NextDouble() * (max - min) + min;
}
public PoissonDiskSampler(float r, int k = 30)
{
this.k = k;
this.r = r;
}
public IEnumerable<Vector3> Generate(float width, float height)
{
var size = r / Mathf.Sqrt(2);
var grid = new Dictionary<(int, int), Vector3>();
var active = new List<Vector3>();
var initial = new Vector3(Random(width), Random(height));
void AddToGrid(Vector3 point)
{
grid.Add(
(Mathf.FloorToInt(point.x / size), Mathf.FloorToInt(point.y / size)),
point
);
active.Add(point);
}
bool IsPointOk(Vector3 point)
{
if (point.x < 0 || point.y < 0 || point.x > width || point.y > height)
{
return false;
}
var x = Mathf.FloorToInt(point.x / size);
var y = Mathf.FloorToInt(point.y / size);
var neighbours = (new List<(int, int)>() {
(x - 1, y + 1), (x, y + 1), (x + 1, y + 1),
(x - 1, y), (x, y), (x + 1, y),
(x - 1, y - 1), (x, y - 1), (x + 1, y - 1)
});
return neighbours
.Where(p => grid.ContainsKey(p))
.All(p => Vector3.Distance(point, grid[p]) > r);
}
Vector3 RandomPoint(Vector3 origin)
{
var angle = Random(Mathf.PI * 2);
var length = Random(2 * r, r);
return origin + new Vector3(Mathf.Sin(angle), Mathf.Cos(angle)) * length;
}
AddToGrid(initial);
yield return initial;
int watchdog = 3000000;
while (active.Count > 0 && watchdog-- > 0)
{
var current = Mathf.FloorToInt(Random(active.Count));
var point = active[current];
var i = 0;
for (; i < k; i++)
{
var candidate = RandomPoint(point);
if (IsPointOk(candidate))
{
AddToGrid(candidate);
yield return candidate;
break;
}
}
if (i >= k)
{
active.RemoveAt(current);
}
}
}
}
}