here's Poisson Disc Sampling Algorithm realization that I use:
using System.Collections.Generic;
using UnityEngine;
using Random = UnityEngine.Random;
namespace Assets.Scripts.Algorithms
{
public static class PoissonDiscSampling
{
public static List<Vector2> GeneratePoints(int seed, float radius, Vector2 sampleRegionSize, int sampleAmount)
{
Random.InitState(seed);
float cellSize = radius / Mathf.Sqrt(2);
var grid = new int[Mathf.FloorToInt(sampleRegionSize.x / cellSize), Mathf.FloorToInt(sampleRegionSize.y / cellSize)];
var points = new List<Vector2>();
var spawnPoints = new List<Vector2>
{
sampleRegionSize / 2
};
while (spawnPoints.Count > 0)
{
int spawnIndex = Random.Range(0, spawnPoints.Count);
Vector2 spawnCenter = spawnPoints[spawnIndex];
bool accepted = false;
for (int i = 0; i < sampleAmount; i++)
{
float angle = Random.value * Mathf.PI * 2;
var direction = new Vector2(Mathf.Sin(angle), Mathf.Cos(angle));
Vector2 candidate = spawnCenter + direction * Random.Range(radius, radius * 2);
if (IsValid(candidate, sampleRegionSize, cellSize, radius, points, grid))
{
points.Add(candidate);
spawnPoints.Add(candidate);
grid[(int)(candidate.x / cellSize), (int)(candidate.y / cellSize)] = points.Count;
accepted = true;
break;
}
}
if (!accepted)
{
spawnPoints.RemoveAt(spawnIndex);
}
}
return points;
}
private static bool IsValid(Vector2 candidate, Vector2 sampleRegionSize, float cellSize, float radius,
IList<Vector2> points, int[,] grid)
{
if (candidate.x >= 0 && candidate.x < sampleRegionSize.x && candidate.y >= 0 && candidate.y < sampleRegionSize.y)
{
int cellX = (int)(candidate.x / cellSize);
int cellY = (int)(candidate.y / cellSize);
int searchStartX = Mathf.Max(0, cellX - 2);
int searchStartY = Mathf.Max(0, cellY - 2);
int searchEndX = Mathf.Min(cellX + 2, grid.GetLength(0) - 1);
int searchEndY = Mathf.Min(cellY + 2, grid.GetLength(1) - 1);
for (int x = searchStartX; x <= searchEndX; x++)
{
for (int y = searchStartY; y <= searchEndY; y++)
{
int pointIndex = grid[x, y] - 1;
if (pointIndex != -1)
{
float sqrDistance = (candidate - points[pointIndex]).sqrMagnitude;
if (sqrDistance < radius * radius)
{
return false;
}
}
}
}
}
return true;
}
}
}
But there's a slight problem with this algorithm realization. On the line:
grid[(int)(candidate.x / cellSize), (int)(candidate.y / cellSize)] = points.Count;
After few iterations it throws
Index Out Of Range Exception
Somehow (I am still not sure why) It goes beyond the size of the grid, sometimes it can be negative and sometimes it can be basically equal the size of the grid.
I'm stuck and I don't know what to do. It seems that there's something that I simply don't see.
Thanks!