ccctool-lib
Version:
409 lines (341 loc) • 12.4 kB
JavaScript
import { isMathMatrix, isMathVector, isNumber } from "../helper/guardClauses.js";
//////////////////////////////////////////////////////////
////////////// Vector //////////////
//////////////////////////////////////////////////////////
export const vecScalMulti = (_v, _s) => {
let result = copyVector(_v);
for (let i = 0; i < result.length; i++) {
result[i] = result[i] * _s;
}
return result;
};
export const vec_Divi = (_v, _s) => {
let result = [];
for (let i = 0; i < _v.length; i++) {
result.push(_v[i] / _s);
}
return result;
};
export const vec_Diff = (_v1, _v2) => {
if (_v1.length != _v2.length) return undefined;
let result = [];
for (let i = 0; i < _v1.length; i++) {
result.push(_v1[i] - _v2[i]);
}
return result;
};
export const vec_Add = (_v1, _v2) => {
if (_v1.length != _v2.length) return undefined;
let result = [];
for (let i = 0; i < _v1.length; i++) {
result.push(_v1[i] + _v2[i]);
}
return result;
};
export const vec_Dot = (_v1, _v2) => {
if (_v1.length != _v2.length) return undefined;
let result = 0;
for (let i = 0; i < _v1.length; i++) {
result += _v1[i] * _v2[i];
}
return result;
};
export const vec_Cross = (_v1, _v2) => {
if (_v1.length != 3 || _v2.length != 3) return undefined;
let result = [undefined, undefined, undefined];
result[0] = _v1[1] * _v2[2] - _v1[2] * _v2[1];
result[1] = _v1[0] * _v2[2] - _v1[2] * _v2[0];
result[2] = _v1[0] * _v2[1] - _v1[1] * _v2[0];
return result;
};
export const vecNorm = (_v) => {
let result = copyVector(_v);
if (vecLength(_v) != 0) {
let tmp = 1 / vecLength(_v);
for (let i = 0; i < result.length; i++) {
result[i] = result[i] * tmp;
}
return result;
}
return result; // vector is [0,0,....];
};
export const vecLength = (_v) => {
let sum = 0;
for (let i = 0; i < _v.length; i++) {
sum += Math.pow(_v[i], 2);
}
return Math.sqrt(sum);
};
export const vec_Distance = (_v1, _v2) => {
if (_v1.length == _v2.length) {
let sum = 0;
for (let i = 0; i < _v1.length; i++) {
sum += Math.pow(_v2[i] - _v1[i], 2);
}
return Math.sqrt(sum);
}
return undefined;
};
//////////////////////////////////////////////////////////
////////////// Matrix Calculation //////////////
//////////////////////////////////////////////////////////
// matrix * matrix multiplication
export const mXm = (_m1, _m2) => {
isMathMatrix(_m1); // throw error if not
isMathMatrix(_m2); // throw error if not
let m1NumRows = _m1.length,
m1NumCols = _m1[0].length,
m2NumRows = _m2.length,
m2NumCols = _m2[0].length,
mResult = new Array(m1NumRows);
if (m1NumCols != m2NumRows) {
throw new TypeError('Error (math) :: "mXm" :: m1 matrix column size and m2 matrix row size are not equal.');
}
for (let r = 0; r < m1NumRows; r++) {
mResult[r] = new Array(m2NumCols);
for (let c = 0; c < m2NumCols; c++) {
mResult[r][c] = 0;
for (let i = 0; i < m1NumCols; i++) {
mResult[r][c] += _m1[r][i] * _m2[i][c];
}
}
}
return mResult;
/*let result = new Array(m1.length).fill(0).map(row => new Array(m2[0].length).fill(0));
return result.map((row, i) => {
return row.map((val, j) => {
return m1[i].reduce((sum, elm, k) => sum + (elm*m2[k][j]) ,0)
})
})*/
};
// matrix * vector multiplication
export const mXv = (_m, _v) => {
isMathMatrix(_m); // throw error if not
isMathVector(_v); // throw error if not
let m1NumRows = _m.length,
m1NumCols = _m[0].length,
vNumRows = _v.length,
mResult = new Array(m1NumRows).fill(0);
if (m1NumCols != vNumRows) {
throw new TypeError('Error (math) :: export const "mXv" :: Matrix column size and vector size are not equal.');
}
for (let r = 0; r < m1NumRows; r++) {
for (let c = 0; c < m1NumCols; c++) {
mResult[r] += _m[r][c] * _v[c];
}
}
return mResult;
};
// invert 3x3 matrix
export const invert3x3 = (_m) => {
isMathMatrix(_m); // throw error if not
let det = determinant(_m);
if (det == 0) throw new TypeError('Error (math) :: export const "invert3x3" :: matrix determinant is null.');
//if (det < 1e-2) throw new TypeError('Error (math) :: export const "invert3x3" :: matrix determinant is null.');
let invdet = 1.0 / det;
let matrix_Inv = [
[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
];
for (let y = 0; y < 3; y++) {
for (let x = 0; x < 3; x++) {
matrix_Inv[y][x] = determinantOfMinor(x, y, _m) * invdet;
if (1 == (x + y) % 2) matrix_Inv[y][x] = -1 * matrix_Inv[y][x];
}
}
return matrix_Inv;
};
export const determinant = (_m) => {
isMathMatrix(_m); // throw error if not
return _m[0][0] * determinantOfMinor(0, 0, _m) - _m[0][1] * determinantOfMinor(0, 1, _m) + _m[0][2] * determinantOfMinor(0, 2, _m);
};
///////////////////////////////////////////////////////////////////
// Inverse Algorithm based on Guassian elimination from http://blog.acipo.com/matrix-inversion-in-javascript/
// Returns the inverse of matrix `M`.
export const matrix_invert = (_m) => {
// I use Guassian Elimination to calculate the inverse:
// (1) 'augment' the matrix (left) by the identity (on the right)
// (2) Turn the matrix on the left into the identity by elemetry row ops
// (3) The matrix on the right is the inverse (was the identity matrix)
// There are 3 elemtary row ops: (I combine b and c in my code)
// (a) Swap 2 rows
// (b) Multiply a row by a scalar
// (c) Add 2 rows
//if the matrix isn't square: exit (error)
if (_m.length !== _m[0].length) {
return;
}
//create the identity matrix (I), and a copy (C) of the original
let i = 0,
ii = 0,
j = 0,
dim = _m.length,
e = 0,
t = 0;
let identityM = [],
copyM = [];
for (i = 0; i < dim; i += 1) {
// Create the row
identityM[identityM.length] = [];
copyM[copyM.length] = [];
for (j = 0; j < dim; j += 1) {
//if we're on the diagonal, put a 1 (for identity)
if (i == j) {
identityM[i][j] = 1;
} else {
identityM[i][j] = 0;
}
// Also, make the copy of the original
copyM[i][j] = _m[i][j];
}
}
// Perform elementary row operations
for (i = 0; i < dim; i += 1) {
// get the element e on the diagonal
e = copyM[i][i];
// if we have a 0 on the diagonal (we'll need to swap with a lower row)
if (e == 0) {
//look through every row below the i'th row
for (ii = i + 1; ii < dim; ii += 1) {
//if the ii'th row has a non-0 in the i'th col
if (copyM[ii][i] != 0) {
//it would make the diagonal have a non-0 so swap it
for (j = 0; j < dim; j++) {
e = copyM[i][j]; //temp store i'th row
copyM[i][j] = copyM[ii][j]; //replace i'th row by ii'th
copyM[ii][j] = e; //repace ii'th by temp
e = identityM[i][j]; //temp store i'th row
identityM[i][j] = identityM[ii][j]; //replace i'th row by ii'th
identityM[ii][j] = e; //repace ii'th by temp
}
//don't bother checking other rows since we've swapped
break;
}
}
//get the new diagonal
e = copyM[i][i];
//if it's still 0, not invertable (error)
if (e == 0) {
return;
}
}
// Scale this row down by e (so we have a 1 on the diagonal)
for (j = 0; j < dim; j++) {
copyM[i][j] = copyM[i][j] / e; //apply to original matrix
identityM[i][j] = identityM[i][j] / e; //apply to identity
}
// Subtract this row (scaled appropriately for each row) from ALL of
// the other rows so that there will be 0's in this column in the
// rows above and below this one
for (ii = 0; ii < dim; ii++) {
// Only apply to other rows (we want a 1 on the diagonal)
if (ii == i) {
continue;
}
// We want to change this element to 0
e = copyM[ii][i];
// Subtract (the row above(or below) scaled by e) from (the
// current row) but start at the i'th column and assume all the
// stuff left of diagonal is 0 (which it should be if we made this
// algorithm correctly)
for (j = 0; j < dim; j++) {
copyM[ii][j] -= e * copyM[i][j]; //apply to original matrix
identityM[ii][j] -= e * identityM[i][j]; //apply to identity
}
}
}
//we've done all operations, C should be the identity
//matrix I should be the inverse:
return identityM;
};
//////////////////////////////////////////////////////////
////////////// Radial //////////////
//////////////////////////////////////////////////////////
export const deg2rad = (_degree) => {
if (!isNumber(_degree)) throw new TypeError('Error (math) :: export const "deg2rad" :: Incorrect input! The parameter "degree" need be a number');
return (_degree / 180) * Math.PI;
};
export const rad2deg = (_rad) => {
if (!isNumber(_rad)) throw new TypeError('Error (math) :: export const "rad2deg" :: Incorrect is not a number');
return (_rad * 180) / Math.PI;
};
export const atan2_360Degree = (_x, _y) => {
if (!isNumber(_x)) throw new TypeError('Error (math) :: export const "atan2_360Degree" :: Incorrect is not a number');
if (!isNumber(_y)) throw new TypeError('Error (math) :: export const "atan2_360Degree" :: Incorrect is not a number');
let tmpRad = Math.atan2(_y, _x);
if (tmpRad < 0) tmpRad = 2 * Math.PI + tmpRad;
return rad2deg(tmpRad);
};
export const degree360ToRad = (_degree) => {
if (!isNumber(_degree)) throw new TypeError('Error (math) :: export const "degree360ToRad" :: Incorrect is not a number');
if (_degree > 180) _degree = _degree - 360;
return deg2rad(_degree);
};
//////////////////////////////////////////////////////////
////////////// Other //////////////
//////////////////////////////////////////////////////////
export const getRatio = (_min, _max, _value) => {
if (!isNumber(_min)) throw new TypeError('Error (math) :: export const "getRatio" :: Incorrect input! The parameter "min" need be a number');
if (!isNumber(_max)) throw new TypeError('Error (math) :: export const "getRatio" :: Incorrect input! The parameter "max" need be a number');
if (_min >= _max) throw new TypeError('Error (math) :: export const "getRatio" :: Incorrect input! The parameter "max" need be a larger than "min"!');
if (!isNumber(_value)) throw new TypeError('Error (math) :: export const "getRatio" :: Incorrect input! The parameter "value" need be a number');
if (_value < _min) throw new TypeError('Error (math) :: export const "getRatio" :: Incorrect input! The parameter "value" need be a larger than "min"!');
if (_value > _max) throw new TypeError('Error (math) :: export const "getRatio" :: Incorrect input! The parameter "value" need be a smaller than "max"!');
return Math.abs(_value - _min) / Math.abs(_max - _min);
};
/*
export const sum = (...theArgs) => {
return theArgs.reduce((previous, current) => {
return previous + current;
});
}
export const sumArray = (array) => {
let sum = 0;
for (let i = 0; i < array.length; i++) {
sum += array[i];
}
return sum;
}
export const midnightFormula(a,b,c){
let results = [];
let discriminant = Math.pow(b,2) - (4*a*c);
if(discriminant>0){
// Roots are real and different
results.push((-b + Math.sqrt(discriminant)) / (2*a));
results.push((-b - Math.sqrt(discriminant)) / (2*a));
}
else if(discriminant==0){
// Roots are real and same
results.push((-b + Math.sqrt(discriminant)) / (2*a));
}
else{
// Roots are complex and different
//let realPart = -b/(2*a);
//let imaginaryPart = Math.sqrt(-discriminant)/(2*a);
}
return results;
}
*/
//////////////////////////////////////////////////
/////////////////// Private ////////////////////
//////////////////////////////////////////////////
function determinantOfMinor(_yPos, _xPos, _m) {
let x1, x2, y1, y2;
if (_xPos == 0) x1 = 1;
else x1 = 0;
if (_xPos == 2) x2 = 1;
else x2 = 2;
if (_yPos == 0) y1 = 1;
else y1 = 0;
if (_yPos == 2) y2 = 1;
else y2 = 2;
return _m[y1][x1] * _m[y2][x2] - _m[y1][x2] * _m[y2][x1];
}
function copyVector(_v) {
var result = [];
for (var i = 0; i < _v.length; i++) {
result.push(_v[i]);
}
return result;
}