svg-path-simplify 0.0.7 → 0.0.9

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 (38) hide show
  1. package/README.md +25 -5
  2. package/dist/svg-path-simplify.esm.js +1250 -562
  3. package/dist/svg-path-simplify.esm.min.js +1 -1
  4. package/dist/svg-path-simplify.js +4756 -4068
  5. package/dist/svg-path-simplify.min.js +1 -1
  6. package/dist/svg-path-simplify.node.js +1250 -562
  7. package/dist/svg-path-simplify.node.min.js +1 -1
  8. package/index.html +89 -29
  9. package/package.json +5 -3
  10. package/src/detect_input.js +17 -10
  11. package/src/dom-polyfill.js +29 -0
  12. package/src/dom-polyfill_back.js +22 -0
  13. package/src/index.js +10 -1
  14. package/src/pathData_simplify_cubic.js +114 -143
  15. package/src/pathData_simplify_cubic_extrapolate.js +64 -35
  16. package/src/pathSimplify-main.js +113 -165
  17. package/src/svgii/geometry.js +8 -155
  18. package/src/svgii/geometry_flatness.js +94 -0
  19. package/src/svgii/pathData_analyze.js +15 -596
  20. package/src/svgii/pathData_convert.js +26 -17
  21. package/src/svgii/pathData_interpolate.js +65 -0
  22. package/src/svgii/pathData_parse.js +25 -9
  23. package/src/svgii/pathData_parse_els.js +245 -0
  24. package/src/svgii/pathData_remove_collinear.js +33 -28
  25. package/src/svgii/pathData_remove_orphaned.js +21 -0
  26. package/src/svgii/pathData_remove_zerolength.js +17 -3
  27. package/src/svgii/pathData_reorder.js +9 -3
  28. package/src/svgii/pathData_simplify_refineCorners.js +160 -0
  29. package/src/svgii/pathData_simplify_refineExtremes.js +208 -0
  30. package/src/svgii/pathData_split.js +43 -15
  31. package/src/svgii/pathData_stringify.js +3 -12
  32. package/src/svgii/rounding.js +35 -27
  33. package/src/svgii/svg_cleanup.js +4 -1
  34. package/testSVG.js +39 -0
  35. package/src/pathData_simplify_cubic_arr.js +0 -50
  36. package/src/svgii/simplify.js +0 -248
  37. package/src/svgii/simplify_bezier.js +0 -470
  38. package/src/svgii/simplify_linetos.js +0 -93
