phaser
Version:
A fast, free and fun HTML5 Game Framework for Desktop and Mobile web browsers from the team at Phaser Studio Inc.
189 lines (177 loc) • 5.78 kB
JavaScript
/**
* @author Benjamin D. Richards <benjamindrichards@gmail.com>
* @copyright 2013-2026 Phaser Studio Inc.
* @license {@link https://opensource.org/licenses/MIT|MIT License}
*/
var Vector2 = require('./Vector2');
var Vector3 = require('./Vector3');
var Vector4 = require('./Vector4');
/**
* Hash a number or list of numbers.
*
* A hash is an unpredictable transformation of an input,
* which always returns the same output from the same input.
* It is useful for generating random data in a predictable way.
*
* For example, you could use a hash to place objects around a scene.
* Given the same inputs, the objects would always appear in the same places,
* even though those places appear random.
*
* This function takes 1-4 numbers as input, and returns a single number from 0-1.
*
* Disclaimer: This function is intended for efficiently generating visual
* variety. It is not intended for cryptographic use. Do not use it for
* security/authentication/encryption purposes. Use a proper tool instead.
*
* Performance note: A 16ms frame has enough time to generate
* tens or hundreds of thousands of hash values, depending on system and other activity.
*
* You can select from different hashing algorithms.
*
* - 0: TRIG. This uses sine functions and dot products to hash the input.
* - 1: PCG. This uses a permuted congruential generator to hash the input,
* but is restricted to integer inputs.
* - 2: PCG_FLOAT. This variant of PCG accepts float inputs.
*
* TRIG is the same algorithm used in Phaser 4's Noise object and relatives.
* It produces decent variety, and accepts any number as input.
* Its precision is 32 bits.
* Its input values may lose distinction if larger than 32 bits (4294967296).
* Its output values cannot differ by more than 1/4294967296.
* This algorithm is designed to work on graphics hardware that lacks
* bitshifting capabilities, and is not state of the art.
*
* PCG is a Permuted Congruential Generator.
* See https://www.pcg-random.org/ for more information on the theory.
* This algorithm is more modern and considered higher quality.
* It runs slightly faster than TRIG.
* It only accepts whole numbers (integers) as input.
* Its precision is 32 bits.
* Its input values may lose distinction if larger than 32 bits (4294967296).
* Its output values cannot differ by more than 1/4294967296.
*
* PCG_FLOAT works just like PCG, but accepts floating-point inputs.
* This conversion process causes it to run slightly slower than TRIG.
* The same precision concerns apply.
*
* If your hash values start clustering, you may be using values
* outside the safe range, where bits available for precision are insufficient.
* You can keep values in a safe range by sampling along circular paths.
* This is why it's useful to have several dimensions of input.
*
* @example
* // Given a `time` value:
* var output = Phaser.Math.Hash([Math.cos(time), Math.sin(time)]);
*
* @function Phaser.Math.Hash
* @since 4.0.0
*
* @param {number|number[]} vector - The number or number list to hash. 1 to 4 numbers.
* @param {number} [algorithm=0] - The algorithm to use. 0 is TRIG. 1 is PCG. 2 is PCG_FLOAT.
*
* @return {number} - A number from 0-1.
*/
var Hash = function (vector, algorithm)
{
if (typeof vector === 'number') { vector = [ vector ]; }
switch (algorithm)
{
case 2:
{
// PCG with float inputs
return pcg(vector, true);
}
case 1:
{
// PCG
return pcg(vector);
}
case 0:
default:
{
// Trig: same as shader hash function.
return trig(vector);
}
}
};
var vec2 = new Vector2();
var vec3 = new Vector3();
var vec4 = new Vector4();
var trig1 = 12.9898;
var trig2 = new Vector2(12.9898, 78.233);
var trig3 = new Vector3(12.9898, 78.233, 9441.8953);
var trig4 = new Vector4(12.9898, 78.233, 9441.8953, 61.99);
var trig = function (vector)
{
var x;
switch (vector.length)
{
case 4:
{
vec4.set(vector[0], vector[1], vector[2], vector[3]);
x = 43757.5453 * Math.sin(vec4.dot(trig4));
break;
}
case 3:
{
vec3.set(vector[0], vector[1], vector[2]);
x = 43757.5453 * Math.sin(vec3.dot(trig3));
break;
}
case 2:
{
vec2.set(vector[0], vector[1]);
x = 43757.5453 * Math.sin(vec2.dot(trig2));
break;
}
case 1:
default:
{
x = 43757.5453 * Math.sin(vector[0] * trig1);
break;
}
}
return x - Math.floor(x);
};
var twoE32 = Math.pow(2, 32);
var pcg = function (vector, convertFloat)
{
// Combine vector.
var a;
switch (vector.length)
{
case 4:
{
a = vector[0] * 19 + vector[1] * 47 + vector[2] * 101 + vector[3] * 173;
break;
}
case 3:
{
a = vector[0] * 19 + vector[1] * 47 + vector[2] * 101;
break;
}
case 2:
{
a = vector[0] * 19 + vector[1] * 47;
break;
}
case 1:
default:
{
a = vector[0] * 19;
break;
}
}
// Expand floating-point numbers into the 32-bit range.
if (convertFloat)
{
a = a * Math.pow(2, 32 - Math.floor(Math.log2(a)));
}
// Perform all math as unsigned 32-bit using >>>.
var state = (a >>> 0) * 747796405 + 2891336453;
var word = ((state >>> ((state >>> 28) + 4)) ^ state) * 277803737;
var x = ((word >>> 22) ^ word) >>> 0;
x /= twoE32;
return x;
};
module.exports = Hash;