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.
Files changed (43) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +3 -76
  3. package/dist/svg-path-simplify.esm.js +5505 -4030
  4. package/dist/svg-path-simplify.esm.min.js +2 -8
  5. package/dist/svg-path-simplify.js +5506 -4029
  6. package/dist/svg-path-simplify.min.js +2 -8
  7. package/dist/svg-path-simplify.pathdata.esm.js +1154 -1042
  8. package/dist/svg-path-simplify.pathdata.esm.min.js +2 -8
  9. package/docs/api.md +127 -0
  10. package/index.html +279 -257
  11. package/package.json +1 -1
  12. package/src/constants.js +10 -1
  13. package/src/index.js +7 -1
  14. package/src/pathData_simplify_cubic.js +1 -18
  15. package/src/pathSimplify-main.js +87 -30
  16. package/src/pathSimplify-only-pathdata.js +2 -2
  17. package/src/svg-getAttributes.js +13 -0
  18. package/src/svg_flatten_transforms.js +1 -1
  19. package/src/svg_getViewbox.js +23 -11
  20. package/src/svg_rootSVG.js +9 -0
  21. package/src/svgii/convert_colors.js +145 -0
  22. package/src/svgii/convert_units.js +159 -0
  23. package/src/svgii/geometry.js +24 -4
  24. package/src/svgii/geometry_bbox.js +2 -1
  25. package/src/svgii/geometry_bbox_element.js +46 -0
  26. package/src/svgii/pathData_analyze.js +34 -14
  27. package/src/svgii/pathData_convert.js +204 -34
  28. package/src/svgii/pathData_parse.js +2 -99
  29. package/src/svgii/pathData_parse_els.js +217 -128
  30. package/src/svgii/pathData_simplify_refineCorners.js +72 -43
  31. package/src/svgii/pathData_stringify.js +6 -5
  32. package/src/svgii/pathData_toPolygon.js +3 -1
  33. package/src/svgii/pathData_transform.js +307 -0
  34. package/src/svgii/poly_normalize.js +21 -1
  35. package/src/svgii/rounding.js +36 -5
  36. package/src/svgii/svg-styles-getTransforms.js +119 -8
  37. package/src/svgii/svg-styles-to-attributes-const.js +26 -6
  38. package/src/svgii/svg_cleanup.js +540 -74
  39. package/src/svgii/svg_el_parse_style_props.js +561 -0
  40. package/src/svgii/transform_qr_decompose.js +74 -0
  41. package/src/svgii/pathData_scale.js +0 -42
  42. package/src/svgii/stringify.js +0 -103
  43. package/src/svgii/svg-styles-to-attributes.js +0 -217
@@ -12,40 +12,56 @@ import { renderPoint, renderPath } from "./visualize";
12
12
 
13
13
  import { checkLineIntersection, getAngle, getDeltaAngle, getDistance, getDistAv, getDistManhattan, getSquareDistance, interpolate, pointAtT, rotatePoint, toParametricAngle } from './geometry';
14
14
  import { getPathArea, getPolygonArea, getRelativeAreaDiff } from './geometry_area';
15
+ import { parsePathDataString } from './pathData_parse';
15
16
  import { pathDataToD } from './pathData_stringify';
16
17
  import { roundPathData } from './rounding';
17
18
  import { renderPoint } from './visualize';
18
19
 
