Skip to content

Commit 439ec41

Browse files
rnd-debugjosdejong
andauthored
Feat/rotate matrix (#1984)
* providing rotationMatrix(theta, v) * increase coverage * adding latex test as is * fixing rounding issues with math.pi * fixing lint * Update rotationMatrix.js remove non-sense doc * Update rotationMatrix.js Remove non-sense from docs * removing nonsense from docs * Renaming functions Co-authored-by: Jos de Jong <[email protected]>
1 parent 7854a9b commit 439ec41

File tree

5 files changed

+459
-0
lines changed

5 files changed

+459
-0
lines changed

src/expression/embeddedDocs/embeddedDocs.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ import { sinDocs } from './function/trigonometry/sin'
214214
import { numericDocs } from './function/utils/numeric'
215215
import { columnDocs } from './function/matrix/column'
216216
import { rowDocs } from './function/matrix/row'
217+
import { rotationMatrixDocs } from './function/matrix/rotationMatrix'
217218

218219
export const embeddedDocs = {
219220

@@ -426,6 +427,7 @@ export const embeddedDocs = {
426427
range: rangeDocs,
427428
resize: resizeDocs,
428429
reshape: reshapeDocs,
430+
rotationMatrix: rotationMatrixDocs,
429431
row: rowDocs,
430432
size: sizeDocs,
431433
sort: sortDocs,
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
export const rotationMatrixDocs = {
2+
name: 'rotationMatrix',
3+
category: 'Matrix',
4+
syntax: [
5+
'rotationMatrix(theta)',
6+
'rotationMatrix(theta, v)',
7+
'rotationMatrix(theta, v, format)'
8+
],
9+
description: 'Returns a 2-D rotation matrix (2x2) for a given angle (in radians). ' +
10+
'Returns a 2-D rotation matrix (3x3) of a given angle (in radians) around given axis.',
11+
examples: [
12+
'rotationMatrix(pi / 2)',
13+
'rotationMatrix(unit("45deg"), [0, 0, 1])',
14+
'rotationMatrix(1, matrix([0, 0, 1]), "sparse")'
15+
],
16+
seealso: [
17+
'cos', 'sin'
18+
]
19+
}

src/factoriesAny.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ export { createOnes } from './function/matrix/ones'
7878
export { createRange } from './function/matrix/range'
7979
export { createReshape } from './function/matrix/reshape'
8080
export { createResize } from './function/matrix/resize'
81+
export { createRotationMatrix } from './function/matrix/rotationMatrix'
8182
export { createRow } from './function/matrix/row'
8283
export { createSize } from './function/matrix/size'
8384
export { createSqueeze } from './function/matrix/squeeze'
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
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

Comments
 (0)