svg-path-simplify 0.1.3 → 0.2.2

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/README.md +10 -0
  2. package/dist/svg-path-simplify.esm.js +3905 -1533
  3. package/dist/svg-path-simplify.esm.min.js +13 -1
  4. package/dist/svg-path-simplify.js +3923 -1551
  5. package/dist/svg-path-simplify.min.js +13 -1
  6. package/dist/svg-path-simplify.min.js.gz +0 -0
  7. package/index.html +61 -31
  8. package/package.json +3 -5
  9. package/src/constants.js +3 -0
  10. package/src/index-node.js +0 -1
  11. package/src/index.js +26 -0
  12. package/src/pathData_simplify_cubic.js +74 -31
  13. package/src/pathData_simplify_cubicsToArcs.js +566 -0
  14. package/src/pathData_simplify_harmonize_cpts.js +170 -0
  15. package/src/pathData_simplify_revertToquadratics.js +21 -0
  16. package/src/pathSimplify-main.js +253 -86
  17. package/src/poly-fit-curve-schneider.js +570 -0
  18. package/src/simplify_poly_RDP.js +146 -0
  19. package/src/simplify_poly_radial_distance.js +100 -0
  20. package/src/svg_getViewbox.js +1 -1
  21. package/src/svgii/geometry.js +389 -63
  22. package/src/svgii/geometry_area.js +2 -1
  23. package/src/svgii/pathData_analyze.js +259 -212
  24. package/src/svgii/pathData_convert.js +91 -663
  25. package/src/svgii/pathData_fromPoly.js +12 -0
  26. package/src/svgii/pathData_parse.js +90 -89
  27. package/src/svgii/pathData_parse_els.js +3 -0
  28. package/src/svgii/pathData_parse_fontello.js +449 -0
  29. package/src/svgii/pathData_remove_collinear.js +44 -37
  30. package/src/svgii/pathData_reorder.js +2 -1
  31. package/src/svgii/pathData_simplify_redraw.js +343 -0
  32. package/src/svgii/pathData_simplify_refineCorners.js +18 -9
  33. package/src/svgii/pathData_simplify_refineExtremes.js +19 -78
  34. package/src/svgii/pathData_split.js +42 -45
  35. package/src/svgii/pathData_toPolygon.js +130 -4
  36. package/src/svgii/poly_analyze.js +470 -14
  37. package/src/svgii/poly_to_pathdata.js +224 -19
  38. package/src/svgii/rounding.js +55 -112
  39. package/src/svgii/svg_cleanup.js +13 -1
  40. package/src/svgii/visualize.js +8 -3
  41. package/{debug.cjs → tests/debug.cjs} +3 -0
  42. /package/{test.js → tests/test.js} +0 -0
  43. /package/{testSVG.js → tests/testSVG.js} +0 -0
@@ -1,315 +1,362 @@
1
1
  import { splitSubpaths } from './pathData_split.js';
2
- import { getAngle, bezierhasExtreme, getPathDataVertices, svgArcToCenterParam, getSquareDistance, getDistAv } from "./geometry.js";
2
+ import { getAngle, bezierhasExtreme, getPathDataVertices, svgArcToCenterParam, getSquareDistance, getDistManhattan, isMultipleOf45, pointAtT, getTatAngles } from "./geometry.js";
3
3
  import { getPolygonArea, getPathArea } from './geometry_area.js';
4
4
  import { getPolyBBox } from './geometry_bbox.js';
5
5
  import { renderPoint, renderPath } from "./visualize.js";
6
6
  import { commandIsFlat } from './geometry_flatness.js';
7
-
7
+ import { roundPathData } from './rounding.js';
8
8
 
9
9
 
10
10
  /**
11
- * analyze path data for
12
- * decimal detection
13
- * sub paths
14
- * directions
15
- * crucial geometry properties
11
+ * create pathdata super set
12
+ * including geometrical properties such as:
13
+ * segment introduces x/y extreme
14
+ * corner
15
+ * inflection/direction change
16
16
  */
17
17
 
