-
Notifications
You must be signed in to change notification settings - Fork 3
/
index.js
179 lines (149 loc) · 5.87 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
/*jshint unused:true*/
/*
Input: matrix ; a 4x4 matrix
Output: translation ; a 3 component vector
scale ; a 3 component vector
skew ; skew factors XY,XZ,YZ represented as a 3 component vector
perspective ; a 4 component vector
quaternion ; a 4 component vector
Returns false if the matrix cannot be decomposed, true if it can
References:
https://github.com/kamicane/matrix3d/blob/master/lib/Matrix3d.js
https://github.com/ChromiumWebApps/chromium/blob/master/ui/gfx/transform_util.cc
http://www.w3.org/TR/css3-transforms/#decomposing-a-3d-matrix
*/
var normalize = require('./normalize')
var create = require('gl-mat4/create')
var clone = require('gl-mat4/clone')
var determinant = require('gl-mat4/determinant')
var invert = require('gl-mat4/invert')
var transpose = require('gl-mat4/transpose')
var vec3 = {
length: require('gl-vec3/length'),
normalize: require('gl-vec3/normalize'),
dot: require('gl-vec3/dot'),
cross: require('gl-vec3/cross')
}
var tmp = create()
var perspectiveMatrix = create()
var tmpVec4 = [0, 0, 0, 0]
var row = [ [0,0,0], [0,0,0], [0,0,0] ]
var pdum3 = [0,0,0]
module.exports = function decomposeMat4(matrix, translation, scale, skew, perspective, quaternion) {
if (!translation) translation = [0,0,0]
if (!scale) scale = [0,0,0]
if (!skew) skew = [0,0,0]
if (!perspective) perspective = [0,0,0,1]
if (!quaternion) quaternion = [0,0,0,1]
//normalize, if not possible then bail out early
if (!normalize(tmp, matrix))
return false
// perspectiveMatrix is used to solve for perspective, but it also provides
// an easy way to test for singularity of the upper 3x3 component.
clone(perspectiveMatrix, tmp)
perspectiveMatrix[3] = 0
perspectiveMatrix[7] = 0
perspectiveMatrix[11] = 0
perspectiveMatrix[15] = 1
// If the perspectiveMatrix is not invertible, we are also unable to
// decompose, so we'll bail early. Constant taken from SkMatrix44::invert.
if (Math.abs(determinant(perspectiveMatrix) < 1e-8))
return false
var a03 = tmp[3], a13 = tmp[7], a23 = tmp[11],
a30 = tmp[12], a31 = tmp[13], a32 = tmp[14], a33 = tmp[15]
// First, isolate perspective.
if (a03 !== 0 || a13 !== 0 || a23 !== 0) {
tmpVec4[0] = a03
tmpVec4[1] = a13
tmpVec4[2] = a23
tmpVec4[3] = a33
// Solve the equation by inverting perspectiveMatrix and multiplying
// rightHandSide by the inverse.
// resuing the perspectiveMatrix here since it's no longer needed
var ret = invert(perspectiveMatrix, perspectiveMatrix)
if (!ret) return false
transpose(perspectiveMatrix, perspectiveMatrix)
//multiply by transposed inverse perspective matrix, into perspective vec4
vec4multMat4(perspective, tmpVec4, perspectiveMatrix)
} else {
//no perspective
perspective[0] = perspective[1] = perspective[2] = 0
perspective[3] = 1
}
// Next take care of translation
translation[0] = a30
translation[1] = a31
translation[2] = a32
// Now get scale and shear. 'row' is a 3 element array of 3 component vectors
mat3from4(row, tmp)
// Compute X scale factor and normalize first row.
scale[0] = vec3.length(row[0])
vec3.normalize(row[0], row[0])
// Compute XY shear factor and make 2nd row orthogonal to 1st.
skew[0] = vec3.dot(row[0], row[1])
combine(row[1], row[1], row[0], 1.0, -skew[0])
// Now, compute Y scale and normalize 2nd row.
scale[1] = vec3.length(row[1])
vec3.normalize(row[1], row[1])
skew[0] /= scale[1]
// Compute XZ and YZ shears, orthogonalize 3rd row
skew[1] = vec3.dot(row[0], row[2])
combine(row[2], row[2], row[0], 1.0, -skew[1])
skew[2] = vec3.dot(row[1], row[2])
combine(row[2], row[2], row[1], 1.0, -skew[2])
// Next, get Z scale and normalize 3rd row.
scale[2] = vec3.length(row[2])
vec3.normalize(row[2], row[2])
skew[1] /= scale[2]
skew[2] /= scale[2]
// At this point, the matrix (in rows) is orthonormal.
// Check for a coordinate system flip. If the determinant
// is -1, then negate the matrix and the scaling factors.
vec3.cross(pdum3, row[1], row[2])
if (vec3.dot(row[0], pdum3) < 0) {
for (var i = 0; i < 3; i++) {
scale[i] *= -1;
row[i][0] *= -1
row[i][1] *= -1
row[i][2] *= -1
}
}
// Now, get the rotations out
quaternion[0] = 0.5 * Math.sqrt(Math.max(1 + row[0][0] - row[1][1] - row[2][2], 0))
quaternion[1] = 0.5 * Math.sqrt(Math.max(1 - row[0][0] + row[1][1] - row[2][2], 0))
quaternion[2] = 0.5 * Math.sqrt(Math.max(1 - row[0][0] - row[1][1] + row[2][2], 0))
quaternion[3] = 0.5 * Math.sqrt(Math.max(1 + row[0][0] + row[1][1] + row[2][2], 0))
if (row[2][1] > row[1][2])
quaternion[0] = -quaternion[0]
if (row[0][2] > row[2][0])
quaternion[1] = -quaternion[1]
if (row[1][0] > row[0][1])
quaternion[2] = -quaternion[2]
return true
}
//will be replaced by gl-vec4 eventually
function vec4multMat4(out, a, m) {
var x = a[0], y = a[1], z = a[2], w = a[3];
out[0] = m[0] * x + m[4] * y + m[8] * z + m[12] * w;
out[1] = m[1] * x + m[5] * y + m[9] * z + m[13] * w;
out[2] = m[2] * x + m[6] * y + m[10] * z + m[14] * w;
out[3] = m[3] * x + m[7] * y + m[11] * z + m[15] * w;
return out;
}
//gets upper-left of a 4x4 matrix into a 3x3 of vectors
function mat3from4(out, mat4x4) {
out[0][0] = mat4x4[0]
out[0][1] = mat4x4[1]
out[0][2] = mat4x4[2]
out[1][0] = mat4x4[4]
out[1][1] = mat4x4[5]
out[1][2] = mat4x4[6]
out[2][0] = mat4x4[8]
out[2][1] = mat4x4[9]
out[2][2] = mat4x4[10]
}
function combine(out, a, b, scale1, scale2) {
out[0] = a[0] * scale1 + b[0] * scale2
out[1] = a[1] * scale1 + b[1] * scale2
out[2] = a[2] * scale1 + b[2] * scale2
}