/random.js
https://github.com/mattdesl/canvas-sketch-util · JavaScript · 328 lines · 283 code · 44 blank · 1 comment · 53 complexity · 45d315f58ff6fccddc06829bc36d814e MD5 · raw file
- var seedRandom = require('seed-random');
- var SimplexNoise = require('simplex-noise');
- var defined = require('defined');
- function createRandom (defaultSeed) {
- defaultSeed = defined(defaultSeed, null);
- var defaultRandom = Math.random;
- var currentSeed;
- var currentRandom;
- var noiseGenerator;
- var _nextGaussian = null;
- var _hasNextGaussian = false;
- setSeed(defaultSeed);
- return {
- value: value,
- createRandom: function (defaultSeed) {
- return createRandom(defaultSeed);
- },
- setSeed: setSeed,
- getSeed: getSeed,
- getRandomSeed: getRandomSeed,
- valueNonZero: valueNonZero,
- permuteNoise: permuteNoise,
- noise1D: noise1D,
- noise2D: noise2D,
- noise3D: noise3D,
- noise4D: noise4D,
- sign: sign,
- boolean: boolean,
- chance: chance,
- range: range,
- rangeFloor: rangeFloor,
- pick: pick,
- shuffle: shuffle,
- onCircle: onCircle,
- insideCircle: insideCircle,
- onSphere: onSphere,
- insideSphere: insideSphere,
- quaternion: quaternion,
- weighted: weighted,
- weightedSet: weightedSet,
- weightedSetIndex: weightedSetIndex,
- gaussian: gaussian
- };
- function setSeed (seed, opt) {
- if (typeof seed === 'number' || typeof seed === 'string') {
- currentSeed = seed;
- currentRandom = seedRandom(currentSeed, opt);
- } else {
- currentSeed = undefined;
- currentRandom = defaultRandom;
- }
- noiseGenerator = createNoise();
- _nextGaussian = null;
- _hasNextGaussian = false;
- }
- function value () {
- return currentRandom();
- }
- function valueNonZero () {
- var u = 0;
- while (u === 0) u = value();
- return u;
- }
- function getSeed () {
- return currentSeed;
- }
- function getRandomSeed () {
- var seed = String(Math.floor(Math.random() * 1000000));
- return seed;
- }
- function createNoise () {
- return new SimplexNoise(currentRandom);
- }
- function permuteNoise () {
- noiseGenerator = createNoise();
- }
- function noise1D (x, frequency, amplitude) {
- if (!isFinite(x)) throw new TypeError('x component for noise() must be finite');
- frequency = defined(frequency, 1);
- amplitude = defined(amplitude, 1);
- return amplitude * noiseGenerator.noise2D(x * frequency, 0);
- }
- function noise2D (x, y, frequency, amplitude) {
- if (!isFinite(x)) throw new TypeError('x component for noise() must be finite');
- if (!isFinite(y)) throw new TypeError('y component for noise() must be finite');
- frequency = defined(frequency, 1);
- amplitude = defined(amplitude, 1);
- return amplitude * noiseGenerator.noise2D(x * frequency, y * frequency);
- }
- function noise3D (x, y, z, frequency, amplitude) {
- if (!isFinite(x)) throw new TypeError('x component for noise() must be finite');
- if (!isFinite(y)) throw new TypeError('y component for noise() must be finite');
- if (!isFinite(z)) throw new TypeError('z component for noise() must be finite');
- frequency = defined(frequency, 1);
- amplitude = defined(amplitude, 1);
- return amplitude * noiseGenerator.noise3D(
- x * frequency,
- y * frequency,
- z * frequency
- );
- }
- function noise4D (x, y, z, w, frequency, amplitude) {
- if (!isFinite(x)) throw new TypeError('x component for noise() must be finite');
- if (!isFinite(y)) throw new TypeError('y component for noise() must be finite');
- if (!isFinite(z)) throw new TypeError('z component for noise() must be finite');
- if (!isFinite(w)) throw new TypeError('w component for noise() must be finite');
- frequency = defined(frequency, 1);
- amplitude = defined(amplitude, 1);
- return amplitude * noiseGenerator.noise4D(
- x * frequency,
- y * frequency,
- z * frequency,
- w * frequency
- );
- }
- function sign () {
- return boolean() ? 1 : -1;
- }
- function boolean () {
- return value() > 0.5;
- }
- function chance (n) {
- n = defined(n, 0.5);
- if (typeof n !== 'number') throw new TypeError('expected n to be a number');
- return value() < n;
- }
- function range (min, max) {
- if (max === undefined) {
- max = min;
- min = 0;
- }
- if (typeof min !== 'number' || typeof max !== 'number') {
- throw new TypeError('Expected all arguments to be numbers');
- }
- return value() * (max - min) + min;
- }
- function rangeFloor (min, max) {
- if (max === undefined) {
- max = min;
- min = 0;
- }
- if (typeof min !== 'number' || typeof max !== 'number') {
- throw new TypeError('Expected all arguments to be numbers');
- }
- return Math.floor(range(min, max));
- }
- function pick (array) {
- if (array.length === 0) return undefined;
- return array[rangeFloor(0, array.length)];
- }
- function shuffle (arr) {
- if (!Array.isArray(arr)) {
- throw new TypeError('Expected Array, got ' + typeof arr);
- }
- var rand;
- var tmp;
- var len = arr.length;
- var ret = arr.slice();
- while (len) {
- rand = Math.floor(value() * len--);
- tmp = ret[len];
- ret[len] = ret[rand];
- ret[rand] = tmp;
- }
- return ret;
- }
- function onCircle (radius, out) {
- radius = defined(radius, 1);
- out = out || [];
- var theta = value() * 2.0 * Math.PI;
- out[0] = radius * Math.cos(theta);
- out[1] = radius * Math.sin(theta);
- return out;
- }
- function insideCircle (radius, out) {
- radius = defined(radius, 1);
- out = out || [];
- onCircle(1, out);
- var r = radius * Math.sqrt(value());
- out[0] *= r;
- out[1] *= r;
- return out;
- }
- function onSphere (radius, out) {
- radius = defined(radius, 1);
- out = out || [];
- var u = value() * Math.PI * 2;
- var v = value() * 2 - 1;
- var phi = u;
- var theta = Math.acos(v);
- out[0] = radius * Math.sin(theta) * Math.cos(phi);
- out[1] = radius * Math.sin(theta) * Math.sin(phi);
- out[2] = radius * Math.cos(theta);
- return out;
- }
- function insideSphere (radius, out) {
- radius = defined(radius, 1);
- out = out || [];
- var u = value() * Math.PI * 2;
- var v = value() * 2 - 1;
- var k = value();
- var phi = u;
- var theta = Math.acos(v);
- var r = radius * Math.cbrt(k);
- out[0] = r * Math.sin(theta) * Math.cos(phi);
- out[1] = r * Math.sin(theta) * Math.sin(phi);
- out[2] = r * Math.cos(theta);
- return out;
- }
- function quaternion (out) {
- out = out || [];
- var u1 = value();
- var u2 = value();
- var u3 = value();
- var sq1 = Math.sqrt(1 - u1);
- var sq2 = Math.sqrt(u1);
- var theta1 = Math.PI * 2 * u2;
- var theta2 = Math.PI * 2 * u3;
- var x = Math.sin(theta1) * sq1;
- var y = Math.cos(theta1) * sq1;
- var z = Math.sin(theta2) * sq2;
- var w = Math.cos(theta2) * sq2;
- out[0] = x;
- out[1] = y;
- out[2] = z;
- out[3] = w;
- return out;
- }
- function weightedSet (set) {
- set = set || [];
- if (set.length === 0) return null;
- return set[weightedSetIndex(set)].value;
- }
- function weightedSetIndex (set) {
- set = set || [];
- if (set.length === 0) return -1;
- return weighted(set.map(function (s) {
- return s.weight;
- }));
- }
- function weighted (weights) {
- weights = weights || [];
- if (weights.length === 0) return -1;
- var totalWeight = 0;
- var i;
- for (i = 0; i < weights.length; i++) {
- totalWeight += weights[i];
- }
- if (totalWeight <= 0) throw new Error('Weights must sum to > 0');
- var random = value() * totalWeight;
- for (i = 0; i < weights.length; i++) {
- if (random < weights[i]) {
- return i;
- }
- random -= weights[i];
- }
- return 0;
- }
- function gaussian (mean, standardDerivation) {
- mean = defined(mean, 0);
- standardDerivation = defined(standardDerivation, 1);
- // https://github.com/openjdk-mirror/jdk7u-jdk/blob/f4d80957e89a19a29bb9f9807d2a28351ed7f7df/src/share/classes/java/util/Random.java#L496
- if (_hasNextGaussian) {
- _hasNextGaussian = false;
- var result = _nextGaussian;
- _nextGaussian = null;
- return mean + standardDerivation * result;
- } else {
- var v1 = 0;
- var v2 = 0;
- var s = 0;
- do {
- v1 = value() * 2 - 1; // between -1 and 1
- v2 = value() * 2 - 1; // between -1 and 1
- s = v1 * v1 + v2 * v2;
- } while (s >= 1 || s === 0);
- var multiplier = Math.sqrt(-2 * Math.log(s) / s);
- _nextGaussian = (v2 * multiplier);
- _hasNextGaussian = true;
- return mean + standardDerivation * (v1 * multiplier);
- }
- }
- }
- module.exports = createRandom();