18
+ export function analyzePathData(pathData = [], {
19
+ detectExtremes = true,
20
+ detectCorners = true,
21
+ detectDirection = true,
22
+ detectSemiExtremes = false,
23
+ debug = false,
24
+ addSquareLength = true,
25
+ addArea = true,
18
26
 
19
- export function addDimensionData(pathData) {
20
-
21
- let M = { x: pathData[0].values[0], y: pathData[0].values[1] };
22
- let p0 = { x: pathData[0].values[0], y: pathData[0].values[1] };
23
- let p;
24
-
25
- pathData[0].dimA = 0;
26
- let len = pathData.length
27
-
28
- for (let c = 2; len && c <= len; c++) {
29
-
30
- let com = pathData[c - 1];
31
- let { type, values } = com;
32
- let valsL = values.slice(-2);
33
-
34
- p = valsL.length ? { x: valsL[0], y: valsL[1] } : M;
27
+ } = {}) {
35
28
 
36
- // update M for Z starting points
37
- if (type === 'M') {
38
- M = p;
39
- }
40
- else if (type.toLowerCase() === 'z') {
41
- p = M;
42
- }
43
-
44
- let dimA = getDistAv(p0, p);
45
- com.dimA = dimA;
46
- com.p0 = p0
47
- com.p = p
48
-
49
-
50
- if(type==='C' || type==='Q') com.cp1 = {x:values[0], y:values[1]}
51
- if(type==='C' ) com.cp2 = {x:values[2], y:values[3]}
52
-
53
- p0=p
54
- }
55
-
56
-
57
- console.log('!!!pathData', pathData);
58
- return pathData
59
- }
60
-
61
-
62
- export function analyzePathData(pathData = []) {
29
+ // get verbose control point data
30
+ pathData = getPathDataVerbose(pathData, { addSquareLength, addArea });
31
+ //pathData = roundPathData(pathData, 3)
63
32
 
33
+ // new pathdata adding properties
64
34
  let pathDataPlus = [];
65
35
 
36
+ //console.log('pathData', pathData);
37
+ //return pathData
38
+
66
39
  let pathPoly = getPathDataVertices(pathData);
67
40
  let bb = getPolyBBox(pathPoly)
68
41
  let { left, right, top, bottom, width, height } = bb;
69
42
 
70
- // initial starting point coordinates
71
- let M0 = { x: pathData[0].values[0], y: pathData[0].values[1] };
72
- let M = { x: pathData[0].values[0], y: pathData[0].values[1] };
73
- let p0 = { x: pathData[0].values[0], y: pathData[0].values[1] };
74
- let p;
75
43
 
76
44
  // init starting point data
77
- pathData[0].idx = 0;
78
- pathData[0].p0 = M;
79
- pathData[0].p = M;
80
- pathData[0].lineto = false;
81
45
  pathData[0].corner = false;
82
46
  pathData[0].extreme = false;
47
+ pathData[0].semiExtreme = false;
83
48
  pathData[0].directionChange = false;
84
49
  pathData[0].closePath = false;
85
- pathData[0].dimA = 0;
50
+ //pathData[0].dimA = 0;
86
51
 
87
52
 
88
53
  // add first M command
89
54
  let pathDataProps = [pathData[0]];
90
- let area0 = 0;
91
55
  let len = pathData.length;
92
56
 
57
+ // threshold for corner angles: 10 deg
58
+ let thresholdCorner = Math.PI * 2 / 360 * 10
59
+
60
+ // define angle threshold for semi extremes
61
+ let thresholdAngle = detectSemiExtremes ? 0.01 : 0.05
62
+
63
+
93
64
  for (let c = 2; len && c <= len; c++) {
94
65
 
95
66
  let com = pathData[c - 1];
96
- let { type, values } = com;
97
- let valsL = values.slice(-2);
67
+ let { type, values, p0, p, cp1 = null, cp2 = null, squareDist = 0, cptArea = 0, dimA = 0 } = com;
68
+
69
+ //next command
70
+ let comN = pathData[c] || null;
98
71
 
99
- /**
100
- * get command points for
101
- * flatness checks:
102
- * this way we can skip certain tests
103
- */
104
- let commandPts = [p0];
105
- let isFlat = false;
106
72
 
107
73
  // init properties
108
- com.idx = c - 1;
109
- com.lineto = false;
110
74
  com.corner = false;
111
75
  com.extreme = false;
76
+ com.semiExtreme = false;
112
77
  com.directionChange = false;
113
78
  com.closePath = false;
114
- com.dimA = 0;
115
- //com.flat = false;
79
+
80
+ // get command points
81
+ let commandPts = (type === 'C' || type === 'Q') ?
82
+ (type === 'C' ? [p0, cp1, cp2, p] : [p0, cp1, p]) :
83
+ ([p0, p]);
84
+
85
+
86
+ // check flatness of command
87
+ let toleranceFlat = 0.01;
88
+ let thresholdLength = dimA * 0.1
89
+ let areaThresh = squareDist * toleranceFlat;
90
+ let isFlat = Math.abs(cptArea) < areaThresh;
91
+
92
+
93
+ // bezier types
94
+ let isBezier = type === 'Q' || type === 'C';
95
+ let isBezierN = comN && (comN.type === 'Q' || comN.type === 'C');
116
96
 
117
97
 
118
98
  /**
119
- * define angle threshold for
120
- * corner detection
99
+ * detect extremes
100
+ * local or absolute
121
101
  */
122
- let angleThreshold = 0.05
123
- p = valsL.length ? { x: valsL[0], y: valsL[1] } : M;
102
+ let hasExtremes = false;
124
103
 
104
+ //!isFlat &&
105
+ if (isBezier) {
125
106
 
126
- // update M for Z starting points
127
- if (type === 'M') {
128
- M = p;
129
- p0 = p
130
- }
131
- else if (type.toLowerCase() === 'z') {
132
- p = M;
107
+
108
+ let dx = type === 'C' ? Math.abs(com.cp2.x - com.p.x) : Math.abs(com.cp1.x - com.p.x)
109
+ let dy = type === 'C' ? Math.abs(com.cp2.y - com.p.y) : Math.abs(com.cp1.y - com.p.y)
110
+
111
+ let horizontal = dy === 0 && dx > 0
112
+ let vertical = dx === 0 && dy > 0
113
+
114
+ if (horizontal || vertical) hasExtremes = true;
115
+
116
+ // is extreme relative to bounding box
117
+ if ((p.x === left || p.y === top || p.x === right || p.y === bottom)) {
118
+ hasExtremes = true;
119
+ }
120
+
121
+ // interpret segment as extreme if it has an implicit extreme
122
+ if (!hasExtremes) {
123
+ let couldHaveExtremes = bezierhasExtreme(null, commandPts)
124
+ if (couldHaveExtremes) {
125
+ let tArr = getTatAngles(commandPts)
126
+ if (tArr.length && (tArr[0] > 0.15)) {
127
+ hasExtremes = true;
128
+ }
129
+ }
130
+ }
133
131
  }
134
132
 
135
- // add on-path points
136
- com.p0 = p0;
137
- com.p = p;
133
+ if (hasExtremes) com.extreme = true
138
134
 
139
- let cp1, cp2, cp1N, cp2N, pN, typeN, area1;
135
+ // Corners and semi extremes
136
+ if (isBezier && isBezierN) {
140
137
 
141
- //let dimA = (width + height) / 2;
142
- let dimA = getDistAv(p0, p);
143
- com.dimA = dimA;
144
- //com.a = dimA;
138
+ // semi extremes
139
+ if (detectSemiExtremes && !com.extreme) {
145
140
 
141
+ let dx1 = Math.abs(p.x - cp2.x)
142
+ let dy1 = Math.abs(p.y - cp2.y)
143
+ let hasSemiExtreme = false;
146
144
 
145
+ // exclude extremes or small deltas
146
+ if (dx1 && dy1 && dx1 > thresholdLength || dy1 > thresholdLength) {
147
+ let ang1 = getAngle(cp2, p)
148
+ let ang2 = getAngle(p, comN.cp1)
147
149
 
148
- /**
149
- * explicit and implicit linetos
150
- * - introduced by Z
151
- */
152
- if (type === 'L') com.lineto = true;
150
+ let ang3 = Math.abs(ang1 + ang2) / 2
151
+ hasSemiExtreme = isMultipleOf45(ang3)
152
+ }
153
153
 
154
- if (type === 'Z') {
155
- com.closePath = true;
156
- // if Z introduces an implicit lineto with a length
157
- if (M.x !== M0.x && M.y !== M0.y) {
158
- com.lineto = true;
154
+ if (hasSemiExtreme) {
155
+ com.semiExtreme = true;
156
+ }
159
157
  }
160
- }
161
158
 
162
- // if bezier
163
- if (type === 'Q' || type === 'C') {
164
- cp1 = { x: values[0], y: values[1] }
165
- cp2 = type === 'C' ? { x: values[2], y: values[3] } : null;
166
- com.cp1 = cp1;
167
- if (cp2) com.cp2 = cp2;
168
- }
169
159
 
160
+ /**
161
+ * Detect direction change points
162
+ * this will prevent distortions when simplifying
163
+ * e.g in the "spine" of an "S" glyph
164
+ */
165
+ let signChange = (com.cptArea < 0 && comN.cptArea > 0) || (com.cptArea > 0 && comN.cptArea < 0) ? true : false;
170
166
 
171
- /**
172
- * check command flatness
173
- * we leave it to the bezier simplifier
174
- * to convert flat beziers to linetos
175
- * otherwise we may strip rather flat starting segments
176
- * preventing a better simplification
177
- */
167
+ if (signChange) com.directionChange = true;
178
168
 
179
- if (values.length > 2) {
180
- if (type === 'Q' || type === 'C') commandPts.push(cp1);
181
- if (type === 'C') commandPts.push(cp2);
182
- commandPts.push(p);
169
+ // check corners
170
+ if (!com.extreme) {
183
171
 
184
- /*
185
- //let commandFlatness = commandIsFlat(commandPts);
186
- let commandFlatness = commandIsFlat(commandPts);
187
- isFlat = commandFlatness.flat;
188
- com.flat = isFlat;
172
+ let cp_0 = cp2 ? cp2 : cp1
173
+ let cp_1 = comN.cp1
189
174
 
190
- if (isFlat) {
191
- com.extreme = false;
192
- //renderPoint(markers, p, 'red', '1%', '0.5')
193
- }
194
- */
175
+ let areaCpt = getPolygonArea([cp_0, p, cp_1], false)
176
+ let threshArea = getSquareDistance(cp_0, cp_1) * 0.01
177
+ let isFlat = Math.abs(areaCpt) < threshArea;
178
+
179
+ let signChange2 = (areaCpt < 0 && com.cptArea > 0) || (areaCpt > 0 && com.cptArea < 0) ? true : false;
195
180
 
181
+ let isCorner=!isFlat && signChange2;
182
+ if (isCorner) com.corner = true;
183
+ }
196
184
  }
197
185
 
198
- /**
199
- * is extreme relative to bounding box
200
- * in case elements are rotated we can't rely on 90degree angles
201
- * so we interpret maximum x/y on-path points as well as extremes
202
- * but we ignore linetos to allow chunk compilation
203
- */
204
- if (!isFlat && type !== 'L' && (p.x === left || p.y === top || p.x === right || p.y === bottom)) {
205
- com.extreme = true;
186
+
187
+ //debug = true;
188
+ if (debug) {
189
+ //if (com.semiExtreme) renderPoint(markers, com.p, 'blue', '2%', '0.5')
190
+ if (com.directionChange) renderPoint(markers, com.p, 'orange', '1.5%', '0.5')
191
+ if (com.corner) renderPoint(markers, com.p, 'magenta', '1.5%', '0.5')
192
+ if (com.extreme) renderPoint(markers, com.p, 'cyan', '1%', '0.5')
193
+
206
194
  }
207
195
 
196
+ pathDataProps.push(com)
208
197
 
209
- //next command
210
- let comN = pathData[c] ? pathData[c] : null;
211
- let comNValsL = comN ? comN.values.slice(-2) : null;
212
- typeN = comN ? comN.type : null;
198
+ }
213
199
 
200
+ //pathDataProps.push(comLast)
214
201
 
215
- // get bezier control points
216
- if (comN && (comN.type === 'Q' || comN.type === 'C')) {
217
- pN = comN ? { x: comNValsL[0], y: comNValsL[1] } : null;
218
202
 
219
- cp1N = { x: comN.values[0], y: comN.values[1] }
220
- cp2N = comN.type === 'C' ? { x: comN.values[2], y: comN.values[3] } : null;
221
- }
203
+ let dimA = (width + height) / 2
204
+ //pathDataPlus.push({ pathData: pathDataProps, bb: bb, dimA: dimA })
205
+ pathDataPlus = { pathData: pathDataProps, bb: bb, dimA: dimA }
222
206
 
207
+ //console.log('pathDataPlus', pathDataPlus);
208
+ return pathDataPlus
223
209
 
224
- /**
225
- * Detect direction change points
226
- * this will prevent distortions when simplifying
227
- * e.g in the "spine" of an "S" glyph
228
- */
229
- area1 = getPolygonArea(commandPts)
230
- let signChange = (area0 < 0 && area1 > 0) || (area0 > 0 && area1 < 0) ? true : false;
231
- // update area
232
- area0 = area1
233
-
234
- if (signChange) {
235
- //renderPoint(svg1, p0, 'orange', '1%', '0.75')
236
- com.directionChange = true;
210
+ }
211
+
212
+
213
+
214
+
215
+ export function addDimensionData(pathData) {
216
+
217
+ let M = { x: pathData[0].values[0], y: pathData[0].values[1] };
218
+ let p0 = { x: pathData[0].values[0], y: pathData[0].values[1] };
219
+ let p;
220
+
221
+ pathData[0].dimA = 0;
222
+ let len = pathData.length
223
+
224
+ for (let c = 2; len && c <= len; c++) {
225
+
226
+ let com = pathData[c - 1];
227
+ let { type, values } = com;
228
+ let valsL = values.slice(-2);
229
+
230
+ p = valsL.length ? { x: valsL[0], y: valsL[1] } : M;
231
+
232
+ // update M for Z starting points
233
+ if (type === 'M') {
234
+ M = p;
235
+ }
236
+ else if (type.toLowerCase() === 'z') {
237
+ p = M;
237
238
  }
238
239
 
240
+ let dimA = getDistManhattan(p0, p);
241
+ com.dimA = dimA;
242
+ com.p0 = p0
243
+ com.p = p
239
244
 
240
- /**
241
- * check extremes or corners
242
- * for adjacent curves by
243
- * control point angles
244
- */
245
- if ((type === 'Q' || type === 'C')) {
246
245
 
247
- if ((type === 'Q' && typeN === 'Q') || (type === 'C' && typeN === 'C')) {
246
+ if (type === 'C' || type === 'Q') com.cp1 = { x: values[0], y: values[1] }
247
+ if (type === 'C') com.cp2 = { x: values[2], y: values[3] }
248
248
 
249
- // check extremes
250
- let cpts = commandPts.slice(1);
249
+ p0 = p
250
+ }
251
251
 
252
- let w = pN ? Math.abs(pN.x - p0.x) : 0
253
- let h = pN ? Math.abs(pN.y - p0.y) : 0
254
- let thresh = (w + h) / 2 * 0.1;
255
- let pts1 = type === 'C' ? [p, cp1N, cp2N, pN] : [p, cp1N, pN];
256
252
 
257
- //let flatness2 = commandIsFlat(pts1, thresh)
258
- //let isFlat2 = flatness2.flat;
253
+ //console.log('!!!pathData', pathData);
254
+ return pathData
255
+ }
259
256
 
260
- /**
261
- * if current and next cubic are flat
262
- * we don't flag them as extremes to allow simplification
263
- */
264
- //let hasExtremes = (isFlat && isFlat2) ? false : (!com.extreme ? bezierhasExtreme(p0, cpts, angleThreshold) : true);
265
257
 
266
- let hasExtremes = (isFlat) ? false : (!com.extreme ? bezierhasExtreme(p0, cpts, angleThreshold) : true);
267
258
 
268
259
 
269
- //let bezierExtreme = bezierhasExtreme(p0, cpts, angleThreshold);
260
+ /**
261
+ * create pathdata super set
262
+ * including geometrical properties such as:
263
+ * start and end points
264
+ * segment square distances and areas
265
+ * elliptic arc parameters
266
+ */
267
+ export function getPathDataVerbose(pathData, {
268
+ addSquareLength = true,
269
+ addArea = false,
270
+ addArcParams = false,
271
+ addAverageDim = true
272
+ } = {}) {
270
273
 
271
- //console.log(isFlat, isFlat2, cpts, hasExtremes, 'com.extreme', com.extreme, 'commandPts', commandPts);
274
+ // initial starting point coordinates
275
+ let com0 = pathData[0];
276
+ let M = { x: com0.values[0], y: com0.values[1] };
277
+ let p0 = M;
278
+ let p = M;
272
279
 
273
- if (hasExtremes) {
274
- com.extreme = true
275
- }
280
+ com0.p0 = p0;
281
+ com0.p = p;
282
+ com0.idx = 0
283
+ com0.dimA = 0
276
284
 
277
- // check corners
278
- else {
279
285
 
280
- let cpts1 = cp2 ? [cp2, p] : [cp1, p];
281
- let cpts2 = cp2 ? [p, cp1N] : [p, cp1N];
286
+ let len = pathData.length;
287
+ let pathDataVerbose = [com0];
282
288
 
283
- let angCom1 = getAngle(...cpts1, true)
284
- let angCom2 = getAngle(...cpts2, true)
285
- let angDiff = Math.abs(angCom1 - angCom2) * 180 / Math.PI
289
+ for (let i = 1; i < len; i++) {
290
+ let com = pathData[i];
291
+ let { type, values } = com;
292
+ let valuesLen = values.length;
286
293
 
294
+ p = valuesLen ? { x: values[valuesLen - 2], y: values[valuesLen - 1] } : M;
295
+ let cp1, cp2;
287
296
 
288
- let cpDist1 = getSquareDistance(...cpts1)
289
- let cpDist2 = getSquareDistance(...cpts2)
297
+ // add on-path points
298
+ com.p0 = p0;
299
+ com.p = p;
300
+ com.dimA = getDistManhattan(p0, p)
290
301
 
291
- let cornerThreshold = 10
292
- let isCorner = angDiff > cornerThreshold && cpDist1 && cpDist2
302
+ // update M for Z starting points
303
+ if (type === 'M') {
304
+ M = p;
305
+ }
293
306
 
294
- if (isCorner) {
295
- com.corner = true;
296
- }
297
- }
307
+ // add bezier control point properties
308
+ if (type === 'Q' || type === 'C') {
309
+ cp1 = { x: values[0], y: values[1] }
310
+ cp2 = type === 'C' ? { x: values[2], y: values[3] } : null;
311
+ com.cp1 = cp1;
312
+ if (cp2) {
313
+ com.cp2 = cp2;
298
314
  }
299
315
  }
300
316
 
317
+ else if (type === 'A') {
318
+ let { rx, ry, cx, cy, startAngle, endAngle, deltaAngle } = svgArcToCenterParam(p0.x, p0.y, ...values)
319
+ com.cx = cx
320
+ com.cy = cy
321
+ com.rx = rx
322
+ com.ry = ry
323
+ com.xAxisRotation = values[2] / 180 * Math.PI
324
+ com.largeArc = values[3]
325
+ com.sweep = values[4]
326
+ com.startAngle = startAngle
327
+ com.endAngle = endAngle
328
+ com.deltaAngle = deltaAngle
329
+ }
301
330
 
302
- pathDataProps.push(com)
303
- p0 = p;
331
+ /**
332
+ * explicit and implicit linetos
333
+ * - introduced by Z
334
+ */
335
+ if (type === 'Z') {
336
+ // if Z introduces an implicit lineto with a length
337
+ if (M.x !== p.x && M.y !== p.y) {
338
+ com.closePath = true;
339
+ }
340
+ }
304
341
 
305
- }
342
+ if (addSquareLength) {
343
+ com.squareDist = getSquareDistance(p0, p)
344
+ }
306
345
 
346
+ if (addArea) {
347
+ let cptArea = 0;
348
+ if (type === 'C') cptArea = getPolygonArea([p0, cp1, cp2, p], false)
349
+ if (type === 'Q') cptArea = getPolygonArea([p0, cp1, p], false)
350
+ com.cptArea = cptArea;
351
+ }
307
352
 
308
- let dimA = (width + height) / 2
309
- //pathDataPlus.push({ pathData: pathDataProps, bb: bb, dimA: dimA })
310
- pathDataPlus = { pathData: pathDataProps, bb: bb, dimA: dimA }
353
+ com.idx = i;
311
354
 
312
- //console.log('pathDataPlus', pathDataPlus);
313
- return pathDataPlus
355
+ // update previous point
356
+ p0 = p;
357
+ pathDataVerbose.push(com)
358
+ }
314
359
 
360
+ //console.log('pathDataVerbose', pathDataVerbose);
361
+ return pathDataVerbose;
315
362
  }