svg-path-simplify 0.4.1 → 0.4.3

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 (48) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +6 -4
  3. package/dist/svg-path-simplify.esm.js +2450 -888
  4. package/dist/svg-path-simplify.esm.min.js +2 -2
  5. package/dist/svg-path-simplify.js +2450 -888
  6. package/dist/svg-path-simplify.min.js +2 -2
  7. package/dist/svg-path-simplify.pathdata.esm.js +167 -85
  8. package/dist/svg-path-simplify.pathdata.esm.min.js +2 -2
  9. package/docs/privacy-webapp.md +24 -0
  10. package/index.html +333 -132
  11. package/package.json +5 -2
  12. package/src/css_parse.js +317 -0
  13. package/src/detect_input.js +34 -4
  14. package/src/pathData_simplify_harmonize_cpts.js +77 -1
  15. package/src/pathSimplify-main.js +246 -262
  16. package/src/pathSimplify-presets.js +243 -0
  17. package/src/poly-fit-curve-schneider.js +14 -7
  18. package/src/simplify_poly_RC.js +102 -0
  19. package/src/simplify_poly_RDP.js +109 -1
  20. package/src/simplify_poly_radial_distance.js +3 -3
  21. package/src/string_helpers.js +144 -0
  22. package/src/svgii/convert_units.js +8 -2
  23. package/src/svgii/geometry.js +182 -3
  24. package/src/svgii/geometry_length.js +237 -0
  25. package/src/svgii/pathData_convert.js +43 -1
  26. package/src/svgii/pathData_fix_directions.js +6 -0
  27. package/src/svgii/pathData_fromPoly.js +3 -3
  28. package/src/svgii/pathData_getLength.js +86 -0
  29. package/src/svgii/pathData_parse.js +2 -0
  30. package/src/svgii/pathData_parse_els.js +189 -189
  31. package/src/svgii/pathData_split_to_groups.js +168 -0
  32. package/src/svgii/pathData_stringify.js +26 -64
  33. package/src/svgii/pathData_toPolygon.js +3 -4
  34. package/src/svgii/poly_analyze.js +61 -0
  35. package/src/svgii/poly_normalize.js +11 -2
  36. package/src/svgii/poly_to_pathdata.js +85 -24
  37. package/src/svgii/rounding.js +8 -7
  38. package/src/svgii/svg-styles-to-attributes-const.js +1 -0
  39. package/src/svgii/svg_cleanup.js +467 -421
  40. package/src/svgii/svg_cleanup_convertPathLength.js +32 -0
  41. package/src/svgii/svg_cleanup_general_svg_atts.js +97 -0
  42. package/src/svgii/svg_cleanup_normalize_transforms.js +83 -0
  43. package/src/svgii/svg_cleanup_remove_els_and_atts.js +72 -0
  44. package/src/svgii/svg_cleanup_ungroup.js +36 -0
  45. package/src/svgii/svg_el_parse_style_props.js +76 -28
  46. package/src/svgii/svg_getElementLength.js +67 -0
  47. package/tests/testSVG_shape.js +59 -0
  48. package/tests/testSVG_transform.js +61 -0
