svg-path-simplify 0.3.5 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/README.md +3 -76
- package/dist/svg-path-simplify.esm.js +5505 -4030
- package/dist/svg-path-simplify.esm.min.js +2 -8
- package/dist/svg-path-simplify.js +5506 -4029
- package/dist/svg-path-simplify.min.js +2 -8
- package/dist/svg-path-simplify.pathdata.esm.js +1154 -1042
- package/dist/svg-path-simplify.pathdata.esm.min.js +2 -8
- package/docs/api.md +127 -0
- package/index.html +279 -257
- package/package.json +1 -1
- package/src/constants.js +10 -1
- package/src/index.js +7 -1
- package/src/pathData_simplify_cubic.js +1 -18
- package/src/pathSimplify-main.js +87 -30
- package/src/pathSimplify-only-pathdata.js +2 -2
- package/src/svg-getAttributes.js +13 -0
- package/src/svg_flatten_transforms.js +1 -1
- package/src/svg_getViewbox.js +23 -11
- package/src/svg_rootSVG.js +9 -0
- package/src/svgii/convert_colors.js +145 -0
- package/src/svgii/convert_units.js +159 -0
- package/src/svgii/geometry.js +24 -4
- package/src/svgii/geometry_bbox.js +2 -1
- package/src/svgii/geometry_bbox_element.js +46 -0
- package/src/svgii/pathData_analyze.js +34 -14
- package/src/svgii/pathData_convert.js +204 -34
- package/src/svgii/pathData_parse.js +2 -99
- package/src/svgii/pathData_parse_els.js +217 -128
- package/src/svgii/pathData_simplify_refineCorners.js +72 -43
- package/src/svgii/pathData_stringify.js +6 -5
- package/src/svgii/pathData_toPolygon.js +3 -1
- package/src/svgii/pathData_transform.js +307 -0
- package/src/svgii/poly_normalize.js +21 -1
- package/src/svgii/rounding.js +36 -5
- package/src/svgii/svg-styles-getTransforms.js +119 -8
- package/src/svgii/svg-styles-to-attributes-const.js +26 -6
- package/src/svgii/svg_cleanup.js +540 -74
- package/src/svgii/svg_el_parse_style_props.js +561 -0
- package/src/svgii/transform_qr_decompose.js +74 -0
- package/src/svgii/pathData_scale.js +0 -42
- package/src/svgii/stringify.js +0 -103
- package/src/svgii/svg-styles-to-attributes.js +0 -217
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import { svgArcToCenterParam } from "./geometry";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* scale pathData
|
|
5
|
+
*/
|
|
6
|
+
export function transformPathData(pathData, matrix) {
|
|
7
|
+
|
|
8
|
+
// new pathdata
|
|
9
|
+
let pathDataTrans = [];
|
|
10
|
+
|
|
11
|
+
// transform point by 2d matrix
|
|
12
|
+
const transformPoint = (pt, matrix) => {
|
|
13
|
+
let { a, b, c, d, e, f } = matrix;
|
|
14
|
+
let { x, y } = pt;
|
|
15
|
+
return { x: a * x + c * y + e, y: b * x + d * y + f };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
//normalize matrix notations object, array or css matrix string
|
|
19
|
+
const normalizeMatrix = (matrix) => {
|
|
20
|
+
matrix =
|
|
21
|
+
typeof matrix === "string"
|
|
22
|
+
? (matrix = matrix
|
|
23
|
+
.replace(/^matrix\(|\)$/g, "")
|
|
24
|
+
.split(",")
|
|
25
|
+
.map(Number))
|
|
26
|
+
: matrix;
|
|
27
|
+
matrix = !Array.isArray(matrix)
|
|
28
|
+
? {
|
|
29
|
+
a: matrix.a,
|
|
30
|
+
b: matrix.b,
|
|
31
|
+
c: matrix.c,
|
|
32
|
+
d: matrix.d,
|
|
33
|
+
e: matrix.e,
|
|
34
|
+
f: matrix.f
|
|
35
|
+
}
|
|
36
|
+
: {
|
|
37
|
+
a: matrix[0],
|
|
38
|
+
b: matrix[1],
|
|
39
|
+
c: matrix[2],
|
|
40
|
+
d: matrix[3],
|
|
41
|
+
e: matrix[4],
|
|
42
|
+
f: matrix[5]
|
|
43
|
+
};
|
|
44
|
+
return matrix;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
const transformArc = (p0, values, matrix) => {
|
|
49
|
+
let [rx, ry, angle, largeArc, sweep, x, y] = values;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* parametrize arc command
|
|
53
|
+
* to get the actual arc params
|
|
54
|
+
*/
|
|
55
|
+
let arcData = svgArcToCenterParam(
|
|
56
|
+
p0.x,
|
|
57
|
+
p0.y,
|
|
58
|
+
values[0],
|
|
59
|
+
values[1],
|
|
60
|
+
angle,
|
|
61
|
+
largeArc,
|
|
62
|
+
sweep,
|
|
63
|
+
x,
|
|
64
|
+
y
|
|
65
|
+
);
|
|
66
|
+
({ rx, ry } = arcData);
|
|
67
|
+
let { a, b, c, d, e, f } = matrix;
|
|
68
|
+
|
|
69
|
+
let ellipsetr = transformEllipse(rx, ry, angle, matrix);
|
|
70
|
+
let p = transformPoint({ x: x, y: y }, matrix);
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
// adjust sweep if flipped
|
|
74
|
+
let denom = a * a + b * b;
|
|
75
|
+
let scaleX = Math.sqrt(denom)
|
|
76
|
+
let scaleY = (a * d - c * b) / scaleX
|
|
77
|
+
|
|
78
|
+
let flipX = scaleX < 0 ? true : false;
|
|
79
|
+
let flipY = scaleY < 0 ? true : false;
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
// adjust sweep
|
|
83
|
+
if (flipX || flipY) {
|
|
84
|
+
sweep = sweep === 0 ? 1 : 0;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
type: 'A',
|
|
89
|
+
values: [
|
|
90
|
+
ellipsetr.rx,
|
|
91
|
+
ellipsetr.ry,
|
|
92
|
+
ellipsetr.ax,
|
|
93
|
+
largeArc,
|
|
94
|
+
sweep,
|
|
95
|
+
p.x,
|
|
96
|
+
p.y]
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// normalize matrix input
|
|
101
|
+
matrix = normalizeMatrix(matrix);
|
|
102
|
+
|
|
103
|
+
let matrixStr = [matrix.a, matrix.b, matrix.c, matrix.d, matrix.e, matrix.f]
|
|
104
|
+
.map((val) => {
|
|
105
|
+
return +val.toFixed(1);
|
|
106
|
+
})
|
|
107
|
+
.join("");
|
|
108
|
+
|
|
109
|
+
// no transform: quit
|
|
110
|
+
if (matrixStr === "100100") {
|
|
111
|
+
//console.log("no transform");
|
|
112
|
+
return pathData;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
pathData.forEach((com, i) => {
|
|
117
|
+
let { type, values } = com;
|
|
118
|
+
let typeRel = type.toLowerCase();
|
|
119
|
+
let comPrev = i > 0 ? pathData[i - 1] : pathData[i];
|
|
120
|
+
let comPrevValues = comPrev.values;
|
|
121
|
+
let comPrevValuesL = comPrevValues.length;
|
|
122
|
+
let p0 = {
|
|
123
|
+
x: comPrevValues[comPrevValuesL - 2],
|
|
124
|
+
y: comPrevValues[comPrevValuesL - 1]
|
|
125
|
+
};
|
|
126
|
+
let p = { x: values[values.length - 2], y: values[values.length - 1] };
|
|
127
|
+
let comT = { type: type, values: [] };
|
|
128
|
+
|
|
129
|
+
switch (typeRel) {
|
|
130
|
+
case "a":
|
|
131
|
+
comT = transformArc(p0, values, matrix)
|
|
132
|
+
break;
|
|
133
|
+
|
|
134
|
+
default:
|
|
135
|
+
// all other point based commands
|
|
136
|
+
if (values.length) {
|
|
137
|
+
for (let i = 0; i < values.length; i += 2) {
|
|
138
|
+
let ptTrans = transformPoint(
|
|
139
|
+
{ x: com.values[i], y: com.values[i + 1] },
|
|
140
|
+
matrix
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
comT.values[i] = ptTrans.x;
|
|
144
|
+
comT.values[i + 1] = ptTrans.y;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
pathDataTrans.push(comT);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
//console.log('pathDataTrans', pathDataTrans);
|
|
154
|
+
|
|
155
|
+
return pathDataTrans;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Based on: https://github.com/fontello/svgpath/blob/master/lib/ellipse.js
|
|
165
|
+
* and fork: https://github.com/kpym/SVGPathy/blob/master/lib/ellipse.js
|
|
166
|
+
*/
|
|
167
|
+
|
|
168
|
+
function transformEllipse(rx, ry, ax, matrix) {
|
|
169
|
+
const torad = Math.PI / 180;
|
|
170
|
+
const epsilon = 1e-7;
|
|
171
|
+
|
|
172
|
+
//normalize matrix object or array notations
|
|
173
|
+
matrix = !Array.isArray(matrix)
|
|
174
|
+
? matrix
|
|
175
|
+
: {
|
|
176
|
+
a: matrix[0],
|
|
177
|
+
b: matrix[1],
|
|
178
|
+
c: matrix[2],
|
|
179
|
+
d: matrix[3],
|
|
180
|
+
e: matrix[4],
|
|
181
|
+
f: matrix[5]
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
// We consider the current ellipse as image of the unit circle
|
|
185
|
+
// by first scale(rx,ry) and then rotate(ax) ...
|
|
186
|
+
// So we apply ma = m x rotate(ax) x scale(rx,ry) to the unit circle.
|
|
187
|
+
let c = Math.cos(ax * torad),
|
|
188
|
+
s = Math.sin(ax * torad);
|
|
189
|
+
let ma = [
|
|
190
|
+
rx * (matrix.a * c + matrix.c * s),
|
|
191
|
+
rx * (matrix.b * c + matrix.d * s),
|
|
192
|
+
ry * (-matrix.a * s + matrix.c * c),
|
|
193
|
+
ry * (-matrix.b * s + matrix.d * c)
|
|
194
|
+
];
|
|
195
|
+
|
|
196
|
+
// ma * transpose(ma) = [ J L ]
|
|
197
|
+
// [ L K ]
|
|
198
|
+
// L is calculated later (if the image is not a circle)
|
|
199
|
+
let J = ma[0] * ma[0] + ma[2] * ma[2],
|
|
200
|
+
K = ma[1] * ma[1] + ma[3] * ma[3];
|
|
201
|
+
|
|
202
|
+
// the sqrt of the discriminant of the characteristic polynomial of ma * transpose(ma)
|
|
203
|
+
// this is also the geometric mean of the eigenvalues
|
|
204
|
+
let D = Math.sqrt(
|
|
205
|
+
((ma[0] - ma[3]) * (ma[0] - ma[3]) + (ma[2] + ma[1]) * (ma[2] + ma[1])) *
|
|
206
|
+
((ma[0] + ma[3]) * (ma[0] + ma[3]) + (ma[2] - ma[1]) * (ma[2] - ma[1]))
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
// the arithmetic mean of the eigenvalues
|
|
210
|
+
let JK = (J + K) / 2;
|
|
211
|
+
|
|
212
|
+
// check if the image is (almost) a circle
|
|
213
|
+
if (D <= epsilon) {
|
|
214
|
+
rx = ry = Math.sqrt(JK);
|
|
215
|
+
ax = 0;
|
|
216
|
+
return { rx: rx, ry: ry, ax: ax };
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// check if ma * transpose(ma) is (almost) diagonal
|
|
220
|
+
if (Math.abs(D - Math.abs(J - K)) <= epsilon) {
|
|
221
|
+
rx = Math.sqrt(J);
|
|
222
|
+
ry = Math.sqrt(K);
|
|
223
|
+
ax = 0;
|
|
224
|
+
return { rx: rx, ry: ry, ax: ax };
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// if it is not a circle, nor diagonal
|
|
228
|
+
let L = ma[0] * ma[1] + ma[2] * ma[3];
|
|
229
|
+
|
|
230
|
+
// {l1,l2} = the two eigen values of ma * transpose(ma)
|
|
231
|
+
let l1 = JK + D / 2,
|
|
232
|
+
l2 = JK - D / 2;
|
|
233
|
+
|
|
234
|
+
// the x - axis - rotation angle is the argument of the l1 - eigenvector
|
|
235
|
+
if (Math.abs(L) <= epsilon && Math.abs(l1 - K) <= epsilon) {
|
|
236
|
+
// if (ax == 90) => ax = 0 and exchange axes
|
|
237
|
+
ax = 0;
|
|
238
|
+
rx = Math.sqrt(l2);
|
|
239
|
+
ry = Math.sqrt(l1);
|
|
240
|
+
return { rx: rx, ry: ry, ax: ax };
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
ax =
|
|
244
|
+
Math.atan(Math.abs(L) > Math.abs(l1 - K) ? (l1 - J) / L : L / (l1 - K)) /
|
|
245
|
+
torad; // the angle in degree
|
|
246
|
+
|
|
247
|
+
// if ax > 0 => rx = sqrt(l1), ry = sqrt(l2), else exchange axes and ax += 90
|
|
248
|
+
if (ax >= 0) {
|
|
249
|
+
// if ax in [0,90]
|
|
250
|
+
rx = Math.sqrt(l1);
|
|
251
|
+
ry = Math.sqrt(l2);
|
|
252
|
+
} else {
|
|
253
|
+
// if ax in ]-90,0[ => exchange axes
|
|
254
|
+
ax += 90;
|
|
255
|
+
rx = Math.sqrt(l2);
|
|
256
|
+
ry = Math.sqrt(l1);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return { rx: rx, ry: ry, ax: ax };
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* scale pathData
|
|
268
|
+
*/
|
|
269
|
+
|
|
270
|
+
export function scalePathData(pathData, scaleX, scaleY) {
|
|
271
|
+
pathData.forEach((com, i) => {
|
|
272
|
+
let { type, values } = com;
|
|
273
|
+
let typeRel = type.toLowerCase();
|
|
274
|
+
|
|
275
|
+
switch (typeRel) {
|
|
276
|
+
case "a":
|
|
277
|
+
com.values = [
|
|
278
|
+
values[0] * scaleX,
|
|
279
|
+
values[1] * scaleY,
|
|
280
|
+
values[2],
|
|
281
|
+
values[3],
|
|
282
|
+
values[4],
|
|
283
|
+
values[5] * scaleX,
|
|
284
|
+
values[6] * scaleY
|
|
285
|
+
];
|
|
286
|
+
break;
|
|
287
|
+
|
|
288
|
+
case "h":
|
|
289
|
+
com.values = [values[0] * scaleX];
|
|
290
|
+
break;
|
|
291
|
+
|
|
292
|
+
case "v":
|
|
293
|
+
com.values = [values[0] * scaleY];
|
|
294
|
+
break;
|
|
295
|
+
|
|
296
|
+
default:
|
|
297
|
+
if (values.length) {
|
|
298
|
+
for (let i = 0; i < values.length; i += 2) {
|
|
299
|
+
com.values[i] *= scaleX;
|
|
300
|
+
com.values[i + 1] *= scaleY;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
});
|
|
306
|
+
return pathData;
|
|
307
|
+
}
|
|
@@ -4,6 +4,12 @@ export function normalizePoly(pts, {
|
|
|
4
4
|
flatten = false
|
|
5
5
|
} = {}) {
|
|
6
6
|
|
|
7
|
+
// is stringified flat point attribute
|
|
8
|
+
if(typeof pts === 'string' && !isNaN(pts[0])){
|
|
9
|
+
pts = toPointArray(pts.split(/,| /).filter(Boolean).map(Number));
|
|
10
|
+
return pts
|
|
11
|
+
}
|
|
12
|
+
|
|
7
13
|
if (flatten) pts = pts.flat(2);
|
|
8
14
|
let poly = toArray ? polyPtsToArray(pts) : polyArrayToObject(pts)
|
|
9
15
|
return poly
|
|
@@ -29,6 +35,11 @@ export function polyArrayToObject(pts) {
|
|
|
29
35
|
return poly
|
|
30
36
|
}
|
|
31
37
|
|
|
38
|
+
else if(pts.length>3){
|
|
39
|
+
pts = toPointArray(pts)
|
|
40
|
+
return pts
|
|
41
|
+
}
|
|
42
|
+
|
|
32
43
|
return pts.map(pt => { return { x: pt[0], y: pt[1] } })
|
|
33
44
|
}
|
|
34
45
|
|
|
@@ -48,4 +59,13 @@ export function polyPtsToArray(pts) {
|
|
|
48
59
|
|
|
49
60
|
poly = Array.from(pts).map(pt => [pt.x, pt.y])
|
|
50
61
|
return poly
|
|
51
|
-
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// convert flat point value array to point object array
|
|
65
|
+
export function toPointArray(pts) {
|
|
66
|
+
let ptArr = [];
|
|
67
|
+
for (let i = 1, l = pts.length; i < l; i += 2) {
|
|
68
|
+
ptArr.push({ x: pts[i - 1], y: pts[i] });
|
|
69
|
+
}
|
|
70
|
+
return ptArr;
|
|
71
|
+
};
|
package/src/svgii/rounding.js
CHANGED
|
@@ -85,32 +85,63 @@ export function detectAccuracy(pathData) {
|
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
|
|
88
|
+
|
|
89
|
+
|
|
88
90
|
export function roundTo(num = 0, decimals = 3) {
|
|
89
91
|
if (!decimals) return Math.round(num);
|
|
90
92
|
let factor = 10 ** decimals;
|
|
91
93
|
return Math.round(num * factor) / factor;
|
|
92
94
|
}
|
|
93
95
|
|
|
96
|
+
/**
|
|
97
|
+
* round to reasonable
|
|
98
|
+
* floating point accuracy
|
|
99
|
+
* based on numeric value
|
|
100
|
+
*/
|
|
101
|
+
export function autoRound(val, integerThresh = 10){
|
|
102
|
+
let decimals=8;
|
|
103
|
+
|
|
104
|
+
//console.log('val', val);
|
|
105
|
+
|
|
106
|
+
if(val>integerThresh){
|
|
107
|
+
decimals=0
|
|
108
|
+
}
|
|
109
|
+
else if(val>5){
|
|
110
|
+
decimals=1
|
|
111
|
+
}else{
|
|
112
|
+
decimals=Math.ceil(integerThresh/val).toString().length
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
let factor = 10 ** decimals;
|
|
116
|
+
return Math.round(val * factor) / factor;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
|
|
94
121
|
|
|
95
122
|
/**
|
|
96
123
|
* round path data
|
|
97
124
|
* either by explicit decimal value or
|
|
98
125
|
* based on suggested accuracy in path data
|
|
99
126
|
*/
|
|
100
|
-
export function roundPathData(pathData,
|
|
127
|
+
export function roundPathData(pathData, decimalsGlobal = -1) {
|
|
101
128
|
|
|
102
|
-
if (
|
|
129
|
+
if (decimalsGlobal < 0) return pathData;
|
|
103
130
|
|
|
104
131
|
let len = pathData.length;
|
|
132
|
+
//let decimals = pathData[0].decimals ? pathData[0].decimals+1 : decimalsGlobal
|
|
133
|
+
let decimals = decimalsGlobal
|
|
134
|
+
//decimals = decimalsGlobal;
|
|
135
|
+
//console.log('decimals subpath', decimals, pathData[0].decimals, 'decimalsGlobal', decimalsGlobal);
|
|
105
136
|
|
|
106
137
|
for (let c = 0; c < len; c++) {
|
|
107
|
-
|
|
108
|
-
let values =
|
|
138
|
+
let com = pathData[c];
|
|
139
|
+
let {values} = com
|
|
140
|
+
//let values = pathData[c].values
|
|
109
141
|
let valLen = values.length;
|
|
110
142
|
if (!valLen) continue
|
|
111
143
|
|
|
112
144
|
for (let v = 0; v < valLen; v++) {
|
|
113
|
-
//pathData[c].values[v] = +values[v].toFixed(decimals);
|
|
114
145
|
pathData[c].values[v] = roundTo(values[v], decimals);
|
|
115
146
|
}
|
|
116
147
|
};
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* transform property object
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { deg2rad } from "../constants";
|
|
7
|
+
|
|
6
8
|
export function parseTransform(transformString, transformOrigin = { x: 0, y: 0 }) {
|
|
7
9
|
|
|
8
10
|
//let regex = /(\w+)\(([^)]+)\)/g;
|
|
@@ -18,12 +20,12 @@ export function parseTransform(transformString, transformOrigin = { x: 0, y: 0 }
|
|
|
18
20
|
console.log('trans', prop, vals);
|
|
19
21
|
|
|
20
22
|
// rotate has origin
|
|
21
|
-
if(prop==='rotate' && vals.length===3){
|
|
22
|
-
transforms.push({prop:'translate', values:[vals[1], vals[2]]})
|
|
23
|
-
transforms.push({prop:'rotate', values:[vals[0]]})
|
|
24
|
-
transforms.push({prop:'translate', values:[-vals[1], -vals[2]]})
|
|
25
|
-
}else{
|
|
26
|
-
transforms.push({prop, values:[vals[0]]})
|
|
23
|
+
if (prop === 'rotate' && vals.length === 3) {
|
|
24
|
+
transforms.push({ prop: 'translate', values: [vals[1], vals[2]] })
|
|
25
|
+
transforms.push({ prop: 'rotate', values: [vals[0]] })
|
|
26
|
+
transforms.push({ prop: 'translate', values: [-vals[1], -vals[2]] })
|
|
27
|
+
} else {
|
|
28
|
+
transforms.push({ prop, values: [vals[0]] })
|
|
27
29
|
}
|
|
28
30
|
})
|
|
29
31
|
|
|
@@ -89,8 +91,7 @@ export function parseCSSTransform(transformString, transformOrigin = { x: 0, y:
|
|
|
89
91
|
break;
|
|
90
92
|
|
|
91
93
|
case 'rotate':
|
|
92
|
-
|
|
93
|
-
console.log('rotate', values);
|
|
94
|
+
//console.log('rotate', values);
|
|
94
95
|
|
|
95
96
|
transformOptions.transforms.push({ rotate: [0, 0, values[0] || 0] });
|
|
96
97
|
break;
|
|
@@ -139,6 +140,116 @@ export function getMatrix({
|
|
|
139
140
|
|
|
140
141
|
|
|
141
142
|
|
|
143
|
+
|
|
144
|
+
export function getMatrixFromTransform(transformations = []) {
|
|
145
|
+
|
|
146
|
+
//console.log('getMatrix2D', transformations, origin);
|
|
147
|
+
// Helper function to multiply two 2D matrices
|
|
148
|
+
|
|
149
|
+
const multiply = (m1, m2) => {
|
|
150
|
+
let mtxN = {
|
|
151
|
+
a: m1.a * m2.a + m1.c * m2.b,
|
|
152
|
+
b: m1.b * m2.a + m1.d * m2.b,
|
|
153
|
+
c: m1.a * m2.c + m1.c * m2.d,
|
|
154
|
+
d: m1.b * m2.c + m1.d * m2.d,
|
|
155
|
+
e: m1.a * m2.e + m1.c * m2.f + m1.e,
|
|
156
|
+
f: m1.b * m2.e + m1.d * m2.f + m1.f
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
//console.log('m1', m1, 'm2', m2, 'mtxN', mtxN);
|
|
160
|
+
return mtxN;
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
// Helper function to create a translation matrix
|
|
165
|
+
const translationMatrix = (x, y) => {
|
|
166
|
+
let mtx ={a: 1, b: 0, c: 0, d: 1, e: x, f: y}
|
|
167
|
+
return mtx
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
// Helper function to create a scaling matrix
|
|
171
|
+
const scalingMatrix = (x, y) => ({
|
|
172
|
+
a: x, b: 0, c: 0, d: y, e: 0, f: 0
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
// get skew or rotation axis matrix
|
|
177
|
+
const angleMatrix = (angles, type) => {
|
|
178
|
+
//const toRad = (angle) => angle * Math.PI / 180;
|
|
179
|
+
let [angleX, angleY=0] = angles.map(ang => ang*deg2rad)
|
|
180
|
+
let m = {}
|
|
181
|
+
//console.log('angles', angles);
|
|
182
|
+
|
|
183
|
+
if (type === 'rot') {
|
|
184
|
+
let cosX = Math.cos(angleX), sinX = Math.sin(angleX);
|
|
185
|
+
m = { a: cosX, b: sinX, c: -sinX, d: cosX, e: 0, f: 0 }
|
|
186
|
+
} else if (type === 'skew') {
|
|
187
|
+
let tanX = Math.tan(angleX), tanY = Math.tan(angleY);
|
|
188
|
+
m = {
|
|
189
|
+
a: 1, b: tanY, c: tanX, d: 1, e: 0, f: 0
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
return m
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
// Start with an identity matrix
|
|
197
|
+
let matrix = { a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 };
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
// Process transformations in the provided order (right-to-left)
|
|
201
|
+
for (let i = 0; i < transformations.length; i++) {
|
|
202
|
+
|
|
203
|
+
let transform = transformations[i];
|
|
204
|
+
|
|
205
|
+
// Get the transformation type (e.g., "translate")
|
|
206
|
+
let type = Object.keys(transform)[0];
|
|
207
|
+
|
|
208
|
+
let values = transform[type];
|
|
209
|
+
//console.log('transform', transform, type, values);
|
|
210
|
+
|
|
211
|
+
let [x, y] = values
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
switch (type) {
|
|
215
|
+
case "matrix":
|
|
216
|
+
let keys = ['a', 'b', 'c', 'd', 'e', 'f'];
|
|
217
|
+
let obj = Object.fromEntries(keys.map((key, i) => [key, values[i]]));
|
|
218
|
+
//console.log('mtx',obj );
|
|
219
|
+
matrix = multiply(matrix, obj);
|
|
220
|
+
break;
|
|
221
|
+
case "translate":
|
|
222
|
+
matrix = multiply(matrix, translationMatrix(x, y));
|
|
223
|
+
break;
|
|
224
|
+
case "skew":
|
|
225
|
+
matrix = multiply(matrix, angleMatrix([x, y], 'skew'));
|
|
226
|
+
break;
|
|
227
|
+
case "rotate":
|
|
228
|
+
matrix = multiply(matrix, angleMatrix([x], 'rot'));
|
|
229
|
+
//console.log('rot', matrix);
|
|
230
|
+
break;
|
|
231
|
+
case "scale":
|
|
232
|
+
matrix = multiply(matrix, scalingMatrix(x, y));
|
|
233
|
+
break;
|
|
234
|
+
|
|
235
|
+
default:
|
|
236
|
+
throw new Error(`Unknown transformation type: ${type}`);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
//let mtxTmp = JSON.parse(JSON.stringify(matrix))
|
|
241
|
+
//console.log('??? mtxTmp', type, mtxTmp, 'transform', transform);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
//console.log('matrix2D', matrix);
|
|
246
|
+
return matrix;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
|
|
142
253
|
export function getMatrix2D(transformations = [], origin = { x: 0, y: 0 }) {
|
|
143
254
|
|
|
144
255
|
//console.log('getMatrix2D', transformations, origin);
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* used to remove unnecessary attribution
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
const shapeEls = [
|
|
7
|
+
export const shapeEls = [
|
|
8
8
|
"polygon",
|
|
9
9
|
"polyline",
|
|
10
10
|
"line",
|
|
@@ -13,16 +13,36 @@ const shapeEls = [
|
|
|
13
13
|
"ellipse",
|
|
14
14
|
]
|
|
15
15
|
|
|
16
|
-
const
|
|
16
|
+
export const horizontalProps = ['x', 'cx', 'rx', 'dx', 'width', 'translateX'];
|
|
17
|
+
export const verticalProps = ['y', 'cy', 'ry', 'dy', 'height', 'translateY'];
|
|
18
|
+
export const transHorizontal = ['scaleX', 'translateX', 'skewX'];
|
|
19
|
+
export const transVertical = ['scaleY', 'translateY', 'skewY'];
|
|
20
|
+
|
|
21
|
+
export const colorProps = ['fill', 'stroke', 'stop-color'];
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
export const geometryEls = [
|
|
17
25
|
"path",
|
|
18
26
|
...shapeEls
|
|
19
|
-
]
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
export const renderedEls = [
|
|
30
|
+
"text",
|
|
31
|
+
"textPath",
|
|
32
|
+
"tspan",
|
|
33
|
+
...geometryEls
|
|
34
|
+
];
|
|
20
35
|
|
|
21
|
-
const textEls = [
|
|
36
|
+
export const textEls = [
|
|
22
37
|
"textPath",
|
|
23
38
|
"text",
|
|
24
39
|
"tspan",
|
|
25
|
-
]
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
export const strokeAtts = ['stroke', 'stroke-width', 'stroke-linecap', 'stroke-linejoin','stroke-linecap', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-miterlimit', 'stroke-opacity' ];
|
|
45
|
+
|
|
26
46
|
|
|
27
47
|
export const attLookup = {
|
|
28
48
|
|
|
@@ -301,9 +321,9 @@ export const attLookup = {
|
|
|
301
321
|
"color": ["black", "rgb(0, 0, 0)", "rgba(0, 0, 0, 0)", "#000", "#000000"],
|
|
302
322
|
|
|
303
323
|
stroke: ["none"],
|
|
304
|
-
"stroke-width": ["1", "1px"],
|
|
305
324
|
opacity: ["1"],
|
|
306
325
|
"fill-opacity": ["1"],
|
|
326
|
+
"stroke-width": ["1", "1px"],
|
|
307
327
|
"stroke-opacity": ["1"],
|
|
308
328
|
"stroke-linecap": ["butt"],
|
|
309
329
|
"stroke-miterlimit": ["4"],
|