1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 | (function(scope, testing) {
|
16 | var decomposeMatrix = (function() {
|
17 | function determinant(m) {
|
18 | return m[0][0] * m[1][1] * m[2][2] +
|
19 | m[1][0] * m[2][1] * m[0][2] +
|
20 | m[2][0] * m[0][1] * m[1][2] -
|
21 | m[0][2] * m[1][1] * m[2][0] -
|
22 | m[1][2] * m[2][1] * m[0][0] -
|
23 | m[2][2] * m[0][1] * m[1][0];
|
24 | }
|
25 |
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 |
|
34 |
|
35 | function inverse(m) {
|
36 | var iDet = 1 / determinant(m);
|
37 | var a = m[0][0], b = m[0][1], c = m[0][2];
|
38 | var d = m[1][0], e = m[1][1], f = m[1][2];
|
39 | var g = m[2][0], h = m[2][1], k = m[2][2];
|
40 | var Ainv = [
|
41 | [(e * k - f * h) * iDet, (c * h - b * k) * iDet,
|
42 | (b * f - c * e) * iDet, 0],
|
43 | [(f * g - d * k) * iDet, (a * k - c * g) * iDet,
|
44 | (c * d - a * f) * iDet, 0],
|
45 | [(d * h - e * g) * iDet, (g * b - a * h) * iDet,
|
46 | (a * e - b * d) * iDet, 0]
|
47 | ];
|
48 | var lastRow = [];
|
49 | for (var i = 0; i < 3; i++) {
|
50 | var val = 0;
|
51 | for (var j = 0; j < 3; j++) {
|
52 | val += m[3][j] * Ainv[j][i];
|
53 | }
|
54 | lastRow.push(val);
|
55 | }
|
56 | lastRow.push(1);
|
57 | Ainv.push(lastRow);
|
58 | return Ainv;
|
59 | }
|
60 |
|
61 | function transposeMatrix4(m) {
|
62 | return [[m[0][0], m[1][0], m[2][0], m[3][0]],
|
63 | [m[0][1], m[1][1], m[2][1], m[3][1]],
|
64 | [m[0][2], m[1][2], m[2][2], m[3][2]],
|
65 | [m[0][3], m[1][3], m[2][3], m[3][3]]];
|
66 | }
|
67 |
|
68 | function multVecMatrix(v, m) {
|
69 | var result = [];
|
70 | for (var i = 0; i < 4; i++) {
|
71 | var val = 0;
|
72 | for (var j = 0; j < 4; j++) {
|
73 | val += v[j] * m[j][i];
|
74 | }
|
75 | result.push(val);
|
76 | }
|
77 | return result;
|
78 | }
|
79 |
|
80 | function normalize(v) {
|
81 | var len = length(v);
|
82 | return [v[0] / len, v[1] / len, v[2] / len];
|
83 | }
|
84 |
|
85 | function length(v) {
|
86 | return Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
|
87 | }
|
88 |
|
89 | function combine(v1, v2, v1s, v2s) {
|
90 | return [v1s * v1[0] + v2s * v2[0], v1s * v1[1] + v2s * v2[1],
|
91 | v1s * v1[2] + v2s * v2[2]];
|
92 | }
|
93 |
|
94 | function cross(v1, v2) {
|
95 | return [v1[1] * v2[2] - v1[2] * v2[1],
|
96 | v1[2] * v2[0] - v1[0] * v2[2],
|
97 | v1[0] * v2[1] - v1[1] * v2[0]];
|
98 | }
|
99 |
|
100 | function decomposeMatrix(matrix) {
|
101 | var m3d = [
|
102 | matrix.slice(0, 4),
|
103 | matrix.slice(4, 8),
|
104 | matrix.slice(8, 12),
|
105 | matrix.slice(12, 16)
|
106 | ];
|
107 |
|
108 |
|
109 | if (m3d[3][3] !== 1) {
|
110 | return null;
|
111 | }
|
112 |
|
113 | var perspectiveMatrix = [];
|
114 | for (var i = 0; i < 4; i++) {
|
115 | perspectiveMatrix.push(m3d[i].slice());
|
116 | }
|
117 |
|
118 | for (var i = 0; i < 3; i++) {
|
119 | perspectiveMatrix[i][3] = 0;
|
120 | }
|
121 |
|
122 | if (determinant(perspectiveMatrix) === 0) {
|
123 | return null;
|
124 | }
|
125 |
|
126 | var rhs = [];
|
127 |
|
128 | var perspective;
|
129 | if (m3d[0][3] || m3d[1][3] || m3d[2][3]) {
|
130 | rhs.push(m3d[0][3]);
|
131 | rhs.push(m3d[1][3]);
|
132 | rhs.push(m3d[2][3]);
|
133 | rhs.push(m3d[3][3]);
|
134 |
|
135 | var inversePerspectiveMatrix = inverse(perspectiveMatrix);
|
136 | var transposedInversePerspectiveMatrix =
|
137 | transposeMatrix4(inversePerspectiveMatrix);
|
138 | perspective = multVecMatrix(rhs, transposedInversePerspectiveMatrix);
|
139 | } else {
|
140 | perspective = [0, 0, 0, 1];
|
141 | }
|
142 |
|
143 | var translate = m3d[3].slice(0, 3);
|
144 |
|
145 | var row = [];
|
146 | row.push(m3d[0].slice(0, 3));
|
147 | var scale = [];
|
148 | scale.push(length(row[0]));
|
149 | row[0] = normalize(row[0]);
|
150 |
|
151 | var skew = [];
|
152 | row.push(m3d[1].slice(0, 3));
|
153 | skew.push(dot(row[0], row[1]));
|
154 | row[1] = combine(row[1], row[0], 1.0, -skew[0]);
|
155 |
|
156 | scale.push(length(row[1]));
|
157 | row[1] = normalize(row[1]);
|
158 | skew[0] /= scale[1];
|
159 |
|
160 | row.push(m3d[2].slice(0, 3));
|
161 | skew.push(dot(row[0], row[2]));
|
162 | row[2] = combine(row[2], row[0], 1.0, -skew[1]);
|
163 | skew.push(dot(row[1], row[2]));
|
164 | row[2] = combine(row[2], row[1], 1.0, -skew[2]);
|
165 |
|
166 | scale.push(length(row[2]));
|
167 | row[2] = normalize(row[2]);
|
168 | skew[1] /= scale[2];
|
169 | skew[2] /= scale[2];
|
170 |
|
171 | var pdum3 = cross(row[1], row[2]);
|
172 | if (dot(row[0], pdum3) < 0) {
|
173 | for (var i = 0; i < 3; i++) {
|
174 | scale[i] *= -1;
|
175 | row[i][0] *= -1;
|
176 | row[i][1] *= -1;
|
177 | row[i][2] *= -1;
|
178 | }
|
179 | }
|
180 |
|
181 | var t = row[0][0] + row[1][1] + row[2][2] + 1;
|
182 | var s;
|
183 | var quaternion;
|
184 |
|
185 | if (t > 1e-4) {
|
186 | s = 0.5 / Math.sqrt(t);
|
187 | quaternion = [
|
188 | (row[2][1] - row[1][2]) * s,
|
189 | (row[0][2] - row[2][0]) * s,
|
190 | (row[1][0] - row[0][1]) * s,
|
191 | 0.25 / s
|
192 | ];
|
193 | } else if (row[0][0] > row[1][1] && row[0][0] > row[2][2]) {
|
194 | s = Math.sqrt(1 + row[0][0] - row[1][1] - row[2][2]) * 2.0;
|
195 | quaternion = [
|
196 | 0.25 * s,
|
197 | (row[0][1] + row[1][0]) / s,
|
198 | (row[0][2] + row[2][0]) / s,
|
199 | (row[2][1] - row[1][2]) / s
|
200 | ];
|
201 | } else if (row[1][1] > row[2][2]) {
|
202 | s = Math.sqrt(1.0 + row[1][1] - row[0][0] - row[2][2]) * 2.0;
|
203 | quaternion = [
|
204 | (row[0][1] + row[1][0]) / s,
|
205 | 0.25 * s,
|
206 | (row[1][2] + row[2][1]) / s,
|
207 | (row[0][2] - row[2][0]) / s
|
208 | ];
|
209 | } else {
|
210 | s = Math.sqrt(1.0 + row[2][2] - row[0][0] - row[1][1]) * 2.0;
|
211 | quaternion = [
|
212 | (row[0][2] + row[2][0]) / s,
|
213 | (row[1][2] + row[2][1]) / s,
|
214 | 0.25 * s,
|
215 | (row[1][0] - row[0][1]) / s
|
216 | ];
|
217 | }
|
218 |
|
219 | return [translate, scale, skew, quaternion, perspective];
|
220 | }
|
221 | return decomposeMatrix;
|
222 | })();
|
223 |
|
224 | function dot(v1, v2) {
|
225 | var result = 0;
|
226 | for (var i = 0; i < v1.length; i++) {
|
227 | result += v1[i] * v2[i];
|
228 | }
|
229 | return result;
|
230 | }
|
231 |
|
232 | function multiplyMatrices(a, b) {
|
233 | return [
|
234 | a[0] * b[0] + a[4] * b[1] + a[8] * b[2] + a[12] * b[3],
|
235 | a[1] * b[0] + a[5] * b[1] + a[9] * b[2] + a[13] * b[3],
|
236 | a[2] * b[0] + a[6] * b[1] + a[10] * b[2] + a[14] * b[3],
|
237 | a[3] * b[0] + a[7] * b[1] + a[11] * b[2] + a[15] * b[3],
|
238 |
|
239 | a[0] * b[4] + a[4] * b[5] + a[8] * b[6] + a[12] * b[7],
|
240 | a[1] * b[4] + a[5] * b[5] + a[9] * b[6] + a[13] * b[7],
|
241 | a[2] * b[4] + a[6] * b[5] + a[10] * b[6] + a[14] * b[7],
|
242 | a[3] * b[4] + a[7] * b[5] + a[11] * b[6] + a[15] * b[7],
|
243 |
|
244 | a[0] * b[8] + a[4] * b[9] + a[8] * b[10] + a[12] * b[11],
|
245 | a[1] * b[8] + a[5] * b[9] + a[9] * b[10] + a[13] * b[11],
|
246 | a[2] * b[8] + a[6] * b[9] + a[10] * b[10] + a[14] * b[11],
|
247 | a[3] * b[8] + a[7] * b[9] + a[11] * b[10] + a[15] * b[11],
|
248 |
|
249 | a[0] * b[12] + a[4] * b[13] + a[8] * b[14] + a[12] * b[15],
|
250 | a[1] * b[12] + a[5] * b[13] + a[9] * b[14] + a[13] * b[15],
|
251 | a[2] * b[12] + a[6] * b[13] + a[10] * b[14] + a[14] * b[15],
|
252 | a[3] * b[12] + a[7] * b[13] + a[11] * b[14] + a[15] * b[15]
|
253 | ];
|
254 | }
|
255 |
|
256 | function toRadians(arg) {
|
257 | var rads = arg.rad || 0;
|
258 | var degs = arg.deg || 0;
|
259 | var grads = arg.grad || 0;
|
260 | var turns = arg.turn || 0;
|
261 | var angle = (degs / 360 + grads / 400 + turns) * (2 * Math.PI) + rads;
|
262 | return angle;
|
263 | }
|
264 |
|
265 | function convertItemToMatrix(item) {
|
266 | switch (item.t) {
|
267 | case 'rotatex':
|
268 | var angle = toRadians(item.d[0]);
|
269 | return [1, 0, 0, 0,
|
270 | 0, Math.cos(angle), Math.sin(angle), 0,
|
271 | 0, -Math.sin(angle), Math.cos(angle), 0,
|
272 | 0, 0, 0, 1];
|
273 | case 'rotatey':
|
274 | var angle = toRadians(item.d[0]);
|
275 | return [Math.cos(angle), 0, -Math.sin(angle), 0,
|
276 | 0, 1, 0, 0,
|
277 | Math.sin(angle), 0, Math.cos(angle), 0,
|
278 | 0, 0, 0, 1];
|
279 | case 'rotate':
|
280 | case 'rotatez':
|
281 | var angle = toRadians(item.d[0]);
|
282 | return [Math.cos(angle), Math.sin(angle), 0, 0,
|
283 | -Math.sin(angle), Math.cos(angle), 0, 0,
|
284 | 0, 0, 1, 0,
|
285 | 0, 0, 0, 1];
|
286 | case 'rotate3d':
|
287 | var x = item.d[0];
|
288 | var y = item.d[1];
|
289 | var z = item.d[2];
|
290 | var angle = toRadians(item.d[3]);
|
291 |
|
292 | var sqrLength = x * x + y * y + z * z;
|
293 | if (sqrLength === 0) {
|
294 | x = 1;
|
295 | y = 0;
|
296 | z = 0;
|
297 | } else if (sqrLength !== 1) {
|
298 | var length = Math.sqrt(sqrLength);
|
299 | x /= length;
|
300 | y /= length;
|
301 | z /= length;
|
302 | }
|
303 |
|
304 | var s = Math.sin(angle / 2);
|
305 | var sc = s * Math.cos(angle / 2);
|
306 | var sq = s * s;
|
307 | return [
|
308 | 1 - 2 * (y * y + z * z) * sq,
|
309 | 2 * (x * y * sq + z * sc),
|
310 | 2 * (x * z * sq - y * sc),
|
311 | 0,
|
312 |
|
313 | 2 * (x * y * sq - z * sc),
|
314 | 1 - 2 * (x * x + z * z) * sq,
|
315 | 2 * (y * z * sq + x * sc),
|
316 | 0,
|
317 |
|
318 | 2 * (x * z * sq + y * sc),
|
319 | 2 * (y * z * sq - x * sc),
|
320 | 1 - 2 * (x * x + y * y) * sq,
|
321 | 0,
|
322 |
|
323 | 0, 0, 0, 1
|
324 | ];
|
325 | case 'scale':
|
326 | return [item.d[0], 0, 0, 0,
|
327 | 0, item.d[1], 0, 0,
|
328 | 0, 0, 1, 0,
|
329 | 0, 0, 0, 1];
|
330 | case 'scalex':
|
331 | return [item.d[0], 0, 0, 0,
|
332 | 0, 1, 0, 0,
|
333 | 0, 0, 1, 0,
|
334 | 0, 0, 0, 1];
|
335 | case 'scaley':
|
336 | return [1, 0, 0, 0,
|
337 | 0, item.d[0], 0, 0,
|
338 | 0, 0, 1, 0,
|
339 | 0, 0, 0, 1];
|
340 | case 'scalez':
|
341 | return [1, 0, 0, 0,
|
342 | 0, 1, 0, 0,
|
343 | 0, 0, item.d[0], 0,
|
344 | 0, 0, 0, 1];
|
345 | case 'scale3d':
|
346 | return [item.d[0], 0, 0, 0,
|
347 | 0, item.d[1], 0, 0,
|
348 | 0, 0, item.d[2], 0,
|
349 | 0, 0, 0, 1];
|
350 | case 'skew':
|
351 | var xAngle = toRadians(item.d[0]);
|
352 | var yAngle = toRadians(item.d[1]);
|
353 | return [1, Math.tan(yAngle), 0, 0,
|
354 | Math.tan(xAngle), 1, 0, 0,
|
355 | 0, 0, 1, 0,
|
356 | 0, 0, 0, 1];
|
357 | case 'skewx':
|
358 | var angle = toRadians(item.d[0]);
|
359 | return [1, 0, 0, 0,
|
360 | Math.tan(angle), 1, 0, 0,
|
361 | 0, 0, 1, 0,
|
362 | 0, 0, 0, 1];
|
363 | case 'skewy':
|
364 | var angle = toRadians(item.d[0]);
|
365 | return [1, Math.tan(angle), 0, 0,
|
366 | 0, 1, 0, 0,
|
367 | 0, 0, 1, 0,
|
368 | 0, 0, 0, 1];
|
369 | case 'translate':
|
370 | var x = item.d[0].px || 0;
|
371 | var y = item.d[1].px || 0;
|
372 | return [1, 0, 0, 0,
|
373 | 0, 1, 0, 0,
|
374 | 0, 0, 1, 0,
|
375 | x, y, 0, 1];
|
376 | case 'translatex':
|
377 | var x = item.d[0].px || 0;
|
378 | return [1, 0, 0, 0,
|
379 | 0, 1, 0, 0,
|
380 | 0, 0, 1, 0,
|
381 | x, 0, 0, 1];
|
382 | case 'translatey':
|
383 | var y = item.d[0].px || 0;
|
384 | return [1, 0, 0, 0,
|
385 | 0, 1, 0, 0,
|
386 | 0, 0, 1, 0,
|
387 | 0, y, 0, 1];
|
388 | case 'translatez':
|
389 | var z = item.d[0].px || 0;
|
390 | return [1, 0, 0, 0,
|
391 | 0, 1, 0, 0,
|
392 | 0, 0, 1, 0,
|
393 | 0, 0, z, 1];
|
394 | case 'translate3d':
|
395 | var x = item.d[0].px || 0;
|
396 | var y = item.d[1].px || 0;
|
397 | var z = item.d[2].px || 0;
|
398 | return [1, 0, 0, 0,
|
399 | 0, 1, 0, 0,
|
400 | 0, 0, 1, 0,
|
401 | x, y, z, 1];
|
402 | case 'perspective':
|
403 | var p = item.d[0].px ? (-1 / item.d[0].px) : 0;
|
404 | return [
|
405 | 1, 0, 0, 0,
|
406 | 0, 1, 0, 0,
|
407 | 0, 0, 1, p,
|
408 | 0, 0, 0, 1];
|
409 | case 'matrix':
|
410 | return [item.d[0], item.d[1], 0, 0,
|
411 | item.d[2], item.d[3], 0, 0,
|
412 | 0, 0, 1, 0,
|
413 | item.d[4], item.d[5], 0, 1];
|
414 | case 'matrix3d':
|
415 | return item.d;
|
416 | default:
|
417 | WEB_ANIMATIONS_TESTING && console.assert(false, 'Transform item type ' + item.t +
|
418 | ' conversion to matrix not yet implemented.');
|
419 | }
|
420 | }
|
421 |
|
422 | function convertToMatrix(transformList) {
|
423 | if (transformList.length === 0) {
|
424 | return [1, 0, 0, 0,
|
425 | 0, 1, 0, 0,
|
426 | 0, 0, 1, 0,
|
427 | 0, 0, 0, 1];
|
428 | }
|
429 | return transformList.map(convertItemToMatrix).reduce(multiplyMatrices);
|
430 | }
|
431 |
|
432 | function makeMatrixDecomposition(transformList) {
|
433 | return [decomposeMatrix(convertToMatrix(transformList))];
|
434 | }
|
435 |
|
436 | scope.dot = dot;
|
437 | scope.makeMatrixDecomposition = makeMatrixDecomposition;
|
438 | scope.transformListToMatrix = convertToMatrix;
|
439 |
|
440 | })(webAnimations1, webAnimationsTesting);
|