From a6d2da2a8ab8afc8609286200f65cb1698dc9619 Mon Sep 17 00:00:00 2001 From: Paul Date: Sun, 9 Nov 2014 01:42:28 -0800 Subject: [PATCH] Math/Utils - various cleanup and deprecations/moves There are no known breaking changes. - Timer - Uses standard Math.min/Math.max (it's better 2, 3 items). - Math - Updated documentation - Marked various Math functions as deprecated, proxying as appropriate - Array-based functions -> ArrayUtils - RNG-based functions -> Utils - Updated core-usage - floor/ceil should not be used (alternatives provided) - Altered for some equivalencies - Also fixes some assorted issues - Marked a few internal functions as private - Utils - Moved polyfills to their own file for better visibility - Moved array functions to ArrayUtils and marked proxies as deprecated - Created Phaser.ArrayUtils for array-related functions - polyfills moved to their own file - Functions given function names - Added Math.trunc --- src/core/Group.js | 2 +- src/geom/Circle.js | 14 +- src/geom/Point.js | 14 +- src/math/Math.js | 454 +++++++++++++++--------------------- src/polyfills.js | 154 ++++++++++++ src/time/Time.js | 10 +- src/utils/ArrayUtils.js | 199 ++++++++++++++++ src/utils/Utils.js | 281 +++++----------------- tasks/manifests/phaser.json | 2 + 9 files changed, 610 insertions(+), 520 deletions(-) create mode 100644 src/polyfills.js create mode 100644 src/utils/ArrayUtils.js diff --git a/src/core/Group.js b/src/core/Group.js index 70ddabc97..f133debfe 100644 --- a/src/core/Group.js +++ b/src/core/Group.js @@ -1595,7 +1595,7 @@ Phaser.Group.prototype.getRandom = function (startIndex, length) { startIndex = startIndex || 0; length = length || this.children.length; - return this.game.math.getRandom(this.children, startIndex, length); + return Phaser.ArrayUtils.getRandomItem(this.children, startIndex, length); }; diff --git a/src/geom/Circle.js b/src/geom/Circle.js index 817e0f8a9..b5db2185a 100644 --- a/src/geom/Circle.js +++ b/src/geom/Circle.js @@ -125,21 +125,13 @@ Phaser.Circle.prototype = { * (can be Circle, Point or anything with x/y properties) * @method Phaser.Circle#distance * @param {object} dest - The target object. Must have visible x and y properties that represent the center of the object. - * @param {boolean} [round] - Round the distance to the nearest integer (default false). + * @param {boolean} [round=false] - Round the distance to the nearest integer. * @return {number} The distance between this Point object and the destination Point object. */ distance: function (dest, round) { - if (typeof round === "undefined") { round = false; } - - if (round) - { - return Phaser.Math.distanceRounded(this.x, this.y, dest.x, dest.y); - } - else - { - return Phaser.Math.distance(this.x, this.y, dest.x, dest.y); - } + var distance = Phaser.Math.distance(this.x, this.y, dest.x, dest.y); + return round ? Math.round(distance) : distance; }, diff --git a/src/geom/Point.js b/src/geom/Point.js index 99342b230..3594a8dcf 100644 --- a/src/geom/Point.js +++ b/src/geom/Point.js @@ -708,21 +708,13 @@ Phaser.Point.rperp = function (a, out) { * @method Phaser.Point.distance * @param {object} a - The target object. Must have visible x and y properties that represent the center of the object. * @param {object} b - The target object. Must have visible x and y properties that represent the center of the object. -* @param {boolean} [round] - Round the distance to the nearest integer (default false). +* @param {boolean} [round=false] - Round the distance to the nearest integer. * @return {number} The distance between this Point object and the destination Point object. */ Phaser.Point.distance = function (a, b, round) { - if (typeof round === "undefined") { round = false; } - - if (round) - { - return Phaser.Math.distanceRounded(a.x, a.y, b.x, b.y); - } - else - { - return Phaser.Math.distance(a.x, a.y, b.x, b.y); - } + var distance = Phaser.Math.distance(a.x, a.y, b.x, b.y); + return round ? Math.round(distance) : distance; }; diff --git a/src/math/Math.js b/src/math/Math.js index e50814652..8ad37ef95 100644 --- a/src/math/Math.js +++ b/src/math/Math.js @@ -12,102 +12,105 @@ Phaser.Math = { /** - * = 2 π - * @method Phaser.Math#PI2 + * Twice PI. + * @property {number} Phaser.Math#PI2 + * @default ~6.283 */ PI2: Math.PI * 2, /** - * Two number are fuzzyEqual if their difference is less than ε. + * Two number are fuzzyEqual if their difference is less than epsilon. + * * @method Phaser.Math#fuzzyEqual * @param {number} a * @param {number} b - * @param {number} epsilon - * @return {boolean} True if |a-b|<ε + * @param {number} [epsilon=(small value)] + * @return {boolean} True if |a-b|b+ε + * @param {number} [epsilon=(small value)] + * @return {boolean} True if a>b+epsilon */ fuzzyGreaterThan: function (a, b, epsilon) { - if (typeof epsilon === "undefined") { epsilon = 0.0001; } + if (typeof epsilon === 'undefined') { epsilon = 0.0001; } return a > b - epsilon; }, /** * @method Phaser.Math#fuzzyCeil + * * @param {number} val - * @param {number} epsilon - * @return {boolean} ceiling(val-ε) + * @param {number} [epsilon=(small value)] + * @return {boolean} ceiling(val-epsilon) */ fuzzyCeil: function (val, epsilon) { - if (typeof epsilon === "undefined") { epsilon = 0.0001; } + if (typeof epsilon === 'undefined') { epsilon = 0.0001; } return Math.ceil(val - epsilon); }, /** * @method Phaser.Math#fuzzyFloor + * * @param {number} val - * @param {number} epsilon - * @return {boolean} floor(val-ε) + * @param {number} [epsilon=(small value)] + * @return {boolean} floor(val-epsilon) */ fuzzyFloor: function (val, epsilon) { - if (typeof epsilon === "undefined") { epsilon = 0.0001; } + if (typeof epsilon === 'undefined') { epsilon = 0.0001; } return Math.floor(val + epsilon); }, /** - * Averages all values passed to the function and returns the result. You can pass as many parameters as you like. + * Averages all values passed to the function and returns the result. + * * @method Phaser.Math#average + * @params {...number} The numbers to average * @return {number} The average of all given values. */ average: function () { - var args = []; + var sum = 0; - for (var _i = 0; _i < (arguments.length - 0); _i++) { - args[_i] = arguments[_i + 0]; + for (var i = 0; i < arguments.length; i++) { + sum += (+arguments[i]); } - var avg = 0; - - for (var i = 0; i < args.length; i++) { - avg += args[i]; - } - - return avg / args.length; + return sum / arguments.length; }, /** * @method Phaser.Math#truncate * @param {number} n - * @return {number} + * @return {integer} + * @deprecated 2.1.4 - Use `Math.trunc` (now with polyfill) */ truncate: function (n) { - return (n > 0) ? Math.floor(n) : Math.ceil(n); + return Math.trunc(n); }, /** @@ -132,7 +135,7 @@ Phaser.Math = { */ snapTo: function (input, gap, start) { - if (typeof start === "undefined") { start = 0; } + if (typeof start === 'undefined') { start = 0; } if (gap === 0) { return input; @@ -158,7 +161,7 @@ Phaser.Math = { */ snapToFloor: function (input, gap, start) { - if (typeof start === "undefined") { start = 0; } + if (typeof start === 'undefined') { start = 0; } if (gap === 0) { return input; @@ -184,7 +187,7 @@ Phaser.Math = { */ snapToCeil: function (input, gap, start) { - if (typeof start === "undefined") { start = 0; } + if (typeof start === 'undefined') { start = 0; } if (gap === 0) { return input; @@ -197,66 +200,53 @@ Phaser.Math = { }, - /** * Snaps a value to the nearest value in an array. + * * @method Phaser.Math#snapToInArray * @param {number} input - * @param {array} arr + * @param {number[]} arr * @param {boolean} sort - True if the array needs to be sorted. * @return {number} + * @deprecated 2.1.4 - See {@link Phaser.Utils.Arrays.findClosest} for an alternative. */ snapToInArray: function (input, arr, sort) { - if (typeof sort === "undefined") { sort = true; } + if (typeof sort === 'undefined') { sort = true; } if (sort) { arr.sort(); } - if (input < arr[0]) { - return arr[0]; - } - - var i = 1; - - while (arr[i] < input) { - i++; - } - - var low = arr[i - 1]; - var high = (i < arr.length) ? arr[i] : Number.POSITIVE_INFINITY; - - return ((high - input) <= (input - low)) ? high : low; + return Phaser.Utils.Arrays.findClosest(input, arr); }, /** - * Round to some place comparative to a 'base', default is 10 for decimal place. + * Round to some place comparative to a `base`, default is 10 for decimal place. + * The `place` is represented by the power applied to `base` to get that place. * - * 'place' is represented by the power applied to 'base' to get that place - * e.g. - * 2000/7 ~= 285.714285714285714285714 ~= (bin)100011101.1011011011011011 + * e.g. 2000/7 ~= 285.714285714285714285714 ~= (bin)100011101.1011011011011011 * - * roundTo(2000/7,3) === 0 - * roundTo(2000/7,2) == 300 - * roundTo(2000/7,1) == 290 - * roundTo(2000/7,0) == 286 - * roundTo(2000/7,-1) == 285.7 - * roundTo(2000/7,-2) == 285.71 - * roundTo(2000/7,-3) == 285.714 - * roundTo(2000/7,-4) == 285.7143 - * roundTo(2000/7,-5) == 285.71429 + * roundTo(2000/7,3) === 0 + * roundTo(2000/7,2) == 300 + * roundTo(2000/7,1) == 290 + * roundTo(2000/7,0) == 286 + * roundTo(2000/7,-1) == 285.7 + * roundTo(2000/7,-2) == 285.71 + * roundTo(2000/7,-3) == 285.714 + * roundTo(2000/7,-4) == 285.7143 + * roundTo(2000/7,-5) == 285.71429 * - * roundTo(2000/7,3,2) == 288 -- 100100000 - * roundTo(2000/7,2,2) == 284 -- 100011100 - * roundTo(2000/7,1,2) == 286 -- 100011110 - * roundTo(2000/7,0,2) == 286 -- 100011110 - * roundTo(2000/7,-1,2) == 285.5 -- 100011101.1 - * roundTo(2000/7,-2,2) == 285.75 -- 100011101.11 - * roundTo(2000/7,-3,2) == 285.75 -- 100011101.11 - * roundTo(2000/7,-4,2) == 285.6875 -- 100011101.1011 - * roundTo(2000/7,-5,2) == 285.71875 -- 100011101.10111 + * roundTo(2000/7,3,2) == 288 -- 100100000 + * roundTo(2000/7,2,2) == 284 -- 100011100 + * roundTo(2000/7,1,2) == 286 -- 100011110 + * roundTo(2000/7,0,2) == 286 -- 100011110 + * roundTo(2000/7,-1,2) == 285.5 -- 100011101.1 + * roundTo(2000/7,-2,2) == 285.75 -- 100011101.11 + * roundTo(2000/7,-3,2) == 285.75 -- 100011101.11 + * roundTo(2000/7,-4,2) == 285.6875 -- 100011101.1011 + * roundTo(2000/7,-5,2) == 285.71875 -- 100011101.10111 * * Note what occurs when we round to the 3rd space (8ths place), 100100000, this is to be assumed * because we are rounding 100011.1011011011011011 which rounds up. @@ -269,8 +259,8 @@ Phaser.Math = { */ roundTo: function (value, place, base) { - if (typeof place === "undefined") { place = 0; } - if (typeof base === "undefined") { base = 10; } + if (typeof place === 'undefined') { place = 0; } + if (typeof base === 'undefined') { base = 10; } var p = Math.pow(base, -place); @@ -287,8 +277,8 @@ Phaser.Math = { */ floorTo: function (value, place, base) { - if (typeof place === "undefined") { place = 0; } - if (typeof base === "undefined") { base = 10; } + if (typeof place === 'undefined') { place = 0; } + if (typeof base === 'undefined') { base = 10; } var p = Math.pow(base, -place); @@ -305,8 +295,8 @@ Phaser.Math = { */ ceilTo: function (value, place, base) { - if (typeof place === "undefined") { place = 0; } - if (typeof base === "undefined") { base = 10; } + if (typeof place === 'undefined') { place = 0; } + if (typeof base === 'undefined') { base = 10; } var p = Math.pow(base, -place); @@ -333,7 +323,7 @@ Phaser.Math = { * @param {number} y1 * @param {number} x2 * @param {number} y2 - * @return {number} + * @return {number} The angle, in radians. */ angleBetween: function (x1, y1, x2, y2) { return Math.atan2(y2 - y1, x2 - x1); @@ -349,7 +339,7 @@ Phaser.Math = { * @param {number} y1 * @param {number} x2 * @param {number} y2 - * @return {number} + * @return {number} The angle, in radians. */ angleBetweenY: function (x1, y1, x2, y2) { return Math.atan2(x2 - x1, y2 - y1); @@ -360,7 +350,7 @@ Phaser.Math = { * @method Phaser.Math#angleBetweenPoints * @param {Phaser.Point} point1 * @param {Phaser.Point} point2 - * @return {number} + * @return {number} The angle, in radians. */ angleBetweenPoints: function (point1, point2) { return Math.atan2(point2.y - point1.y, point2.x - point1.x); @@ -371,7 +361,7 @@ Phaser.Math = { * @method Phaser.Math#angleBetweenPointsY * @param {Phaser.Point} point1 * @param {Phaser.Point} point2 - * @return {number} + * @return {number} The angle, in radians. */ angleBetweenPointsY: function (point1, point2) { return Math.atan2(point2.x - point1.x, point2.y - point1.y); @@ -405,9 +395,10 @@ Phaser.Math = { * @method Phaser.Math#normalizeLatitude * @param {number} lat - The latitude to normalize, in degrees. * @return {number} Returns the latitude, fit within the [-90,90] range. + * @deprecated 2.1.4 - Use {@link Phaser.Math#clamp}. */ normalizeLatitude: function (lat) { - return Math.max(-90, Math.min(90, lat)); + return Phaser.Math.clamp(lat, -90, 90); }, /** @@ -415,53 +406,25 @@ Phaser.Math = { * @method Phaser.Math#normalizeLongitude * @param {number} lng - The longitude to normalize, in degrees. * @return {number} Returns the longitude, fit within the [-180,180] range. + * @deprecated 2.1.4 - Use {@link Phaser.Math#wrap}. */ normalizeLongitude: function (lng) { - - if (lng % 360 == 180) - { - return 180; - } - - lng = lng % 360; - return lng < -180 ? lng + 360 : lng > 180 ? lng - 360 : lng; - + return Phaser.Math.wrap(lng, -180, 180); }, /** * Generate a random bool result based on the chance value. - *

+ * * Returns true or false based on the chance value (default 50%). For example if you wanted a player to have a 30% chance * of getting a bonus, call chanceRoll(30) - true means the chance passed, false means it failed. - *

+ * * @method Phaser.Math#chanceRoll * @param {number} chance - The chance of receiving the value. A number between 0 and 100 (effectively 0% to 100%). * @return {boolean} True if the roll passed, or false otherwise. + * @deprecated 2.1.4 - Use {@link Phaser.Utils.chanceRoll} */ chanceRoll: function (chance) { - - if (typeof chance === "undefined") { chance = 50; } - - if (chance <= 0) - { - return false; - } - else if (chance >= 100) - { - return true; - } - else - { - if (Math.random() * 100 >= chance) - { - return false; - } - else - { - return true; - } - } - + return Phaser.Utils.chanceRoll(chance); }, /** @@ -491,14 +454,12 @@ Phaser.Math = { * `start` up to but not including `end`. If `start` is less than `stop` a * zero-length range is created unless a negative `step` is specified. * - * @static * @method Phaser.Math#numberArrayStep * @param {number} [start=0] - The start of the range. * @param {number} end - The end of the range. * @param {number} [step=1] - The value to increment or decrement by. * @returns {Array} Returns the new array of numbers. * @example - * * Phaser.Math.numberArrayStep(4); * // => [0, 1, 2, 3] * @@ -544,7 +505,7 @@ Phaser.Math = { // use `Array(length)` so engines like Chakra and V8 avoid slower modes // http://youtu.be/XAqIpGU8ZZk#t=17m25s var index = -1; - var length = Phaser.Math.max(Phaser.Math.ceil((end - start) / (step || 1)), 0); + var length = Math.max(Phaser.Math.roundAwayFromZero((end - start) / (step || 1)), 0); var result = new Array(length); while (++index < length) @@ -563,20 +524,11 @@ Phaser.Math = { * @method Phaser.Math#maxAdd * @param {number} value - The value to add the amount to. * @param {number} amount - The amount to add to the value. - * @param {number} max- The maximum the value is allowed to be. + * @param {number} max - The maximum the value is allowed to be. * @return {number} */ maxAdd: function (value, amount, max) { - - value += amount; - - if (value > max) - { - value = max; - } - - return value; - + return Math.min(value + amount, max); }, /** @@ -589,16 +541,7 @@ Phaser.Math = { * @return {number} The new value. */ minSub: function (value, amount, min) { - - value -= amount; - - if (value < min) - { - value = min; - } - - return value; - + return Math.max(value - amount, min); }, /** @@ -661,67 +604,58 @@ Phaser.Math = { * @param {number} min - The minimum the value can be. * @param {number} max - The maximum the value can be. * @return {number} The limited value. + * @deprecated 2.1.4 - Use {@link Phaser.Math#clamp} */ limitValue: function(value, min, max) { - - return value < min ? min : value > max ? max : value; - + return Phaser.Math.clamp(value, min, max); }, /** * Randomly returns either a 1 or -1. * * @method Phaser.Math#randomSign - * @return {number} 1 or -1 + * @return {number} Either 1 or -1 + * @deprecated 2.1.4 - Use {@link Phaser.Utils.randomChoice} or other */ randomSign: function () { - - return (Math.random() > 0.5) ? 1 : -1; - + return Phaser.Utils.randomChoice(-1, 1); }, /** * Returns true if the number given is odd. * * @method Phaser.Math#isOdd - * @param {number} n - The number to check. - * @return {boolean} True if the given number is odd. False if the given number is even. + * @param {integer} n - The number to check. + * @return {boolean} True if the given number is odd. False if the given number is even. */ isOdd: function (n) { - + // Does not work with extremely large values return (n & 1); - }, /** * Returns true if the number given is even. * * @method Phaser.Math#isEven - * @param {number} n - The number to check. + * @param {integer} n - The number to check. * @return {boolean} True if the given number is even. False if the given number is odd. */ isEven: function (n) { - - if (n & 1) - { - return false; - } - else - { - return true; - } - + // Does not work with extremely large values + return !(n & 1); }, /** - * Updated version of Math.min that can be passed either an array of numbers or the numbers as parameters. - * See http://jsperf.com/math-s-min-max-vs-homemade/5 + * Variation of Math.min that can be passed either an array of numbers or the numbers as parameters. + * + * Prefer the standard `Math.min` function when appropriate. * * @method Phaser.Math#min * @return {number} The lowest value from those given. + * @see {@link http://jsperf.com/math-s-min-max-vs-homemade} */ min: function () { - + if (arguments.length === 1 && typeof arguments[0] === 'object') { var data = arguments[0]; @@ -730,7 +664,7 @@ Phaser.Math = { { var data = arguments; } - + for (var i = 1, min = 0, len = data.length; i < len; i++) { if (data[i] < data[min]) @@ -744,13 +678,16 @@ Phaser.Math = { }, /** - * Updated version of Math.max that can be passed either an array of numbers or the numbers as parameters. + * Variation of Math.max that can be passed either an array of numbers or the numbers as parameters. + * + * Prefer the standard `Math.max` function when appropriate. * * @method Phaser.Math#max * @return {number} The largest value from those given. + * @see {@link http://jsperf.com/math-s-min-max-vs-homemade} */ max: function () { - + if (arguments.length === 1 && typeof arguments[0] === 'object') { var data = arguments[0]; @@ -759,7 +696,7 @@ Phaser.Math = { { var data = arguments; } - + for (var i = 1, max = 0, len = data.length; i < len; i++) { if (data[i] > data[max]) @@ -773,7 +710,7 @@ Phaser.Math = { }, /** - * Updated version of Math.min that can be passed a property and either an array of objects or the objects as parameters. + * Vartiation of Math.min that can be passed a property and either an array of objects or the objects as parameters. * It will find the lowest matching property value from the given objects. * * @method Phaser.Math#minProperty @@ -803,7 +740,7 @@ Phaser.Math = { }, /** - * Updated version of Math.max that can be passed a property and either an array of objects or the objects as parameters. + * Variation of Math.max that can be passed a property and either an array of objects or the objects as parameters. * It will find the largest matching property value from the given objects. * * @method Phaser.Math#maxProperty @@ -969,6 +906,7 @@ Phaser.Math = { * @param {number} p1 * @param {number} t * @return {number} + * @private */ linear: function (p0, p1, t) { return (p1 - p0) * t + p0; @@ -979,6 +917,7 @@ Phaser.Math = { * @param {number} n * @param {number} i * @return {number} + * @private */ bernstein: function (n, i) { return this.factorial(n) / this.factorial(i) / this.factorial(n - i); @@ -988,6 +927,7 @@ Phaser.Math = { * @method Phaser.Math#factorial * @param {number} value - the number you want to evaluate * @return {number} + * @private */ factorial : function( value ){ @@ -1004,6 +944,7 @@ Phaser.Math = { } return res; + }, /** @@ -1016,6 +957,7 @@ Phaser.Math = { * @param {number} p3 * @param {number} t * @return {number} + * @private */ catmullRom: function (p0, p1, p2, p3, t) { @@ -1044,29 +986,10 @@ Phaser.Math = { * @param {number} startIndex - Optional offset off the front of the array. Default value is 0, or the beginning of the array. * @param {number} length - Optional restriction on the number of values you want to randomly select from. * @return {object} The random object that was selected. + * @deprecated 2.1.4 - Use {@link Phaser.Utils.Arrays.getRandomItem} */ getRandom: function (objects, startIndex, length) { - - if (typeof startIndex === "undefined") { startIndex = 0; } - if (typeof length === "undefined") { length = 0; } - - if (objects != null) { - - var l = length; - - if ((l === 0) || (l > objects.length - startIndex)) - { - l = objects.length - startIndex; - } - - if (l > 0) - { - return objects[startIndex + Math.floor(Math.random() * l)]; - } - } - - return null; - + return Phaser.Utils.Arrays.getRandomItem(objects, startIndex, length); }, /** @@ -1078,78 +1001,74 @@ Phaser.Math = { * @param {number} startIndex - Optional offset off the front of the array. Default value is 0, or the beginning of the array. * @param {number} length - Optional restriction on the number of values you want to randomly select from. * @return {object} The random object that was removed. + * @deprecated 2.1.4 - Use {@link Phaser.Utils.Arrays.removeRandomItem} */ removeRandom: function (objects, startIndex, length) { - - if (typeof startIndex === "undefined") { startIndex = 0; } - if (typeof length === "undefined") { length = 0; } - - if (objects != null) { - - var l = length; - - if ((l === 0) || (l > objects.length - startIndex)) - { - l = objects.length - startIndex; - } - - if (l > 0) - { - var idx = startIndex + Math.floor(Math.random() * l); - var removed = objects.splice(idx, 1); - return removed[0]; - } - } - - return null; - + return Phaser.Utils.Arrays.removeRandomItem(objects, startIndex, length); }, /** - * Round down to the next whole number. E.g. floor(1.7) == 1, and floor(-2.7) == -2. + * _Do not use this function._ + * + * Round to the next whole number _towards_ zero. + * + * E.g. `floor(1.7) == 1`, and `floor(-2.7) == -2`. * * @method Phaser.Math#floor - * @param {number} Value Any number. - * @return {number} The rounded value of that number. + * @param {number} value - Any number. + * @return {integer} The rounded value of that number. + * @deprecated 2.1.4 - Use {@link Phaser.Math#truncate} or instead. */ floor: function (value) { - - var n = value | 0; - - return (value > 0) ? (n) : ((n != value) ? (n - 1) : (n)); - + return Math.trunc(value); }, /** - * Round up to the next whole number. E.g. ceil(1.3) == 2, and ceil(-2.3) == -3. + * _Do not use this function._ + * + * Round to the next whole number _away_ from zero. + * + * E.g. `ceil(1.3) == 2`, and `ceil(-2.3) == -3`. * * @method Phaser.Math#ceil * @param {number} value - Any number. - * @return {number} The rounded value of that number. + * @return {integer} The rounded value of that number. + * @deprecated 2.1.4 - Use {@link Phaser.Math#roundAwayFromZero} instead. */ ceil: function (value) { - var n = value | 0; - return (value > 0) ? ((n != value) ? (n + 1) : (n)) : (n); + return Phaser.Math.roundAwayFromZero(value); + }, + + /** + * Round to the next whole number _away_ from zero. + * + * @method Phaser.Math#roundAwayFromZero + * @param {number} value - Any number. + * @return {integer} The rounded value of that number. + */ + roundAwayFromZero: function (value) { + // "Opposite" of truncate. + return (value > 0) ? Math.ceil(value) : Math.floor(value); }, /** * Generate a sine and cosine table simultaneously and extremely quickly. Based on research by Franky of scene.at - *

+ * * The parameters allow you to specify the length, amplitude and frequency of the wave. Once you have called this function * you should get the results via getSinTable() and getCosTable(). This generator is fast enough to be used in real-time. - *

+ * * @method Phaser.Math#sinCosGenerator * @param {number} length - The length of the wave * @param {number} sinAmplitude - The amplitude to apply to the sine table (default 1.0) if you need values between say -+ 125 then give 125 as the value * @param {number} cosAmplitude - The amplitude to apply to the cosine table (default 1.0) if you need values between say -+ 125 then give 125 as the value * @param {number} frequency - The frequency of the sine and cosine table data - * @return {Array} Returns the sine table + * @return {{sin:number[], cos:number[]}} Returns the table data. */ sinCosGenerator: function (length, sinAmplitude, cosAmplitude, frequency) { - if (typeof sinAmplitude === "undefined") { sinAmplitude = 1.0; } - if (typeof cosAmplitude === "undefined") { cosAmplitude = 1.0; } - if (typeof frequency === "undefined") { frequency = 1.0; } + if (typeof sinAmplitude === 'undefined') { sinAmplitude = 1.0; } + if (typeof cosAmplitude === 'undefined') { cosAmplitude = 1.0; } + if (typeof frequency === 'undefined') { frequency = 1.0; } var sin = sinAmplitude; var cos = cosAmplitude; @@ -1179,6 +1098,7 @@ Phaser.Math = { * @method Phaser.Math#shift * @param {array} stack - The array to shift. * @return {any} The shifted value. + * @deprecated 2.1.4 - Not used internally, better to do locally */ shift: function (stack) { @@ -1190,23 +1110,14 @@ Phaser.Math = { }, /** - * Shuffles the data in the given array into a new order + * Shuffles the data in the given array into a new order. * @method Phaser.Math#shuffleArray * @param {array} array - The array to shuffle * @return {array} The array + * @deprecated 2.1.4 - Use {@link Phaser.Utils.Arrays.shuffle} */ shuffleArray: function (array) { - - for (var i = array.length - 1; i > 0; i--) { - - var j = Math.floor(Math.random() * (i + 1)); - var temp = array[i]; - array[i] = array[j]; - array[j] = temp; - } - - return array; - + return Phaser.Utils.Arrays.shuffle(array); }, /** @@ -1256,6 +1167,7 @@ Phaser.Math = { * @param {number} x2 * @param {number} y2 * @return {number} The distance between this Point object and the destination Point object. + * @deprecated 2.1.4 - Do the rounding locally. */ distanceRounded: function (x1, y1, x2, y2) { @@ -1264,8 +1176,7 @@ Phaser.Math = { }, /** - * Force a value within the boundaries of two values. - * Clamp value to range + * Force a value within the boundaries by clamping `x` to the range `[a, b]`. * * @method Phaser.Math#clamp * @param {number} x @@ -1273,21 +1184,22 @@ Phaser.Math = { * @param {number} b * @return {number} */ - clamp: function ( x, a, b ) { + clamp: function (x, a, b) { return ( x < a ) ? a : ( ( x > b ) ? b : x ); }, /** - * Clamp value to range * @return {number} */ - mapLinear: function ( x, a1, a2, b1, b2 ) { + mapLinear: function (x, a1, a2, b1, b2) { return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 ); @@ -1334,7 +1247,7 @@ Phaser.Math = { * @param {number} max * @return {number} */ - smoothstep: function ( x, min, max ) { + smoothstep: function (x, min, max) { x = Math.max(0, Math.min(1, (x - min) / (max - min))); return x * x * (3 - 2 * x); @@ -1350,7 +1263,7 @@ Phaser.Math = { * @param {number} max * @return {number} */ - smootherstep: function ( x, min, max ) { + smootherstep: function (x, min, max) { x = Math.max(0, Math.min(1, (x - min) / (max - min))); return x * x * x * (x * (x * 6 - 15) + 10); @@ -1358,21 +1271,22 @@ Phaser.Math = { }, /** - * A value representing the sign of the value. - * -1 for negative, +1 for positive, 0 if value is 0 + * A value representing the sign of the value: -1 for negative, +1 for positive, 0 if value is 0. + * + * This works differently from `Math.sign` for value of NaN and -0, etc. * * @method Phaser.Math#sign * @param {number} x - * @return {number} + * @return {integer} An integer in {-1, 0, 1} */ - sign: function ( x ) { + sign: function (x) { return ( x < 0 ) ? -1 : ( ( x > 0 ) ? 1 : 0 ); }, /** - * Work out what percentage value a is of value b using the given base. + * Work out what percentage value `a` is of value `b` using the given base. * * @method Phaser.Math#percent * @param {number} a - The value to work out the percentage for. @@ -1403,16 +1317,15 @@ Phaser.Math = { * Convert degrees to radians. * * @method Phaser.Math#degToRad - * @return {function} + * @param {number} degrees - Angle in degrees. + * @return {number} Angle in radians. */ degToRad: (function() { var degreeToRadiansFactor = Math.PI / 180; - return function ( degrees ) { - + return function (degrees) { return degrees * degreeToRadiansFactor; - }; }()), @@ -1421,16 +1334,15 @@ Phaser.Math = { * Convert degrees to radians. * * @method Phaser.Math#radToDeg - * @return {function} + * @param {number} radians - Angle in radians. + * @return {number} Angle in degrees */ radToDeg: (function() { var radianToDegreesFactor = 180 / Math.PI; - return function ( radians ) { - + return function (radians) { return radians * radianToDegreesFactor; - }; }()) diff --git a/src/polyfills.js b/src/polyfills.js new file mode 100644 index 000000000..3bba83bdf --- /dev/null +++ b/src/polyfills.js @@ -0,0 +1,154 @@ +/** +* @copyright 2014 Photon Storm Ltd. +* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License} +*/ + +// ES6 Math.trunc - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/trunc +if (!Math.trunc) { + Math.trunc = function trunc(x) { + return x < 0 ? Math.ceil(x) : Math.floor(x); + }; +} + +/** +* A polyfill for Function.prototype.bind +*/ +if (!Function.prototype.bind) { + + /* jshint freeze: false */ + Function.prototype.bind = (function () { + + var slice = Array.prototype.slice; + + return function (thisArg) { + + var target = this, boundArgs = slice.call(arguments, 1); + + if (typeof target !== 'function') + { + throw new TypeError(); + } + + function bound() { + var args = boundArgs.concat(slice.call(arguments)); + target.apply(this instanceof bound ? this : thisArg, args); + } + + bound.prototype = (function F(proto) { + if (proto) + { + F.prototype = proto; + } + + if (!(this instanceof F)) + { + /* jshint supernew: true */ + return new F; + } + })(target.prototype); + + return bound; + }; + })(); +} + +/** +* A polyfill for Array.isArray +*/ +if (!Array.isArray) +{ + Array.isArray = function (arg) + { + return Object.prototype.toString.call(arg) == '[object Array]'; + }; +} + +/** +* A polyfill for Array.forEach +* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach +*/ +if (!Array.prototype.forEach) +{ + Array.prototype.forEach = function(fun /*, thisArg */) + { + "use strict"; + + if (this === void 0 || this === null) + { + throw new TypeError(); + } + + var t = Object(this); + var len = t.length >>> 0; + + if (typeof fun !== "function") + { + throw new TypeError(); + } + + var thisArg = arguments.length >= 2 ? arguments[1] : void 0; + + for (var i = 0; i < len; i++) + { + if (i in t) + { + fun.call(thisArg, t[i], i, t); + } + } + }; +} + +/** +* Low-budget Float32Array knock-off, suitable for use with P2.js in IE9 +* Source: http://www.html5gamedevs.com/topic/5988-phaser-12-ie9/ +* Cameron Foale (http://www.kibibu.com) +*/ +if (typeof window.Uint32Array !== "function" && typeof window.Uint32Array !== "object") +{ + var CheapArray = function(type) + { + var proto = new Array(); // jshint ignore:line + + window[type] = function(arg) { + + if (typeof(arg) === "number") + { + Array.call(this, arg); + this.length = arg; + + for (var i = 0; i < this.length; i++) + { + this[i] = 0; + } + } + else + { + Array.call(this, arg.length); + + this.length = arg.length; + + for (var i = 0; i < this.length; i++) + { + this[i] = arg[i]; + } + } + }; + + window[type].prototype = proto; + window[type].constructor = window[type]; + }; + + CheapArray('Uint32Array'); // jshint ignore:line + CheapArray('Int16Array'); // jshint ignore:line +} + +/** + * Also fix for the absent console in IE9 + */ +if (!window.console) +{ + window.console = {}; + window.console.log = window.console.assert = function(){}; + window.console.warn = window.console.assert = function(){}; +} + diff --git a/src/time/Time.js b/src/time/Time.js index cd5165ed4..6b2f2d4cf 100644 --- a/src/time/Time.js +++ b/src/time/Time.js @@ -288,7 +288,7 @@ Phaser.Time.prototype = { this.elapsed = this.now - this.prevTime; // time to call this function again in ms in case we're using timers instead of RequestAnimationFrame to update the game - this.timeToCall = Math.floor(this.game.math.max(0, (1000.0 / this.desiredFps) - (this.timeCallExpected - time))); + this.timeToCall = Math.floor(Math.max(0, (1000.0 / this.desiredFps) - (this.timeCallExpected - time))); // time when the next call is expected if using timers this.timeCallExpected = time + this.timeToCall; @@ -316,16 +316,16 @@ Phaser.Time.prototype = { if (this.advancedTiming) { - this.msMin = this.game.math.min(this.msMin, this.elapsed); - this.msMax = this.game.math.max(this.msMax, this.elapsed); + this.msMin = Math.min(this.msMin, this.elapsed); + this.msMax = Math.max(this.msMax, this.elapsed); this.frames++; if (this.now > this._timeLastSecond + 1000) { this.fps = Math.round((this.frames * 1000) / (this.now - this._timeLastSecond)); - this.fpsMin = this.game.math.min(this.fpsMin, this.fps); - this.fpsMax = this.game.math.max(this.fpsMax, this.fps); + this.fpsMin = Math.min(this.fpsMin, this.fps); + this.fpsMax = Math.max(this.fpsMax, this.fps); this._timeLastSecond = this.now; this.frames = 0; } diff --git a/src/utils/ArrayUtils.js b/src/utils/ArrayUtils.js new file mode 100644 index 000000000..a5a8bd6b8 --- /dev/null +++ b/src/utils/ArrayUtils.js @@ -0,0 +1,199 @@ +/* jshint supernew: true */ + +/** +* @author Richard Davey +* @copyright 2014 Photon Storm Ltd. +* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License} +*/ + +/** +* Utility functions for dealing with Arrays. +* +* @class Phaser.ArrayUtils +* @static +*/ +Phaser.ArrayUtils = { + + /** + * Fetch a random entry from the given array. + * Will return null if random selection is missing, or array has no entries. + * + * @method + * @param {any[]} objects - An array of objects. + * @param {integer} startIndex - Optional offset off the front of the array. Default value is 0, or the beginning of the array. + * @param {integer} length - Optional restriction on the number of values you want to randomly select from. + * @return {object} The random object that was selected. + */ + getRandomItem: function (objects, startIndex, length) { + + if (typeof startIndex === 'undefined') { startIndex = 0; } + if (typeof length === 'undefined') { length = 0; } + + if (objects == null) { // undefined or null + return null; + } + + var l = length; + + if ((l === 0) || (l > objects.length - startIndex)) + { + l = objects.length - startIndex; + } + + var randomIndex = startIndex + Math.floor(Math.random() * l); + return objects[randomIndex] || null; + + }, + + /** + * Removes a random object from the given array and returns it. + * Will return null if random selection is missing, or array has no entries. + * + * @method + * @param {any[]} objects - An array of objects. + * @param {integer} startIndex - Optional offset off the front of the array. Default value is 0, or the beginning of the array. + * @param {integer} length - Optional restriction on the number of values you want to randomly select from. + * @return {object} The random object that was removed. + */ + removeRandomItem: function (objects, startIndex, length) { + + if (typeof startIndex === 'undefined') { startIndex = 0; } + if (typeof length === 'undefined') { length = 0; } + + if (objects == null) { // undefined or null + return null; + } + + var l = length; + + if ((l === 0) || (l > objects.length - startIndex)) + { + l = objects.length - startIndex; + } + + if (l > 0) + { + var idx = startIndex + Math.floor(Math.random() * l); + var removed = objects.splice(idx, 1); + return removed[0]; + } + + return null; + + }, + + /** + * A standard Fisher-Yates Array shuffle implementation which modifies the array in place. + * + * @method + * @param {array} array - The array to shuffle. + * @return {array} The original array, now shuffled. + */ + shuffle: function (array) { + + for (var i = array.length - 1; i > 0; i--) + { + var j = Math.floor(Math.random() * (i + 1)); + var temp = array[i]; + array[i] = array[j]; + array[j] = temp; + } + + return array; + + }, + + /** + * Transposes the elements of the given Array. + * + * @method + * @param {array} array - The array to transpose. + * @return {array} The transposed array. + */ + transpose: function (array) { + + var result = new Array(array[0].length); + + for (var i = 0; i < array[0].length; i++) + { + result[i] = new Array(array.length - 1); + + for (var j = array.length - 1; j > -1; j--) + { + result[i][j] = array[j][i]; + } + } + + return result; + + }, + + /** + * Rotates the given array. + * Based on the routine from http://jsfiddle.net/MrPolywhirl/NH42z/ + * + * @method + * @param {array} matrix - The array to rotate. + * @param {number|string} direction - The amount to rotate. Either a number: 90, -90, 270, -270, 180 or a string: 'rotateLeft', 'rotateRight' or 'rotate180' + * @return {array} The rotated array + */ + rotate: function (matrix, direction) { + + if (typeof direction !== 'string') + { + direction = ((direction % 360) + 360) % 360; + } + + if (direction === 90 || direction === -270 || direction === 'rotateLeft') + { + matrix = Phaser.Utils.transposeArray(matrix); + matrix = matrix.reverse(); + } + else if (direction === -90 || direction === 270 || direction === 'rotateRight') + { + matrix = matrix.reverse(); + matrix = Phaser.Utils.transposeArray(matrix); + } + else if (Math.abs(direction) === 180 || direction === 'rotate180') + { + for (var i = 0; i < matrix.length; i++) + { + matrix[i].reverse(); + } + + matrix = matrix.reverse(); + } + + return matrix; + + }, + + /** + * Snaps a value to the nearest value in an array. + * The result will always be in the range `[first_value, last_value]`. + * + * @method + * @param {number} value - The search value + * @param {number[]} arr - The input array which _must_ be sorted. + * @return {number} The nearest value found. + */ + findClosest: function (value, arr) { + + if (value < arr[0]) { + return arr[0]; + } + + var i = 1; + + while (arr[i] < value) { + i++; + } + + var low = arr[i - 1]; + var high = (i < arr.length) ? arr[i] : Number.POSITIVE_INFINITY; + + return ((high - value) <= (value - low)) ? high : low; + + }, + +}; diff --git a/src/utils/Utils.js b/src/utils/Utils.js index 767e91dc5..998a56df7 100644 --- a/src/utils/Utils.js +++ b/src/utils/Utils.js @@ -77,68 +77,68 @@ Phaser.Utils = { }, /** - * Transposes the elements of the given Array. - * - * @method Phaser.Utils.transposeArray - * @param {array} array - The array to transpose. - * @return {array} The transposed array. - */ - transposeArray: function (array) { - - var result = new Array(array[0].length); - - for (var i = 0; i < array[0].length; i++) - { - result[i] = new Array(array.length - 1); - - for (var j = array.length - 1; j > -1; j--) - { - result[i][j] = array[j][i]; - } - } - - return result; - + * Generate a random bool result based on the chance value. + * + * Returns true or false based on the chance value (default 50%). For example if you wanted a player to have a 30% chance + * of getting a bonus, call chanceRoll(30) - true means the chance passed, false means it failed. + * + * @method Phaser.Math#chanceRoll + * @param {number} chance - The chance of receiving the value. A number between 0 and 100 (effectively 0% to 100%). + * @return {boolean} True if the roll passed, or false otherwise. + */ + chanceRoll: function (chance) { + if (typeof chance === 'undefined') { chance = 50; } + return chance > 0 && (Math.random() * 100 <= chance); }, /** - * Rotates the given array. - * Based on the routine from http://jsfiddle.net/MrPolywhirl/NH42z/ - * - * @method Phaser.Utils.rotateArray - * @param {array} matrix - The array to rotate. - * @param {number|string} direction - The amount to rotate. Either a number: 90, -90, 270, -270, 180 or a string: 'rotateLeft', 'rotateRight' or 'rotate180' - * @return {array} The rotated array - */ + * Choose between one of two values randomly. + * + * @method Phaser.Utils#randomChoice + * @param {any} choice1 + * @param {any} choice2 + * @return {any} The randomly selected choice + */ + randomChoice: function (choice1, choice2) { + return (Math.random() < 0.5) ? choice1 : choice2; + }, + + /** + * Transposes the elements of the given Array. + * + * @method Phaser.Utils.transposeArray + * @param {array} array - The array to transpose. + * @return {array} The transposed array. + * @deprecated 2.1.4 - Use Phaser.ArrayUtils.transpose + */ + transposeArray: function (array) { + return Phaser.ArrayUtils.transpose(array); + }, + + /** + * Rotates the given array. + * Based on the routine from http://jsfiddle.net/MrPolywhirl/NH42z/ + * + * @method Phaser.Utils.rotateArray + * @param {array} matrix - The array to rotate. + * @param {number|string} direction - The amount to rotate. Either a number: 90, -90, 270, -270, 180 or a string: 'rotateLeft', 'rotateRight' or 'rotate180' + * @return {array} The rotated array + * @deprecated 2.1.4 - Use Phaser.ArrayUtils.rotate + */ rotateArray: function (matrix, direction) { + return Phaser.ArrayUtils.rotate(matrix, direction); + }, - if (typeof direction !== 'string') - { - direction = ((direction % 360) + 360) % 360; - } - - if (direction === 90 || direction === -270 || direction === 'rotateLeft') - { - matrix = Phaser.Utils.transposeArray(matrix); - matrix = matrix.reverse(); - } - else if (direction === -90 || direction === 270 || direction === 'rotateRight') - { - matrix = matrix.reverse(); - matrix = Phaser.Utils.transposeArray(matrix); - } - else if (Math.abs(direction) === 180 || direction === 'rotate180') - { - for (var i = 0; i < matrix.length; i++) - { - matrix[i].reverse(); - } - - matrix = matrix.reverse(); - } - - return matrix; - + /** + * A standard Fisher-Yates Array shuffle implementation. + * + * @method Phaser.Utils.shuffle + * @param {array} array - The array to shuffle. + * @return {array} The shuffled array. + * @deprecated 2.1.4 - User Phaser.ArrayUtils.shuffle + */ + shuffle: function (array) { + return Phaser.ArrayUtils.shuffle(array); }, /** @@ -184,30 +184,9 @@ Phaser.Utils = { }, - /** - * A standard Fisher-Yates Array shuffle implementation. - * @method Phaser.Utils.shuffle - * @param {array} array - The array to shuffle. - * @return {array} The shuffled array. - */ - shuffle: function (array) { - - for (var i = array.length - 1; i > 0; i--) - { - var j = Math.floor(Math.random() * (i + 1)); - var temp = array[i]; - array[i] = array[j]; - array[j] = temp; - } - - return array; - - }, - /** * Javascript string pad http://www.webtoolkit.info/. - * pad = the string to pad it out with (defaults to a space) - * dir = 1 (left), 2 (right), 3 (both) + * * @method Phaser.Utils.pad * @param {string} str - The target string. * @param {number} len - The number of characters to be added. @@ -248,7 +227,8 @@ Phaser.Utils = { }, /** - * This is a slightly modified version of jQuery.isPlainObject. A plain object is an object whose internal class property is [object Object]. + * This is a slightly modified version of jQuery.isPlainObject. + * A plain object is an object whose internal class property is [object Object]. * @method Phaser.Utils.isPlainObject * @param {object} obj - The object to inspect. * @return {boolean} - true if the object is plain, otherwise false. @@ -411,144 +391,3 @@ Phaser.Utils = { } }; - -/** -* A polyfill for Function.prototype.bind -*/ -if (typeof Function.prototype.bind !== 'function') { - - /* jshint freeze: false */ - Function.prototype.bind = (function () { - - var slice = Array.prototype.slice; - - return function (thisArg) { - - var target = this, boundArgs = slice.call(arguments, 1); - - if (typeof target !== 'function') - { - throw new TypeError(); - } - - function bound() { - var args = boundArgs.concat(slice.call(arguments)); - target.apply(this instanceof bound ? this : thisArg, args); - } - - bound.prototype = (function F(proto) { - if (proto) - { - F.prototype = proto; - } - - if (!(this instanceof F)) - { - return new F; - } - })(target.prototype); - - return bound; - }; - })(); -} - -/** -* A polyfill for Array.isArray -*/ -if (!Array.isArray) -{ - Array.isArray = function (arg) - { - return Object.prototype.toString.call(arg) == '[object Array]'; - }; -} - -/** -* A polyfill for Array.forEach -* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach -*/ -if (!Array.prototype.forEach) -{ - Array.prototype.forEach = function(fun /*, thisArg */) - { - "use strict"; - - if (this === void 0 || this === null) - { - throw new TypeError(); - } - - var t = Object(this); - var len = t.length >>> 0; - - if (typeof fun !== "function") - { - throw new TypeError(); - } - - var thisArg = arguments.length >= 2 ? arguments[1] : void 0; - - for (var i = 0; i < len; i++) - { - if (i in t) - { - fun.call(thisArg, t[i], i, t); - } - } - }; -} - -/** -* Low-budget Float32Array knock-off, suitable for use with P2.js in IE9 -* Source: http://www.html5gamedevs.com/topic/5988-phaser-12-ie9/ -* Cameron Foale (http://www.kibibu.com) -*/ -if (typeof window.Uint32Array !== "function" && typeof window.Uint32Array !== "object") -{ - var CheapArray = function(type) - { - var proto = new Array(); // jshint ignore:line - - window[type] = function(arg) { - - if (typeof(arg) === "number") - { - Array.call(this, arg); - this.length = arg; - - for (var i = 0; i < this.length; i++) - { - this[i] = 0; - } - } - else - { - Array.call(this, arg.length); - - this.length = arg.length; - - for (var i = 0; i < this.length; i++) - { - this[i] = arg[i]; - } - } - }; - - window[type].prototype = proto; - window[type].constructor = window[type]; - }; - - CheapArray('Uint32Array'); // jshint ignore:line - CheapArray('Int16Array'); // jshint ignore:line -} - -/** - * Also fix for the absent console in IE9 - */ -if (!window.console) -{ - window.console = {}; - window.console.log = window.console.assert = function(){}; - window.console.warn = window.console.assert = function(){}; -} diff --git a/tasks/manifests/phaser.json b/tasks/manifests/phaser.json index 4307f7dee..54d0a6c5f 100644 --- a/tasks/manifests/phaser.json +++ b/tasks/manifests/phaser.json @@ -1,6 +1,7 @@ [ "src/Intro.js", "src/Phaser.js", + "src/polyfill.js", "src/utils/Utils.js", "src/geom/Circle.js", @@ -91,6 +92,7 @@ "src/sound/Sound.js", "src/sound/SoundManager.js", + "src/utils/ArrayUtils.js", "src/utils/Debug.js", "src/utils/Color.js",