Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/rotate matrix #1984

Merged
merged 11 commits into from Oct 7, 2020
2 changes: 2 additions & 0 deletions src/expression/embeddedDocs/embeddedDocs.js
Expand Up @@ -214,6 +214,7 @@ import { sinDocs } from './function/trigonometry/sin'
import { numericDocs } from './function/utils/numeric'
import { columnDocs } from './function/matrix/column'
import { rowDocs } from './function/matrix/row'
import { rotationMatrixDocs } from './function/matrix/rotationMatrix'

export const embeddedDocs = {

Expand Down Expand Up @@ -426,6 +427,7 @@ export const embeddedDocs = {
range: rangeDocs,
resize: resizeDocs,
reshape: reshapeDocs,
rotationMatrix: rotationMatrixDocs,
row: rowDocs,
size: sizeDocs,
sort: sortDocs,
Expand Down
19 changes: 19 additions & 0 deletions src/expression/embeddedDocs/function/matrix/rotationMatrix.js
@@ -0,0 +1,19 @@
export const rotationMatrixDocs = {
name: 'rotationMatrix',
category: 'Matrix',
syntax: [
'rotationMatrix(theta)',
'rotationMatrix(theta, v)',
'rotationMatrix(theta, v, format)'
],
description: 'Returns a 2-D rotation matrix (2x2) for a given angle (in radians). ' +
'Returns a 2-D rotation matrix (3x3) of a given angle (in radians) around given axis.',
examples: [
'rotationMatrix(pi / 2)',
'rotationMatrix(unit("45deg"), [0, 0, 1])',
'rotationMatrix(1, matrix([0, 0, 1]), "sparse")'
],
seealso: [
'cos', 'sin'
]
}
1 change: 1 addition & 0 deletions src/factoriesAny.js
Expand Up @@ -78,6 +78,7 @@ export { createOnes } from './function/matrix/ones'
export { createRange } from './function/matrix/range'
export { createReshape } from './function/matrix/reshape'
export { createResize } from './function/matrix/resize'
export { createRotationMatrix } from './function/matrix/rotationMatrix'
export { createRow } from './function/matrix/row'
export { createSize } from './function/matrix/size'
export { createSqueeze } from './function/matrix/squeeze'
Expand Down
185 changes: 185 additions & 0 deletions src/function/matrix/rotationMatrix.js
@@ -0,0 +1,185 @@
import { isBigNumber } from '../../utils/is'
import { factory } from '../../utils/factory'

const name = 'rotationMatrix'
const dependencies = [
'typed',
'config',
'multiplyScalar',
'addScalar',
'unaryMinus',
'norm',
'matrix',
'BigNumber',
'DenseMatrix',
'SparseMatrix',
'cos',
'sin'
]

export const createRotationMatrix = /* #__PURE__ */ factory(name, dependencies, (
{
typed, config, multiplyScalar,
addScalar, unaryMinus, norm, BigNumber,
matrix, DenseMatrix, SparseMatrix, cos, sin
}) => {
/**
* Create a 2-dimensional counter-clockwise rotation matrix (2x2) for a given angle (expressed in radians).
* Create a 2-dimensional counter-clockwise rotation matrix (3x3) by a given angle (expressed in radians) around a given axis (1x3).
*
* Syntax:
*
* math.rotationMatrix(theta)
* math.rotationMatrix(theta, format)
* math.rotationMatrix(theta, [v])
* math.rotationMatrix(theta, [v], format)
*
* Examples:
*
* math.rotationMatrix(math.pi / 2) // returns [[0, -1], [1, 0]]
* math.rotationMatrix(math.bignumber(45)) // returns [[ bignumber(1 / sqrt(2)), - bignumber(1 / sqrt(2))], [ bignumber(1 / sqrt(2)), bignumber(1 / sqrt(2))]]
* math.rotationMatrix(math.complex(1 + i)) // returns [[cos(1 + i), -sin(1 + i)], [sin(1 + i), cos(1 + i)]]
* math.rotationMatrix(math.unit('1rad')) // returns [[cos(1), -sin(1)], [sin(1), cos(1)]]
*
* math.rotationMatrix(math.pi / 2, [0, 1, 0]) // returns [[0, 0, 1], [0, 1, 0], [-1, 0, 0]]
* math.rotationMatrix(math.pi / 2, matrix([0, 1, 0])) // returns matrix([[0, 0, 1], [0, 1, 0], [-1, 0, 0]])
*
*
* See also:
*
* matrix, cos, sin
*
*
* @param {number | BigNumber | Complex | Unit} theta Rotation angle
* @param {Array | Matrix} [v] Rotation axis
* @param {string} [format] Result Matrix storage format
* @return {Array | Matrix} Rotation matrix
*/

return typed(name, {
'': function () {
return (config.matrix === 'Matrix') ? matrix([]) : []
},

string: function (format) {
return matrix(format)
},

'number | BigNumber | Complex | Unit': function (theta) {
return _rotationMatrix2x2(theta, config.matrix === 'Matrix' ? 'dense' : undefined)
},

'number | BigNumber | Complex | Unit, string': function (theta, format) {
return _rotationMatrix2x2(theta, format)
},

'number | BigNumber | Complex | Unit, Array': function (theta, v) {
const matrixV = matrix(v)
_validateVector(matrixV)
return _rotationMatrix3x3(theta, matrixV, config.matrix === 'Matrix' ? 'dense' : undefined)
},

'number | BigNumber | Complex | Unit, Matrix': function (theta, v) {
_validateVector(v)
return _rotationMatrix3x3(theta, v, config.matrix === 'Matrix' ? 'dense' : undefined)
},

'number | BigNumber | Complex | Unit, Array, string': function (theta, v, format) {
const matrixV = matrix(v)
_validateVector(matrixV)
return _rotationMatrix3x3(theta, matrixV, format)
},

'number | BigNumber | Complex | Unit, Matrix, string': function (theta, v, format) {
_validateVector(v)
return _rotationMatrix3x3(theta, v, format)
}

})

/**
* Returns 2x2 matrix of 2D rotation of angle theta
*
* @param {number | BigNumber | Complex | Unit} theta The rotation angle
* @param {string} format The result Matrix storage format
* @returns {Matrix}
* @private
*/
function _rotationMatrix2x2 (theta, format) {
const Big = isBigNumber(theta)

const minusOne = Big ? new BigNumber(-1) : -1
const cosTheta = cos(theta)
const sinTheta = sin(theta)
const data = [[cosTheta, multiplyScalar(minusOne, sinTheta)], [sinTheta, cosTheta]]

return _convertToFormat(data, format)
}

function _validateVector (v) {
const size = v.size()
if (size.length < 1 || size[0] !== 3) {
throw new RangeError('Vector must be of dimensions 1x3')
}
}

function _mul (array) {
return array.reduce((p, curr) => multiplyScalar(p, curr))
}

function _convertToFormat (data, format) {
if (format) {
if (format === 'sparse') {
return new SparseMatrix(data)
}
if (format === 'dense') {
return new DenseMatrix(data)
}
throw new TypeError(`Unknown matrix type "${format}"`)
}
return data
}

/**
* Returns a 3x3 matrix of rotation of angle theta around vector v
*
* @param {number | BigNumber | Complex | Unit} theta The rotation angle
* @param {Matrix} v The rotation axis vector
* @param {string} format The storage format of the resulting matrix
* @returns {Matrix}
* @private
*/
function _rotationMatrix3x3 (theta, v, format) {
const normV = norm(v)
if (normV === 0) {
return _convertToFormat([], format)
}

const Big = isBigNumber(theta) ? BigNumber : null

const one = Big ? new Big(1) : 1
const minusOne = Big ? new Big(-1) : -1
const vx = Big ? new Big(v.get([0]) / normV) : v.get([0]) / normV
const vy = Big ? new Big(v.get([1]) / normV) : v.get([1]) / normV
const vz = Big ? new Big(v.get([2]) / normV) : v.get([2]) / normV
const c = cos(theta)
const oneMinusC = addScalar(one, unaryMinus(c))
const s = sin(theta)

const r11 = addScalar(c, _mul([vx, vx, oneMinusC]))
const r12 = addScalar(_mul([vx, vy, oneMinusC]), _mul([minusOne, vz, s]))
const r13 = addScalar(_mul([vx, vz, oneMinusC]), _mul([vy, s]))

const r21 = addScalar(_mul([vx, vy, oneMinusC]), _mul([vz, s]))
const r22 = addScalar(c, _mul([vy, vy, oneMinusC]))
const r23 = addScalar(_mul([vy, vz, oneMinusC]), _mul([minusOne, vx, s]))

const r31 = addScalar(_mul([vx, vz, oneMinusC]), _mul([minusOne, vy, s]))
const r32 = addScalar(_mul([vy, vz, oneMinusC]), _mul([vx, s]))
const r33 = addScalar(c, _mul([vz, vz, oneMinusC]))

const data = [[r11, r12, r13], [r21, r22, r23], [r31, r32, r33]]

return _convertToFormat(data, format)
}
})