@@ -0,0 +1,86 @@
1
+ import { deg2rad } from "../constants";
2
+ import { svgArcToCenterParam, toParametricAngle } from "./geometry";
3
+ import { getCircleArcLength, getEllipseLengthLG, getLegendreGaussValues, getLength, waArr_global } from "./geometry_length";
4
+ import { getPathDataVerbose } from "./pathData_analyze";
5
+ import { splitSubpaths } from "./pathData_split";
6
+
7
+
8
+ export function getPathDataLength(pathData = []) {
9
+ let len = 0
10
+ let pathDataArr = splitSubpaths(pathData);
11
+
12
+ for (let i = 0; i < pathDataArr.length; i++) {
13
+ let pathData = pathDataArr[i]
14
+
15
+ // add verbose point data if not present
16
+ if (pathData[0].p === undefined) pathData = getPathDataVerbose(pathData);
17
+
18
+ // Calculate Legendre Gauss weight and abscissa values
19
+ if (!waArr_global.length) {
20
+ //console.log('no LG');
21
+ let waArr = getLegendreGaussValues(48)
22
+ waArr.forEach(wa => {
23
+ waArr_global.push(wa)
24
+ })
25
+ }
26
+
27
+ let waArr = waArr_global;
28
+
29
+ pathData.forEach(com => {
30
+ let { type, values, p0, p, cp1 = null, cp2 = null } = com;
31
+ let pts = [p0]
32
+ if (type === 'C' || type === 'Q') pts.push(cp1)
33
+ if (type === 'C') pts.push(cp2)
34
+ pts.push(p)
35
+ let comLen = 0
36
+
37
+ if (type === 'A') {
38
+
39
+ // get parametrized arc properties
40
+ let [largeArc, sweep] = [com.values[3], com.values[4]];
41
+ let arcData = svgArcToCenterParam(p0.x, p0.y, com.values[0], com.values[1], com.values[2], largeArc, sweep, p.x, p.y, false)
42
+ let { cx, cy, rx, ry, startAngle, endAngle, deltaAngle, xAxisRotation } = arcData
43
+
44
+ //is circle
45
+ if (rx === ry) {
46
+ comLen = getCircleArcLength(rx, Math.abs(deltaAngle))
47
+ }
48
+
49
+ // is ellipse
50
+ else {
51
+ xAxisRotation = xAxisRotation * deg2rad;
52
+ startAngle = toParametricAngle((startAngle - xAxisRotation), rx, ry)
53
+ endAngle = toParametricAngle((endAngle - xAxisRotation), rx, ry)
54
+
55
+ // recalculate parametrized delta
56
+ let deltaAngle_param = endAngle - startAngle;
57
+
58
+ let signChange = deltaAngle > 0 && deltaAngle_param < 0 || deltaAngle < 0 && deltaAngle_param > 0;
59
+
60
+ //deltaAngle = xAxisRotation>0 ? endAngle- startAngle: deltaAngle;
61
+ deltaAngle = signChange ? deltaAngle : deltaAngle_param;
62
+
63
+ // adjust end angle
64
+ if (sweep && startAngle > endAngle) {
65
+ endAngle += Math.PI * 2
66
+ }
67
+
68
+ if (!sweep && startAngle < endAngle) {
69
+ endAngle -= Math.PI * 2
70
+ }
71
+ comLen = getEllipseLengthLG(rx, ry, startAngle, endAngle, waArr)
72
+ }
73
+ }
74
+
75
+ else {
76
+ comLen = getLength(pts, {
77
+ t: 1,
78
+ waArr
79
+ })
80
+ }
81
+ len += comLen;
82
+ })
83
+ }
84
+
85
+ return len;
86
+ }
@@ -103,8 +103,10 @@ const sanitizeArc = (val='', valueIndex=0) => {
103
103
 
104
104
 
105
105
  export function parsePathDataString(d, debug = true, limit=0) {
106
+ if(!d) return []
106
107
  d = d.trim();
107
108
 
109
+
108
110
  if(limit) console.log('!!!limit', limit);
109
111
 
110
112
  let pathDataObj = {
@@ -16,160 +16,25 @@ import { autoRound, roundTo } from './rounding.js';
16
16
  import { attLookup } from './svg-styles-to-attributes-const.js';
17
17
  import { qrDecomposeMatrix } from './transform_qr_decompose.js';
18
18
 
19
-
20
- export function pathElToShape(el, {
21
- convert_rects = false,
22
- convert_ellipses = false,
23
- convert_poly = false,
24
- convert_lines = false
25
- } = {}) {
26
-
27
- //console.log('pathElToShape', convert_rects, convert_ellipses, convert_lines );
28
-
29
- let pathData = parsePathDataNormalized(el.getAttribute('d'));
30
- let coms = Array.from(new Set(pathData.map(com => com.type))).join('')
31
-
32
- let hasArcs = (/[a]/gi).test(coms)
33
- let hasBeziers = (/[csqt]/gi).test(coms)
34
- let hasLines = (/[l]/gi).test(coms)
35
- let isPoly = !(/[acqts]/gi).test(coms)
36
- let closed = (/[z]/gi).test(coms)
37
- let shape = null;
38
- let type = null
39
-
40
- let attributes = getElementAtts(el)
41
- let attsNew = {}
42
- let decimals = 7;
43
-
44
- if (isPoly) {
45
-
46
- // is line
47
- if (pathData.length === 2 && convert_lines) {
48
- type = 'line'
49
- shape = document.createElementNS(svgNs, type)
50
- let [x1, y1, x2, y2] = [...pathData[0].values, ...pathData[1].values].map(val => roundTo(val, decimals))
51
- attsNew = { x1, y1, x2, y2 }
52
- }
53
- // polygon, polyline or rect
54
- else {
55
-
56
- let vertices = getPathDataVertices(pathData);
57
- let bb = getPolyBBox(vertices)
58
- let areaPoly = getPolygonArea(vertices, true)
59
- let areaRect = bb.width * bb.height;
60
- let areaDiff = Math.abs(1 - areaRect / areaPoly);
61
-
62
- // is rect
63
- if (convert_rects && areaDiff < 0.01) {
64
- type = 'rect'
65
- shape = document.createElementNS(svgNs, type)
66
- let { x, y, width, height } = bb
67
- attsNew = { x, y, width, height }
68
-
69
- }
70
- // polyline or polygon
71
- else if(convert_poly) {
72
- type = closed ? 'polygon' : 'polyline';
73
- shape = document.createElementNS(svgNs, type)
74
- let points = vertices.map(pt => { return [pt.x, pt.y] }).flat().map(val => roundTo(val, decimals)).join(' ')
75
- attsNew = { points }
76
- }
77
- }
78
- }
79
- // circles or ellipses
80
- else if (!hasLines && convert_ellipses) {
81
-
82
- // try to convert cubics to arcs
83
- if (!hasArcs && hasBeziers) {
84
- pathData = pathDataCubicsToArc(pathData, { areaThreshold: 2.5 })
85
- hasArcs = pathData.filter(com => com.type === 'A').length;
86
- }
87
-
88
-
89
- if (hasArcs) {
90
- let pathData2 = getPathDataVerbose(pathData, { addArcParams: true })
91
- let arcComs = pathData2.filter(com => com.type === 'A')
92
-
93
- let cxVals = new Set();
94
- let cyVals = new Set();
95
- let rxVals = new Set();
96
- let ryVals = new Set();
97
-
98
- if (arcComs.length > 1) {
99
- //console.log('!!!arcComs', arcComs);
100
- pathData2.forEach(com => {
101
- if (com.type === 'A') {
102
- //console.log('params', com, com.cx, com.cy, com.rx, com.ry);
103
- cxVals.add(roundTo(com.cx, decimals))
104
- cyVals.add(roundTo(com.cy, decimals))
105
- rxVals.add(roundTo(com.rx, decimals))
106
- ryVals.add(roundTo(com.ry, decimals))
107
- }
108
- })
109
- }
110
-
111
- cxVals = Array.from(cxVals)
112
- cyVals = Array.from(cyVals)
113
- rxVals = Array.from(rxVals)
114
- ryVals = Array.from(ryVals)
115
-
116
- if(cxVals.length===1 && cyVals.length===1 && rxVals.length===1 && ryVals.length===1){
117
- let [rx, ry, cx, cy] = [rxVals[0], ryVals[0], cxVals[0], cyVals[0]]
118
- type = rx===ry ? 'circle' : 'ellipse';
119
- shape = document.createElementNS(svgNs, type)
120
- attsNew = type==='circle' ? { r:rx, cx, cy } : {rx, ry, cx, cy}
121
- }
122
- }
123
- }
124
-
125
-
126
- // if el could be replaced
127
- if (shape) {
128
- let ignore = ['id', 'class']
129
-
130
- // set shape attributes
131
- for (let att in attsNew) {
132
- shape.setAttribute(att, attsNew[att])
133
- }
134
-
135
- // copy old attributes
136
- for (let att in attributes) {
137
- //console.log(attributes);
138
- if (attLookup.atts[att].includes(type) || ignore.includes(att) || att.startsWith('data-')) {
139
- shape.setAttribute(att, attributes[att])
140
- }
141
- }
142
-
143
- // replace
144
- el = shape;
145
- }
146
-
147
- return el;
148
-
149
- }
150
-
19
+ /**
20
+ * Convert shapes to paths
21
+ * converts also transforms
22
+ */
151
23
  export function shapeElToPath(el, { width = 0,
152
24
  height = 0,
153
- convert_rects = false,
154
- convert_ellipses = false,
155
- convert_poly = false,
156
- convert_lines = false,
157
- //matrix={a:1, b:0, c:0, d:1, e:0, f:0},
158
- matrix=null
25
+ convertShapes = [],
26
+ matrix = null
159
27
 
160
28
  } = {}) {
161
29
 
30
+
162
31
  let nodeName = el.nodeName.toLowerCase();
163
32
  //console.log('shapeElToPath', nodeName);
164
33
 
165
34
 
166
- if (
167
- nodeName === 'path' && !matrix ||
168
- nodeName === 'rect' && !convert_rects ||
169
- (nodeName === 'circle' || nodeName === 'ellipse') && !convert_ellipses ||
170
- (nodeName === 'polygon' || nodeName === 'polyline') && !convert_poly ||
171
- (nodeName === 'line') && !convert_lines
172
- ) return el;
35
+
36
+ if (!convertShapes.includes(nodeName)) return el;
37
+ //console.log(convertShapes);
173
38
 
174
39
 
175
40
  let pathData = getPathDataFromEl(el, { width, height });
@@ -178,8 +43,9 @@ export function shapeElToPath(el, { width = 0,
178
43
  let exclude = ['d', 'x', 'y', 'x1', 'y1', 'x2', 'y2', 'cx', 'cy', 'dx', 'dy', 'r', 'rx', 'ry', 'width', 'height', 'points'];
179
44
 
180
45
  // transform pathData
181
- if(matrix && Object.values(matrix).join('')!=='100100'){
46
+ if (matrix && Object.values(matrix).join('') !== '100100') {
182
47
  pathData = transformPathData(pathData, matrix)
48
+ //console.log('transformPathData', pathData);
183
49
  exclude.push('transform', 'transform-origin')
184
50
  }
185
51
 
@@ -203,15 +69,8 @@ export function shapeElToPath(el, { width = 0,
203
69
  return pathN
204
70
 
205
71
  }
206
- /*
207
- export function copyAttributes(newEl, oldEl){
208
-
209
- let attributes = [...oldEl.attributes].map(att => att.name);
210
72
 
211
73
 
212
- }
213
- */
214
-
215
74
 
216
75
  // retrieve pathdata from svg geometry elements
217
76
  export function getPathDataFromEl(el, {
@@ -233,7 +92,6 @@ export function getPathDataFromEl(el, {
233
92
 
234
93
  // convert relative and physical units to user-units
235
94
  let atts = svgElUnitsToPixel(el, { width, height })
236
- //console.log('atts', atts);
237
95
 
238
96
  switch (type) {
239
97
  case 'path':
@@ -244,39 +102,7 @@ export function getPathDataFromEl(el, {
244
102
  case 'rect':
245
103
  attNames = ['x', 'y', 'width', 'height', 'rx', 'ry'];
246
104
  ({ x=0, y=0, width=0, height=0, rx=0, ry=0 } = atts);
247
-
248
- if (!rx && !ry) {
249
- pathData = [
250
- { type: "M", values: [x, y] },
251
- { type: "L", values: [x + width, y] },
252
- { type: "L", values: [x + width, y + height] },
253
- { type: "L", values: [x, y + height] },
254
- { type: "Z", values: [] }
255
- ];
256
- } else {
257
-
258
- rx = rx ? rx : ry;
259
- ry = ry ? ry : rx;
260
-
261
- if (rx > width / 2) {
262
- rx = width / 2;
263
- }
264
- if (ry > height / 2) {
265
- ry = height / 2;
266
- }
267
- pathData = [
268
- { type: "M", values: [x + rx, y] },
269
- { type: "L", values: [x + width - rx, y] },
270
- { type: "A", values: [rx, ry, 0, 0, 1, x + width, y + ry] },
271
- { type: "L", values: [x + width, y + height - ry] },
272
- { type: "A", values: [rx, ry, 0, 0, 1, x + width - rx, y + height] },
273
- { type: "L", values: [x + rx, y + height] },
274
- { type: "A", values: [rx, ry, 0, 0, 1, x, y + height - ry] },
275
- { type: "L", values: [x, y + ry] },
276
- { type: "A", values: [rx, ry, 0, 0, 1, x + rx, y] },
277
- { type: "Z", values: [] }
278
- ];
279
- }
105
+ pathData = rectToPathData(x, y, width, height, rx, ry);
280
106
  break;
281
107
 
282
108
  case 'circle':
@@ -296,7 +122,6 @@ export function getPathDataFromEl(el, {
296
122
  ry = ry ? ry : r;
297
123
  }
298
124
 
299
-
300
125
  // simplified radii for circles
301
126
  let rxS = isCircle && r >= 1 ? 1 : rx;
302
127
  let ryS = isCircle && r >= 1 ? 1 : ry;
@@ -339,4 +164,179 @@ export function getPathDataFromEl(el, {
339
164
 
340
165
  return stringify ? stringifyPathData(pathData) : pathData;
341
166
 
342
- };
167
+ };
168
+
169
+
170
+ export function rectToPathData(x = 0, y = 0, width = 0, height = 0, rx = 0, ry = 0) {
171
+ let pathData = [];
172
+
173
+ if (!rx && !ry) {
174
+ pathData = [
175
+ { type: "M", values: [x, y] },
176
+ { type: "L", values: [x + width, y] },
177
+ { type: "L", values: [x + width, y + height] },
178
+ { type: "L", values: [x, y + height] },
179
+ { type: "Z", values: [] }
180
+ ];
181
+ } else {
182
+
183
+ rx = rx ? rx : ry;
184
+ ry = ry ? ry : rx;
185
+
186
+ if (rx > width / 2) {
187
+ rx = width / 2;
188
+ }
189
+ if (ry > height / 2) {
190
+ ry = height / 2;
191
+ }
192
+ pathData = [
193
+ { type: "M", values: [x + rx, y] },
194
+ { type: "L", values: [x + width - rx, y] },
195
+ { type: "A", values: [rx, ry, 0, 0, 1, x + width, y + ry] },
196
+ { type: "L", values: [x + width, y + height - ry] },
197
+ { type: "A", values: [rx, ry, 0, 0, 1, x + width - rx, y + height] },
198
+ { type: "L", values: [x + rx, y + height] },
199
+ { type: "A", values: [rx, ry, 0, 0, 1, x, y + height - ry] },
200
+ { type: "L", values: [x, y + ry] },
201
+ { type: "A", values: [rx, ry, 0, 0, 1, x + rx, y] },
202
+ { type: "Z", values: [] }
203
+ ];
204
+ }
205
+
206
+ return pathData
207
+ }
208
+
209
+
210
+
211
+
212
+
213
+ export function pathElToShape(el, {
214
+ convertShapes = [],
215
+ } = {}) {
216
+
217
+ //console.log('pathElToShape', convert_rects, convert_ellipses, convert_lines );
218
+
219
+ let pathData = parsePathDataNormalized(el.getAttribute('d'));
220
+ let coms = Array.from(new Set(pathData.map(com => com.type))).join('')
221
+
222
+ let hasArcs = (/[a]/gi).test(coms)
223
+ let hasBeziers = (/[csqt]/gi).test(coms)
224
+ let hasLines = (/[l]/gi).test(coms)
225
+ let isPoly = !(/[acqts]/gi).test(coms)
226
+ let closed = (/[z]/gi).test(coms)
227
+ let shape = null;
228
+ let type = null
229
+
230
+ let attributes = getElementAtts(el)
231
+ let attsNew = {}
232
+ let decimals = 7;
233
+
234
+
235
+ if (isPoly) {
236
+
237
+ //console.log('pathsToShapes', isPoly);
238
+
239
+ // is line
240
+ if (pathData.length === 2 && convertShapes.includes('line')) {
241
+ type = 'line'
242
+ shape = document.createElementNS(svgNs, type)
243
+ let [x1, y1, x2, y2] = [...pathData[0].values, ...pathData[1].values].map(val => roundTo(val, decimals))
244
+ attsNew = { x1, y1, x2, y2 }
245
+ }
246
+ // polygon, polyline or rect
247
+ else {
248
+
249
+ let vertices = getPathDataVertices(pathData);
250
+ let bb = getPolyBBox(vertices)
251
+ let areaPoly = getPolygonArea(vertices, true)
252
+ let areaRect = bb.width * bb.height;
253
+ let areaDiff = Math.abs(1 - areaRect / areaPoly);
254
+
255
+ // is rect
256
+ if (convertShapes.includes('rect') && areaDiff < 0.01) {
257
+ type = 'rect'
258
+ shape = document.createElementNS(svgNs, type)
259
+ let { x, y, width, height } = bb
260
+ attsNew = { x, y, width, height }
261
+
262
+ }
263
+ // polyline or polygon
264
+ else if (convertShapes.includes('polygon') || convertShapes.includes('polyline')) {
265
+ type = closed ? 'polygon' : 'polyline';
266
+ shape = document.createElementNS(svgNs, type)
267
+ let points = vertices.map(pt => { return [pt.x, pt.y] }).flat().map(val => roundTo(val, decimals)).join(' ')
268
+ attsNew = { points }
269
+ }
270
+ }
271
+ }
272
+ // circles or ellipses
273
+ else if (!hasLines && (convertShapes.includes('circle') || convertShapes.includes('ellipse'))) {
274
+
275
+ // try to convert cubics to arcs
276
+ if (!hasArcs && hasBeziers) {
277
+ pathData = pathDataCubicsToArc(pathData, { areaThreshold: 2.5 })
278
+ hasArcs = pathData.filter(com => com.type === 'A').length;
279
+ }
280
+
281
+
282
+ if (hasArcs) {
283
+ let pathData2 = getPathDataVerbose(pathData, { addArcParams: true })
284
+ let arcComs = pathData2.filter(com => com.type === 'A')
285
+
286
+ let cxVals = new Set();
287
+ let cyVals = new Set();
288
+ let rxVals = new Set();
289
+ let ryVals = new Set();
290
+
291
+ if (arcComs.length > 1) {
292
+ //console.log('!!!arcComs', arcComs);
293
+ pathData2.forEach(com => {
294
+ if (com.type === 'A') {
295
+ //console.log('params', com, com.cx, com.cy, com.rx, com.ry);
296
+ cxVals.add(roundTo(com.cx, decimals))
297
+ cyVals.add(roundTo(com.cy, decimals))
298
+ rxVals.add(roundTo(com.rx, decimals))
299
+ ryVals.add(roundTo(com.ry, decimals))
300
+ }
301
+ })
302
+ }
303
+
304
+ cxVals = Array.from(cxVals)
305
+ cyVals = Array.from(cyVals)
306
+ rxVals = Array.from(rxVals)
307
+ ryVals = Array.from(ryVals)
308
+
309
+ if (cxVals.length === 1 && cyVals.length === 1 && rxVals.length === 1 && ryVals.length === 1) {
310
+ let [rx, ry, cx, cy] = [rxVals[0], ryVals[0], cxVals[0], cyVals[0]]
311
+ type = rx === ry ? 'circle' : 'ellipse';
312
+ shape = document.createElementNS(svgNs, type)
313
+ attsNew = type === 'circle' ? { r: rx, cx, cy } : { rx, ry, cx, cy }
314
+ }
315
+ }
316
+ }
317
+
318
+
319
+ // if el could be replaced
320
+ if (shape) {
321
+ let ignore = ['id', 'class']
322
+
323
+ // set shape attributes
324
+ for (let att in attsNew) {
325
+ shape.setAttribute(att, attsNew[att])
326
+ }
327
+
328
+ // copy old attributes
329
+ for (let att in attributes) {
330
+ //console.log(attributes);
331
+ if (attLookup.atts[att].includes(type) || ignore.includes(att) || att.startsWith('data-')) {
332
+ shape.setAttribute(att, attributes[att])
333
+ }
334
+ }
335
+ // replace
336
+ el = shape;
337
+ }
338
+
339
+ //console.log(el);
340
+ return el;
341
+
342
+ }
@@ -0,0 +1,168 @@
1
+ import { svgNs } from "../constants";
2
+ import { getPathDataVertices, isPointInPolygon } from "./geometry";
3
+ import { checkBBoxIntersections, getPolyBBox } from "./geometry_bbox";
4
+ import { convertPathData } from "./pathData_convert";
5
+ import { pathDataToD } from "./pathData_stringify";
6
+ import { roundTo } from "./rounding";
7
+ import { renderPoint } from "./visualize";
8
+
9
+ export function splitCompundGroups(pathDataPlusArr = [], {
10
+ toRelative = true,
11
+ toShorthands = true,
12
+ minifyD = 0,
13
+ decimals = 3,
14
+ addDimensions = false
15
+ } = {}) {
16
+
17
+ //console.log('???pathDataPlusArr', pathDataPlusArr);
18
+ let pathDataSplit = [];
19
+ pathDataPlusArr = JSON.parse(JSON.stringify(pathDataPlusArr))
20
+ let len = pathDataPlusArr.length;
21
+
22
+ //let bb0 =
23
+ let xArr = [];
24
+ let yArr = []
25
+
26
+ // refine bbox and add cpt polygon
27
+ for (let i = 0; i < len; i++) {
28
+ let sub = pathDataPlusArr[i]
29
+ let { pathData, bb } = sub
30
+
31
+ // console.log(bb);
32
+ // include control points for better overlapping approximation
33
+ //let poly = getPathDataVertices(pathData, true);
34
+ //let bb2 = getPolyBBox(poly);
35
+
36
+ if (bb.width && bb.height) {
37
+ } else {
38
+ let poly = getPathDataVertices(pathData, true);
39
+ bb = getPolyBBox(poly);
40
+ pathDataPlusArr[i].bb = bb;
41
+ //console.log(bb, sub);
42
+ }
43
+
44
+ xArr.push(bb.left, bb.right)
45
+ yArr.push(bb.top, bb.bottom)
46
+ sub.includes = []
47
+ }
48
+
49
+
50
+ /**
51
+ * check overlapping
52
+ * sub paths
53
+ */
54
+ for (let i = 0, l = pathDataPlusArr.length; i < l; i++) {
55
+ let sub1 = pathDataPlusArr[i];
56
+ let { bb, poly } = sub1;
57
+
58
+ for (let j = 0; j < l; j++) {
59
+
60
+ let sub1 = pathDataPlusArr[j];
61
+ if (i === j) continue;
62
+
63
+ //let [bb1, poly1] = [sub1.bb, sub1.poly];
64
+ let bb1 = sub1.bb;
65
+ //let poly1 = sub1.poly
66
+
67
+ // test sample on-path points
68
+ let ptM = { x: bb1.x + bb1.width * 0.5, y: bb1.y + bb1.height * 0.5 };
69
+
70
+
71
+ let inPoly = false;
72
+ if (ptM.x >= bb.x && ptM.y >= bb.y && ptM.x <= bb.right && ptM.y <= bb.bottom) {
73
+ inPoly = true;
74
+ pathDataPlusArr[i].includes.push(j);
75
+ }
76
+
77
+ }
78
+ }
79
+
80
+
81
+ /**
82
+ * combine overlapping
83
+ * compound paths
84
+ */
85
+ for (let i = 0, l = pathDataPlusArr.length; i < l; i++) {
86
+ let sub = pathDataPlusArr[i];
87
+ let { includes } = sub;
88
+
89
+ includes.forEach(s => {
90
+ let pathData = pathDataPlusArr[s].pathData;
91
+ if (pathData.length) {
92
+ pathDataPlusArr[i].pathData.push(...pathData);
93
+ pathDataPlusArr[s].pathData = [];
94
+ }
95
+ });
96
+ }
97
+
98
+ // remove empty els due to grouping
99
+ pathDataPlusArr = pathDataPlusArr.filter(sub => sub.pathData.length);
100
+
101
+ // try to find row left to right order
102
+ //pathDataPlusArr = pathDataPlusArr.sort((a, b) => ((a.bb.x + a.bb.y * 3) - (b.bb.x + b.bb.y * 3)))
103
+ //pathDataPlusArr = pathDataPlusArr.sort((a, b) => ((a.bb.x + a.bb.y * 2) - (b.bb.x + b.bb.y * 2)))
104
+ pathDataPlusArr = pathDataPlusArr.sort((a, b) => ((a.bb.x ) - (b.bb.x)))
105
+
106
+ // create SVG
107
+ let x = Math.min(...xArr);
108
+ let y = Math.min(...yArr);
109
+ let right = Math.max(...xArr);
110
+ let bottom = Math.max(...yArr);
111
+ let width = right - x;
112
+ let height = bottom - y;
113
+
114
+ [x, y, width, height] = [x, y, width, height].map(val => roundTo(val, decimals));
115
+
116
+ let dimensionAtts = addDimensions ? `width="${width}" height="${height}"` : ''
117
+ let svgSplit = `<svg ${dimensionAtts} viewBox="${x} ${y} ${width} ${height}" xmlns="${svgNs}">`;
118
+
119
+ pathDataPlusArr.forEach(sub => {
120
+ let { pathData } = sub;
121
+
122
+ pathData = convertPathData(pathData, { toRelative, toShorthands, decimals });
123
+ let d = pathDataToD(pathData, minifyD);
124
+ svgSplit += `<path d="${d}"/>`;
125
+
126
+ });
127
+
128
+ svgSplit += '</svg>';
129
+
130
+ let splitObj = { pathData: pathDataPlusArr, svg: svgSplit }
131
+ //console.log('splitObj', splitObj);
132
+ return splitObj
133
+
134
+ }
135
+
136
+
137
+ /*
138
+ function checkBBoxIntersections2(bb, bb1) {
139
+ let [x, y, width, height, right, bottom] = [
140
+ bb.x,
141
+ bb.y,
142
+ bb.width,
143
+ bb.height,
144
+ bb.x + bb.width,
145
+ bb.y + bb.height
146
+ ];
147
+ let [x1, y1, width1, height1, right1, bottom1] = [
148
+ bb1.x,
149
+ bb1.y,
150
+ bb1.width,
151
+ bb1.height,
152
+ bb1.x + bb1.width,
153
+ bb1.y + bb1.height
154
+ ];
155
+ let intersects = false;
156
+ //console.log('bb', bb, bb1);
157
+ //console.log();
158
+
159
+ if (x < x1 && right > right1 && y < y1 && bottom > bottom1) {
160
+ intersects = true;
161
+ }
162
+
163
+ console.log('???', intersects, 'dims', width, height, '2', width1, height1);
164
+
165
+
166
+ return intersects;
167
+ }
168
+ */