@@ -32,7 +32,7 @@ export function revertCubicQuadratic(p0 = {}, cp1 = {}, cp2 = {}, p = {}) {
32
32
  let values = [cp1.x, cp1.y, cp2.x, cp2.y, p.x, p.y];
33
33
  let comN = {type, values}
34
34
 
35
- if (dist1 < threshold) {
35
+ if (dist1 && threshold && dist1 < threshold) {
36
36
  cp1_Q = checkLineIntersection(p0, cp1, p, cp2, false);
37
37
  if (cp1_Q) {
38
38
  //renderPoint(markers, cp1_Q )
@@ -55,13 +55,19 @@ export function convertPathData(pathData, {
55
55
  decimals = 3
56
56
  } = {}) {
57
57
 
58
+
59
+ //console.log(toShorthands, toRelative, decimals);
60
+
58
61
  //if(decimals>-1 && decimals<2) pathData = roundPathData(pathData, decimals);
59
62
  if (toShorthands) pathData = pathDataToShorthands(pathData);
60
63
 
61
64
  // pre round - before relative conversion to minimize distortions
62
- pathData = roundPathData(pathData, decimals);
65
+ if(decimals>-1 && toRelative) pathData = roundPathData(pathData, decimals);
63
66
  if (toRelative) pathData = pathDataToRelative(pathData);
64
67
  if (decimals > -1) pathData = roundPathData(pathData, decimals);
68
+
69
+
70
+
65
71
  return pathData
66
72
  }
67
73
 
@@ -374,7 +380,7 @@ export function pathDataToLonghands(pathData, decimals = -1, test = true) {
374
380
  * L, L, C, Q => H, V, S, T
375
381
  * reversed method: pathDataToLonghands()
376
382
  */
377
- export function pathDataToShorthands(pathData, decimals = -1, test = true) {
383
+ export function pathDataToShorthands(pathData, decimals = -1, test = false) {
378
384
 
379
385
  //pathData = JSON.parse(JSON.stringify(pathData))
380
386
  //console.log('has dec', pathData);
@@ -388,29 +394,28 @@ export function pathDataToShorthands(pathData, decimals = -1, test = true) {
388
394
  hasRel = /[astvqmhlc]/g.test(commandTokens);
389
395
  }
390
396
 
391
- pathData = test && hasRel ? pathDataToAbsolute(pathData, decimals) : pathData;
397
+ pathData = test && hasRel ? pathDataToAbsoluteOrRelative(pathData) : pathData;
398
+
399
+ let len = pathData.length
400
+ let pathDataShorts = new Array(len);
392
401
 
393
402
  let comShort = {
394
403
  type: "M",
395
404
  values: pathData[0].values
396
405
  };
397
406
 
398
- if (pathData[0].decimals) {
399
- //console.log('has dec');
400
- comShort.decimals = pathData[0].decimals
401
- }
402
-
403
- let pathDataShorts = [comShort];
407
+ pathDataShorts[0] = comShort;
404
408
 
405
409
  let p0 = { x: pathData[0].values[0], y: pathData[0].values[1] };
406
410
  let p;
407
411
  let tolerance = 0.01
408
412
 
409
- for (let i = 1, len = pathData.length; i < len; i++) {
413
+ for (let i = 1; i < len; i++) {
410
414
 
411
415
  let com = pathData[i];
412
416
  let { type, values } = com;
413
- let valuesLast = values.slice(-2);
417
+ let valuesLen = values.length;
418
+ let valuesLast = [values[valuesLen-2], values[valuesLen-1]];
414
419
 
415
420
  // previoius command
416
421
  let comPrev = pathData[i - 1];
@@ -462,7 +467,8 @@ export function pathDataToShorthands(pathData, decimals = -1, test = true) {
462
467
  if (typePrev !== 'Q') {
463
468
  //console.log('skip T:', type, typePrev);
464
469
  p0 = { x: valuesLast[0], y: valuesLast[1] };
465
- pathDataShorts.push(com);
470
+ //pathDataShorts.push(com);
471
+ pathDataShorts[i] = com;
466
472
  continue;
467
473
  }
468
474
 
@@ -492,7 +498,9 @@ export function pathDataToShorthands(pathData, decimals = -1, test = true) {
492
498
 
493
499
  if (typePrev !== 'C') {
494
500
  //console.log('skip S', typePrev);
495
- pathDataShorts.push(com);
501
+ //pathDataShorts.push(com);
502
+ pathDataShorts[i] = com;
503
+
496
504
  p0 = { x: valuesLast[0], y: valuesLast[1] };
497
505
  continue;
498
506
  }
@@ -525,21 +533,22 @@ export function pathDataToShorthands(pathData, decimals = -1, test = true) {
525
533
  };
526
534
  }
527
535
 
528
-
529
536
  // add decimal info
530
537
  if (com.decimals || com.decimals === 0) {
531
538
  comShort.decimals = com.decimals
532
539
  }
533
540
 
534
-
535
541
  // round final values
536
542
  if (decimals > -1) {
537
543
  comShort.values = comShort.values.map(val => { return +val.toFixed(decimals) })
538
544
  }
539
545
 
540
546
  p0 = { x: valuesLast[0], y: valuesLast[1] };
541
- pathDataShorts.push(comShort);
547
+ pathDataShorts[i] = comShort;
548
+ //pathDataShorts.push(comShort);
542
549
  }
550
+
551
+ //console.log('pathDataShorts', pathDataShorts);
543
552
  return pathDataShorts;
544
553
  }
545
554
 
@@ -0,0 +1,65 @@
1
+ export function interpolatedPathData(pathData1, pathData2, t = 0.5) {
2
+
3
+ /**
4
+ * linear interpolation helper (LERP)
5
+ * respecting non-interpolatable
6
+ * Boolean values – required by arc commands
7
+ */
8
+ const interpolateValues = (values1 = [], values2 = [], t = 0) => {
9
+
10
+ // return start or end for t=0 or 1
11
+ if (t === 0) return values1;
12
+ if (t === 1) return values2;
13
+
14
+ let len = values1.length;
15
+ let isArc = len === 7;
16
+
17
+ // interpolated values: copy values1
18
+ let valuesI = values1.slice();
19
+
20
+ for (let i = 0; i < len; i++) {
21
+ let valI
22
+
23
+ // skip Boolean arc command values (largeArc and sweep)
24
+ if (isArc && (i === 3 || i === 4)) {
25
+ continue
26
+ }
27
+
28
+ // interpolate
29
+ valuesI[i] = (values2[i] - values1[i]) * t + values1[i];
30
+ }
31
+ return valuesI;
32
+ }
33
+
34
+
35
+ // interpolation impossible
36
+ if (pathData1.length !== pathData2.length) {
37
+ throw new Error("Paths are not compatible");
38
+ }
39
+
40
+
41
+ // interpolate command coordinates
42
+ let pathDataI = [];
43
+ pathData1.forEach((com, c) => {
44
+ let {
45
+ type,
46
+ values
47
+ } = com;
48
+ let [type2, values2] = [pathData2[c].type, pathData2[c].values];
49
+
50
+ // interpolate command values
51
+ let valuesInter = interpolateValues(values, values2, t);
52
+
53
+ pathDataI.push({
54
+ type: type,
55
+ values: valuesInter,
56
+ });
57
+ });
58
+
59
+ // serialize to "d" attribute string
60
+ let dInter = pathDataI.map(com => `${com.type} ${com.values.join(' ')}`).join(' ');
61
+ return {
62
+ pathData: pathDataI,
63
+ d: dInter
64
+ };
65
+ }
@@ -1,6 +1,7 @@
1
1
  //import { arcToBezier, quadratic2Cubic } from './convert.js';
2
2
  //import { getAngle, bezierhasExtreme, getDistance } from "./geometry";
3
3
  import { pathDataToAbsoluteOrRelative, pathDataToLonghands, pathDataArcsToCubics, pathDataQuadraticToCubic } from './pathData_convert.js';
4
+ import { pathDataToD } from './pathData_stringify.js';
4
5
 
5
6
 
6
7
 
@@ -15,10 +16,10 @@ export function normalizePathData(pathData = [],
15
16
  quadraticToCubic = false,
16
17
  arcToCubic = false,
17
18
  arcAccuracy = 2,
18
- } = {},
19
19
 
20
- {
20
+ // assume we need full normalization
21
21
  hasRelatives = true, hasShorthands = true, hasQuadratics = true, hasArcs = true, testTypes = false
22
+
22
23
  } = {}
23
24
  ) {
24
25
 
@@ -71,15 +72,29 @@ export function parsePathDataNormalized(d,
71
72
  ) {
72
73
 
73
74
 
74
- let pathDataObj = parsePathDataString(d);
75
- let { hasRelatives, hasShorthands, hasQuadratics, hasArcs } = pathDataObj;
76
- let pathData = pathDataObj.pathData;
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;
77
92
 
78
93
  // normalize
79
94
  pathData = normalizePathData(pathData,
80
- { toAbsolute, toLonghands, quadraticToCubic, arcToCubic, arcAccuracy },
81
- //{test:true}
82
- { hasRelatives, hasShorthands, hasQuadratics, hasArcs }
95
+ { toAbsolute, toLonghands, quadraticToCubic, arcToCubic, arcAccuracy,
96
+ hasRelatives, hasShorthands, hasQuadratics, hasArcs
97
+ },
83
98
  )
84
99
 
85
100
  return pathData;
@@ -450,7 +465,8 @@ export function parsePathDataString(d, debug = true) {
450
465
  if (debug === 'log') {
451
466
  console.log(feedback);
452
467
  } else {
453
- throw new Error(feedback)
468
+ //throw new Error(feedback)
469
+ console.warn(feedback)
454
470
  }
455
471
  }
456
472
 
@@ -0,0 +1,245 @@
1
+ //import { pathDataToAbsoluteOrRelative, pathDataToLonghands, cubicToArc } from './pathData_convert.js';
2
+ import { parsePathDataString, parsePathDataNormalized, stringifyPathData } from './pathData_parse.js';
3
+
4
+ export function shapeElToPath(el) {
5
+
6
+ let nodeName = el.nodeName.toLowerCase();
7
+ if (nodeName === 'path') return el;
8
+
9
+ let pathData = getPathDataFromEl(el);
10
+ let d = pathData.map(com => { return `${com.type} ${com.values} ` }).join(' ')
11
+ let attributes = [...el.attributes].map(att => att.name);
12
+
13
+ let pathN = document.createElementNS('http://www.w3.org/2000/svg', 'path');
14
+ pathN.setAttribute('d', d);
15
+
16
+ let exclude = ['x', 'y', 'cx', 'cy', 'dx', 'dy', 'r', 'rx', 'ry', 'width', 'height', 'points']
17
+
18
+ attributes.forEach(att => {
19
+ if (!exclude.includes(att)) {
20
+ let val = el.getAttribute(att);
21
+ pathN.setAttribute(att, val)
22
+ }
23
+ })
24
+
25
+ //el.replaceWith(pathN)
26
+ return pathN
27
+
28
+ }
29
+
30
+
31
+ // retrieve pathdata from svg geometry elements
32
+ export function getPathDataFromEl(el, stringify = false) {
33
+
34
+ let pathData = [];
35
+ let type = el.nodeName;
36
+ let atts, attNames, d, x, y, width, height, r, rx, ry, cx, cy, x1, x2, y1, y2;
37
+
38
+ // convert relative or absolute units
39
+ const svgElUnitsToPixel = (el, decimals = 9) => {
40
+ //console.log(this);
41
+ const svg = el.nodeName !== "svg" ? el.closest("svg") : el;
42
+
43
+ // convert real life units to pixels
44
+ const translateUnitToPixel = (value) => {
45
+
46
+ if (value === null) {
47
+ return 0
48
+ }
49
+ //default dpi = 96
50
+ let dpi = 96;
51
+ let unit = value.match(/([a-z]+)/gi);
52
+ unit = unit ? unit[0] : "";
53
+ let val = parseFloat(value);
54
+ let rat;
55
+
56
+ // no unit - already pixes/user unit
57
+ if (!unit) {
58
+ return val;
59
+ }
60
+
61
+ switch (unit) {
62
+ case "in":
63
+ rat = dpi;
64
+ break;
65
+ case "pt":
66
+ rat = (1 / 72) * 96;
67
+ break;
68
+ case "cm":
69
+ rat = (1 / 2.54) * 96;
70
+ break;
71
+ case "mm":
72
+ rat = ((1 / 2.54) * 96) / 10;
73
+ break;
74
+ // just a default approximation
75
+ case "em":
76
+ case "rem":
77
+ rat = 16;
78
+ break;
79
+ default:
80
+ rat = 1;
81
+ }
82
+ let valuePx = val * rat;
83
+ return +valuePx.toFixed(decimals);
84
+ };
85
+
86
+ // svg width and height attributes
87
+ let width = svg.getAttribute("width");
88
+ width = width ? translateUnitToPixel(width) : 300;
89
+ let height = svg.getAttribute("height");
90
+ height = width ? translateUnitToPixel(height) : 150;
91
+
92
+ //prefer viewBox values
93
+ let vB = svg.getAttribute("viewBox");
94
+ vB = vB
95
+ ? vB
96
+ .replace(/,/g, " ")
97
+ .split(" ")
98
+ .filter(Boolean)
99
+ .map((val) => {
100
+ return +val;
101
+ })
102
+ : [];
103
+
104
+ let w = vB.length ? vB[2] : width;
105
+ let h = vB.length ? vB[3] : height;
106
+ let scaleX = w / 100;
107
+ let scaleY = h / 100;
108
+ let scalRoot = Math.sqrt((Math.pow(scaleX, 2) + Math.pow(scaleY, 2)) / 2);
109
+
110
+ let attsH = ["x", "width", "x1", "x2", "rx", "cx", "r"];
111
+ let attsV = ["y", "height", "y1", "y2", "ry", "cy"];
112
+
113
+
114
+ let atts = el.getAttributeNames();
115
+ atts.forEach((att) => {
116
+ let val = el.getAttribute(att);
117
+ let valAbs = val;
118
+ if (attsH.includes(att) || attsV.includes(att)) {
119
+ let scale = attsH.includes(att) ? scaleX : scaleY;
120
+ scale = att === "r" && w != h ? scalRoot : scale;
121
+ let unit = val.match(/([a-z|%]+)/gi);
122
+ unit = unit ? unit[0] : "";
123
+ if (val.includes("%")) {
124
+ valAbs = parseFloat(val) * scale;
125
+ }
126
+ //absolute units
127
+ else {
128
+ valAbs = translateUnitToPixel(val);
129
+ }
130
+ el.setAttribute(att, +valAbs);
131
+ }
132
+ });
133
+ }
134
+
135
+ svgElUnitsToPixel(el)
136
+
137
+ const getAtts = (attNames) => {
138
+ atts = {}
139
+ attNames.forEach(att => {
140
+ atts[att] = +el.getAttribute(att)
141
+ })
142
+ return atts
143
+ }
144
+
145
+ switch (type) {
146
+ case 'path':
147
+ d = el.getAttribute("d");
148
+ pathData = parsePathDataNormalized(d);
149
+ break;
150
+
151
+ case 'rect':
152
+ attNames = ['x', 'y', 'width', 'height', 'rx', 'ry'];
153
+ ({ x, y, width, height, rx, ry } = getAtts(attNames));
154
+
155
+
156
+ if (!rx && !ry) {
157
+ pathData = [
158
+ { type: "M", values: [x, y] },
159
+ { type: "L", values: [x + width, y] },
160
+ { type: "L", values: [x + width, y + height] },
161
+ { type: "L", values: [x, y + height] },
162
+ { type: "Z", values: [] }
163
+ ];
164
+ } else {
165
+
166
+ if (rx > width / 2) {
167
+ rx = width / 2;
168
+ }
169
+ if (ry > height / 2) {
170
+ ry = height / 2;
171
+ }
172
+ pathData = [
173
+ { type: "M", values: [x + rx, y] },
174
+ { type: "L", values: [x + width - rx, y] },
175
+ { type: "A", values: [rx, ry, 0, 0, 1, x + width, y + ry] },
176
+ { type: "L", values: [x + width, y + height - ry] },
177
+ { type: "A", values: [rx, ry, 0, 0, 1, x + width - rx, y + height] },
178
+ { type: "L", values: [x + rx, y + height] },
179
+ { type: "A", values: [rx, ry, 0, 0, 1, x, y + height - ry] },
180
+ { type: "L", values: [x, y + ry] },
181
+ { type: "A", values: [rx, ry, 0, 0, 1, x + rx, y] },
182
+ { type: "Z", values: [] }
183
+ ];
184
+ }
185
+ break;
186
+
187
+ case 'circle':
188
+ case 'ellipse':
189
+
190
+ attNames = ['cx', 'cy', 'rx', 'ry', 'r'];
191
+ ({ cx, cy, r, rx, ry } = getAtts(attNames));
192
+
193
+ let isCircle = type === 'circle';
194
+
195
+ if (isCircle) {
196
+ r = r;
197
+ rx = r
198
+ ry = r
199
+ } else {
200
+ rx = rx ? rx : r;
201
+ ry = ry ? ry : r;
202
+ }
203
+
204
+ // simplified radii for cirecles
205
+ let rxS = isCircle && r>=1 ? 1 : rx;
206
+ let ryS = isCircle && r>=1 ? 1 : rx;
207
+
208
+ pathData = [
209
+ { type: "M", values: [cx + rx, cy] },
210
+ { type: "A", values: [rxS, ryS, 0, 1, 1, cx - rx, cy] },
211
+ { type: "A", values: [rxS, ryS, 0, 1, 1, cx + rx, cy] },
212
+ ];
213
+
214
+ break;
215
+ case 'line':
216
+ attNames = ['x1', 'y1', 'x2', 'y2'];
217
+ ({ x1, y1, x2, y2 } = getAtts(attNames));
218
+ pathData = [
219
+ { type: "M", values: [x1, y1] },
220
+ { type: "L", values: [x2, y2] }
221
+ ];
222
+ break;
223
+ case 'polygon':
224
+ case 'polyline':
225
+
226
+ let points = el.getAttribute('points').replaceAll(',', ' ').split(' ').filter(Boolean)
227
+
228
+ for (let i = 0; i < points.length; i += 2) {
229
+ pathData.push({
230
+ type: (i === 0 ? "M" : "L"),
231
+ values: [+points[i], +points[i + 1]]
232
+ });
233
+ }
234
+ if (type === 'polygon') {
235
+ pathData.push({
236
+ type: "Z",
237
+ values: []
238
+ });
239
+ }
240
+ break;
241
+ }
242
+
243
+ return stringify ? stringifyPathData(pathData) : pathData;
244
+
245
+ };
@@ -1,22 +1,27 @@
1
- import { checkBezierFlatness, getDistAv, getSquareDistance } from "./geometry.js";
1
+ import { getDistAv, getSquareDistance } from "./geometry.js";
2
2
  import { getPolygonArea } from "./geometry_area.js";
3
+ import { checkBezierFlatness, commandIsFlat } from "./geometry_flatness.js";
3
4
  import { renderPoint } from "./visualize.js";
4
5
 
5
- export function pathDataRemoveColinear(pathData, tolerance = 1, flatBezierToLinetos = true) {
6
+ export function pathDataRemoveColinear(pathData, {
7
+ tolerance = 1,
8
+ //toleranceCubics = null,
9
+ flatBezierToLinetos = true
10
+ }={}) {
6
11
 
12
+ //toleranceCubics = !toleranceCubics ? tolerance : toleranceCubics;
7
13
  let pathDataN = [pathData[0]];
8
14
 
9
-
10
- let lastType = 'L';
11
15
  let M = { x: pathData[0].values[0], y: pathData[0].values[1] }
12
16
  let p0 = M;
13
17
  let p = M
14
18
  let isClosed = pathData[pathData.length - 1].type.toLowerCase() === 'z'
15
19
 
16
20
  for (let c = 1, l = pathData.length; c < l; c++) {
17
- let comPrev = pathData[c - 1];
21
+ //let comPrev = pathData[c - 1];
18
22
  let com = pathData[c];
19
23
  let comN = pathData[c + 1] || pathData[l - 1];
24
+ //let p1 = comN.type.toLowerCase() === 'z' ? M : { x: comN.values[comN.values.length - 2], y: comN.values[comN.values.length - 1] }
20
25
  let p1 = comN.type.toLowerCase() === 'z' ? M : { x: comN.values[comN.values.length - 2], y: comN.values[comN.values.length - 1] }
21
26
 
22
27
  let { type, values } = com;
@@ -25,19 +30,18 @@ export function pathDataRemoveColinear(pathData, tolerance = 1, flatBezierToLine
25
30
 
26
31
  let area = getPolygonArea([p0, p, p1], true)
27
32
 
28
- let distSquare0 = getSquareDistance(p0, p)
29
- let distSquare1 = getSquareDistance(p, p1)
33
+ //let distSquare0 = getSquareDistance(p0, p)
34
+ //let distSquare1 = getSquareDistance(p, p1)
30
35
  let distSquare = getSquareDistance(p0, p1)
31
- //distSquare = (distSquare0+distSquare1) / 2;
36
+ //distSquare = (distSquare0+distSquare1) * 0.5
32
37
 
33
- let distMax = distSquare / 200 * tolerance
38
+ let distMax = distSquare ? distSquare / 333 * tolerance : 0
34
39
 
35
40
  let isFlat = area < distMax;
36
41
  let isFlatBez = false;
37
42
 
38
43
 
39
44
  if (!flatBezierToLinetos && type === 'C') isFlat = false;
40
- //let isFlat = flatBezierToLinetos && type === 'C' ? area < distMax : false
41
45
 
42
46
  // convert flat beziers to linetos
43
47
  if (flatBezierToLinetos && (type === 'C' || type === 'Q')) {
@@ -46,40 +50,41 @@ export function pathDataRemoveColinear(pathData, tolerance = 1, flatBezierToLine
46
50
  [{ x: values[0], y: values[1] }, { x: values[2], y: values[3] }] :
47
51
  (type === 'Q' ? [{ x: values[0], y: values[1] }] : []);
48
52
 
53
+ isFlatBez = commandIsFlat([p0, ...cpts, p],{tolerance});
49
54
 
50
- //let areaBez = getPolygonArea([p0, ...cpts, p], true)
51
-
52
- isFlatBez = checkBezierFlatness(p0, cpts, p)
53
- // console.log();
54
-
55
- //isFlatBez = areaBez < distMax * 0.25
56
- //console.log('isFlatBez', isFlatBez);
57
- //isFlatBez = false
58
-
59
- //&& comPrev.type !== 'C'
60
- if (isFlatBez && c < l - 1 && comPrev.type !== 'C') {
55
+ if (isFlatBez && c < l - 1 ) {
61
56
  type = "L"
62
57
  com.type = "L"
63
58
  com.values = valsL
64
-
65
- //renderPoint(markers, p)
59
+ //renderPoint(markers, p, 'cyan', '1%', '0.5')
66
60
  }
67
-
68
61
  }
69
62
 
70
- // update end point
71
- p0 = p;
72
63
 
73
64
  // colinear – exclude arcs (as always =) as semicircles won't have an area
74
65
  //&& comN.type==='L'
75
66
  if ( isFlat && c < l - 1 && (type === 'L' || (flatBezierToLinetos && isFlatBez)) ) {
76
- //console.log(area,distMax );
77
- //renderPoint(markers, p)
78
67
 
68
+ /*
69
+ console.log(area, distMax );
79
70
  //if(comN.type!=='L' ){}
71
+
72
+ if(p0.x === p.x && p0.y === p.y){
73
+
74
+ }
75
+
76
+ renderPoint(markers, p0, 'blue', '1.5%', '1')
77
+ renderPoint(markers, p, 'red', '1%', '1')
78
+ renderPoint(markers, p1, 'cyan', '0.5%', '1')
79
+ */
80
+
81
+
80
82
  continue;
81
83
  }
82
84
 
85
+ // update end point
86
+ p0 = p;
87
+
83
88
 
84
89
  if (type === 'M') {
85
90
  M = p
@@ -0,0 +1,21 @@
1
+ export function removeOrphanedM(pathData) {
2
+
3
+ let pathDataN = []
4
+ for (let i = 0, l = pathData.length; i < l; i++) {
5
+ let com = pathData[i];
6
+ if (!com) continue;
7
+ let { type = null, values = [] } = com;
8
+ let comN = pathData[i + 1] ? pathData[i + 1] : null;
9
+ if ((type === 'M' || type === 'm')) {
10
+
11
+ if (!comN || (comN && (comN.type === 'Z' || comN.type === 'z'))) {
12
+ if(comN) i++
13
+ continue
14
+ }
15
+ }
16
+ pathDataN.push(com)
17
+ }
18
+
19
+ return pathDataN;
20
+
21
+ }
@@ -1,4 +1,5 @@
1
1
 
2
+ /*
2
3
  // remove zero-length segments introduced by rounding
3
4
  export function removeZeroLengthLinetos_post(pathData) {
4
5
  let pathDataOpt = []
@@ -13,6 +14,7 @@ export function removeZeroLengthLinetos_post(pathData) {
13
14
  })
14
15
  return pathDataOpt
15
16
  }
17
+ */
16
18
 
17
19
  export function removeZeroLengthLinetos(pathData) {
18
20
 
@@ -24,16 +26,28 @@ export function removeZeroLengthLinetos(pathData) {
24
26
 
25
27
  for (let c = 1, l = pathData.length; c < l; c++) {
26
28
  let com = pathData[c];
29
+ let comPrev = pathData[c-1]
30
+ let comNext = pathData[c+1] || null
27
31
  let { type, values } = com;
28
32
 
29
- let valsL = values.slice(-2);
30
- p = { x: valsL[0], y: valsL[1] };
33
+ // zero length segments are simetimes used in icons for dots
34
+ let isDot = comPrev.type.toLowerCase() ==='m' && !comNext;
35
+
36
+ let valsLen = values.length;
37
+ p = { x: values[valsLen-2], y: values[valsLen-1] };
31
38
 
32
39
  // skip lineto
33
- if (type === 'L' && p.x === p0.x && p.y === p0.y) {
40
+ if (!isDot && type === 'L' && p.x === p0.x && p.y === p0.y) {
34
41
  continue
35
42
  }
36
43
 
44
+
45
+ // skip minified zero length
46
+ if (!isDot && (type === 'l' || type === 'v' || type === 'h')) {
47
+ let noLength = type === 'l' ? (values.join('') === '00') : values[0] === 0;
48
+ if(noLength) continue
49
+ }
50
+
37
51
  pathDataN.push(com)
38
52
  p0 = p;
39
53
  }