svg-path-simplify 0.2.4 → 0.2.7

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.
@@ -159,7 +159,7 @@ function getDeltaAngle(centerPoint, startPoint, endPoint, largeArc = false) {
159
159
  * http://jsfiddle.net/justin_c_rounds/Gd2S2/light/
160
160
  */
161
161
 
162
- function checkLineIntersection(p1 = null, p2 = null, p3 = null, p4 = null, exact = true, debug = false) {
162
+ function checkLineIntersection(p1 = null, p2 = null, p3 = null, p4 = null, exact = true, respectDirection = false, debug = false) {
163
163
  // if the lines intersect, the result contains the x and y of the intersection (treating the lines as infinite) and booleans for whether line segment 1 or line segment 2 contain the point
164
164
  let denominator, a, b, numerator1, numerator2;
165
165
  let intersectionPoint = {};
@@ -171,7 +171,9 @@ function checkLineIntersection(p1 = null, p2 = null, p3 = null, p4 = null, exact
171
171
 
172
172
  try {
173
173
  denominator = ((p4.y - p3.y) * (p2.x - p1.x)) - ((p4.x - p3.x) * (p2.y - p1.y));
174
- if (denominator == 0) {
174
+
175
+ // parallel or colinear
176
+ if (denominator === 0) {
175
177
  return false;
176
178
  }
177
179
  } catch {
@@ -200,8 +202,13 @@ function checkLineIntersection(p1 = null, p2 = null, p3 = null, p4 = null, exact
200
202
 
201
203
  }
202
204
 
203
- if (exact && !intersection) {
205
+ // direction
206
+ if (!exact && respectDirection && ((a > 0 && b < 0) || (a < 0 && b > 0))) {
207
+ intersection = false;
208
+ return false
209
+ }
204
210
 
211
+ if (exact && !intersection) {
205
212
  return false;
206
213
  }
207
214
 
@@ -210,6 +217,44 @@ function checkLineIntersection(p1 = null, p2 = null, p3 = null, p4 = null, exact
210
217
  return intersectionPoint;
211
218
  }
212
219
 
220
+ /** Get relationship between a point and a polygon using ray-casting algorithm
221
+ * based on timepp's answer
222
+ * https://stackoverflow.com/questions/217578/how-can-i-determine-whether-a-2d-point-is-within-a-polygon#63436180
223
+ */
224
+ function isPointInPolygon(pt, polygon, bb, skipBB = false) {
225
+ const between = (p, a, b) => (p >= a && p <= b) || (p <= a && p >= b);
226
+ let inside = false;
227
+
228
+ // not in bbox - quit || no bbox defined
229
+ if (!skipBB || !bb.bottom) {
230
+ if (bb.left > pt.x || bb.top > pt.y || bb.bottom < pt.y || bb.right < pt.x) {
231
+ return false;
232
+ }
233
+ }
234
+
235
+ let l=polygon.length;
236
+ for (let i = l - 1, j = 0; j < l; i = j, j++) {
237
+ const A = polygon[i];
238
+ const B = polygon[j];
239
+ // corner cases
240
+ if ((pt.x == A.x && pt.y == A.y) || (pt.x == B.x && pt.y == B.y))
241
+ return true;
242
+ if (A.y == B.y && pt.y == A.y && between(pt.x, A.x, B.x)) return true;
243
+ if (between(pt.y, A.y, B.y)) {
244
+ /**
245
+ * if pt inside the vertical range filter out "ray pass vertex" problem
246
+ * by treating the line a little lower
247
+ */
248
+ if ((pt.y == A.y && B.y >= A.y) || (pt.y == B.y && A.y >= B.y)) continue;
249
+ // calc cross product `ptA X ptB`, pt lays on left side of AB if c > 0
250
+ const c = (A.x - pt.x) * (B.y - pt.y) - (B.x - pt.x) * (A.y - pt.y);
251
+ if (c == 0) return true;
252
+ if (A.y < B.y == c > 0) inside = !inside;
253
+ }
254
+ }
255
+ return inside ? true : false;
256
+ }
257
+
213
258
  /**
214
259
  * Linear interpolation (LERP) helper
215
260
  */
@@ -2369,6 +2414,7 @@ function detectAccuracy(pathData) {
2369
2414
  }
2370
2415
 
2371
2416
  let dim_min = dims.sort();
2417
+
2372
2418
  let sliceIdx = Math.ceil(dim_min.length / 8);
2373
2419
  dim_min = dim_min.slice(0, sliceIdx);
2374
2420
  let minVal = dim_min.reduce((a, b) => a + b, 0) / sliceIdx;
@@ -2472,6 +2518,7 @@ function analyzePathData(pathData = [], {
2472
2518
  (type === 'C' ? [p0, cp1, cp2, p] : [p0, cp1, p]) :
2473
2519
  ([p0, p]);
2474
2520
  let thresholdLength = dimA * 0.1;
2521
+ let threshold = thresholdLength*0.01;
2475
2522
 
2476
2523
  // bezier types
2477
2524
  let isBezier = type === 'Q' || type === 'C';
@@ -2488,10 +2535,12 @@ function analyzePathData(pathData = [], {
2488
2535
  let dx = type === 'C' ? Math.abs(com.cp2.x - com.p.x) : Math.abs(com.cp1.x - com.p.x);
2489
2536
  let dy = type === 'C' ? Math.abs(com.cp2.y - com.p.y) : Math.abs(com.cp1.y - com.p.y);
2490
2537
 
2491
- let horizontal = dy === 0 && dx > 0;
2492
- let vertical = dx === 0 && dy > 0;
2538
+ let horizontal = (dy === 0 || dy<threshold ) && dx > 0;
2539
+ let vertical = (dx === 0 || dx<threshold ) && dy > 0;
2493
2540
 
2494
- if (horizontal || vertical) hasExtremes = true;
2541
+ if (horizontal || vertical) {
2542
+ hasExtremes = true;
2543
+ }
2495
2544
 
2496
2545
  // is extreme relative to bounding box
2497
2546
  if ((p.x === left || p.y === top || p.x === right || p.y === bottom)) {
@@ -2503,7 +2552,7 @@ function analyzePathData(pathData = [], {
2503
2552
  let couldHaveExtremes = bezierhasExtreme(null, commandPts);
2504
2553
  if (couldHaveExtremes) {
2505
2554
  let tArr = getTatAngles(commandPts);
2506
- if (tArr.length && (tArr[0] > 0.15)) {
2555
+ if (tArr.length && (tArr[0] > 0.2)) {
2507
2556
  hasExtremes = true;
2508
2557
  }
2509
2558
  }
@@ -3107,7 +3156,7 @@ function pathDataToShorthands(pathData, decimals = -1, test = false) {
3107
3156
  let dx1 = (p0.x - cpPrev.x);
3108
3157
  let dy1 = (p0.y - cpPrev.y);
3109
3158
 
3110
- maxDist = getDistManhattan(cpPrev, cpFirst) * 0.05;
3159
+ maxDist = getDistManhattan(cpPrev, cpFirst) * 0.025;
3111
3160
 
3112
3161
  // reflected cp
3113
3162
  let cpR = { x: cpPrev.x + dx1 * 2, y: cpPrev.y + dy1 * 2 };
@@ -3895,12 +3944,14 @@ function shapeElToPath(el) {
3895
3944
  let attributes = [...el.attributes].map(att => att.name);
3896
3945
 
3897
3946
  let pathN = document.createElementNS('http://www.w3.org/2000/svg', 'path');
3947
+
3898
3948
  pathN.setAttribute('d', d);
3899
3949
 
3900
- let exclude = ['x', 'y', 'cx', 'cy', 'dx', 'dy', 'r', 'rx', 'ry', 'width', 'height', 'points'];
3950
+ let exclude = ['x', 'y', 'x1', 'y1', 'x2', 'y2', 'cx', 'cy', 'dx', 'dy', 'r', 'rx', 'ry', 'width', 'height', 'points'];
3901
3951
 
3902
3952
  attributes.forEach(att => {
3903
3953
  if (!exclude.includes(att)) {
3954
+
3904
3955
  let val = el.getAttribute(att);
3905
3956
  pathN.setAttribute(att, val);
3906
3957
  }
@@ -3914,7 +3965,7 @@ function shapeElToPath(el) {
3914
3965
  function getPathDataFromEl(el, stringify = false) {
3915
3966
 
3916
3967
  let pathData = [];
3917
- let type = el.nodeName;
3968
+ let type = el.nodeName.toLowerCase();
3918
3969
  let atts, attNames, d, x, y, width, height, r, rx, ry, cx, cy, x1, x2, y1, y2;
3919
3970
 
3920
3971
  // convert relative or absolute units
@@ -4537,6 +4588,125 @@ function addClosePathLineto(pathData) {
4537
4588
  return pathData;
4538
4589
  }
4539
4590
 
4591
+ /**
4592
+ * reverse pathdata
4593
+ * make sure all command coordinates are absolute and
4594
+ * shorthands are converted to long notation
4595
+ */
4596
+ function reversePathData(pathData, {
4597
+ arcToCubic = false,
4598
+ quadraticToCubic = false,
4599
+ toClockwise = false,
4600
+ returnD = false
4601
+ } = {}) {
4602
+
4603
+ /**
4604
+ * Add closing lineto:
4605
+ * needed for path reversing or adding points
4606
+ */
4607
+ const addClosePathLineto = (pathData) => {
4608
+ let closed = pathData[pathData.length - 1].type.toLowerCase() === "z";
4609
+ let M = pathData[0];
4610
+ let [x0, y0] = [M.values[0], M.values[1]];
4611
+ let lastCom = closed ? pathData[pathData.length - 2] : pathData[pathData.length - 1];
4612
+ let [xE, yE] = [lastCom.values[lastCom.values.length - 2], lastCom.values[lastCom.values.length - 1]];
4613
+
4614
+ if (closed && (x0 != xE || y0 != yE)) {
4615
+
4616
+ pathData.pop();
4617
+ pathData.push(
4618
+ {
4619
+ type: "L",
4620
+ values: [x0, y0]
4621
+ },
4622
+ {
4623
+ type: "Z",
4624
+ values: []
4625
+ }
4626
+ );
4627
+ }
4628
+ return pathData;
4629
+ };
4630
+
4631
+ // helper to rearrange control points for all command types
4632
+ const reverseControlPoints = (type, values) => {
4633
+ let controlPoints = [];
4634
+ let endPoints = [];
4635
+ if (type !== "A") {
4636
+ for (let p = 0; p < values.length; p += 2) {
4637
+ controlPoints.push([values[p], values[p + 1]]);
4638
+ }
4639
+ endPoints = controlPoints.pop();
4640
+ controlPoints.reverse();
4641
+ }
4642
+ // is arc
4643
+ else {
4644
+
4645
+ let sweep = values[4] == 0 ? 1 : 0;
4646
+ controlPoints = [values[0], values[1], values[2], values[3], sweep];
4647
+ endPoints = [values[5], values[6]];
4648
+ }
4649
+ return { controlPoints, endPoints };
4650
+ };
4651
+
4652
+ // start compiling new path data
4653
+ let pathDataNew = [];
4654
+
4655
+ let closed =
4656
+ pathData[pathData.length - 1].type.toLowerCase() === "z" ? true : false;
4657
+ if (closed) {
4658
+ // add lineto closing space between Z and M
4659
+ pathData = addClosePathLineto(pathData);
4660
+ // remove Z closepath
4661
+ pathData.pop();
4662
+ }
4663
+
4664
+ // define last point as new M if path isn't closed
4665
+ let valuesLast = pathData[pathData.length - 1].values;
4666
+ let valuesLastL = valuesLast.length;
4667
+ let M = closed
4668
+ ? pathData[0]
4669
+ : {
4670
+ type: "M",
4671
+ values: [valuesLast[valuesLastL - 2], valuesLast[valuesLastL - 1]]
4672
+ };
4673
+ // starting M stays the same – unless the path is not closed
4674
+ pathDataNew.push(M);
4675
+
4676
+ // reverse path data command order for processing
4677
+ pathData.reverse();
4678
+ for (let i = 1; i < pathData.length; i++) {
4679
+ let com = pathData[i];
4680
+ let type = com.type;
4681
+ let values = com.values;
4682
+ let comPrev = pathData[i - 1];
4683
+ let typePrev = comPrev.type;
4684
+ let valuesPrev = comPrev.values;
4685
+
4686
+ // get reversed control points and new end coordinates
4687
+ let controlPointsPrev = reverseControlPoints(typePrev, valuesPrev).controlPoints;
4688
+ let endPoints = reverseControlPoints(type, values).endPoints;
4689
+
4690
+ // create new path data
4691
+ let newValues = [];
4692
+ newValues = [controlPointsPrev, endPoints].flat();
4693
+ pathDataNew.push({
4694
+ type: typePrev,
4695
+ values: newValues.flat()
4696
+ });
4697
+ }
4698
+
4699
+ // add previously removed Z close path
4700
+ if (closed) {
4701
+ pathDataNew.push({
4702
+ type: "z",
4703
+ values: []
4704
+ });
4705
+ }
4706
+
4707
+ return pathDataNew;
4708
+ }
4709
+
4540
4710
  function simplifyRDP(pts, {
4541
4711
  quality = 0.9,
4542
4712
  width = 0,
@@ -6287,6 +6457,8 @@ const attLookup = {
6287
6457
  ...textEls,
6288
6458
  ],
6289
6459
 
6460
+ "fill-rule": ["svg", "g", "path", "polygon", "text", "textPath"],
6461
+
6290
6462
  opacity: [
6291
6463
  "svg",
6292
6464
  "g",
@@ -6651,26 +6823,30 @@ function cleanUpSVG(svgMarkup, {
6651
6823
  removeUnused = true,
6652
6824
  stylesToAttributes = true,
6653
6825
  removePrologue = true,
6826
+ removeIds = false,
6827
+ removeClassNames = false,
6828
+ removeDimensions = false,
6654
6829
  fixHref = true,
6655
6830
  mergePaths = false,
6656
6831
  cleanupSVGAtts = true,
6657
6832
  removeNameSpaced = true,
6658
6833
  attributesToGroup = true,
6834
+ shapesToPaths = false,
6659
6835
  decimals = -1,
6660
6836
  excludedEls = [],
6661
6837
  } = {}) {
6662
6838
 
6663
- svgMarkup = cleanSvgPrologue(svgMarkup);
6839
+ attributesToGroup = cleanupSVGAtts ? true : false;
6664
6840
 
6665
6841
  // replace namespaced refs
6666
6842
  if (fixHref) svgMarkup = svgMarkup.replaceAll("xlink:href=", "href=");
6667
6843
 
6668
6844
  let svg = new DOMParser()
6669
-
6670
6845
  .parseFromString(svgMarkup, "text/html")
6671
6846
  .querySelector("svg");
6672
6847
 
6673
6848
  if (cleanupSVGAtts) {
6849
+
6674
6850
  let allowed = ['viewBox', 'xmlns', 'width', 'height', 'id', 'class', 'fill', 'stroke', 'stroke-width', 'stroke-linecap', 'stroke-linejoin'];
6675
6851
  removeExcludedAttribues(svg, allowed);
6676
6852
 
@@ -6682,11 +6858,22 @@ function cleanUpSVG(svgMarkup, {
6682
6858
  let els = svg.querySelectorAll('*');
6683
6859
  let elProps = [];
6684
6860
 
6861
+ let geometryElements = ['polygon', 'polyline', 'line', 'rect', 'circle', 'ellipse'];
6862
+
6685
6863
  for (let i = 0; i < els.length; i++) {
6686
6864
  let el = els[i];
6687
6865
 
6688
6866
  let name = el.nodeName.toLowerCase();
6689
6867
 
6868
+ // convert shapes
6869
+ if (shapesToPaths && name !== 'path' && geometryElements.includes(name)) {
6870
+ let path = shapeElToPath(el);
6871
+ el.replaceWith(path);
6872
+ name = 'path';
6873
+ el = path;
6874
+
6875
+ }
6876
+
6690
6877
  // remove hidden elements
6691
6878
  let style = el.getAttribute('style') || '';
6692
6879
  let isHiddenByStyle = style ? style.trim().includes('display:none') : false;
@@ -6706,11 +6893,25 @@ function cleanUpSVG(svgMarkup, {
6706
6893
  }
6707
6894
 
6708
6895
  // group styles
6709
-
6710
6896
  if (attributesToGroup || mergePaths) {
6711
6897
  moveAttributesToGroup(elProps, mergePaths);
6712
6898
  }
6713
6899
 
6900
+ if (removeDimensions) {
6901
+ svg.removeAttribute('width');
6902
+ svg.removeAttribute('height');
6903
+ }
6904
+
6905
+ if (removeClassNames || removeIds) {
6906
+ let att = removeClassNames ? 'class' : 'id';
6907
+ let selector = `[${att}]`;
6908
+ let els = svg.querySelectorAll(selector);
6909
+ svg.removeAttribute(att);
6910
+ els.forEach(el => {
6911
+ el.removeAttribute(att);
6912
+ });
6913
+ }
6914
+
6714
6915
  if (returnDom) return svg
6715
6916
  let markup = stringifySVG(svg);
6716
6917
 
@@ -6722,11 +6923,39 @@ function moveAttributesToGroup(elProps = [], mergePaths = true) {
6722
6923
  let combine = [[elProps[0]]];
6723
6924
  let idx = 0;
6724
6925
  let lastProps = '';
6725
- for (let i = 0; i < elProps.length; i++) {
6926
+ let l = elProps.length;
6927
+ let itemsWithProps = elProps.filter(item => item.propstr);
6928
+ let path0;
6929
+
6930
+ // merge paths without properties
6931
+ if (!itemsWithProps.length && mergePaths) {
6932
+ let item0 = elProps[0];
6933
+ path0 = item0.el;
6934
+ let dCombined = item0.propsFiltered.d;
6935
+
6936
+ for (let i = 1; i < l; i++) {
6937
+ let item = elProps[i];
6938
+ let path = item.el;
6939
+
6940
+ let d = item.propsFiltered.d;
6941
+ let isAbs = d.startsWith('M');
6942
+ let dAbs = isAbs ? d : parsePathDataString(d).pathData.map(com => `${com.type} ${com.values.join(' ')}`).join(' ');
6943
+
6944
+ dCombined += dAbs;
6945
+
6946
+ // delete path el
6947
+ path.remove();
6948
+ }
6949
+
6950
+ path0.setAttribute('d', dCombined);
6951
+ return
6952
+ }
6953
+
6954
+ // add to combine chunks
6955
+ for (let i = 0; i < l; i++) {
6726
6956
  let item = elProps[i];
6727
6957
  let props = item.propsFiltered;
6728
6958
  let propstr = [];
6729
-
6730
6959
  for (let prop in props) {
6731
6960
  if (prop !== 'd' && prop !== 'id') {
6732
6961
  propstr.push(`${prop}:${props[prop]}`);
@@ -6735,16 +6964,15 @@ function moveAttributesToGroup(elProps = [], mergePaths = true) {
6735
6964
  propstr = propstr.join('_');
6736
6965
  item.propstr = propstr;
6737
6966
 
6738
- if (propstr === lastProps) {
6967
+ if (l > 1 && propstr === lastProps) {
6739
6968
  combine[idx].push(item);
6740
6969
  } else {
6741
- if (combine[idx].length) {
6970
+ if (l > 1 && combine[idx].length) {
6742
6971
  combine.push([]);
6743
6972
  idx++;
6744
6973
  }
6745
6974
  }
6746
6975
  lastProps = propstr;
6747
-
6748
6976
  }
6749
6977
 
6750
6978
  // add att groups
@@ -6782,10 +7010,15 @@ function moveAttributesToGroup(elProps = [], mergePaths = true) {
6782
7010
  }
6783
7011
 
6784
7012
  if (mergePaths) {
6785
- let path0 = group[0].el;
7013
+ group = group.filter(Boolean);
7014
+ let l = group.length;
7015
+ // nothing to merge
7016
+ if (l === 1) return group[0].el;
7017
+
7018
+ path0 = group[0].el;
6786
7019
  let dCombined = group[0].propsFiltered.d;
6787
7020
 
6788
- for (let i = 1; i < group.length; i++) {
7021
+ for (let i = 1; i < l; i++) {
6789
7022
  let item = group[i];
6790
7023
  let path = item.el;
6791
7024
  let d = item.propsFiltered.d;
@@ -6810,20 +7043,6 @@ function moveAttributesToGroup(elProps = [], mergePaths = true) {
6810
7043
 
6811
7044
  }
6812
7045
 
6813
- function cleanSvgPrologue(svgString) {
6814
- return (
6815
- svgString
6816
- // Remove XML prologues like <?xml ... ?>
6817
- .replace(/<\?xml[\s\S]*?\?>/gi, "")
6818
- // Remove DOCTYPE declarations
6819
- .replace(/<!DOCTYPE[\s\S]*?>/gi, "")
6820
- // Remove comments <!-- ... -->
6821
- .replace(/<!--[\s\S]*?-->/g, "")
6822
- // Trim extra whitespace
6823
- .trim()
6824
- );
6825
- }
6826
-
6827
7046
  function removeExcludedAttribues(el, allowed = ['viewBox', 'xmlns', 'width', 'height', 'id', 'class']) {
6828
7047
  let atts = [...el.attributes].map((att) => att.name);
6829
7048
  atts.forEach((att) => {
@@ -6833,13 +7052,22 @@ function removeExcludedAttribues(el, allowed = ['viewBox', 'xmlns', 'width', 'he
6833
7052
  });
6834
7053
  }
6835
7054
 
6836
- function stringifySVG(svg) {
7055
+ function stringifySVG(svg, omitNamespace = false) {
6837
7056
  let markup = new XMLSerializer().serializeToString(svg);
7057
+
7058
+ if (omitNamespace) {
7059
+ markup = markup.replaceAll('xmlns="http://www.w3.org/2000/svg"', '');
7060
+ }
7061
+
6838
7062
  markup = markup
6839
7063
  .replace(/\t/g, "")
6840
7064
  .replace(/[\n\r|]/g, "\n")
6841
7065
  .replace(/\n\s*\n/g, '\n')
6842
- .replace(/ +/g, ' ');
7066
+ .replace(/ +/g, ' ')
7067
+
7068
+ .replace(/> </g, '><')
7069
+ .trim();
7070
+
6843
7071
 
6844
7072
  return markup
6845
7073
  }
@@ -7406,296 +7634,14 @@ function getViewBox(svg = null, decimals = -1) {
7406
7634
  return viewBox
7407
7635
  }
7408
7636
 
7409
- function redrawPathData(pathData, {
7410
- tolerance = 1
7411
-
7412
- } = {}) {
7413
- let chunks = [];
7414
- let chunk = [];
7415
-
7416
- let l = pathData.length;
7417
-
7418
- for (let i = 1; i < l; i++) {
7419
- let com = pathData[i];
7420
- let { type, values, p0, cp1 = null, cp2 = null, p, extreme = null, semiExtreme = null, corner = null, directionChange } = com;
7421
-
7422
- let comN = pathData[i + 1] || null;
7423
-
7424
- /*
7425
- if (extreme || corner || semiExtreme || directionChange) {
7426
-
7427
- if (extreme) renderPoint(markers, com.p, 'cyan', '1%', '0.5')
7428
-
7429
- if (semiExtreme) renderPoint(markers, com.p, 'orange', '1%', '0.5')
7430
- if (corner) renderPoint(markers, com.p, 'magenta', '1.75%', '0.5')
7431
- }
7432
- */
7433
-
7434
- if (extreme || corner || (comN && comN.type !== type)) {
7435
- chunk.push(com);
7436
- chunks.push(chunk);
7437
- chunk = [];
7438
- continue
7439
- }
7440
-
7441
- chunk.push(com);
7442
-
7443
- }
7444
-
7445
- console.log('!!!chunks', chunks);
7446
-
7447
- renderChunks(chunks);
7448
-
7449
- // cleanup chunks
7450
-
7451
- let chunksLen = chunks.length;
7452
-
7453
-
7454
-
7455
- for (let c = 0; c < chunksLen; c++) {
7456
- let chunk = chunks[c];
7457
- let chunkN = chunks[c + 1] || null;
7458
-
7459
- let chunkLen = chunk.length;
7460
-
7461
- if (chunkLen === 1 && chunkN && chunkN[0].type === 'C') ;
7462
-
7463
- }
7464
-
7465
- chunks = chunks.filter(Boolean);
7466
-
7467
- // test render
7468
-
7469
- let pathDataC = [pathData[0]];
7470
-
7471
- /**
7472
- * combine chunk based
7473
- */
7474
- for (let c = 0; c < chunks.length; c++) {
7475
- let chunk = chunks[c];
7476
- let chunkLen = chunk.length;
7477
- let comChunk0 = chunk[0];
7478
- let comChunk1 = chunk[chunkLen - 1];
7479
- let thresh = getDistManhattan(comChunk0.p0, comChunk1.p) * 0.05;
7480
-
7481
- // commands in chunk
7482
- for (let i = 0, l = chunkLen; i < l; i++) {
7483
- let com = chunk[i];
7484
- chunk[i + 1];
7485
- chunk[l - 1];
7486
-
7487
- let isBezier = comChunk0.type === 'C' && comChunk1.type === 'C';
7488
-
7489
- let { type, values, p0, cp1 = null, cp2 = null, p = null, extreme, semiExtreme = null, corner = null } = com;
7490
-
7491
- let pI1 = null, pI2 = null;
7492
- let cp1_S = null, cp2_S = null;
7493
- let cp2_M = null;
7494
- let cp1_M = null;
7495
- let pathDataS = [];
7496
- let tSplit = 0.666;
7497
- let comMid = null;
7498
-
7499
- // 0. adjust Extreme cpts
7500
- if (isBezier) {
7501
- let dx1 = Math.abs(comChunk0.p0.x - comChunk0.cp1.x);
7502
- let dy1 = Math.abs(comChunk0.p0.y - comChunk0.cp1.y);
7503
- let dx2 = Math.abs(comChunk1.p.x - comChunk1.cp2.x);
7504
- let dy2 = Math.abs(comChunk1.p.y - comChunk1.cp2.y);
7505
-
7506
- let vertical1 = dx1 < thresh && dx1 < dy1;
7507
- let horizontal1 = dy1 < thresh && dx1 > dy1;
7508
-
7509
- let vertical2 = dx2 < thresh && dx2 < dy2;
7510
- let horizontal2 = dy2 < thresh && dx2 > dy2;
7511
-
7512
- if (horizontal1) comChunk0.cp1.y = comChunk0.p0.y;
7513
- if (horizontal2) comChunk1.cp2.y = comChunk1.p.y;
7514
- if (vertical1) comChunk0.cp1.x = comChunk0.p0.x;
7515
- if (vertical2) comChunk1.cp2.x = comChunk1.p.x;
7516
- }
7517
-
7518
- // test render - original pathdata
7519
- let pathDataChunk = [
7520
- { type: 'M', values: [com.p0.x, com.p0.y] },
7521
- { type, values },
7522
- ];
7523
-
7524
- pathDataToD(pathDataChunk);
7525
- // renderPath(markers, d, stroke, '1%', '0.5')
7526
- // continue
7527
- /*
7528
-
7529
- */
7530
-
7531
- // 1. only one command in chunk - nothing to simplify
7532
- if (chunkLen === 1 || type !== 'C') {
7533
- pathDataC.push(com);
7534
- }
7535
-
7536
- // 2. could be simplified
7537
- else {
7538
- // 2.1 has semi extreme - extrapolate
7539
- // 2.2 has sdirection change
7540
- let semiExtremes = chunk.filter(ch => ch.semiExtreme);
7541
- let comsDirectionChange = chunk.filter(ch => ch.directionChange);
7542
-
7543
- if (semiExtremes.length || comsDirectionChange.length) {
7544
-
7545
- // semiExtreme command
7546
- comMid = semiExtremes.length ? semiExtremes[0] : comsDirectionChange[0];
7547
-
7548
- // zero length cpt vectors
7549
- if (comChunk0.p0.x === comChunk0.cp1.x && comChunk0.p0.y === comChunk0.cp1.y) {
7550
- comChunk0.cp1 = pointAtT([comChunk0.p0, comChunk0.cp1, comChunk0.cp2, comChunk0.p], 0.5);
7551
- }
7552
- else if (comChunk1.p.x === comChunk1.cp2.x && comChunk1.p.y === comChunk1.cp2.y) {
7553
- comChunk1.cp2 = pointAtT([comChunk1.p0, comChunk1.cp1, comChunk1.cp2, comChunk1.p], 0.5);
7554
- }
7555
-
7556
- pI1 = checkLineIntersection(comMid.p, comMid.cp2, comChunk0.p0, comChunk0.cp1, false);
7557
- pI2 = checkLineIntersection(comMid.p, comMid.cp2, comChunk1.p, comChunk1.cp2, false);
7558
-
7559
- // intersections try to extrapolate cpts
7560
- if (pI1 && pI2) {
7561
-
7562
- cp1_S = pointAtT([comChunk0.p0, pI1], tSplit);
7563
- cp2_S = pointAtT([comChunk1.p, pI2], tSplit);
7564
-
7565
- cp2_M = pointAtT([comMid.p, pI1], tSplit);
7566
- cp1_M = pointAtT([comMid.p, pI2], tSplit);
7567
-
7568
- /*
7569
- renderPoint(markers, cp1_S, 'magenta', '1%', '1' )
7570
- */
7571
-
7572
- pathDataS = [
7573
- { type: 'M', values: [comChunk0.p0.x, comChunk0.p0.y] },
7574
- {
7575
- type: 'C', values: [
7576
- cp1_S.x, cp1_S.y,
7577
- cp2_M.x, cp2_M.y,
7578
- comMid.p.x,
7579
- comMid.p.y
7580
- ]
7581
- },
7582
- {
7583
- type: 'C', values: [
7584
- cp1_M.x, cp1_M.y,
7585
- cp2_S.x, cp2_S.y,
7586
- comChunk1.p.x,
7587
- comChunk1.p.y,
7588
- ]
7589
- },
7590
- ];
7591
- pathDataToD(pathDataS);
7592
-
7593
- pathDataC.push(
7594
- {
7595
- type: 'C', values: [
7596
- cp1_S.x, cp1_S.y,
7597
- cp2_M.x, cp2_M.y,
7598
- comMid.p.x,
7599
- comMid.p.y
7600
- ],
7601
- p0: comChunk0.p0,
7602
- cp1: cp1_S,
7603
- cp2: cp2_M,
7604
- p: comMid.p,
7605
- dimA: getDistManhattan(comChunk0.p0, comMid.p)
7606
- },
7607
-
7608
- {
7609
- type: 'C', values: [
7610
- cp1_M.x, cp1_M.y,
7611
- cp2_S.x, cp2_S.y,
7612
- comChunk1.p.x,
7613
- comChunk1.p.y,
7614
- ],
7615
- p0: comMid.p,
7616
- cp1: cp1_M,
7617
- cp2: cp2_S,
7618
- p: comChunk1.p,
7619
- extreme: true,
7620
- dimA: getDistManhattan(comMid.p, comChunk1.p)
7621
-
7622
- }
7623
- );
7624
- break
7625
-
7626
- }
7627
- } else {
7628
- pathDataC.push(com);
7629
- }
7630
-
7631
- }
7632
-
7633
- }
7634
-
7635
- }
7636
-
7637
- /*
7638
- // render
7639
- let d = pathDataToD(pathDataC)
7640
- console.log(d);
7641
- renderPath(markers, d, 'red', '1%', '0.5')
7642
- */
7643
-
7644
- return pathDataC
7645
-
7646
- }
7647
-
7648
- function renderChunks(chunks) {
7649
-
7650
- console.log('chunks', chunks);
7651
-
7652
- let stroke = 'green';
7653
-
7654
- /**
7655
- * combine chunk based
7656
- */
7657
- for (let c = 0; c < chunks.length; c++) {
7658
- let chunk = chunks[c];
7659
- let chunkLen = chunk.length;
7660
-
7661
- stroke = c % 2 === 0 ? 'orange' : 'green';
7662
- let comChunk0 = chunk[0];
7663
- let comChunk1 = chunk[chunkLen - 1];
7664
-
7665
- let pathDataChunk = [
7666
- { type: 'M', values: [comChunk0.p0.x, comChunk0.p0.y] }
7667
- ];
7668
-
7669
- // commands in chunk
7670
- for (let i = 0, l = chunkLen; i < l; i++) {
7671
- let com = chunk[i];
7672
- chunk[i + 1];
7673
- chunk[l - 1];
7674
- comChunk0.type === 'C' && comChunk1.type === 'C';
7675
-
7676
- let { type, values, p0, cp1 = null, cp2 = null, p = null, extreme, semiExtreme = null, corner = null } = com;
7677
-
7678
- // test render - original pathdata
7679
- pathDataChunk.push(
7680
- { type, values },
7681
- );
7682
-
7683
- }
7684
-
7685
- let d = pathDataToD(pathDataChunk);
7686
- renderPath(markers, d, stroke, '1%', '0.5');
7687
-
7688
- }
7689
- }
7690
-
7691
- function pathDataRevertCubicToQuadratic(pathData) {
7637
+ function pathDataRevertCubicToQuadratic(pathData, tolerance=1) {
7692
7638
 
7693
7639
  for (let c = 1, l = pathData.length; c < l; c++) {
7694
7640
  let com = pathData[c];
7695
7641
  let { type, values, p0, cp1 = null, cp2 = null, p = null } = com;
7696
7642
  if (type === 'C') {
7697
7643
 
7698
- let comQ = revertCubicQuadratic(p0, cp1, cp2, p);
7644
+ let comQ = revertCubicQuadratic(p0, cp1, cp2, p, tolerance);
7699
7645
  if (comQ.type === 'Q') {
7700
7646
  comQ.extreme = com.extreme;
7701
7647
  comQ.corner = com.corner;
@@ -8210,12 +8156,59 @@ export function combineCubicsToArcs(pathData = [], {
8210
8156
  }
8211
8157
  */
8212
8158
 
8159
+ function getPathDataPolyPrecise(pathData = []) {
8160
+
8161
+ let poly = [];
8162
+ for (let i = 0; i < pathData.length; i++) {
8163
+ let com = pathData[i];
8164
+ let prev = i > 0 ? pathData[i - 1] : pathData[i];
8165
+ let { type, values } = com;
8166
+ let p0 = { x: prev.values[prev.values.length - 2], y: prev.values[prev.values.length - 1] };
8167
+ let p = values.length ? { x: values[values.length - 2], y: values[values.length - 1] } : '';
8168
+ let cp1 = values.length ? { x: values[0], y: values[1] } : '';
8169
+
8170
+ switch (type) {
8171
+
8172
+ // convert to cubic to get polygon
8173
+ case 'A':
8174
+ if (typeof arcToBezier$1 !== 'function') {
8175
+
8176
+ break;
8177
+ }
8178
+ let cubic = arcToBezier$1(p0, values);
8179
+ cubic.forEach(com => {
8180
+ let vals = com.values;
8181
+ let cp1 = { x: vals[0], y: vals[1] };
8182
+ let cp2 = { x: vals[2], y: vals[3] };
8183
+ let p = { x: vals[4], y: vals[5] };
8184
+ poly.push(cp1, cp2, p);
8185
+ });
8186
+ break;
8187
+
8188
+ case 'C':
8189
+ let cp2 = { x: values[2], y: values[3] };
8190
+ poly.push(cp1, cp2);
8191
+ break;
8192
+ case 'Q':
8193
+ poly.push(cp1);
8194
+ break;
8195
+ }
8196
+
8197
+ // M and L commands
8198
+ if (type.toLowerCase() !== 'z') {
8199
+ poly.push(p);
8200
+ }
8201
+ }
8202
+
8203
+ return poly;
8204
+ }
8205
+
8213
8206
  function pathDataToPolygon(pathData, {
8214
8207
  angles = [],
8215
8208
  split = 0,
8216
8209
  getPathData = true,
8217
- width=0,
8218
- height=0
8210
+ width = 0,
8211
+ height = 0
8219
8212
  } = {}) {
8220
8213
 
8221
8214
  let l = pathData.length;
@@ -8231,9 +8224,9 @@ function pathDataToPolygon(pathData, {
8231
8224
 
8232
8225
  split = !split ? 1 : split;
8233
8226
 
8234
- if(width && height){
8235
- minLength = (width+height) * 0.025 / split;
8236
- }else {
8227
+ if (width && height) {
8228
+ minLength = (width + height) * 0.025 / split;
8229
+ } else {
8237
8230
 
8238
8231
  let lengths = pathData.map(com => com.dimA || 0).filter(Boolean).sort();
8239
8232
  minLength = lengths[0];
@@ -8259,8 +8252,8 @@ function pathDataToPolygon(pathData, {
8259
8252
  split = Math.ceil(length / minLength);
8260
8253
 
8261
8254
  let tArr = [];
8262
- for(let i=1; i<split; i++){
8263
- tArr.push(1/split*i);
8255
+ for (let i = 1; i < split; i++) {
8256
+ tArr.push(1 / split * i);
8264
8257
  }
8265
8258
 
8266
8259
  tArr.forEach(t => {
@@ -8274,10 +8267,10 @@ function pathDataToPolygon(pathData, {
8274
8267
  M = p;
8275
8268
  }
8276
8269
 
8277
- p.area = com.cptArea|| 0;
8278
- p.isExtreme = com.extreme|| false;
8279
- p.isCorner = com.corner|| false;
8280
- p.isDirChange = com.directionChange || false ;
8270
+ p.area = com.cptArea || 0;
8271
+ p.isExtreme = com.extreme || false;
8272
+ p.isCorner = com.corner || false;
8273
+ p.isDirChange = com.directionChange || false;
8281
8274
 
8282
8275
  // segment end point
8283
8276
  pts.push(p);
@@ -8291,7 +8284,7 @@ function pathDataToPolygon(pathData, {
8291
8284
 
8292
8285
  // reduce poly vertices
8293
8286
 
8294
- pts = simplifyRD(pts, {quality:0.5, width, height});
8287
+ pts = simplifyRD(pts, { quality: 0.5, width, height });
8295
8288
 
8296
8289
  /*
8297
8290
  pts.forEach(pt => {
@@ -8304,6 +8297,103 @@ function pathDataToPolygon(pathData, {
8304
8297
  return getPathData ? pathDataPoly : pts;
8305
8298
  }
8306
8299
 
8300
+ function pathDataLineToCubic(pathData) {
8301
+
8302
+ for (let c = 1, l = pathData.length; c < l; c++) {
8303
+ let com = pathData[c];
8304
+ let { type, values, p0, cp1 = null, cp2 = null, p = null } = com;
8305
+ if (type === 'L') {
8306
+
8307
+ let cp1 = interpolate(p0, p, 0.333);
8308
+ let cp2 = interpolate(p, p0, 0.333);
8309
+
8310
+ pathData[c].type = 'C';
8311
+ pathData[c].values = [cp1.x, cp1.y, cp2.x, cp2.y, p.x, p.y];
8312
+ pathData[c].cp1 = cp1;
8313
+ pathData[c].cp2 = cp2;
8314
+
8315
+ }
8316
+ }
8317
+ return pathData
8318
+ }
8319
+
8320
+ /**
8321
+ * fix sub path directions
8322
+ * pathdata must be be normalized to
8323
+ * absolute and longhand commands
8324
+ * toClockwise = force default direction
8325
+ */
8326
+
8327
+ function fixPathDataDirections(pathDataArr = [], toClockwise = false) {
8328
+
8329
+ let polys = [];
8330
+
8331
+ pathDataArr.forEach((sub, i) => {
8332
+ let pathData = sub.pathData;
8333
+
8334
+ let vertices = getPathDataPolyPrecise(pathData);
8335
+ let area = getPolygonArea(vertices);
8336
+ let isClockwise = area >= 0;
8337
+ polys.push({ pts: vertices, bb: getPolyBBox(vertices), cw: isClockwise, index: i, inter: 0, includes: [], includedIn: [] });
8338
+ });
8339
+
8340
+ // check poly intersections
8341
+ let l = polys.length;
8342
+ for (let i = 0; i < l; i++) {
8343
+ let prev = polys[i];
8344
+ let bb0 = prev.bb;
8345
+
8346
+ for (let j = 0; j < l; j++) {
8347
+
8348
+ let poly = polys[j];
8349
+ let bb = poly.bb;
8350
+
8351
+ // skip if the same poly or parent
8352
+ if (i === j || poly.includes.includes(i)) continue
8353
+
8354
+ // if mid point is in previous polygon
8355
+ let ptMid = { x: bb.left + bb.width / 2, y: bb.top + bb.height / 2 };
8356
+ let inPoly = isPointInPolygon(ptMid, prev.pts, bb0);
8357
+
8358
+ if (inPoly) {
8359
+ polys[j].inter += 1;
8360
+ poly.includedIn.push(i);
8361
+ prev.includes.push(j);
8362
+ }
8363
+ }
8364
+ }
8365
+
8366
+ // reverse paths
8367
+ for (let i = 0; i < l; i++) {
8368
+
8369
+ let poly = polys[i];
8370
+ let { cw, includedIn, includes } = poly;
8371
+
8372
+ // outer path direction to counter clockwise
8373
+ if (!includedIn.length && cw && !toClockwise
8374
+ || !includedIn.length && !cw && toClockwise
8375
+ ) {
8376
+ pathDataArr[i].pathData = reversePathData(pathDataArr[i].pathData);
8377
+ polys[i].cw = polys[i].cw ? false : true;
8378
+ cw = polys[i].cw;
8379
+ }
8380
+
8381
+ // reverse inner sub paths
8382
+ for (let j = 0; j < includes.length; j++) {
8383
+ let ind = includes[j];
8384
+ let child = polys[ind];
8385
+
8386
+ if (child.cw === cw) {
8387
+ pathDataArr[ind].pathData = reversePathData(pathDataArr[ind].pathData);
8388
+ polys[ind].cw = polys[ind].cw ? false : true;
8389
+ }
8390
+ }
8391
+ }
8392
+
8393
+ return pathDataArr
8394
+
8395
+ }
8396
+
8307
8397
  function svgPathSimplify(input = '', {
8308
8398
 
8309
8399
  // return svg markup or object
@@ -8331,6 +8421,12 @@ function svgPathSimplify(input = '', {
8331
8421
 
8332
8422
  refineExtremes = true,
8333
8423
  simplifyCorners = false,
8424
+ removeDimensions = false,
8425
+ removeIds = false,
8426
+ removeClassNames = false,
8427
+ omitNamespace = false,
8428
+
8429
+ fixDirections = false,
8334
8430
 
8335
8431
  keepExtremes = true,
8336
8432
  keepCorners = true,
@@ -8344,7 +8440,6 @@ function svgPathSimplify(input = '', {
8344
8440
  toPolygon = false,
8345
8441
 
8346
8442
  removeOrphanSubpaths = false,
8347
-
8348
8443
  simplifyRound = false,
8349
8444
 
8350
8445
  scale = 1,
@@ -8352,32 +8447,28 @@ function svgPathSimplify(input = '', {
8352
8447
  crop = false,
8353
8448
  alignToOrigin = false,
8354
8449
 
8355
- // svg path optimizations
8356
8450
  decimals = 3,
8357
8451
  autoAccuracy = true,
8358
8452
 
8359
8453
  minifyD = 0,
8360
8454
  tolerance = 1,
8361
- reverse = false,
8455
+ reversePath = false,
8362
8456
 
8363
- // svg cleanup options
8364
- cleanupSVGAtts=true,
8457
+ cleanupSVGAtts = true,
8365
8458
  removePrologue = true,
8366
8459
  stylesToAttributes = true,
8367
8460
  fixHref = true,
8368
- removeNameSpaced=true,
8369
- attributesToGroup=false,
8461
+ removeNameSpaced = true,
8462
+ attributesToGroup = false,
8370
8463
  mergePaths = false,
8371
8464
  removeHidden = true,
8372
8465
  removeUnused = true,
8373
- shapesToPaths = true,
8466
+ shapesToPaths = false,
8467
+ lineToCubic = false,
8374
8468
 
8375
8469
  tMin = 0,
8376
8470
  tMax = 1,
8377
8471
 
8378
- // redraw - for messed up paths
8379
- redraw = false,
8380
-
8381
8472
  } = {}) {
8382
8473
 
8383
8474
  // clamp tolerance and scale
@@ -8435,14 +8526,16 @@ function svgPathSimplify(input = '', {
8435
8526
  else {
8436
8527
 
8437
8528
  let returnDom = true;
8438
- svg = cleanUpSVG(input, { cleanupSVGAtts, returnDom, removeHidden, removeUnused, removeNameSpaced, attributesToGroup, stylesToAttributes, removePrologue, fixHref ,mergePaths }
8529
+ svg = cleanUpSVG(input, { removeIds, removeClassNames, removeDimensions, cleanupSVGAtts, returnDom, removeHidden, removeUnused, removeNameSpaced, stylesToAttributes, removePrologue, fixHref, mergePaths, shapesToPaths }
8439
8530
  );
8440
8531
 
8441
8532
  if (shapesToPaths) {
8442
8533
  let shapes = svg.querySelectorAll('polygon, polyline, line, rect, circle, ellipse');
8443
8534
  shapes.forEach(shape => {
8444
8535
  let path = shapeElToPath(shape);
8536
+
8445
8537
  shape.replaceWith(path);
8538
+
8446
8539
  });
8447
8540
  }
8448
8541
 
@@ -8595,23 +8688,6 @@ function svgPathSimplify(input = '', {
8595
8688
  // remove zero length linetos
8596
8689
  if (removeColinear || removeZeroLength) pathDataSub = removeZeroLengthLinetos(pathDataSub);
8597
8690
 
8598
- /**
8599
- * try to redraw messed up paths
8600
- * based on significant points suchas
8601
- * extremes, semi-extremes and corners
8602
- */
8603
- if (redraw) {
8604
- addExtremes = true;
8605
- addSemiExtremes = true;
8606
- simplifyCorners = false;
8607
- keepCorners = true;
8608
- keepExtremes = true;
8609
- optimizeOrder = true;
8610
-
8611
- tMin = 0;
8612
- tMax = 0;
8613
- }
8614
-
8615
8691
  // sort to top left
8616
8692
  if (optimizeOrder) pathDataSub = pathDataToTopLeft(pathDataSub);
8617
8693
 
@@ -8621,6 +8697,11 @@ function svgPathSimplify(input = '', {
8621
8697
  if (addExtremes || addSemiExtremes) pathDataSub = addExtremePoints(pathDataSub,
8622
8698
  { tMin, tMax, addExtremes, addSemiExtremes, angles: [30] });
8623
8699
 
8700
+ // reverse
8701
+ if(reversePath) {
8702
+ pathDataSub = reversePathData(pathDataSub);
8703
+ }
8704
+
8624
8705
  // analyze pathdata to add info about signicant properties such as extremes, corners
8625
8706
  let pathDataPlus = analyzePathData(pathDataSub, {
8626
8707
  detectSemiExtremes: addSemiExtremes,
@@ -8633,7 +8714,7 @@ function svgPathSimplify(input = '', {
8633
8714
 
8634
8715
  if (refineClosing) pathData = refineClosingCommand(pathData, { threshold: dimA * 0.001 });
8635
8716
 
8636
- pathData = simplifyBezier ? simplifyPathDataCubic(pathData, { simplifyBezier, keepInflections, keepExtremes, keepCorners, extrapolateDominant, revertToQuadratics, tolerance, reverse }) : pathData;
8717
+ pathData = simplifyBezier ? simplifyPathDataCubic(pathData, { simplifyBezier, keepInflections, keepExtremes, keepCorners, extrapolateDominant, revertToQuadratics, tolerance }) : pathData;
8637
8718
 
8638
8719
  // refine extremes
8639
8720
  if (refineExtremes) {
@@ -8642,29 +8723,6 @@ function svgPathSimplify(input = '', {
8642
8723
  pathData = refineAdjacentExtremes(pathData, { threshold: thresholdEx, tolerance });
8643
8724
  }
8644
8725
 
8645
- /**
8646
- * try redrawing
8647
- */
8648
-
8649
- if (redraw) {
8650
-
8651
- /*
8652
- pathData = addExtremePoints(pathData,
8653
- { tMin: 0, tMax: 1, addExtremes: true, addSemiExtremes: true })
8654
-
8655
- pathData = analyzePathData(pathDataSub, {
8656
- detectSemiExtremes: true,
8657
- detectExtremes: true,
8658
- }).pathData;
8659
-
8660
- */
8661
-
8662
- (bb.width + bb.height) * 0.1;
8663
-
8664
- pathData = redrawPathData(pathData, { tolerance, threshold: dimA * 0.001 });
8665
-
8666
- }
8667
-
8668
8726
  // cubic to arcs
8669
8727
  if (cubicToArc) pathData = pathDataCubicsToArc(pathData, { areaThreshold: 2.5 });
8670
8728
 
@@ -8684,13 +8742,14 @@ function svgPathSimplify(input = '', {
8684
8742
  if (simplifyRound) pathData = refineRoundSegments(pathData);
8685
8743
 
8686
8744
  // simplify to quadratics
8687
- if (revertToQuadratics) pathData = pathDataRevertCubicToQuadratic(pathData);
8745
+ if (revertToQuadratics) pathData = pathDataRevertCubicToQuadratic(pathData, tolerance);
8746
+
8747
+ if (lineToCubic) pathData = pathDataLineToCubic(pathData);
8688
8748
 
8689
8749
  // optimize close path
8690
8750
  if (optimizeOrder) pathData = optimizeClosePath(pathData, { autoClose });
8691
8751
 
8692
8752
  // update
8693
-
8694
8753
  pathDataPlusArr.push({ pathData, bb });
8695
8754
 
8696
8755
  } // end sup paths
@@ -8709,6 +8768,11 @@ function svgPathSimplify(input = '', {
8709
8768
  pathDataPlusArr = isPortrait ? pathDataPlusArr.sort((a, b) => a.bb.y - b.bb.y || a.bb.x - b.bb.x) : pathDataPlusArr.sort((a, b) => a.bb.x - b.bb.x || a.bb.y - b.bb.y);
8710
8769
  }
8711
8770
 
8771
+ // fix path directions
8772
+ if (fixDirections) {
8773
+ pathDataPlusArr = fixPathDataDirections(pathDataPlusArr);
8774
+ }
8775
+
8712
8776
  // flatten compound paths
8713
8777
  pathData = [];
8714
8778
  pathDataPlusArr.forEach(sub => {
@@ -8718,11 +8782,10 @@ function svgPathSimplify(input = '', {
8718
8782
  if (autoAccuracy) {
8719
8783
  decimals = detectAccuracy(pathData);
8720
8784
  pathOptions.decimals = decimals;
8721
-
8722
8785
  }
8723
8786
 
8724
8787
  // collect for merged svg paths
8725
- mergePaths= false;
8788
+ mergePaths = false;
8726
8789
  if (el && mergePaths) {
8727
8790
  pathData_merged.push(...pathData);
8728
8791
  }
@@ -8825,7 +8888,16 @@ function svgPathSimplify(input = '', {
8825
8888
  }
8826
8889
  }
8827
8890
 
8828
- svg = stringifySVG(svg);
8891
+ // remove fill rules
8892
+ if (fixDirections) {
8893
+ let elsFill = svg.querySelectorAll('path[fill-rule], path[clip-rule]');
8894
+ elsFill.forEach(el => {
8895
+ el.removeAttribute('fill-rule');
8896
+ el.removeAttribute('clip-rule');
8897
+ });
8898
+ }
8899
+
8900
+ svg = stringifySVG(svg, omitNamespace);
8829
8901
 
8830
8902
  svgSizeOpt = svg.length;
8831
8903
 
@@ -8869,6 +8941,9 @@ export {fitCurveN as fitCurveN}
8869
8941
 
8870
8942
  import {parsePathDataString_plus} from './svgii/pathData_parse2';
8871
8943
  export {parsePathDataString_plus as parsePathDataString_plus}
8944
+
8945
+ import {getPathDataFromEl} from './svgii/pathData_parse_els';
8946
+ export{getPathDataFromEl as getPathDataFromEl};
8872
8947
  */
8873
8948
 
8874
8949
  // IIFE