UNPKG

onsight

Version:

Interactive, easy to use JavaScript game framework.

251 lines (209 loc) 11.3 kB
// ANGLES // radiansToDegrees() Converts radians to degrees // degreesToRadians() Converts degrees to radians // equalizeAngle0to360() Clamps degrees in between 0 and 360 // COMMON // clamp() Clamps a number between min and max // roundTo() Returns a number rounded to 'decimalPlaces' // EASING // damp() Time based linear interpolation // lerp() Linear interpolation // smoothstep() Sigmoid-like interpolation // smootherstep() Perlin smoothstep // FUZZY // fuzzyFloat() Compares two numbers to see if they're roughly the same // fuzzyVector() Compares two Vector / Quaternion // GEOMETRY // isPowerOfTwo() Checks if a number is power of 2 // NUMBERS // addCommas() Formats a number into a string with commas added for large numbers // countDecimals() Counts significant decimal places // isNumber() Checks if 'number' is a valid number // noZero() Ensures number is not equal to zero // sanity() Ensures number is a Number // POLYGONS // lineCollision() Check if two lines are intersecting // lineRectCollision() Checks if a line is intersecting a rectangle // triangleArea() Computes the area of a triangle defined by 3 points // RANDOM // randomFloat() Random float from min (inclusive) to max (exclusive) // randomInt() Random integer from min (inclusive) to max (exclusive) // UUID // randomUUID() Returns randomized UUID // toUUIDArray() Converts object list or object array to UUID array import { Vector3 } from '../math/Vector3.js'; const _lut = [ '00', '01', '02', '03', '04', '05', '06', '07', '08', '09', '0a', '0b', '0c', '0d', '0e', '0f', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '1a', '1b', '1c', '1d', '1e', '1f', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '2a', '2b', '2c', '2d', '2e', '2f', '30', '31', '32', '33', '34', '35', '36', '37', '38', '39', '3a', '3b', '3c', '3d', '3e', '3f', '40', '41', '42', '43', '44', '45', '46', '47', '48', '49', '4a', '4b', '4c', '4d', '4e', '4f', '50', '51', '52', '53', '54', '55', '56', '57', '58', '59', '5a', '5b', '5c', '5d', '5e', '5f', '60', '61', '62', '63', '64', '65', '66', '67', '68', '69', '6a', '6b', '6c', '6d', '6e', '6f', '70', '71', '72', '73', '74', '75', '76', '77', '78', '79', '7a', '7b', '7c', '7d', '7e', '7f', '80', '81', '82', '83', '84', '85', '86', '87', '88', '89', '8a', '8b', '8c', '8d', '8e', '8f', '90', '91', '92', '93', '94', '95', '96', '97', '98', '99', '9a', '9b', '9c', '9d', '9e', '9f', 'a0', 'a1', 'a2', 'a3', 'a4', 'a5', 'a6', 'a7', 'a8', 'a9', 'aa', 'ab', 'ac', 'ad', 'ae', 'af', 'b0', 'b1', 'b2', 'b3', 'b4', 'b5', 'b6', 'b7', 'b8', 'b9', 'ba', 'bb', 'bc', 'bd', 'be', 'bf', 'c0', 'c1', 'c2', 'c3', 'c4', 'c5', 'c6', 'c7', 'c8', 'c9', 'ca', 'cb', 'cc', 'cd', 'ce', 'cf', 'd0', 'd1', 'd2', 'd3', 'd4', 'd5', 'd6', 'd7', 'd8', 'd9', 'da', 'db', 'dc', 'dd', 'de', 'df', 'e0', 'e1', 'e2', 'e3', 'e4', 'e5', 'e6', 'e7', 'e8', 'e9', 'ea', 'eb', 'ec', 'ed', 'ee', 'ef', 'f0', 'f1', 'f2', 'f3', 'f4', 'f5', 'f6', 'f7', 'f8', 'f9', 'fa', 'fb', 'fc', 'fd', 'fe', 'ff' ]; const v0 = new Vector3(); const v1 = new Vector3(); const vc = new Vector3(); class MathUtils { /******************** ANGLES ********************/ static radiansToDegrees(radians) { return radians * (180 / Math.PI); } static degreesToRadians(degrees) { return (Math.PI / 180) * degrees; } static equalizeAngle0to360(angle, degrees = true) { let equalized = (degrees) ? angle : MathUtils.radiansToDegrees(angle); while (equalized < 0) { equalized += 360; } while (equalized >= 360) { equalized -= 360; } return (degrees) ? equalized : MathUtils.degreesToRadians(equalized); } /******************** COMMON ********************/ /** Clamps a number between min and max */ static clamp(number, min, max) { number = Number(number); if (number < min) number = min; if (number > max) number = max; return number; } /** Returns a number rounded to 'decimalPlaces' */ static roundTo(number, decimalPlaces = 0) { const shift = Math.pow(10, decimalPlaces); return Math.round(number * shift) / shift; } /******************** EASING ********************/ /** http://www.rorydriscoll.com/2016/03/07/frame-rate-independent-damping-using-lerp/ */ static damp(a, b, lambda, dt) { return MathUtils.lerp(a, b, 1 - Math.exp(-lambda * dt)); } /** https://en.wikipedia.org/wiki/Linear_interpolation */ static lerp(a, b, t) { return (1 - t) * a + t * b; } /** https://en.wikipedia.org/wiki/Smoothstep */ static smoothstep(a, b, t) { t = 3 * t * t - 2 * t * t * t; t = Math.min(1, Math.max(0, t)); return a + ((b - a) * t); } /** https://en.wikipedia.org/wiki/Smoothstep */ static smootherstep(a, b, t) { t = t * t * t * (t * (t * 6 - 15) + 10); t = Math.min(1, Math.max(0, t)); return a + ((b - a) * t); } /******************** FUZZY ********************/ /** Compares two numbers to see if they're roughly the same */ static fuzzyFloat(a, b, tolerance = 0.001) { return ((a < (b + tolerance)) && (a > (b - tolerance))); } static fuzzyVector(a, b, tolerance = 0.001) { if (MathUtils.fuzzyFloat(a.x, b.x, tolerance) === false) return false; if (MathUtils.fuzzyFloat(a.y, b.y, tolerance) === false) return false; if (('z' in a) && ('z' in b) && MathUtils.fuzzyFloat(a.z, b.z, tolerance) === false) return false; if (('w' in a) && ('w' in b) && MathUtils.fuzzyFloat(a.w, b.w, tolerance) === false) return false; return true; } /******************** GEOMETRY ********************/ /** Checks if a number is power of 2 */ static isPowerOfTwo(value) { // // OPTION: And return (value & (value - 1)) === 0 && value !== 0; // OPTION: Log2 // return Math.log2(value) % 1 === 0; } /******************** NUMBERS ********************/ /** Formats a number into a string with commas added for large numbers (i.e. 12000 => 12,000) */ static addCommas(number) { return number.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,'); } /** Counts significant decimal places */ static countDecimals(number) { if (Math.floor(number.valueOf()) === number.valueOf()) return 0; return number.toString().split('.')[1].length || 0; } /** Checks if 'number' is a valid, finite number */ static isNumber(number) { return (number != null && typeof number === 'number' && Number.isFinite(number)); } /** Ensures number is not equal to zero */ static noZero(number, min = 0.00001) { min = Math.abs(min); number = MathUtils.sanity(number); if (number >= 0 && number < min) number = min; if (number < 0 && number > min * -1.0) number = min * -1.0; return number; } /** Ensures number is a Number */ static sanity(number) { if (MathUtils.isNumber(number)) return number; return 0; } /******************** POLYGON ********************/ /** Check if two lines are intersecting */ static lineCollision(x1, y1, x2, y2, x3, y3, x4, y4) { // Calculate the direction of the lines let denom = ((y4 - y3) * (x2 - x1)) - ((x4 - x3) * (y2 - y1)); if (MathUtils.fuzzyFloat(denom, 0, 0.0000001)) return false; let ua = (((x4 - x3) * (y1 - y3)) - ((y4 - y3) * (x1 - x3))) / denom; let ub = (((x2 - x1) * (y1 - y3)) - ((y2 - y1) * (x1 - x3))) / denom; if ((ua >= 0) && (ua <= 1) && (ub >= 0) && (ub <= 1)) { // If intersecting, the intersection points are // crossX = x1 + (uA * (x2 - x1)); // crossY = y1 + (uA * (y2 - y1)); return true; } return false; } /** Checks if a line is intersecting a rectangle */ static lineRectCollision(x1, y1, x2, y2, left, top, right, down) { const rectLeft = MathUtils.lineCollision(x1, y1, x2, y2, left, top, left, down); const rectRight = MathUtils.lineCollision(x1, y1, x2, y2, right, top, right, down); const rectTop = MathUtils.lineCollision(x1, y1, x2, y2, left, top, right, top); const rectDown = MathUtils.lineCollision(x1, y1, x2, y2, left, down, right, down); return (rectLeft || rectRight || rectTop || rectDown); } /** * Computes the area of a triangle defined by 3 points (points as arrays [ x, y, z ]) * * @param {Vec3} a * @param {Vec3} b * @param {Vec3} c * @returns {Number} area of the triangle */ static triangleArea(a, b, c) { v0.subVectors(c, b); v1.subVectors(a, b); vc.crossVectors(v0, v1); return (vc.length() * 0.5); } /******************** RANDOM ********************/ /** Random float from min (inclusive) to max (exclusive) */ static randomFloat(min, max) { return min + Math.random() * (max - min); } /** Random integer from min (inclusive) to max (exclusive), randomInt(0, 3) => expected output: 0, 1 or 2 */ static randomInt(min = 0, max = 1) { return min + Math.floor(Math.random() * (max - min)); } /******************** UUID ********************/ /** Returns randomized UUID */ static randomUUID() { if (window.crypto && window.crypto.randomUUID) return crypto.randomUUID(); // https://github.com/mrdoob/three.js/blob/dev/src/math/MathUtils.js // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/21963136#21963136 const d0 = Math.random() * 0xffffffff | 0; const d1 = Math.random() * 0xffffffff | 0; const d2 = Math.random() * 0xffffffff | 0; const d3 = Math.random() * 0xffffffff | 0; const uuid = _lut[d0 & 0xff] + _lut[d0 >> 8 & 0xff] + _lut[d0 >> 16 & 0xff] + _lut[d0 >> 24 & 0xff] + '-' + _lut[d1 & 0xff] + _lut[d1 >> 8 & 0xff] + '-' + _lut[d1 >> 16 & 0x0f | 0x40] + _lut[d1 >> 24 & 0xff] + '-' + _lut[d2 & 0x3f | 0x80] + _lut[d2 >> 8 & 0xff] + '-' + _lut[d2 >> 16 & 0xff] + _lut[d2 >> 24 & 0xff] + _lut[d3 & 0xff] + _lut[d3 >> 8 & 0xff] + _lut[d3 >> 16 & 0xff] + _lut[d3 >> 24 & 0xff]; return uuid.toLowerCase(); // .toLowerCase() flattens concatenated strings to save heap memory space } /** Converts object list or object array to UUID array */ static toUUIDArray(...objects) { if (objects.length > 0 && Array.isArray(objects[0])) objects = objects[0]; const uuids = []; for (const object of objects) { if (typeof object === 'object') { if (object.uuid) uuids.push(object.uuid); } } return uuids; } } export { MathUtils };