103 lines
2.9 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} |