19
- export function revertCubicQuadratic(p0 = {}, cp1 = {}, cp2 = {}, p = {}, tolerance=1) {
20
20
 
21
- // test if cubic can be simplified to quadratic
22
- let cp1X = interpolate(p0, cp1, 1.5)
23
- let cp2X = interpolate(p, cp2, 1.5)
21
+ export function parsePathDataNormalized(d,
22
+ {
23
+ // necessary for most calculations
24
+ toAbsolute = true,
25
+ toLonghands = true,
24
26
 
25
- let dist0 = getDistManhattan(p0, p)
26
- let threshold = dist0 * 0.01 * tolerance;
27
- let dist1 = getDistManhattan(cp1X, cp2X)
27
+ // not necessary unless you need cubics only
28
+ quadraticToCubic = false,
28
29
 
29
- let cp1_Q = null;
30
- let type = 'C'
31
- let values = [cp1.x, cp1.y, cp2.x, cp2.y, p.x, p.y];
32
- let comN = { type, values }
30
+ // mostly a fallback if arc calculations fail
31
+ arcToCubic = false,
32
+ // arc to cubic precision - adds more segments for better precision
33
+ arcAccuracy = 4,
34
+ } = {}
35
+ ) {
33
36
 
34
- if (dist1 < threshold ) {
35
- cp1_Q = checkLineIntersection(p0, cp1, p, cp2, false);
36
- if (cp1_Q) {
37
- //renderPoint(markers, cp1_Q )
38
- comN.type = 'Q'
39
- comN.values = [cp1_Q.x, cp1_Q.y, p.x, p.y];
40
- comN.p0 = p0;
41
- comN.cp1 = cp1_Q;
42
- comN.cp2 = null;
43
- comN.p = p
44
- }
37
+ // is already array
38
+ let isArray = Array.isArray(d);
39
+
40
+ // normalize native pathData to regular array
41
+ let hasConstructor = isArray && typeof d[0] === 'object' && typeof d[0].constructor === 'function'
42
+ /*
43
+ if (hasConstructor) {
44
+ d = d.map(com => { return { type: com.type, values: com.values } })
45
+ console.log('hasConstructor', hasConstructor, (typeof d[0].constructor), d);
45
46
  }
47
+ */
46
48
 
47
- return comN
49
+ let pathDataObj = isArray ? d : parsePathDataString(d);
50
+
51
+ let { hasRelatives = true, hasShorthands = true, hasQuadratics = true, hasArcs = true } = pathDataObj;
52
+ let pathData = hasConstructor ? pathDataObj : pathDataObj.pathData;
53
+
54
+ //console.log('???quadraticToCubic', quadraticToCubic);
48
55
 
56
+ // normalize
57
+ pathData = normalizePathData(pathData,
58
+ {
59
+ toAbsolute, toLonghands, quadraticToCubic, arcToCubic, arcAccuracy,
60
+ hasRelatives, hasShorthands, hasQuadratics, hasArcs
61
+ },
62
+ )
63
+
64
+ return pathData;
49
65
  }
50
66
 
51
67
 
@@ -63,11 +79,14 @@ export function convertPathData(pathData, {
63
79
  hasShorthands = true,
64
80
  hasQuadratics = true,
65
81
  hasArcs = true,
82
+ optimizeArcs = true,
66
83
  testTypes = false
67
84
 
68
85
 
69
86
  } = {}) {
70
87
 
88
+
89
+
71
90
  // pathdata properties - test= true adds a manual test
72
91
  if (testTypes) {
73
92
  //console.log('test for conversions');
@@ -85,27 +104,178 @@ export function convertPathData(pathData, {
85
104
  toShorthands = toLonghands ? false : toShorthands
86
105
 
87
106
 
88
- //console.log(toShorthands, toRelative, decimals);
89
- if (hasQuadratics && quadraticToCubic) pathData = pathDataQuadraticToCubic(pathData);
90
- if (hasArcs && arcToCubic) pathData = pathDataArcsToCubics(pathData);
107
+ if (toAbsolute) pathData = pathDataToAbsolute(pathData);
108
+ if (hasShorthands && toLonghands) pathData = pathDataToLonghands(pathData);
109
+
110
+
111
+ // minify semicircle radii
112
+ if (optimizeArcs) pathData = optimizeArcPathData(pathData);
113
+
91
114
 
92
115
  //if(decimals>-1 && decimals<2) pathData = roundPathData(pathData, decimals);
93
116
  if (toShorthands) pathData = pathDataToShorthands(pathData);
94
- if (hasShorthands && toLonghands) pathData = pathDataToLonghands(pathData);
95
117
 
96
- if (toAbsolute) pathData = pathDataToAbsolute(pathData);
118
+ if (hasArcs && arcToCubic) pathData = pathDataArcsToCubics(pathData)
119
+
120
+ //console.log(toShorthands, toRelative, decimals);
121
+ if (hasQuadratics && quadraticToCubic) pathData = pathDataQuadraticToCubic(pathData);
122
+
97
123
 
98
124
  // pre round - before relative conversion to minimize distortions
99
125
  if (decimals > -1 && toRelative) pathData = roundPathData(pathData, decimals);
126
+
127
+
100
128
  if (toRelative) pathData = pathDataToRelative(pathData);
101
129
  if (decimals > -1) pathData = roundPathData(pathData, decimals);
102
130
 
131
+ return pathData
132
+ }
103
133
 
134
+ /**
135
+ *
136
+ * @param {*} pathData
137
+ * @returns
138
+ */
104
139
 
105
- return pathData
140
+ export function optimizeArcPathData(pathData = []) {
141
+ pathData.forEach((com, i) => {
142
+ let { type, values } = com;
143
+ if (type === 'A') {
144
+ let [rx, ry, largeArc, x, y] = [values[0], values[1], values[3], values[5], values[6]];
145
+ let comPrev = pathData[i - 1]
146
+ let [x0, y0] = [comPrev.values[comPrev.values.length - 2], comPrev.values[comPrev.values.length - 1]];
147
+ let M = { x: x0, y: y0 };
148
+ let p = { x, y };
149
+ //largeArc=true
150
+ //let pMid = {x: Math.abs(x-x0), y:Math.abs(y-y0)}
151
+
152
+ // rx and ry are large enough
153
+ if (rx >= 1 && (x === x0 || y === y0)) {
154
+ let diff = Math.abs(rx - ry) / rx;
155
+
156
+ // rx~==ry
157
+ if (diff < 0.01) {
158
+
159
+ // test radius against mid point
160
+ let pMid = interpolate(M, p, 0.5)
161
+ let distM = getDistance(pMid, M)
162
+ let rDiff = Math.abs(distM - rx) / rx
163
+
164
+ // half distance between mid and start point should be ~ equal
165
+ if(rDiff<0.01){
166
+ pathData[i].values[0] = 1;
167
+ pathData[i].values[1] = 1;
168
+ pathData[i].values[2] = 0;
169
+ }
170
+ }
171
+ }
172
+ }
173
+ })
174
+ return pathData;
106
175
  }
107
176
 
108
177
 
178
+
179
+ /**
180
+ * parse normalized
181
+ */
182
+
183
+ export function normalizePathData(pathData = [],
184
+ {
185
+ toAbsolute = true,
186
+ toLonghands = true,
187
+ quadraticToCubic = false,
188
+ arcToCubic = false,
189
+ arcAccuracy = 2,
190
+
191
+ // assume we need full normalization
192
+ hasRelatives = true, hasShorthands = true, hasQuadratics = true, hasArcs = true, testTypes = false
193
+
194
+ } = {}
195
+ ) {
196
+
197
+
198
+ return convertPathData(pathData, { toAbsolute, toLonghands, quadraticToCubic, arcToCubic, arcAccuracy, hasRelatives, hasShorthands, hasQuadratics, hasArcs, testTypes, decimals: -1 })
199
+ }
200
+
201
+ /*
202
+ export function normalizePathData(pathData = [],
203
+ {
204
+ toAbsolute = true,
205
+ toLonghands = true,
206
+ quadraticToCubic = false,
207
+ arcToCubic = false,
208
+ arcAccuracy = 2,
209
+
210
+ // assume we need full normalization
211
+ hasRelatives = true, hasShorthands = true, hasQuadratics = true, hasArcs = true, testTypes = false
212
+
213
+ } = {}
214
+ ) {
215
+
216
+ // pathdata properties - test= true adds a manual test
217
+ if (testTypes) {
218
+ //console.log('test for conversions');
219
+ let commands = Array.from(new Set(pathData.map(com => com.type))).join('');
220
+ hasRelatives = /[lcqamts]/gi.test(commands);
221
+ hasQuadratics = /[qt]/gi.test(commands);
222
+ hasArcs = /[a]/gi.test(commands);
223
+ hasShorthands = /[vhst]/gi.test(commands);
224
+ isPoly = /[mlz]/gi.test(commands);
225
+ }
226
+
227
+ if ((hasQuadratics && quadraticToCubic) || (hasArcs && arcToCubic)) {
228
+ toLonghands = true
229
+ toAbsolute = true
230
+ }
231
+
232
+ if (hasRelatives && toAbsolute) pathData = pathDataToAbsoluteOrRelative(pathData, false);
233
+ if (hasShorthands && toLonghands) pathData = pathDataToLonghands(pathData, -1, false);
234
+ if (hasArcs && arcToCubic) pathData = pathDataArcsToCubics(pathData, arcAccuracy);
235
+ if (hasQuadratics && quadraticToCubic) pathData = pathDataQuadraticToCubic(pathData);
236
+
237
+ return pathData;
238
+
239
+ }
240
+ */
241
+
242
+
243
+
244
+
245
+ export function revertCubicQuadratic(p0 = {}, cp1 = {}, cp2 = {}, p = {}, tolerance = 1) {
246
+
247
+ // test if cubic can be simplified to quadratic
248
+ let cp1X = interpolate(p0, cp1, 1.5)
249
+ let cp2X = interpolate(p, cp2, 1.5)
250
+
251
+ let dist0 = getDistManhattan(p0, p)
252
+ let threshold = dist0 * 0.01 * tolerance;
253
+ let dist1 = getDistManhattan(cp1X, cp2X)
254
+
255
+ let cp1_Q = null;
256
+ let type = 'C'
257
+ let values = [cp1.x, cp1.y, cp2.x, cp2.y, p.x, p.y];
258
+ let comN = { type, values }
259
+
260
+ if (dist1 < threshold) {
261
+ cp1_Q = checkLineIntersection(p0, cp1, p, cp2, false, true);
262
+ if (cp1_Q) {
263
+ //renderPoint(markers, cp1_Q )
264
+ comN.type = 'Q'
265
+ comN.values = [cp1_Q.x, cp1_Q.y, p.x, p.y];
266
+ comN.p0 = p0;
267
+ comN.cp1 = cp1_Q;
268
+ comN.cp2 = null;
269
+ comN.p = p
270
+ }
271
+ }
272
+
273
+ return comN
274
+
275
+ }
276
+
277
+
278
+
109
279
  /**
110
280
  * convert cubic circle approximations
111
281
  * to more compact arcs
@@ -218,7 +388,7 @@ export function pathDataToAbsoluteOrRelative(pathData, toRelative = false, decim
218
388
  let { type, values } = com;
219
389
  let vLen = values.length;
220
390
  let typeRel = type.toLowerCase();
221
- let typeAbs =type.toUpperCase();
391
+ let typeAbs = type.toUpperCase();
222
392
  let typeNew = toRelative ? typeRel : typeAbs;
223
393
 
224
394
  if (type !== typeNew) {
@@ -463,7 +633,7 @@ export function pathDataToShorthands(pathData, decimals = -1, test = false) {
463
633
  let dy1 = (p0.y - cpPrev.y)
464
634
 
465
635
  //adjust maxDist
466
- maxDist = getDistManhattan(cpPrev, cpFirst) * 0.025
636
+ maxDist = getDistManhattan(cpPrev, cpFirst) * 0.01
467
637
 
468
638
  // reflected cp
469
639
  let cpR = { x: cpPrev.x + dx1 * 2, y: cpPrev.y + dy1 * 2 }
@@ -495,13 +665,13 @@ export function pathDataToShorthands(pathData, decimals = -1, test = false) {
495
665
  }
496
666
 
497
667
  // V
498
- if (isVertical) {
668
+ if (isVertical) {
499
669
  //console.log('is V', w, h);
500
670
  comShort = {
501
671
  type: "V",
502
672
  values: [values[1]]
503
673
  };
504
- }
674
+ }
505
675
  break;
506
676
 
507
677
  case "Q":
@@ -522,7 +692,7 @@ export function pathDataToShorthands(pathData, decimals = -1, test = false) {
522
692
  type: "S",
523
693
  values: [values[2], values[3], p.x, p.y]
524
694
  };
525
- }
695
+ }
526
696
  break;
527
697
  default:
528
698
  comShort = {
@@ -1,104 +1,7 @@
1
1
  //import { arcToBezier, quadratic2Cubic } from './convert.js';
2
2
  //import { getAngle, bezierhasExtreme, getDistance } from "./geometry";
3
- import { pathDataToAbsoluteOrRelative, pathDataToLonghands, pathDataArcsToCubics, pathDataQuadraticToCubic } from './pathData_convert.js';
4
- import { pathDataToD } from './pathData_stringify.js';
5
-
6
-
7
-
8
- /**
9
- * parse normalized
10
- */
11
-
12
- export function normalizePathData(pathData = [],
13
- {
14
- toAbsolute = true,
15
- toLonghands = true,
16
- quadraticToCubic = false,
17
- arcToCubic = false,
18
- arcAccuracy = 2,
19
-
20
- // assume we need full normalization
21
- hasRelatives = true, hasShorthands = true, hasQuadratics = true, hasArcs = true, testTypes = false
22
-
23
- } = {}
24
- ) {
25
-
26
- // pathdata properties - test= true adds a manual test
27
- if (testTypes) {
28
- //console.log('test for conversions');
29
- let commands = Array.from(new Set(pathData.map(com => com.type))).join('');
30
- hasRelatives = /[lcqamts]/gi.test(commands);
31
- hasQuadratics = /[qt]/gi.test(commands);
32
- hasArcs = /[a]/gi.test(commands);
33
- hasShorthands = /[vhst]/gi.test(commands);
34
- isPoly = /[mlz]/gi.test(commands);
35
- }
36
-
37
-
38
- /**
39
- * normalize:
40
- * convert to all absolute
41
- * all longhands
42
- */
43
-
44
- if ((hasQuadratics && quadraticToCubic) || (hasArcs && arcToCubic)) {
45
- toLonghands = true
46
- toAbsolute = true
47
- }
48
-
49
- if (hasRelatives && toAbsolute) pathData = pathDataToAbsoluteOrRelative(pathData, false);
50
- if (hasShorthands && toLonghands) pathData = pathDataToLonghands(pathData, -1, false);
51
- if (hasArcs && arcToCubic) pathData = pathDataArcsToCubics(pathData, arcAccuracy);
52
- if (hasQuadratics && quadraticToCubic) pathData = pathDataQuadraticToCubic(pathData);
53
-
54
- return pathData;
55
-
56
- }
57
-
58
- export function parsePathDataNormalized(d,
59
- {
60
- // necessary for most calculations
61
- toAbsolute = true,
62
- toLonghands = true,
63
-
64
- // not necessary unless you need cubics only
65
- quadraticToCubic = false,
66
-
67
- // mostly a fallback if arc calculations fail
68
- arcToCubic = false,
69
- // arc to cubic precision - adds more segments for better precision
70
- arcAccuracy = 4,
71
- } = {}
72
- ) {
73
-
74
-
75
- // is already array
76
- let isArray = Array.isArray(d);
77
-
78
- // normalize native pathData to regular array
79
- let hasConstructor = isArray && typeof d[0] === 'object' && typeof d[0].constructor === 'function'
80
- /*
81
- if (hasConstructor) {
82
- d = d.map(com => { return { type: com.type, values: com.values } })
83
- console.log('hasConstructor', hasConstructor, (typeof d[0].constructor), d);
84
- }
85
- */
86
-
87
- let pathDataObj = isArray ? d : parsePathDataString(d);
88
-
89
-
90
- let { hasRelatives = true, hasShorthands = true, hasQuadratics = true, hasArcs = true } = pathDataObj;
91
- let pathData = hasConstructor ? pathDataObj : pathDataObj.pathData;
92
-
93
- // normalize
94
- pathData = normalizePathData(pathData,
95
- { toAbsolute, toLonghands, quadraticToCubic, arcToCubic, arcAccuracy,
96
- hasRelatives, hasShorthands, hasQuadratics, hasArcs
97
- },
98
- )
99
-
100
- return pathData;
101
- }
3
+ //import { pathDataToAbsoluteOrRelative, pathDataToLonghands, pathDataArcsToCubics, pathDataQuadraticToCubic } from './pathData_convert.js';
4
+ //import { pathDataToD } from './pathData_stringify.js';
102
5
 
103
6
 
104
7
  const commandSet = new Set([