blob: fbb2a61fd71a30c98c4108f61047d8266a5186c7 [file] [log] [blame]
/*-------------------------------------------------------------------------
* 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;
};
});