/**
* @module Math
* @submodule Random
* @for p5
* @requires core
*/
import p5 from '../core/main';
// variables used for random number generators
const randomStateProp = '_lcg_random_state';
// Set to values from http://en.wikipedia.org/wiki/Numerical_Recipes
// m is basically chosen to be large (as it is the max period)
// and for its relationships to a and c
const m = 4294967296;
// a - 1 should be divisible by m's prime factors
const a = 1664525;
// c and m should be co-prime
const c = 1013904223;
let y2 = 0;
// Linear Congruential Generator that stores its state at instance[stateProperty]
p5.prototype._lcg = function(stateProperty) {
// define the recurrence relationship
this[stateProperty] = (a * this[stateProperty] + c) % m;
// return a float in [0, 1)
// we've just used % m, so / m is always < 1
return this[stateProperty] / m;
};
p5.prototype._lcgSetSeed = function(stateProperty, val) {
// pick a random seed if val is undefined or null
// the >>> 0 casts the seed to an unsigned 32-bit integer
this[stateProperty] = (val == null ? Math.random() * m : val) >>> 0;
};
/**
* Sets the seed value for random().
*
* By default, random() produces different results each time the program
* is run. Set the seed parameter to a constant to return the same
* pseudo-random numbers each time the software is run.
*
* @method randomSeed
* @param {Number} seed the seed value
* @example
*
*
* randomSeed(99);
* for (let i = 0; i < 100; i++) {
* let r = random(0, 255);
* stroke(r);
* line(i, 0, i, 100);
* }
*
*
*
* @alt
* many vertical lines drawn in white, black or grey.
*
*/
p5.prototype.randomSeed = function(seed) {
this._lcgSetSeed(randomStateProp, seed);
this._gaussian_previous = false;
};
/**
* Return a random floating-point number.
*
* Takes either 0, 1 or 2 arguments.
*
* If no argument is given, returns a random number from 0
* up to (but not including) 1.
*
* If one argument is given and it is a number, returns a random number from 0
* up to (but not including) the number.
*
* If one argument is given and it is an array, returns a random element from
* that array.
*
* If two arguments are given, returns a random number from the
* first argument up to (but not including) the second argument.
*
* @method random
* @param {Number} [min] the lower bound (inclusive)
* @param {Number} [max] the upper bound (exclusive)
* @return {Number} the random number
* @example
*
*
* for (let i = 0; i < 100; i++) {
* let r = random(50);
* stroke(r * 5);
* line(50, i, 50 + r, i);
* }
*
*
*
*
* for (let i = 0; i < 100; i++) {
* let r = random(-50, 50);
* line(50, i, 50 + r, i);
* }
*
*
*
*
* // Get a random element from an array using the random(Array) syntax
* let words = ['apple', 'bear', 'cat', 'dog'];
* let word = random(words); // select random word
* text(word, 10, 50); // draw the word
*
*
*
* @alt
* 100 horizontal lines from center canvas to right. size+fill change each time
* 100 horizontal lines from center of canvas. height & side change each render
* word displayed at random. Either apple, bear, cat, or dog
*
*/
/**
* @method random
* @param {Array} choices the array to choose from
* @return {*} the random element from the array
* @example
*/
p5.prototype.random = function(min, max) {
p5._validateParameters('random', arguments);
let rand;
if (this[randomStateProp] != null) {
rand = this._lcg(randomStateProp);
} else {
rand = Math.random();
}
if (typeof min === 'undefined') {
return rand;
} else if (typeof max === 'undefined') {
if (min instanceof Array) {
return min[Math.floor(rand * min.length)];
} else {
return rand * min;
}
} else {
if (min > max) {
const tmp = min;
min = max;
max = tmp;
}
return rand * (max - min) + min;
}
};
/**
*
* Returns a random number fitting a Gaussian, or
* normal, distribution. There is theoretically no minimum or maximum
* value that randomGaussian() might return. Rather, there is
* just a very low probability that values far from the mean will be
* returned; and a higher probability that numbers near the mean will
* be returned.
*
* Takes either 0, 1 or 2 arguments.
* If no args, returns a mean of 0 and standard deviation of 1.
* If one arg, that arg is the mean (standard deviation is 1).
* If two args, first is mean, second is standard deviation.
*
* @method randomGaussian
* @param {Number} mean the mean
* @param {Number} sd the standard deviation
* @return {Number} the random number
* @example
*
*
* for (let y = 0; y < 100; y++) {
* let x = randomGaussian(50, 15);
* line(50, y, x, y);
* }
*
*
*
*
* let distribution = new Array(360);
*
* function setup() {
* createCanvas(100, 100);
* for (let i = 0; i < distribution.length; i++) {
* distribution[i] = floor(randomGaussian(0, 15));
* }
* }
*
* function draw() {
* background(204);
*
* translate(width / 2, width / 2);
*
* for (let i = 0; i < distribution.length; i++) {
* rotate(TWO_PI / distribution.length);
* stroke(0);
* let dist = abs(distribution[i]);
* line(0, 0, dist, 0);
* }
* }
*
*
* @alt
* 100 horizontal lines from center of canvas. height & side change each render
* black lines radiate from center of canvas. size determined each render
*/
p5.prototype.randomGaussian = function(mean, sd) {
let y1, x1, x2, w;
if (this._gaussian_previous) {
y1 = y2;
this._gaussian_previous = false;
} else {
do {
x1 = this.random(2) - 1;
x2 = this.random(2) - 1;
w = x1 * x1 + x2 * x2;
} while (w >= 1);
w = Math.sqrt(-2 * Math.log(w) / w);
y1 = x1 * w;
y2 = x2 * w;
this._gaussian_previous = true;
}
const m = mean || 0;
const s = sd || 1;
return y1 * s + m;
};
export default p5;