| /*------------------------------------------------------------------------- |
| * drawElements Quality Program OpenGL ES Utilities |
| * ------------------------------------------------ |
| * |
| * Copyright 2014 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ |
| |
| 'use strict'; |
| goog.provide('framework.delibs.debase.deMath'); |
| |
| /** @typedef { (Int8Array|Uint8Array|Uint8ClampedArray|Int16Array|Uint16Array|Int32Array|Uint32Array|Float32Array|Float64Array) } */ |
| goog.TypedArray; |
| |
| /** @typedef { (Array<number>|Array<boolean>|goog.TypedArray) } */ |
| goog.NumberArray; |
| |
| goog.scope(function() { |
| |
| var deMath = framework.delibs.debase.deMath; |
| |
| var DE_ASSERT = function(x) { |
| if (!x) |
| throw new Error('Assert failed'); |
| }; |
| |
| /** @const */ deMath.INT32_SIZE = 4; |
| |
| deMath.deInRange32 = function(a, mn, mx) { |
| return (a >= mn) && (a <= mx); |
| }; |
| |
| deMath.deInBounds32 = function(a, mn, mx) { |
| return (a >= mn) && (a < mx); |
| }; |
| |
| /** |
| * @param {number} a |
| * @return {number} |
| */ |
| deMath.deFloatFrac = function(a) { return a - Math.floor(a); }; |
| |
| /** |
| * Transform a 64-bit float number into a 32-bit float number. |
| * Native dEQP uses 32-bit numbers, so sometimes 64-bit floating numbers in JS should be transformed into 32-bit ones to ensure the correctness of the result. |
| * @param {number} a |
| * @return {number} |
| */ |
| deMath.toFloat32 = (function() { |
| var FLOAT32ARRAY1 = new Float32Array(1); |
| return function(a) { |
| FLOAT32ARRAY1[0] = a; |
| return FLOAT32ARRAY1[0]; |
| }; |
| })(); |
| |
| /** @const */ deMath.INV_LOG_2_FLOAT32 = deMath.toFloat32(1.44269504089); /** 1.0 / log_e(2.0) */ |
| |
| /** |
| * Check if a value is a power-of-two. |
| * @param {number} a Input value. |
| * @return {boolean} return True if input is a power-of-two value, false otherwise. |
| * (Also returns true for zero). |
| */ |
| deMath.deIsPowerOfTwo32 = function(a) { |
| return ((a & (a - 1)) == 0); |
| }; |
| |
| /** |
| * Align an integer to given power-of-two size. |
| * @param {number} val The number to align. |
| * @param {number} align The size to align to. |
| * @return {number} The aligned value |
| */ |
| deMath.deAlign32 = function(val, align) { |
| if (!deMath.deIsPowerOfTwo32(align)) |
| throw new Error('Not a power of 2: ' + align); |
| return ((val + align - 1) & ~(align - 1)) & 0xFFFFFFFF; //0xFFFFFFFF make sure it returns a 32 bit calculation in 64 bit browsers. |
| }; |
| |
| /** |
| * Compute the bit population count of an integer. |
| * @param {number} a |
| * @return {number} The number of one bits in |
| */ |
| deMath.dePop32 = function(a) { |
| /** @type {number} */ var mask0 = 0x55555555; /* 1-bit values. */ |
| /** @type {number} */ var mask1 = 0x33333333; /* 2-bit values. */ |
| /** @type {number} */ var mask2 = 0x0f0f0f0f; /* 4-bit values. */ |
| /** @type {number} */ var mask3 = 0x00ff00ff; /* 8-bit values. */ |
| /** @type {number} */ var mask4 = 0x0000ffff; /* 16-bit values. */ |
| /** @type {number} */ var t = a & 0xFFFFFFFF; /* Crop to 32-bit value */ |
| t = (t & mask0) + ((t >> 1) & mask0); |
| t = (t & mask1) + ((t >> 2) & mask1); |
| t = (t & mask2) + ((t >> 4) & mask2); |
| t = (t & mask3) + ((t >> 8) & mask3); |
| t = (t & mask4) + (t >> 16); |
| return t; |
| }; |
| |
| deMath.clamp = function(val, minParm, maxParm) { |
| return Math.min(Math.max(val, minParm), maxParm); |
| }; |
| |
| /** |
| * @param {Array<number>} values |
| * @param {number} minParm |
| * @param {number} maxParm |
| * @return {Array<number>} |
| */ |
| deMath.clampVector = function(values, minParm, maxParm) { |
| var result = []; |
| for (var i = 0; i < values.length; i++) |
| result.push(deMath.clamp(values[i], minParm, maxParm)); |
| return result; |
| }; |
| |
| deMath.imod = function(a, b) { |
| var m = a % b; |
| return m < 0 ? m + b : m; |
| }; |
| |
| deMath.mirror = function(a) { |
| return a >= 0 ? a : -(1 + a); |
| }; |
| |
| /** |
| * @param {goog.NumberArray} a Source array |
| * @param {goog.NumberArray} indices |
| * @return {Array<number>} Swizzled array |
| */ |
| deMath.swizzle = function(a, indices) { |
| if (!indices.length) |
| throw new Error('Argument must be an array'); |
| var dst = []; |
| for (var i = 0; i < indices.length; i++) |
| dst.push(a[indices[i]]); |
| return dst; |
| }; |
| |
| /** |
| * Shift left elements of array a by elements of array b |
| * dst[n] a[n] << b[n] |
| * @param {goog.NumberArray} a |
| * @param {goog.NumberArray} b |
| * @return {Array<number>} Result array |
| */ |
| deMath.arrayShiftLeft = function(a, b) { |
| if (a.length != b.length) |
| throw new Error('Arrays must have the same size'); |
| var dst = []; |
| for (var i = 0; i < a.length; i++) |
| dst.push(a[i] << b[i]); |
| return dst; |
| }; |
| |
| /** |
| * Multiply two vectors, element by element |
| * @param {goog.NumberArray} a |
| * @param {goog.NumberArray} b |
| * @return {Array<number>} Result array |
| */ |
| |
| deMath.multiply = function(a, b) { |
| if (a.length != b.length) |
| throw new Error('Arrays must have the same size'); |
| var dst = []; |
| for (var i = 0; i < a.length; i++) |
| dst.push(a[i] * b[i]); |
| return dst; |
| }; |
| |
| /** |
| * Divide two vectors, element by element |
| * @param {goog.NumberArray} a |
| * @param {goog.NumberArray} b |
| * @return {Array<number>} Result array |
| * @throws {Error} |
| */ |
| |
| deMath.divide = function(a, b) { |
| if (a.length != b.length) |
| throw new Error('Arrays must have the same size'); |
| var dst = []; |
| for (var i = 0; i < a.length; i++) { |
| if (b[i] === 0) |
| throw new Error('Division by 0'); |
| dst.push(a[i] / b[i]); |
| } |
| return dst; |
| }; |
| |
| /** |
| * Divide vector by a scalar |
| * @param {goog.NumberArray} a |
| * @param {number} b |
| * @return {Array<number>} Result array |
| */ |
| deMath.divideScale = function(a, b) { |
| var dst = []; |
| for (var i = 0; i < a.length; i++) |
| dst.push(a[i] / b); |
| return dst; |
| }; |
| |
| /** |
| * @param {number} a |
| * @param {number} b |
| * @return {number} |
| */ |
| deMath.mod = function(a, b) { |
| return a - b * Math.floor(a / b); |
| }; |
| |
| /** |
| * Modulus vector by a scalar |
| * @param {goog.NumberArray} a |
| * @param {number} b |
| * @return {Array<number>} Result array |
| */ |
| deMath.modScale = function(a, b) { |
| var dst = []; |
| for (var i = 0; i < a.length; i++) |
| dst.push(deMath.mod(a[i], b)); |
| return dst; |
| }; |
| |
| /** |
| * Multiply vector by a scalar |
| * @param {goog.NumberArray} a |
| * @param {number} b |
| * @return {Array<number>} Result array |
| */ |
| deMath.scale = function(a, b) { |
| var dst = []; |
| for (var i = 0; i < a.length; i++) |
| dst.push(a[i] * b); |
| return dst; |
| }; |
| |
| /** |
| * Add vector and scalar, element by element |
| * @param {goog.NumberArray} a |
| * @param {number} b |
| * @return {Array<number>} Result array |
| */ |
| deMath.addScalar = function(a, b) { |
| if (!Array.isArray(a)) |
| throw new Error('First argument must be an array.'); |
| if (typeof b !== 'number') |
| throw new Error('Second argument must be a number.'); |
| var dst = []; |
| for (var i = 0; i < a.length; i++) |
| dst.push(a[i] + b); |
| return dst; |
| }; |
| |
| /** |
| * Add two vectors, element by element |
| * @param {goog.NumberArray} a |
| * @param {goog.NumberArray} b |
| * @return {Array<number>} Result array |
| */ |
| deMath.add = function(a, b) { |
| if (a.length != b.length) |
| throw new Error('Arrays must have the same size'); |
| var dst = []; |
| for (var i = 0; i < a.length; i++) |
| dst.push(a[i] + b[i]); |
| return dst; |
| }; |
| |
| /** |
| * Subtract two vectors, element by element |
| * @param {goog.NumberArray} a |
| * @param {goog.NumberArray} b |
| * @return {Array<number>} Result array |
| */ |
| |
| deMath.subtract = function(a, b) { |
| if (a.length != b.length) |
| throw new Error('Arrays must have the same size'); |
| var dst = []; |
| for (var i = 0; i < a.length; i++) |
| dst.push(a[i] - b[i]); |
| return dst; |
| }; |
| |
| /** |
| * Subtract vector and scalar, element by element |
| * @param {goog.NumberArray} a |
| * @param {number} b |
| * @return {Array<number>} Result array |
| */ |
| deMath.subScalar = function(a, b) { |
| if (!Array.isArray(a)) |
| throw new Error('First argument must be an array.'); |
| if (typeof b !== 'number') |
| throw new Error('Second argument must be a number.'); |
| var dst = []; |
| for (var i = 0; i < a.length; i++) |
| dst.push(a[i] - b); |
| return dst; |
| }; |
| |
| /** |
| * Calculate absolute difference between two vectors |
| * @param {goog.NumberArray} a |
| * @param {goog.NumberArray} b |
| * @return {Array<number>} abs(diff(a - b)) |
| */ |
| deMath.absDiff = function(a, b) { |
| if (a.length != b.length) |
| throw new Error('Arrays must have the same size'); |
| var dst = []; |
| for (var i = 0; i < a.length; i++) |
| dst.push(Math.abs(a[i] - b[i])); |
| return dst; |
| }; |
| |
| /** |
| * Calculate absolute value of a vector |
| * @param {goog.NumberArray} a |
| * @return {Array<number>} abs(a) |
| */ |
| deMath.abs = function(a) { |
| var dst = []; |
| for (var i = 0; i < a.length; i++) |
| dst.push(Math.abs(a[i])); |
| return dst; |
| }; |
| |
| /** |
| * Is a <= b (element by element)? |
| * @param {goog.NumberArray} a |
| * @param {goog.NumberArray} b |
| * @return {Array<boolean>} Result array of booleans |
| */ |
| deMath.lessThanEqual = function(a, b) { |
| if (a.length != b.length) |
| throw new Error('Arrays must have the same size'); |
| var dst = []; |
| for (var i = 0; i < a.length; i++) |
| dst.push(a[i] <= b[i]); |
| return dst; |
| }; |
| |
| /** |
| * Is a === b (element by element)? |
| * @param {goog.NumberArray} a |
| * @param {goog.NumberArray} b |
| * @return {boolean} Result |
| */ |
| deMath.equal = function(a, b) { |
| if (a.length != b.length) |
| throw new Error('Arrays must have the same size'); |
| for (var i = 0; i < a.length; i++) { |
| if (a[i] !== b[i]) |
| return false; |
| } |
| return true; |
| }; |
| |
| /** |
| * Are all values in the array true? |
| * @param {Array<boolean>} a |
| * @return {boolean} |
| */ |
| deMath.boolAll = function(a) { |
| for (var i = 0; i < a.length; i++) |
| if (a[i] == false) |
| return false; |
| return true; |
| }; |
| |
| /** |
| * deMath.assign(a, b) element by element |
| * @param {goog.NumberArray} a |
| * @return {Array<number>} |
| */ |
| deMath.assign = function(a) { |
| var dst = []; |
| for (var i = 0; i < a.length; i++) |
| dst.push(a[i]); |
| return dst; |
| }; |
| |
| /** |
| * deMath.max(a, b) element by element |
| * @param {goog.NumberArray} a |
| * @param {goog.NumberArray} b |
| * @return {Array<number>} |
| */ |
| deMath.max = function(a, b) { |
| if (a.length != b.length) |
| throw new Error('Arrays must have the same size'); |
| var dst = []; |
| for (var i = 0; i < a.length; i++) |
| dst.push(Math.max(a[i], b[i])); |
| return dst; |
| }; |
| |
| /** |
| * deMath.min(a, b) element by element |
| * @param {goog.NumberArray} a |
| * @param {goog.NumberArray} b |
| * @return {Array<number>} |
| */ |
| deMath.min = function(a, b) { |
| if (a.length != b.length) |
| throw new Error('Arrays must have the same size'); |
| var dst = []; |
| for (var i = 0; i < a.length; i++) |
| dst.push(Math.min(a[i], b[i])); |
| return dst; |
| }; |
| |
| // Nearest-even rounding in case of tie (fractional part 0.5), otherwise ordinary rounding. |
| deMath.rint = function(a) { |
| var floorVal = Math.floor(a); |
| var fracVal = a - floorVal; |
| |
| if (fracVal != 0.5) |
| return Math.round(a); // Ordinary case. |
| |
| var roundUp = (floorVal % 2) != 0; |
| |
| return floorVal + (roundUp ? 1 : 0); |
| }; |
| |
| /** |
| * wrap the number, so that it fits in the range [minValue, maxValue] |
| * @param {number} v |
| * @param {number} minValue |
| * @param {number} maxValue |
| * @return {number} |
| */ |
| deMath.wrap = function(v, minValue, maxValue) { |
| var range = maxValue - minValue + 1; |
| |
| if (v < minValue) { |
| v += range * (Math.floor((minValue - v) / range) + 1); |
| } |
| return minValue + Math.floor((v - minValue) % range); |
| }; |
| |
| /** |
| * Round number to int by dropping fractional part |
| * it is equivalent of GLSL int() constructor |
| * @param {number} a |
| * @return {number} |
| */ |
| deMath.intCast = function(a) { |
| var v; |
| if (a >= 0) |
| v = Math.floor(a); |
| else |
| v = Math.ceil(a); |
| return deMath.wrap(v, -0x80000000, 0x7FFFFFFF); |
| }; |
| |
| /** |
| * Round number to uint by dropping fractional part |
| * it is equivalent of GLSL uint() constructor |
| * @param {number} a |
| * @return {number} |
| */ |
| deMath.uintCast = function(a) { |
| var v; |
| if (a >= 0) |
| v = Math.floor(a); |
| else |
| v = Math.ceil(a); |
| return deMath.wrap(v, 0, 0xFFFFFFFF); |
| }; |
| |
| /** |
| * @param {number} a |
| * @return {number} |
| */ |
| deMath.logToFloor = function(a) { |
| assertMsgOptions(a > 0, 'Value is less or equal than zero', false, true); |
| return 31 - deMath.clz32(a); |
| }; |
| |
| /** |
| * Find intersection of two rectangles |
| * @param {goog.NumberArray} a Array [x, y, width, height] |
| * @param {goog.NumberArray} b Array [x, y, width, height] |
| * @return {Array<number>} |
| */ |
| deMath.intersect = function(a, b) { |
| if (a.length != 4) |
| throw new Error('Array "a" must have length 4 but has length: ' + a.length); |
| if (b.length != 4) |
| throw new Error('Array "b" must have length 4 but has length: ' + b.length); |
| var x0 = Math.max(a[0], b[0]); |
| var y0 = Math.max(a[1], b[1]); |
| var x1 = Math.min(a[0] + a[2], b[0] + b[2]); |
| var y1 = Math.min(a[1] + a[3], b[1] + b[3]); |
| var w = Math.max(0, x1 - x0); |
| var h = Math.max(0, y1 - y0); |
| |
| return [x0, y0, w, h]; |
| }; |
| |
| /** deMath.deMathHash |
| * @param {number} a |
| * @return {number} |
| */ |
| deMath.deMathHash = function(a) { |
| var key = a; |
| key = (key ^ 61) ^ (key >> 16); |
| key = key + (key << 3); |
| key = key ^ (key >> 4); |
| key = key * 0x27d4eb2d; /* prime/odd constant */ |
| key = key ^ (key >> 15); |
| return key; |
| }; |
| |
| /** |
| * Converts a byte array to a number. Cannot convert numbers larger than 52 bits. |
| * @param {Uint8Array} array |
| * @return {number} |
| */ |
| deMath.arrayToNumber = function(array) { |
| DE_ASSERT(array.length <= 6 || (array.length == 6 && array[5] <= 127)); |
| /** @type {number} */ var result = 0; |
| |
| for (var ndx = 0; ndx < array.length; ndx++) { |
| result += array[ndx] * Math.pow(256, ndx); |
| } |
| |
| return result; |
| }; |
| |
| /** |
| * Fills a byte array with a number |
| * @param {Uint8Array} array Output array (already resized) |
| * @param {number} number |
| */ |
| deMath.numberToArray = function(array, number) { |
| DE_ASSERT(Number.isInteger(number)); |
| for (var byteNdx = 0; byteNdx < array.length; byteNdx++) { |
| /** @type {number} */ var acumzndx = !byteNdx ? number : Math.floor(number / Math.pow(256, byteNdx)); |
| array[byteNdx] = acumzndx & 0xFF; |
| } |
| }; |
| |
| /** |
| * Obtains the bit fragment from a number |
| * @param {number} x |
| * @param {number} firstNdx |
| * @param {number} lastNdx |
| * @return {number} |
| */ |
| deMath.getBitRange = function(x, firstNdx, lastNdx) { |
| DE_ASSERT(lastNdx - firstNdx <= 52); |
| var shifted = deMath.shiftRight(x, firstNdx); |
| var bitSize = lastNdx - firstNdx; |
| var mask; |
| if (bitSize < 32) |
| mask = (1 << bitSize) - 1; |
| else |
| mask = Math.pow(2, bitSize) - 1; |
| var masked = deMath.binaryAnd(shifted, mask); |
| return masked; |
| }; |
| |
| /** |
| * Obtains the bit fragment from a Uint32Array representing a number. |
| * (ArrayBuffer representations are used in tcuFloat.) |
| * |
| * Cannot return more than 52 bits ((lastNdx - firstNdx) <= 52). |
| * |
| * @param {Uint32Array} array |
| * @param {number} firstNdx |
| * @param {number} lastNdx |
| * @return {number} |
| */ |
| deMath.getArray32BitRange = function(array, firstNdx, lastNdx) { |
| DE_ASSERT(0 <= firstNdx && firstNdx < (array.length * 32)); |
| DE_ASSERT(0 < lastNdx && lastNdx <= (array.length * 32)); |
| DE_ASSERT((lastNdx - firstNdx) <= 52); |
| |
| // Example of how this works for a 64-bit number (Uint32Array of length 2). |
| // |
| // * Note that the shift operators in the code << and >>> are pointing in |
| // the opposite direction of this diagram, since LSB is shown on the left. |
| // |
| // [array[0], array[1] ] |
| // [00000011111111111111111111111111, 11111111111100000000000000000000] |
| // ^LSB MSB^ ^LSB MSB^ |
| // |
| // [00000011111111111111111111111111, 11111111111100000000000000000000] |
| // \ \ |
| // firstNdx = 6 (inclusive) lastNdx = 44 (exclusive) |
| // blockIndexA = 0 blockIndexB = 1 |
| // |
| // [00000011111111111111111111111111, 11111111111100000000000000000000] |
| // \-----\ \-------------------\ |
| // bitsToBeginningOfBlock = 6 bitsFromEndOfBlock = 20 |
| // |
| // -------------blockA------------- -------------blockB------------- |
| // [00000011111111111111111111111111, 11111111111100000000000000000000] |
| // ^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^ |
| // blockATruncated blockBTruncated |
| // \--blockATruncatedLength--\ |
| // |
| // 11111111111111111111111111 111111111111 |
| // ^^^^^^^^^^^^^^^^^^^^^^^^^^--^^^^^^^^^^^^ return value (38 bits) |
| |
| /** @type {number} */ var blockIndexA = Math.floor(firstNdx / 32); |
| /** @type {number} */ var bitsToBeginningOfBlock = firstNdx % 32; |
| /** @type {number} */ var blockIndexB = Math.floor((lastNdx - 1) / 32); |
| /** @type {number} */ var bitsFromEndOfBlock = 31 - ((lastNdx - 1) % 32); |
| |
| /** @type {number} */ var blockB = array[blockIndexB]; |
| // Chop off the most significant `bitsFromEndOfBlock` bits from blockB. |
| // Note: Initially this logic used a bitmask instead. But there are subtle |
| // corner cases in JS that caused results to sometimes come out negative. |
| // This truncation method is just used to avoid that complexity. |
| /** @type {number} */ var blockBTruncated = (blockB << bitsFromEndOfBlock) >>> bitsFromEndOfBlock; |
| |
| if (blockIndexA == blockIndexB) { |
| // firstNdx and lastNdx are in the same block. |
| // Chop off the least significant `bitsToBeginningOfBlock` bits from blockBTruncated. |
| return blockBTruncated >>> bitsToBeginningOfBlock; |
| } else { |
| // firstNdx and lastNdx are in different blocks. |
| /** @type {number} */ var blockA = array[blockIndexA]; |
| // Chop off the least significant `bitsToBeginningOfBlock` bits from blockA. |
| /** @type {number} */ var blockATruncated = blockA >>> bitsToBeginningOfBlock; |
| /** @type {number} */ var blockATruncatedLength = 32 - bitsToBeginningOfBlock; |
| |
| // Concatenate blockATruncated and blockBTruncated. |
| // Conceptually equivalent to: |
| // blockATruncated | (blockBTruncated << blockATruncatedLength) |
| // except that wouldn't work for numbers larger than 32 bits. |
| return blockATruncated + (blockBTruncated * Math.pow(2, blockATruncatedLength)); |
| } |
| }; |
| |
| /** |
| * Split a large signed number into low and high 32bit dwords. |
| * @param {number} x |
| * @return {Array<number>} |
| */ |
| deMath.split32 = function(x) { |
| var ret = []; |
| ret[1] = Math.floor(x / 0x100000000); |
| ret[0] = x - ret[1] * 0x100000000; |
| return ret; |
| }; |
| |
| /** |
| * Split a signed number's low 32bit dwords into low and high 16bit dwords. |
| * @param {number} x |
| * @return {Array<number>} |
| */ |
| deMath.split16 = function(x) { |
| var ret = []; |
| x = x & 0xffffffff; |
| ret[1] = Math.floor(x / 0x10000); |
| ret[0] = x - ret[1] * 0x10000; |
| return ret; |
| }; |
| |
| /** |
| * Recontruct a number from high and low 32 bit dwords |
| * @param {Array<number>} x |
| * @return {number} |
| */ |
| deMath.join32 = function(x) { |
| var v0 = x[0] >= 0 ? x[0] : 0x100000000 + x[0]; |
| var v1 = x[1]; |
| var val = v1 * 0x100000000 + v0; |
| return val; |
| }; |
| |
| //Bit operations with the help of arrays |
| |
| /** |
| * @enum |
| */ |
| deMath.BinaryOp = { |
| AND: 0, |
| OR: 1, |
| XOR: 2 |
| }; |
| |
| /** |
| * Performs a normal (native) binary operation |
| * @param {number} valueA First operand |
| * @param {number} valueB Second operand |
| * @param {deMath.BinaryOp} operation The desired operation to perform |
| * @return {number} |
| */ |
| deMath.doNativeBinaryOp = function(valueA, valueB, operation) { |
| switch (operation) { |
| case deMath.BinaryOp.AND: |
| return valueA & valueB; |
| case deMath.BinaryOp.OR: |
| return valueA | valueB; |
| case deMath.BinaryOp.XOR: |
| return valueA ^ valueB; |
| default: |
| throw new Error('Unknown operation: ' + operation); |
| } |
| }; |
| |
| /** |
| * Performs a binary operation between two operands |
| * with the help of arrays to avoid losing the internal binary representation. |
| * @param {number} valueA First operand |
| * @param {number} valueB Second operand |
| * @param {deMath.BinaryOp} binaryOpParm The desired operation to perform |
| * @return {number} |
| */ |
| deMath.binaryOp = function(valueA, valueB, binaryOpParm) { |
| //quick path if values fit in signed 32 bit range |
| if (deMath.deInRange32(valueA, -0x80000000, 0x7FFFFFFF) && deMath.deInRange32(valueB, -0x80000000, 0x7FFFFFFF)) |
| return deMath.doNativeBinaryOp(valueA, valueB, binaryOpParm); |
| |
| var x = deMath.split32(valueA); |
| var y = deMath.split32(valueB); |
| var z = []; |
| for (var i = 0; i < 2; i++) |
| z[i] = deMath.doNativeBinaryOp(x[i], y[i], binaryOpParm); |
| var ret = deMath.join32(z); |
| return ret; |
| }; |
| |
| /** |
| * @param {number} a |
| * @param {number} b |
| * @return {number} |
| */ |
| deMath.binaryAnd = function(a, b) { |
| return deMath.binaryOp(a, b, deMath.BinaryOp.AND); |
| }; |
| |
| /** |
| * @param {goog.NumberArray} a |
| * @param {number} b |
| * @return {Array<number>} |
| */ |
| deMath.binaryAndVecScalar = function(a, b) { |
| if (!Array.isArray(a)) |
| throw new Error('First argument must be an array.'); |
| if (typeof b !== 'number') |
| throw new Error('Second argument must be a number.'); |
| var dst = []; |
| for (var i = 0; i < a.length; i++) |
| dst.push(deMath.binaryOp(a[i], b, deMath.BinaryOp.AND)); |
| return dst; |
| }; |
| |
| /** |
| * @param {number} a |
| * @param {number} b |
| * @return {number} |
| */ |
| deMath.binaryOr = function(a, b) { |
| return deMath.binaryOp(a, b, deMath.BinaryOp.OR); |
| }; |
| |
| /** |
| * @param {goog.NumberArray} a |
| * @param {number} b |
| * @return {Array<number>} |
| */ |
| deMath.binaryOrVecScalar = function(a, b) { |
| if (!Array.isArray(a)) |
| throw new Error('First argument must be an array.'); |
| if (typeof b !== 'number') |
| throw new Error('Second argument must be a number.'); |
| var dst = []; |
| for (var i = 0; i < a.length; i++) |
| dst.push(deMath.binaryOp(a[i], b, deMath.BinaryOp.OR)); |
| return dst; |
| }; |
| |
| /** |
| * @param {number} a |
| * @param {number} b |
| * @return {number} |
| */ |
| deMath.binaryXor = function(a, b) { |
| return deMath.binaryOp(a, b, deMath.BinaryOp.XOR); |
| }; |
| |
| /** |
| * @param {goog.NumberArray} a |
| * @param {number} b |
| * @return {Array<number>} |
| */ |
| deMath.binaryXorVecScalar = function(a, b) { |
| if (!Array.isArray(a)) |
| throw new Error('First argument must be an array.'); |
| if (typeof b !== 'number') |
| throw new Error('Second argument must be a number.'); |
| var dst = []; |
| for (var i = 0; i < a.length; i++) |
| dst.push(deMath.binaryOp(a[i], b, deMath.BinaryOp.XOR)); |
| return dst; |
| }; |
| |
| /** |
| * Performs a binary NOT operation on an operand |
| * @param {number} value Operand |
| * @return {number} |
| */ |
| deMath.binaryNot = function(value) { |
| //quick path if value fits in signed 32 bit range |
| if (deMath.deInRange32(value, -0x80000000, 0x7FFFFFFF)) |
| return ~value; |
| |
| var x = deMath.split32(value); |
| x[0] = ~x[0]; |
| x[1] = ~x[1]; |
| var ret = deMath.join32(x); |
| return ret; |
| }; |
| |
| /** |
| * Shifts the given value 'steps' bits to the left. Replaces << operator |
| * This function should be used if the expected value will be wider than 32-bits. |
| * @param {number} value |
| * @param {number} steps |
| * @return {number} |
| */ |
| deMath.shiftLeft = function(value, steps) { |
| //quick path |
| if (steps < 31) { |
| var v = value * (1 << steps); |
| if (deMath.deInRange32(v, -0x80000000, 0x7FFFFFFF)) |
| return v; |
| } |
| |
| if (steps == 0) |
| return value; |
| else if (steps < 32) { |
| var mask = (1 << 32 - steps) - 1; |
| var x = deMath.split32(value); |
| var highBits = x[0] & (~mask); |
| var y = highBits >> (32 - steps); |
| if (highBits < 0) { |
| var m = (1 << steps) - 1; |
| y &= m; |
| } |
| var result = []; |
| result[0] = x[0] << steps; |
| result[1] = x[1] << steps; |
| result[1] |= y; |
| |
| return deMath.join32(result); |
| } else { |
| var x = deMath.split32(value); |
| var result = []; |
| result[0] = 0; |
| result[1] = x[0] << steps - 32; |
| return deMath.join32(result); |
| } |
| }; |
| |
| /** |
| * @param {Array<number>} a |
| * @param {number} b |
| */ |
| deMath.shiftLeftVecScalar = function(a, b) { |
| var dst = []; |
| for (var i = 0; i < a.length; i++) |
| dst.push(deMath.shiftLeft(a[i], b)); |
| return dst; |
| }; |
| |
| /** |
| * Shifts the given value 'steps' bits to the right. Replaces >> operator |
| * This function should be used if the value is wider than 32-bits |
| * @param {number} value |
| * @param {number} steps |
| * @return {number} |
| */ |
| deMath.shiftRight = function(value, steps) { |
| //quick path |
| if (deMath.deInRange32(value, -0x80000000, 0x7FFFFFFF) && steps < 32) |
| return value >> steps; |
| |
| if (steps == 0) |
| return value; |
| else if (steps < 32) { |
| if (steps == 0) |
| return value; |
| var mask = (1 << steps) - 1; |
| var x = deMath.split32(value); |
| var lowBits = x[1] & mask; |
| var result = []; |
| var m = (1 << 32 - steps) - 1; |
| result[0] = (x[0] >> steps) & m; |
| result[1] = x[1] >> steps; |
| result[0] |= lowBits << 32 - steps; |
| return deMath.join32(result); |
| } else { |
| var x = deMath.split32(value); |
| var result = []; |
| result[0] = x[1] >> steps - 32; |
| result[1] = value < 0 ? -1 : 0; |
| return deMath.join32(result); |
| } |
| }; |
| |
| /** |
| * @param {Array<number>} a |
| * @param {number} b |
| */ |
| deMath.shiftRightVecScalar = function(a, b) { |
| var dst = []; |
| for (var i = 0; i < a.length; i++) |
| dst.push(deMath.shiftRight(a[i], b)); |
| return dst; |
| }; |
| |
| /** deMath.logicalAndBool over two arrays of booleans |
| * @param {Array<boolean>} a |
| * @param {Array<boolean>} b |
| * @return {Array<boolean>} |
| */ |
| deMath.logicalAndBool = function(a, b) { |
| if (!Array.isArray(a)) |
| throw new Error('The first parameter is not an array: (' + typeof(a) + ')' + a); |
| if (!Array.isArray(b)) |
| throw new Error('The second parameter is not an array: (' + typeof(b) + ')' + b); |
| if (a.length != b.length) |
| throw new Error('The lengths of the passed arrays are not equivalent. (' + a.length + ' != ' + b.length + ')'); |
| |
| /** @type {Array<boolean>} */ var result = []; |
| for (var i = 0; i < a.length; i++) { |
| if (a[i] & b[i]) |
| result.push(true); |
| else |
| result.push(false); |
| } |
| return result; |
| }; |
| |
| /** deMath.logicalOrBool over two arrays of booleans |
| * @param {Array<boolean>} a |
| * @param {Array<boolean>} b |
| * @return {Array<boolean>} |
| */ |
| deMath.logicalOrBool = function(a, b) { |
| if (!Array.isArray(a)) |
| throw new Error('The first parameter is not an array: (' + typeof(a) + ')' + a); |
| if (!Array.isArray(b)) |
| throw new Error('The second parameter is not an array: (' + typeof(b) + ')' + b); |
| if (a.length != b.length) |
| throw new Error('The lengths of the passed arrays are not equivalent. (' + a.length + ' != ' + b.length + ')'); |
| |
| /** @type {Array<boolean>} */ var result = []; |
| for (var i = 0; i < a.length; i++) { |
| if (a[i] | b[i]) |
| result.push(true); |
| else |
| result.push(false); |
| } |
| return result; |
| }; |
| |
| /** deMath.logicalNotBool over an array of booleans |
| * @param {Array<boolean>} a |
| * @return {Array<boolean>} |
| */ |
| deMath.logicalNotBool = function(a) { |
| if (!Array.isArray(a)) |
| throw new Error('The passed value is not an array: (' + typeof(a) + ')' + a); |
| |
| /** @type {Array<boolean>} */ var result = []; |
| for (var i = 0; i < a.length; i++) |
| result.push(!a[i]); |
| return result; |
| }; |
| |
| /** deMath.greaterThan over two arrays of booleans |
| * @param {Array<number>} a |
| * @param {Array<number>} b |
| * @return {Array<boolean>} |
| */ |
| deMath.greaterThan = function(a, b) { |
| if (!Array.isArray(a)) |
| throw new Error('The first parameter is not an array: (' + typeof(a) + ')' + a); |
| if (!Array.isArray(b)) |
| throw new Error('The second parameter is not an array: (' + typeof(b) + ')' + b); |
| if (a.length != b.length) |
| throw new Error('The lengths of the passed arrays are not equivalent. (' + a.length + ' != ' + b.length + ')'); |
| |
| /** @type {Array<boolean>} */ var result = []; |
| for (var i = 0; i < a.length; i++) |
| result.push(a[i] > b[i]); |
| return result; |
| }; |
| |
| /** deMath.greaterThan over two arrays of booleans |
| * @param {Array<number>} a |
| * @param {Array<number>} b |
| * @return {Array<boolean>} |
| */ |
| deMath.greaterThanEqual = function(a, b) { |
| if (!Array.isArray(a)) |
| throw new Error('The first parameter is not an array: (' + typeof(a) + ')' + a); |
| if (!Array.isArray(b)) |
| throw new Error('The second parameter is not an array: (' + typeof(b) + ')' + b); |
| if (a.length != b.length) |
| throw new Error('The lengths of the passed arrays are not equivalent. (' + a.length + ' != ' + b.length + ')'); |
| |
| /** @type {Array<boolean>} */ var result = []; |
| for (var i = 0; i < a.length; i++) |
| result.push(a[i] >= b[i]); |
| return result; |
| }; |
| |
| /** |
| * Array of float to array of int (0, 255) |
| * @param {Array<number>} a |
| * @return {Array<number>} |
| */ |
| |
| deMath.toIVec = function(a) { |
| /** @type {Array<number>} */ var res = []; |
| for (var i = 0; i < a.length; i++) |
| res.push(deMath.clamp(Math.floor(a[i] * 255), 0, 255)); |
| return res; |
| }; |
| |
| /** |
| * @param {number} a |
| * @return {number} |
| */ |
| deMath.clz32 = function(a) { |
| /** @type {number} */ var maxValue = 2147483648; // max 32 bit number |
| /** @type {number} */ var leadingZeros = 0; |
| while (a < maxValue) { |
| maxValue = maxValue >>> 1; |
| leadingZeros++; |
| } |
| return leadingZeros; |
| }; |
| |
| /** |
| * @param {number} a |
| * @param {number} exponent |
| * @return {number} |
| */ |
| deMath.deLdExp = function(a, exponent) { |
| return deMath.ldexp(a, exponent); |
| }; |
| |
| /** |
| * @param {number} a |
| * @param {number} exponent |
| * @return {number} |
| */ |
| deMath.deFloatLdExp = function(a, exponent) { |
| return deMath.ldexp(a, exponent); |
| }; |
| |
| /** |
| * @param {number} value |
| * @return {Array<number>} |
| */ |
| deMath.frexp = (function() { |
| var data = new DataView(new ArrayBuffer(8)); |
| |
| return function(value) { |
| if (value === 0) return [value, 0]; |
| data.setFloat64(0, value); |
| var bits = (data.getUint32(0) >>> 20) & 0x7FF; |
| if (bits === 0) { |
| data.setFloat64(0, value * Math.pow(2, 64)); |
| bits = ((data.getUint32(0) >>> 20) & 0x7FF) - 64; |
| } |
| var exponent = bits - 1022, |
| mantissa = deMath.ldexp(value, -exponent); |
| return [mantissa, exponent]; |
| } |
| })(); |
| |
| /** |
| * @param {number} mantissa |
| * @param {number} exponent |
| * @return {number} |
| */ |
| deMath.ldexp = function(mantissa, exponent) { |
| return exponent > 1023 ? // avoid multiplying by infinity |
| mantissa * Math.pow(2, 1023) * Math.pow(2, exponent - 1023) : |
| exponent < -1074 ? // avoid multiplying by zero |
| mantissa * Math.pow(2, -1074) * Math.pow(2, exponent + 1074) : |
| mantissa * Math.pow(2, exponent); |
| }; |
| |
| /** |
| * @param {number} a |
| * @return {number} |
| */ |
| deMath.deCbrt = function(a) { |
| return deMath.deSign(a) * Math.pow(Math.abs(a), 1.0 / 3.0); |
| }; |
| |
| /** |
| * @param {number} x |
| * @return {number} |
| */ |
| deMath.deSign = function(x) { |
| return isNaN(x) ? x : ((x > 0.0) - (x < 0.0)); |
| }; |
| |
| deMath.deFractExp = function(x) { |
| var result = { |
| significand: x, |
| exponent: 0 |
| }; |
| |
| if (isFinite(x)) { |
| var r = deMath.frexp(x); |
| result.exponent = r[1] - 1; |
| result.significand = r[0] * 2; |
| } |
| return result; |
| }; |
| |
| }); |