/**
* This module is used to create different point distributions that can be
* turned into different tile sets when made into a graph format. There are
* various different distributions that can be used to create interesting
* tile patterns when turned into a voronoi diagram.
*
* @tutorial Point-Distributions
*
* @class PointDistribution
*/
"use strict";
import Poisson from "poisson-disk-sample";
import Vector from "../geometry/Vector";
import Rectangle from "../geometry/Rectangle";
import Rand from "./Rand";
/**
* Creates a random distribution of points in a particular bounding box
* with a particular average distance between points.
*
* @export
* @param {Rectangle} bbox The bounding box to create the points in
* @param {number} d Average distance between points
* @param {number} [seed=null] If specified use a local seed for creating the point
* distribution. Otherwise, use the current global seed for generation
* @returns {Vector[]} The list of randomly distributed points
* @memberof PointDistribution
*/
export function random(bbox, d, seed = null) {
const rng = seed ? new Rand(seed) : Rand;
const nPoints = bbox.area / (d * d);
let points = [];
for (let i = 0; i < nPoints; i++) {
points.push(rng.vector(bbox));
}
return points;
}
/**
* Creates a square grid like distribution of points in a particular bounding
* box with a particular distance between points.
*
* @export
* @param {Rectangle} bbox The bounding box to create the points in
* @param {number} d Average distance between points
* @returns {Vector[]} The list of randomly distributed points
* @memberof PointDistribution
*/
export function square(bbox, d) {
const dx = d / 2;
const dy = dx;
let points = [];
for (let y = 0; y < bbox.height; y += d) {
for (let x = 0; x < bbox.width; x += d) {
points.push(new Vector(dx + x, dy + y));
}
}
return points;
}
/**
* Creates a uniform hexagonal distribution of points in a particular bounding
* box with a particular distance between points. The hexagons can also be
* specified to have a particular width or height as well as creating hexagons
* that have "pointy" tops or "flat" tops. By default it makes flat tops.
*
* @export
* @param {Rectangle} bbox The bounding box to create the points in
* @param {number} d Average distance between points
* @param {boolean} [flatTop=true] Create hecagons with flat tops by default.
* Otherwise go with the pointy top hexagons.
* @param {number} w The width of the hexagon tiles
* @param {number} h The height of the hexagon tiles
* @returns {Vector[]} The list of randomly distributed points
* @memberof PointDistribution
*/
export function hexagon(bbox, d, flatTop = true, w, h) {
// Need to allow for the change of height and width
// Running into "Uncaught Voronoi.closeCells() > this makes no sense!"
const dx = d / 2;
const dy = dx;
let points = [];
const altitude = Math.sqrt(3) / 2 * d;
var N = Math.sqrt(bbox.area / (d * d));
for (let y = 0; y < N; y++) {
for (let x = 0; x < N; x++) {
points.push(new Vector((0.5 + x) / N * bbox.width,
(0.25 + 0.5 * x % 2 + y) / N * bbox.height));
// points.push(new Vector((y % 2) * dx + x * d + dx, y * d + dy)); // Pointy Top
// points.push(new Vector(x * d, (x % 2) * dx + y * d)); // Flat Top
}
}
return points;
}
/**
* Creates a blue noise distribution of points in a particular bounding box
* with a particular average distance between points. This is done by
* creating a grid system and picking a random point in each grid. This has
* the effect of creating a less random distribution of points. The second
* parameter m determins the spacing between points in the grid. This ensures
* that no two points are in the same grid.
*
* @summary Create a jittered grid based random blue noise point distribution.
*
* @export
* @param {Rectangle} bbox The bounding box to create the points in
* @param {number} d Average distance between points
* @param {number} [seed=null] If specified use a local seed for creating the point
* distribution. Otherwise, use the current global seed for generation
* @param {number} [m=0] Maximum distance away from the edge of the grid that a
* point can be placed. This acts to increase the padding between points.
* This makes the noise less random. This number must be smaller than d.
* @returns {Vector[]} The list of randomly distributed points
* @memberof PointDistribution
*/
export function jitteredGrid(bbox, d, seed = null, m = 0) {
const rng = seed ? new Rand(seed) : Rand;
let points = [];
let pointBox;
for (let y = 0; y < bbox.height - d; y += d) {
for (let x = 0; x < bbox.width - d; x += d) {
// Local bbox for the point to generate in
const boxPos = new Vector(x - d + m, y - d + m);
pointBox = new Rectangle(boxPos, x - m, y - m);
points.push(rng.vector(pointBox));
}
}
return points;
}
/**
* Creates a poisson, or blue noise distribution of points in a particular
* bounding box with a particular average distance between points. This is
* done by using poisson disk sampling which tries to create points so that the
* distance between neighbors is as close to a fixed number (the distance d)
* as possible. This algorithm is implemented using the poisson dart throwing
* algorithm.
*
* @summary Create a blue noise distribution of points using poisson disk
* sampling.
*
* @export
* @param {Rectangle} bbox The bounding box to create the points in
* @param {number} d Average distance between points
* @returns {Vector[]} The list of randomly distributed points
*
* @see {@link https://www.jasondavies.com/poisson-disc/}
* @see {@link https://github.com/jeffrey-hearn/poisson-disk-sample}
* @memberof PointDistribution
*/
export function poisson(bbox, d) {
var sampler = new Poisson(bbox.width, bbox.height, d, d);
var solution = sampler.sampleUntilSolution();
var points = solution.map(point => new Vector(point));
return points;
}
/**
* Creates a blue noise distribution of points in a particular bounding box
* with a particular average distance between points. This is done by using
* recursive wang tiles to create this distribution of points.
*
* @summary Not Implemented Yet
*
* @export
* @param {Rectangle} bbox The bounding box to create the points in
* @param {number} d Average distance between points
* @returns {Vector[]} The list of randomly distributed points
* @memberof PointDistribution
*/
export function recursiveWang(bbox, d) {
throw "Error: Not Implemented";
}
/**
* Creates a circular distribution of points in a particular bounding box
* with a particular average distance between points.
*
* @summary Not Implemented Yet
*
* @export
* @param {Rectangle} bbox The bounding box to create the points in
* @param {number} d Average distance between points
* @returns {Vector[]} The list of randomly distributed points
* @memberof PointDistribution
*/
export function circular(bbox, d) {
throw "Error: Not Implemented";
}