/* * 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; } } }