/* * The MIT License * * Copyright (c) 2016-2021 JOML * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package com.jozufozu.flywheel.repack.joml.sampling; import java.util.ArrayList; import com.jozufozu.flywheel.repack.joml.Random; import com.jozufozu.flywheel.repack.joml.Vector2f; /** * Generates Poisson samples. *
* The algorithm implemented here is based on Fast Poisson Disk Sampling in Arbitrary * Dimensions. * * @author Kai Burjack */ public class PoissonSampling { /** * Generates Poisson samples on a disk. *
* The algorithm implemented here is based on Fast Poisson Disk Sampling in Arbitrary
* Dimensions.
*
* @author Kai Burjack
*/
public static class Disk {
private final Vector2f[] grid;
private final float diskRadius;
private final float diskRadiusSquared;
private final float minDist;
private final float minDistSquared;
private final float cellSize;
private final int numCells;
private final Random rnd;
private final ArrayList processList;
/**
* Create a new instance of {@link Disk} which computes poisson-distributed samples on a disk with the given radius diskRadius
and notifies the given
* callback
for each found sample point.
*
* The samples are distributed evenly on the disk with a minimum distance to one another of at least minDist
.
*
* @param seed
* the seed to initialize the random number generator with
* @param diskRadius
* the disk radius
* @param minDist
* the minimum distance between any two generated samples
* @param k
* determines how many samples are tested before rejection. Higher values produce better results. Typical values are 20 to 30
* @param callback
* will be notified about each sample point
*/
public Disk(long seed, float diskRadius, float minDist, int k, Callback2d callback) {
this.diskRadius = diskRadius;
this.diskRadiusSquared = diskRadius * diskRadius;
this.minDist = minDist;
this.minDistSquared = minDist * minDist;
this.rnd = new Random(seed);
this.cellSize = minDist / (float) Math.sqrt(2.0);
this.numCells = (int) ((diskRadius * 2) / cellSize) + 1;
this.grid = new Vector2f[numCells * numCells];
this.processList = new ArrayList();
compute(k, callback);
}
private void compute(int k, Callback2d callback) {
float x, y;
do {
x = rnd.nextFloat() * 2.0f - 1.0f;
y = rnd.nextFloat() * 2.0f - 1.0f;
} while (x * x + y * y > 1.0f);
Vector2f initial = new Vector2f(x, y);
processList.add(initial);
callback.onNewSample(initial.x, initial.y);
insert(initial);
while (!processList.isEmpty()) {
int i = rnd.nextInt(processList.size());
Vector2f sample = (Vector2f) processList.get(i);
boolean found = false;
search: for (int s = 0; s < k; s++) {
float angle = rnd.nextFloat() * (float) Math.PI2;
float radius = minDist * (rnd.nextFloat() + 1.0f);
x = (float) (radius * Math.sin_roquen_9(angle + Math.PIHalf));
y = (float) (radius * Math.sin_roquen_9(angle));
x += sample.x;
y += sample.y;
if (x * x + y * y > diskRadiusSquared)
continue search;
if (!searchNeighbors(x, y)) {
found = true;
callback.onNewSample(x, y);
Vector2f f = new Vector2f(x, y);
processList.add(f);
insert(f);
break;
}
}
if (!found) {
processList.remove(i);
}
}
}
private boolean searchNeighbors(float px, float py) {
int row = (int) ((py + diskRadius) / cellSize);
int col = (int) ((px + diskRadius) / cellSize);
if (grid[row * numCells + col] != null)
return true;
int minX = Math.max(0, col - 1);
int minY = Math.max(0, row - 1);
int maxX = Math.min(col + 1, numCells - 1);
int maxY = Math.min(row + 1, numCells - 1);
for (int y = minY; y <= maxY; y++) {
for (int x = minX; x <= maxX; x++) {
Vector2f v = grid[y * numCells + x];
if (v != null) {
float dx = v.x - px;
float dy = v.y - py;
if (dx * dx + dy * dy < minDistSquared) {
return true;
}
}
}
}
return false;
}
private void insert(Vector2f p) {
int row = (int) ((p.y + diskRadius) / cellSize);
int col = (int) ((p.x + diskRadius) / cellSize);
grid[row * numCells + col] = p;
}
}
}