|
| 1 | +import { isBigNumber } from '../../utils/is' |
| 2 | +import { factory } from '../../utils/factory' |
| 3 | + |
| 4 | +const name = 'rotationMatrix' |
| 5 | +const dependencies = [ |
| 6 | + 'typed', |
| 7 | + 'config', |
| 8 | + 'multiplyScalar', |
| 9 | + 'addScalar', |
| 10 | + 'unaryMinus', |
| 11 | + 'norm', |
| 12 | + 'matrix', |
| 13 | + 'BigNumber', |
| 14 | + 'DenseMatrix', |
| 15 | + 'SparseMatrix', |
| 16 | + 'cos', |
| 17 | + 'sin' |
| 18 | +] |
| 19 | + |
| 20 | +export const createRotationMatrix = /* #__PURE__ */ factory(name, dependencies, ( |
| 21 | + { |
| 22 | + typed, config, multiplyScalar, |
| 23 | + addScalar, unaryMinus, norm, BigNumber, |
| 24 | + matrix, DenseMatrix, SparseMatrix, cos, sin |
| 25 | + }) => { |
| 26 | + /** |
| 27 | + * Create a 2-dimensional counter-clockwise rotation matrix (2x2) for a given angle (expressed in radians). |
| 28 | + * Create a 2-dimensional counter-clockwise rotation matrix (3x3) by a given angle (expressed in radians) around a given axis (1x3). |
| 29 | + * |
| 30 | + * Syntax: |
| 31 | + * |
| 32 | + * math.rotationMatrix(theta) |
| 33 | + * math.rotationMatrix(theta, format) |
| 34 | + * math.rotationMatrix(theta, [v]) |
| 35 | + * math.rotationMatrix(theta, [v], format) |
| 36 | + * |
| 37 | + * Examples: |
| 38 | + * |
| 39 | + * math.rotationMatrix(math.pi / 2) // returns [[0, -1], [1, 0]] |
| 40 | + * math.rotationMatrix(math.bignumber(45)) // returns [[ bignumber(1 / sqrt(2)), - bignumber(1 / sqrt(2))], [ bignumber(1 / sqrt(2)), bignumber(1 / sqrt(2))]] |
| 41 | + * math.rotationMatrix(math.complex(1 + i)) // returns [[cos(1 + i), -sin(1 + i)], [sin(1 + i), cos(1 + i)]] |
| 42 | + * math.rotationMatrix(math.unit('1rad')) // returns [[cos(1), -sin(1)], [sin(1), cos(1)]] |
| 43 | + * |
| 44 | + * math.rotationMatrix(math.pi / 2, [0, 1, 0]) // returns [[0, 0, 1], [0, 1, 0], [-1, 0, 0]] |
| 45 | + * math.rotationMatrix(math.pi / 2, matrix([0, 1, 0])) // returns matrix([[0, 0, 1], [0, 1, 0], [-1, 0, 0]]) |
| 46 | + * |
| 47 | + * |
| 48 | + * See also: |
| 49 | + * |
| 50 | + * matrix, cos, sin |
| 51 | + * |
| 52 | + * |
| 53 | + * @param {number | BigNumber | Complex | Unit} theta Rotation angle |
| 54 | + * @param {Array | Matrix} [v] Rotation axis |
| 55 | + * @param {string} [format] Result Matrix storage format |
| 56 | + * @return {Array | Matrix} Rotation matrix |
| 57 | + */ |
| 58 | + |
| 59 | + return typed(name, { |
| 60 | + '': function () { |
| 61 | + return (config.matrix === 'Matrix') ? matrix([]) : [] |
| 62 | + }, |
| 63 | + |
| 64 | + string: function (format) { |
| 65 | + return matrix(format) |
| 66 | + }, |
| 67 | + |
| 68 | + 'number | BigNumber | Complex | Unit': function (theta) { |
| 69 | + return _rotationMatrix2x2(theta, config.matrix === 'Matrix' ? 'dense' : undefined) |
| 70 | + }, |
| 71 | + |
| 72 | + 'number | BigNumber | Complex | Unit, string': function (theta, format) { |
| 73 | + return _rotationMatrix2x2(theta, format) |
| 74 | + }, |
| 75 | + |
| 76 | + 'number | BigNumber | Complex | Unit, Array': function (theta, v) { |
| 77 | + const matrixV = matrix(v) |
| 78 | + _validateVector(matrixV) |
| 79 | + return _rotationMatrix3x3(theta, matrixV, config.matrix === 'Matrix' ? 'dense' : undefined) |
| 80 | + }, |
| 81 | + |
| 82 | + 'number | BigNumber | Complex | Unit, Matrix': function (theta, v) { |
| 83 | + _validateVector(v) |
| 84 | + return _rotationMatrix3x3(theta, v, config.matrix === 'Matrix' ? 'dense' : undefined) |
| 85 | + }, |
| 86 | + |
| 87 | + 'number | BigNumber | Complex | Unit, Array, string': function (theta, v, format) { |
| 88 | + const matrixV = matrix(v) |
| 89 | + _validateVector(matrixV) |
| 90 | + return _rotationMatrix3x3(theta, matrixV, format) |
| 91 | + }, |
| 92 | + |
| 93 | + 'number | BigNumber | Complex | Unit, Matrix, string': function (theta, v, format) { |
| 94 | + _validateVector(v) |
| 95 | + return _rotationMatrix3x3(theta, v, format) |
| 96 | + } |
| 97 | + |
| 98 | + }) |
| 99 | + |
| 100 | + /** |
| 101 | + * Returns 2x2 matrix of 2D rotation of angle theta |
| 102 | + * |
| 103 | + * @param {number | BigNumber | Complex | Unit} theta The rotation angle |
| 104 | + * @param {string} format The result Matrix storage format |
| 105 | + * @returns {Matrix} |
| 106 | + * @private |
| 107 | + */ |
| 108 | + function _rotationMatrix2x2 (theta, format) { |
| 109 | + const Big = isBigNumber(theta) |
| 110 | + |
| 111 | + const minusOne = Big ? new BigNumber(-1) : -1 |
| 112 | + const cosTheta = cos(theta) |
| 113 | + const sinTheta = sin(theta) |
| 114 | + const data = [[cosTheta, multiplyScalar(minusOne, sinTheta)], [sinTheta, cosTheta]] |
| 115 | + |
| 116 | + return _convertToFormat(data, format) |
| 117 | + } |
| 118 | + |
| 119 | + function _validateVector (v) { |
| 120 | + const size = v.size() |
| 121 | + if (size.length < 1 || size[0] !== 3) { |
| 122 | + throw new RangeError('Vector must be of dimensions 1x3') |
| 123 | + } |
| 124 | + } |
| 125 | + |
| 126 | + function _mul (array) { |
| 127 | + return array.reduce((p, curr) => multiplyScalar(p, curr)) |
| 128 | + } |
| 129 | + |
| 130 | + function _convertToFormat (data, format) { |
| 131 | + if (format) { |
| 132 | + if (format === 'sparse') { |
| 133 | + return new SparseMatrix(data) |
| 134 | + } |
| 135 | + if (format === 'dense') { |
| 136 | + return new DenseMatrix(data) |
| 137 | + } |
| 138 | + throw new TypeError(`Unknown matrix type "${format}"`) |
| 139 | + } |
| 140 | + return data |
| 141 | + } |
| 142 | + |
| 143 | + /** |
| 144 | + * Returns a 3x3 matrix of rotation of angle theta around vector v |
| 145 | + * |
| 146 | + * @param {number | BigNumber | Complex | Unit} theta The rotation angle |
| 147 | + * @param {Matrix} v The rotation axis vector |
| 148 | + * @param {string} format The storage format of the resulting matrix |
| 149 | + * @returns {Matrix} |
| 150 | + * @private |
| 151 | + */ |
| 152 | + function _rotationMatrix3x3 (theta, v, format) { |
| 153 | + const normV = norm(v) |
| 154 | + if (normV === 0) { |
| 155 | + return _convertToFormat([], format) |
| 156 | + } |
| 157 | + |
| 158 | + const Big = isBigNumber(theta) ? BigNumber : null |
| 159 | + |
| 160 | + const one = Big ? new Big(1) : 1 |
| 161 | + const minusOne = Big ? new Big(-1) : -1 |
| 162 | + const vx = Big ? new Big(v.get([0]) / normV) : v.get([0]) / normV |
| 163 | + const vy = Big ? new Big(v.get([1]) / normV) : v.get([1]) / normV |
| 164 | + const vz = Big ? new Big(v.get([2]) / normV) : v.get([2]) / normV |
| 165 | + const c = cos(theta) |
| 166 | + const oneMinusC = addScalar(one, unaryMinus(c)) |
| 167 | + const s = sin(theta) |
| 168 | + |
| 169 | + const r11 = addScalar(c, _mul([vx, vx, oneMinusC])) |
| 170 | + const r12 = addScalar(_mul([vx, vy, oneMinusC]), _mul([minusOne, vz, s])) |
| 171 | + const r13 = addScalar(_mul([vx, vz, oneMinusC]), _mul([vy, s])) |
| 172 | + |
| 173 | + const r21 = addScalar(_mul([vx, vy, oneMinusC]), _mul([vz, s])) |
| 174 | + const r22 = addScalar(c, _mul([vy, vy, oneMinusC])) |
| 175 | + const r23 = addScalar(_mul([vy, vz, oneMinusC]), _mul([minusOne, vx, s])) |
| 176 | + |
| 177 | + const r31 = addScalar(_mul([vx, vz, oneMinusC]), _mul([minusOne, vy, s])) |
| 178 | + const r32 = addScalar(_mul([vy, vz, oneMinusC]), _mul([vx, s])) |
| 179 | + const r33 = addScalar(c, _mul([vz, vz, oneMinusC])) |
| 180 | + |
| 181 | + const data = [[r11, r12, r13], [r21, r22, r23], [r31, r32, r33]] |
| 182 | + |
| 183 | + return _convertToFormat(data, format) |
| 184 | + } |
| 185 | +}) |
0 commit comments