mirror of
https://github.com/Xevion/procedural-placement.git
synced 2025-12-08 12:08:08 -06:00
implement poisson sampling
This commit is contained in:
@@ -1,23 +1,26 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using NUnit.Framework.Constraints;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
public static class PointGeneration {
|
public static class PointGeneration {
|
||||||
public static List<Vector2> random_sampling(int numPoints, Vector2 regionSize) {
|
public static List<Vector2> random_sampling(int numPoints, Vector2 regionSize) {
|
||||||
// Create a new empty list of points, and add some randomly
|
// Create a new empty list of points, and add some randomly
|
||||||
var points = new List<Vector2>();
|
List<Vector2> points = new List<Vector2>();
|
||||||
for (var i = 0; i < numPoints; i++) {
|
for (var i = 0; i < numPoints; i++) {
|
||||||
points.Add(
|
points.Add(
|
||||||
new Vector2(
|
new Vector2(
|
||||||
Random.Range(-regionSize.x, regionSize.x),
|
Random.Range(0, regionSize.x * 2),
|
||||||
Random.Range(-regionSize.y, regionSize.y)));
|
Random.Range(0, regionSize.y * 2)
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
return points;
|
return points;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<Vector2> poisson_sampling(int numPoints, Vector2 regionSize, int radius) {
|
public static List<Vector2> poisson_sampling(int numPoints, Vector2 regionSize, int radius, int attempts = 30) {
|
||||||
float cellSize = radius / Mathf.Sqrt(2);
|
float cellSize = radius / Mathf.Sqrt(2);
|
||||||
|
|
||||||
|
// A grid for
|
||||||
int[,] grid = new int[Mathf.CeilToInt(regionSize.x / cellSize), Mathf.CeilToInt(regionSize.y / cellSize)];
|
int[,] grid = new int[Mathf.CeilToInt(regionSize.x / cellSize), Mathf.CeilToInt(regionSize.y / cellSize)];
|
||||||
List<Vector2> points = new List<Vector2>();
|
List<Vector2> points = new List<Vector2>();
|
||||||
List<Vector2> spawnPoints = new List<Vector2>();
|
List<Vector2> spawnPoints = new List<Vector2>();
|
||||||
@@ -26,8 +29,58 @@ public static class PointGeneration {
|
|||||||
while (spawnPoints.Count > 0) {
|
while (spawnPoints.Count > 0) {
|
||||||
int spawnIndex = Random.Range(0, spawnPoints.Count);
|
int spawnIndex = Random.Range(0, spawnPoints.Count);
|
||||||
Vector2 spawnCenter = spawnPoints[spawnIndex];
|
Vector2 spawnCenter = spawnPoints[spawnIndex];
|
||||||
|
bool candidateAccepted = false;
|
||||||
|
|
||||||
|
for (int i = 0; i < attempts; i++) {
|
||||||
|
float angle = Random.value * Mathf.PI * 2; // Random radian angle
|
||||||
|
Vector2 dir = new Vector2(Mathf.Sin(angle), Mathf.Cos(angle));
|
||||||
|
Vector2 candidate = spawnCenter + dir * Random.Range(radius, radius * 2);
|
||||||
|
|
||||||
|
if (poisson_valid(candidate, regionSize, cellSize, points, grid, radius)) {
|
||||||
|
points.Add(candidate);
|
||||||
|
spawnPoints.Add(candidate);
|
||||||
|
grid[(int) (candidate.x / cellSize), (int) (candidate.y / cellSize)] = points.Count;
|
||||||
|
candidateAccepted = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!candidateAccepted) {
|
||||||
|
spawnPoints.RemoveAt(spawnIndex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return points;
|
return points;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool IsValidPoint(Vector2 point, Vector2 region) {
|
||||||
|
return point.x >= 0 && point.y >= 0 && point.x < region.x && point.y < region.y;
|
||||||
|
}
|
||||||
|
private static bool poisson_valid(Vector2 candidate, Vector2 regionSize, float cellSize, List<Vector2> points,
|
||||||
|
int[,] grid, float radius) {
|
||||||
|
// Check that the point is valid
|
||||||
|
if (!IsValidPoint(candidate, regionSize))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
int cellX = (int) (candidate.x / cellSize);
|
||||||
|
int cellY = (int) (candidate.y / cellSize);
|
||||||
|
int searchStartX = Mathf.Max(0, cellX - 2);
|
||||||
|
int searchEndX = Mathf.Min(cellX + 2, grid.GetLength(0) - 1);
|
||||||
|
int searchStartY = Mathf.Max(0, cellY - 2);
|
||||||
|
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) continue;
|
||||||
|
|
||||||
|
float sqrDst = (candidate - points[pointIndex]).sqrMagnitude;
|
||||||
|
if (sqrDst < radius * radius) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -41,7 +41,7 @@ public class PointRendering : MonoBehaviour {
|
|||||||
_points = PointGeneration.random_sampling(numPoints, regionSize);
|
_points = PointGeneration.random_sampling(numPoints, regionSize);
|
||||||
break;
|
break;
|
||||||
case SamplingTypes.Poisson:
|
case SamplingTypes.Poisson:
|
||||||
_points = new List<Vector2>();
|
_points = PointGeneration.poisson_sampling(numPoints, regionSize, 1, 30);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new ArgumentOutOfRangeException();
|
throw new ArgumentOutOfRangeException();
|
||||||
|
|||||||
Reference in New Issue
Block a user