@turbox3d/math
Version:
Large-scale graphics application math library
338 lines • 12.5 kB
JavaScript
import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray";
import { Tolerance } from './base/Tolerance';
import { Vector2 } from './base/Vector2';
var _lut = [];
for (var i = 0; i < 256; i++) {
_lut[i] = (i < 16 ? '0' : '') + i.toString(16);
}
var _seed = 1234567;
var MathUtils = {
DEG2RAD: Math.PI / 180,
RAD2DEG: 180 / Math.PI,
generateUUID: function generateUUID() {
// http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/21963136#21963136
var d0 = Math.random() * 0xffffffff | 0;
var d1 = Math.random() * 0xffffffff | 0;
var d2 = Math.random() * 0xffffffff | 0;
var d3 = Math.random() * 0xffffffff | 0;
var uuid = "".concat(_lut[d0 & 0xff] + _lut[d0 >> 8 & 0xff] + _lut[d0 >> 16 & 0xff] + _lut[d0 >> 24 & 0xff], "-").concat(_lut[d1 & 0xff]).concat(_lut[d1 >> 8 & 0xff], "-").concat(_lut[d1 >> 16 & 0x0f | 0x40]).concat(_lut[d1 >> 24 & 0xff], "-").concat(_lut[d2 & 0x3f | 0x80]).concat(_lut[d2 >> 8 & 0xff], "-").concat(_lut[d2 >> 16 & 0xff]).concat(_lut[d2 >> 24 & 0xff]).concat(_lut[d3 & 0xff]).concat(_lut[d3 >> 8 & 0xff]).concat(_lut[d3 >> 16 & 0xff]).concat(_lut[d3 >> 24 & 0xff]);
// .toUpperCase() here flattens concatenated strings to save heap memory space.
return uuid.toUpperCase();
},
clamp: function clamp(value, min, max) {
return Math.max(min, Math.min(max, value));
},
// compute euclidian modulo of m % n
// https://en.wikipedia.org/wiki/Modulo_operation
euclideanModulo: function euclideanModulo(n, m) {
return (n % m + m) % m;
},
// Linear mapping from range <a1, a2> to range <b1, b2>
mapLinear: function mapLinear(x, a1, a2, b1, b2) {
return b1 + (x - a1) * (b2 - b1) / (a2 - a1);
},
// https://en.wikipedia.org/wiki/Linear_interpolation
lerp: function lerp(x, y, t) {
return (1 - t) * x + t * y;
},
// http://www.rorydriscoll.com/2016/03/07/frame-rate-independent-damping-using-lerp/
damp: function damp(x, y, lambda, dt) {
return MathUtils.lerp(x, y, 1 - Math.exp(-lambda * dt));
},
// https://www.desmos.com/calculator/vcsjnyz7x4
pingpong: function pingpong(x) {
var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
return length - Math.abs(MathUtils.euclideanModulo(x, length * 2) - length);
},
// http://en.wikipedia.org/wiki/Smoothstep
smoothstep: function smoothstep(x, min, max) {
if (x <= min) return 0;
if (x >= max) return 1;
x = (x - min) / (max - min);
return x * x * (3 - 2 * x);
},
smootherstep: function smootherstep(x, min, max) {
if (x <= min) return 0;
if (x >= max) return 1;
x = (x - min) / (max - min);
return x * x * x * (x * (x * 6 - 15) + 10);
},
// Random integer from <low, high> interval
randInt: function randInt(low, high) {
return low + Math.floor(Math.random() * (high - low + 1));
},
// Random float from <low, high> interval
randFloat: function randFloat(low, high) {
return low + Math.random() * (high - low);
},
// Random float from <-range/2, range/2> interval
randFloatSpread: function randFloatSpread(range) {
return range * (0.5 - Math.random());
},
// Deterministic pseudo-random float in the interval [ 0, 1 ]
seededRandom: function seededRandom(s) {
if (s !== undefined) _seed = s % 2147483647;
// Park-Miller algorithm
_seed = _seed * 16807 % 2147483647;
return (_seed - 1) / 2147483646;
},
degToRad: function degToRad(degrees) {
return degrees * MathUtils.DEG2RAD;
},
radToDeg: function radToDeg(radians) {
return radians * MathUtils.RAD2DEG;
},
isPowerOfTwo: function isPowerOfTwo(value) {
return (value & value - 1) === 0 && value !== 0;
},
ceilPowerOfTwo: function ceilPowerOfTwo(value) {
return Math.pow(2, Math.ceil(Math.log(value) / Math.LN2));
},
floorPowerOfTwo: function floorPowerOfTwo(value) {
return Math.pow(2, Math.floor(Math.log(value) / Math.LN2));
},
setQuaternionFromProperEuler: function setQuaternionFromProperEuler(q, a, b, c, order) {
// Intrinsic Proper Euler Angles - see https://en.wikipedia.org/wiki/Euler_angles
// rotations are applied to the axes in the order specified by 'order'
// rotation by angle 'a' is applied first, then by angle 'b', then by angle 'c'
// angles are in radians
var cos = Math.cos;
var sin = Math.sin;
var c2 = cos(b / 2);
var s2 = sin(b / 2);
var c13 = cos((a + c) / 2);
var s13 = sin((a + c) / 2);
var c1_3 = cos((a - c) / 2);
var s1_3 = sin((a - c) / 2);
var c3_1 = cos((c - a) / 2);
var s3_1 = sin((c - a) / 2);
switch (order) {
case 'XYX':
q.set(c2 * s13, s2 * c1_3, s2 * s1_3, c2 * c13);
break;
case 'YZY':
q.set(s2 * s1_3, c2 * s13, s2 * c1_3, c2 * c13);
break;
case 'ZXZ':
q.set(s2 * c1_3, s2 * s1_3, c2 * s13, c2 * c13);
break;
case 'XZX':
q.set(c2 * s13, s2 * s3_1, s2 * c3_1, c2 * c13);
break;
case 'YXY':
q.set(s2 * c3_1, c2 * s13, s2 * s3_1, c2 * c13);
break;
case 'ZYZ':
q.set(s2 * s3_1, s2 * c3_1, c2 * s13, c2 * c13);
break;
default:
console.warn("THREE.MathUtils: .setQuaternionFromProperEuler() encountered an unknown order: ".concat(order));
}
},
isEqual: function isEqual(num1, num2, tol) {
var tolerance = tol || Tolerance.global.numTol;
if (num1 === num2) {
return true;
}
return Math.abs(num1 - num2) <= tolerance;
},
isZero: function isZero(num, tol) {
return MathUtils.isEqual(num, 0, tol);
},
/**
* Check wether num1 is smaller than num2 with specified tolerance.
* Global (default) tolerance will be used if tol is not provided.
* @param num1
* @param num2
* @param tol
*/
isSmaller: function isSmaller(num1, num2, tol) {
var tolerance = tol || Tolerance.global.numTol;
var diff = num1 - num2;
return diff < -tolerance;
},
/**
* Check whether num1 is bigger than num2 with specified tolerance.
* Global (default) tolerance will be used if tol is not provided.
* @param num1
* @param num2
* @param tol
*/
isBigger: function isBigger(num1, num2, tol) {
var tolerance = tol || Tolerance.global.numTol;
var diff = num1 - num2;
return diff > tolerance;
},
/**
* Check whether num1 is smaller than or equal to num2 with specified tolerance.
* Global (default) tolerance will be used if tol is not provided.
* @param num1
* @param num2
* @param tol
*/
isSmallerOrEqual: function isSmallerOrEqual(num1, num2, tol) {
var tolerance = tol || Tolerance.global.numTol;
var diff = num1 - num2;
return diff <= tolerance;
},
/**
* Check whether num1 is bigger than or equal to num2 with specified tolerance.
* Global (default) tolerance will be used if tol is not provided.
* @param num1
* @param num2
* @param tol
*/
isBiggerOrEqual: function isBiggerOrEqual(num1, num2, tol) {
var tolerance = tol || Tolerance.global.numTol;
var diff = num1 - num2;
return diff >= -tolerance;
},
/**
* Check whether a number is in a range with specified tolerance.
* Global (default) tolerance will be used if tol is not provided.
* @param value the value to be checked.
* @param min the range's lower limit.
* @param max the range's upper limit.
* @param minOpen whether the range's lower limit is open, default is false.
* @param maxOpen whether the range's upper limit is open, default is false.
* @param tol
*/
isInRange: function isInRange(value, min, max, minOpen, maxOpen, tol) {
var tolerance = tol || Tolerance.global.numTol;
var diffMin = value - min;
var diffMax = value - max;
var biggerMin = minOpen ? diffMin > tolerance : diffMin >= -tolerance;
var smallerMax = maxOpen ? diffMax < -tolerance : diffMax <= tolerance;
return biggerMin && smallerMax;
},
/**
* Compare two numbers:
* If num1 is fuzzy equal to num2, return 0.
* If num1 is fuzzy bigger than num2, return 1.
* If num1 is fuzzy smaller than num2, return -1.
* If num1 or num2 is a NaN, throw exception.
* @param num1
* @param num2
* @param tol
*/
compare: function compare(num1, num2, tol) {
if (Number.isNaN(num1) || Number.isNaN(num2)) {
throw Error('Invalid NaN number');
}
if (MathUtils.isEqual(num1, num2, tol)) {
return 0;
}
if (num1 < num2) {
return -1;
}
return 1;
},
/**
* 单向线性插值
*/
interpolation: function interpolation(start, end) {
var segments = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 10;
var v = end.subtracted(start);
var step = v.length / segments;
var points = [];
var n = v.normalized();
for (var index = 0; index < segments - 1; index++) {
points.push(start.added(n.clone().multiplyScalar(step * (index + 1))));
}
points.unshift(start.clone());
points.push(end.clone());
return points;
},
/**
* 根据四方点生成网格
* @param quadPositions 四方点,左上角为起点,按照顺时针顺序
* @param widthSegments 宽度分割段数
* @param heightSegments 高度分割段数
*/
generateMeshByQuad: function generateMeshByQuad(quadPositions) {
var widthSegments = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 10;
var heightSegments = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 10;
var indices = [];
var vertices = [];
var normals = [];
var uvs = [];
var a1 = MathUtils.interpolation(quadPositions[0], quadPositions[1], widthSegments);
var a2 = MathUtils.interpolation(quadPositions[3], quadPositions[2], widthSegments);
var a3 = a1.map(function (v, i) {
return MathUtils.interpolation(v.clone(), a2[i].clone(), heightSegments);
});
var _loop = function _loop(_i) {
if (_i === 0) {
vertices.push.apply(vertices, _toConsumableArray(a1.flatMap(function (a) {
return a.toArray();
})));
} else if (_i === heightSegments) {
vertices.push.apply(vertices, _toConsumableArray(a2.flatMap(function (a) {
return a.toArray();
})));
} else {
vertices.push.apply(vertices, _toConsumableArray(a3.flatMap(function (a) {
return a[_i].toArray();
})));
}
for (var _j = 0; _j <= widthSegments; _j++) {
normals.push(0, 0, 1);
uvs.push(_j * (1 / widthSegments), _i * (1 / heightSegments));
}
};
for (var _i = 0; _i <= heightSegments; _i++) {
_loop(_i);
}
for (var _i2 = 0; _i2 < heightSegments; _i2++) {
for (var j = 0; j < widthSegments; j++) {
var a = _i2 * (widthSegments + 1) + (j + 1);
var b = _i2 * (widthSegments + 1) + j;
var c = (_i2 + 1) * (widthSegments + 1) + j;
var d = (_i2 + 1) * (widthSegments + 1) + (j + 1);
indices.push(a, b, d);
indices.push(b, c, d);
}
}
return {
indices: indices,
vertices: vertices,
normals: normals,
uvs: uvs
};
},
/** 把无序的点排序成顺逆时针的邻接点,只支持凸多边形 */clockwisePoints: function clockwisePoints(points) {
var clockwise = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
var newPoints = [points[0]];
var _loop2 = function _loop2() {
var p1 = newPoints[newPoints.length - 1];
var waitComparePoints = points.filter(function (wp) {
return wp !== p1;
});
var _loop3 = function _loop3() {
var p2 = waitComparePoints[index];
var v = new Vector2(p2.x - p1.x, p2.y - p1.y);
var otherPoints = points.filter(function (p) {
return p !== p1 && p !== p2;
});
var otherVs = otherPoints.map(function (p) {
return new Vector2(p.x - p1.x, p.y - p1.y);
});
var isAllMatched = otherVs.every(function (ov) {
return clockwise ? v.cross(ov) < 0 : v.cross(ov) > 0;
});
if (isAllMatched) {
newPoints.push(p2);
return 1; // break
}
};
for (var index = 0; index < waitComparePoints.length; index++) {
if (_loop3()) break;
}
};
for (var _i3 = 0; _i3 < points.length - 1; _i3++) {
_loop2();
}
return newPoints;
}
};
export { MathUtils };