1 | 'use strict';
|
2 |
|
3 | var regTransformTypes = /matrix|translate|scale|rotate|skewX|skewY/,
|
4 | regTransformSplit = /\s*(matrix|translate|scale|rotate|skewX|skewY)\s*\(\s*(.+?)\s*\)[\s,]*/,
|
5 | regNumericValues = /[-+]?(?:\d*\.\d+|\d+\.?)(?:[eE][-+]?\d+)?/g;
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 | exports.transform2js = function(transformString) {
|
15 |
|
16 |
|
17 | var transforms = [],
|
18 |
|
19 | current;
|
20 |
|
21 |
|
22 | transformString.split(regTransformSplit).forEach(function(item) {
|
23 |
|
24 | var num;
|
25 |
|
26 | if (item) {
|
27 |
|
28 | if (regTransformTypes.test(item)) {
|
29 |
|
30 | transforms.push(current = { name: item });
|
31 |
|
32 | } else {
|
33 |
|
34 | while (num = regNumericValues.exec(item)) {
|
35 | num = Number(num);
|
36 | if (current.data)
|
37 | current.data.push(num);
|
38 | else
|
39 | current.data = [num];
|
40 | }
|
41 | }
|
42 | }
|
43 | });
|
44 |
|
45 |
|
46 | return current && current.data ? transforms : [];
|
47 | };
|
48 |
|
49 |
|
50 |
|
51 |
|
52 |
|
53 |
|
54 |
|
55 | exports.transformsMultiply = function(transforms) {
|
56 |
|
57 |
|
58 | transforms = transforms.map(function(transform) {
|
59 | if (transform.name === 'matrix') {
|
60 | return transform.data;
|
61 | }
|
62 | return transformToMatrix(transform);
|
63 | });
|
64 |
|
65 |
|
66 | transforms = {
|
67 | name: 'matrix',
|
68 | data: transforms.length > 0 ? transforms.reduce(multiplyTransformMatrices) : []
|
69 | };
|
70 |
|
71 | return transforms;
|
72 |
|
73 | };
|
74 |
|
75 |
|
76 |
|
77 |
|
78 |
|
79 |
|
80 | var mth = exports.mth = {
|
81 |
|
82 | rad: function(deg) {
|
83 | return deg * Math.PI / 180;
|
84 | },
|
85 |
|
86 | deg: function(rad) {
|
87 | return rad * 180 / Math.PI;
|
88 | },
|
89 |
|
90 | cos: function(deg) {
|
91 | return Math.cos(this.rad(deg));
|
92 | },
|
93 |
|
94 | acos: function(val, floatPrecision) {
|
95 | return +(this.deg(Math.acos(val)).toFixed(floatPrecision));
|
96 | },
|
97 |
|
98 | sin: function(deg) {
|
99 | return Math.sin(this.rad(deg));
|
100 | },
|
101 |
|
102 | asin: function(val, floatPrecision) {
|
103 | return +(this.deg(Math.asin(val)).toFixed(floatPrecision));
|
104 | },
|
105 |
|
106 | tan: function(deg) {
|
107 | return Math.tan(this.rad(deg));
|
108 | },
|
109 |
|
110 | atan: function(val, floatPrecision) {
|
111 | return +(this.deg(Math.atan(val)).toFixed(floatPrecision));
|
112 | }
|
113 |
|
114 | };
|
115 |
|
116 |
|
117 |
|
118 |
|
119 |
|
120 |
|
121 |
|
122 |
|
123 | exports.matrixToTransform = function(transform, params) {
|
124 | var floatPrecision = params.floatPrecision,
|
125 | data = transform.data,
|
126 | transforms = [],
|
127 | sx = +Math.hypot(data[0], data[1]).toFixed(params.transformPrecision),
|
128 | sy = +((data[0] * data[3] - data[1] * data[2]) / sx).toFixed(params.transformPrecision),
|
129 | colsSum = data[0] * data[2] + data[1] * data[3],
|
130 | rowsSum = data[0] * data[1] + data[2] * data[3],
|
131 | scaleBefore = rowsSum != 0 || sx == sy;
|
132 |
|
133 |
|
134 | if (data[4] || data[5]) {
|
135 | transforms.push({ name: 'translate', data: data.slice(4, data[5] ? 6 : 5) });
|
136 | }
|
137 |
|
138 |
|
139 | if (!data[1] && data[2]) {
|
140 | transforms.push({ name: 'skewX', data: [mth.atan(data[2] / sy, floatPrecision)] });
|
141 |
|
142 |
|
143 | } else if (data[1] && !data[2]) {
|
144 | transforms.push({ name: 'skewY', data: [mth.atan(data[1] / data[0], floatPrecision)] });
|
145 | sx = data[0];
|
146 | sy = data[3];
|
147 |
|
148 |
|
149 |
|
150 | } else if (!colsSum || (sx == 1 && sy == 1) || !scaleBefore) {
|
151 | if (!scaleBefore) {
|
152 | sx = (data[0] < 0 ? -1 : 1) * Math.hypot(data[0], data[2]);
|
153 | sy = (data[3] < 0 ? -1 : 1) * Math.hypot(data[1], data[3]);
|
154 | transforms.push({ name: 'scale', data: [sx, sy] });
|
155 | }
|
156 | var rotate = [mth.acos(data[0] / sx, floatPrecision) * ((scaleBefore ? 1 : sy) * data[1] < 0 ? -1 : 1)];
|
157 |
|
158 | if (rotate[0]) transforms.push({ name: 'rotate', data: rotate });
|
159 |
|
160 | if (rowsSum && colsSum) transforms.push({
|
161 | name: 'skewX',
|
162 | data: [mth.atan(colsSum / (sx * sx), floatPrecision)]
|
163 | });
|
164 |
|
165 |
|
166 | if (rotate[0] && (data[4] || data[5])) {
|
167 | transforms.shift();
|
168 | var cos = data[0] / sx,
|
169 | sin = data[1] / (scaleBefore ? sx : sy),
|
170 | x = data[4] * (scaleBefore || sy),
|
171 | y = data[5] * (scaleBefore || sx),
|
172 | denom = (Math.pow(1 - cos, 2) + Math.pow(sin, 2)) * (scaleBefore || sx * sy);
|
173 | rotate.push(((1 - cos) * x - sin * y) / denom);
|
174 | rotate.push(((1 - cos) * y + sin * x) / denom);
|
175 | }
|
176 |
|
177 |
|
178 | } else if (data[1] || data[2]) {
|
179 | return transform;
|
180 | }
|
181 |
|
182 | if (scaleBefore && (sx != 1 || sy != 1) || !transforms.length) transforms.push({
|
183 | name: 'scale',
|
184 | data: sx == sy ? [sx] : [sx, sy]
|
185 | });
|
186 |
|
187 | return transforms;
|
188 | };
|
189 |
|
190 |
|
191 |
|
192 |
|
193 |
|
194 |
|
195 |
|
196 | function transformToMatrix(transform) {
|
197 |
|
198 | if (transform.name === 'matrix') return transform.data;
|
199 |
|
200 | var matrix;
|
201 |
|
202 | switch (transform.name) {
|
203 | case 'translate':
|
204 |
|
205 | matrix = [1, 0, 0, 1, transform.data[0], transform.data[1] || 0];
|
206 | break;
|
207 | case 'scale':
|
208 |
|
209 | matrix = [transform.data[0], 0, 0, transform.data[1] || transform.data[0], 0, 0];
|
210 | break;
|
211 | case 'rotate':
|
212 |
|
213 | var cos = mth.cos(transform.data[0]),
|
214 | sin = mth.sin(transform.data[0]),
|
215 | cx = transform.data[1] || 0,
|
216 | cy = transform.data[2] || 0;
|
217 |
|
218 | matrix = [cos, sin, -sin, cos, (1 - cos) * cx + sin * cy, (1 - cos) * cy - sin * cx];
|
219 | break;
|
220 | case 'skewX':
|
221 |
|
222 | matrix = [1, 0, mth.tan(transform.data[0]), 1, 0, 0];
|
223 | break;
|
224 | case 'skewY':
|
225 |
|
226 | matrix = [1, mth.tan(transform.data[0]), 0, 1, 0, 0];
|
227 | break;
|
228 | }
|
229 |
|
230 | return matrix;
|
231 |
|
232 | }
|
233 |
|
234 |
|
235 |
|
236 |
|
237 |
|
238 |
|
239 |
|
240 |
|
241 |
|
242 |
|
243 |
|
244 | exports.transformArc = function(arc, transform) {
|
245 |
|
246 | var a = arc[0],
|
247 | b = arc[1],
|
248 | rot = arc[2] * Math.PI / 180,
|
249 | cos = Math.cos(rot),
|
250 | sin = Math.sin(rot),
|
251 | h = Math.pow(arc[5] * cos + arc[6] * sin, 2) / (4 * a * a) +
|
252 | Math.pow(arc[6] * cos - arc[5] * sin, 2) / (4 * b * b);
|
253 | if (h > 1) {
|
254 | h = Math.sqrt(h);
|
255 | a *= h;
|
256 | b *= h;
|
257 | }
|
258 | var ellipse = [a * cos, a * sin, -b * sin, b * cos, 0, 0],
|
259 | m = multiplyTransformMatrices(transform, ellipse),
|
260 |
|
261 | lastCol = m[2] * m[2] + m[3] * m[3],
|
262 | squareSum = m[0] * m[0] + m[1] * m[1] + lastCol,
|
263 | root = Math.hypot(m[0] - m[3], m[1] + m[2]) * Math.hypot(m[0] + m[3], m[1] - m[2]);
|
264 |
|
265 | if (!root) {
|
266 | arc[0] = arc[1] = Math.sqrt(squareSum / 2);
|
267 | arc[2] = 0;
|
268 | } else {
|
269 | var majorAxisSqr = (squareSum + root) / 2,
|
270 | minorAxisSqr = (squareSum - root) / 2,
|
271 | major = Math.abs(majorAxisSqr - lastCol) > 1e-6,
|
272 | sub = (major ? majorAxisSqr : minorAxisSqr) - lastCol,
|
273 | rowsSum = m[0] * m[2] + m[1] * m[3],
|
274 | term1 = m[0] * sub + m[2] * rowsSum,
|
275 | term2 = m[1] * sub + m[3] * rowsSum;
|
276 | arc[0] = Math.sqrt(majorAxisSqr);
|
277 | arc[1] = Math.sqrt(minorAxisSqr);
|
278 | arc[2] = ((major ? term2 < 0 : term1 > 0) ? -1 : 1) *
|
279 | Math.acos((major ? term1 : term2) / Math.hypot(term1, term2)) * 180 / Math.PI;
|
280 | }
|
281 |
|
282 | if ((transform[0] < 0) !== (transform[3] < 0)) {
|
283 |
|
284 | arc[4] = 1 - arc[4];
|
285 | }
|
286 |
|
287 | return arc;
|
288 |
|
289 | };
|
290 |
|
291 |
|
292 |
|
293 |
|
294 |
|
295 |
|
296 |
|
297 |
|
298 | function multiplyTransformMatrices(a, b) {
|
299 |
|
300 | return [
|
301 | a[0] * b[0] + a[2] * b[1],
|
302 | a[1] * b[0] + a[3] * b[1],
|
303 | a[0] * b[2] + a[2] * b[3],
|
304 | a[1] * b[2] + a[3] * b[3],
|
305 | a[0] * b[4] + a[2] * b[5] + a[4],
|
306 | a[1] * b[4] + a[3] * b[5] + a[5]
|
307 | ];
|
308 |
|
309 | }
|