svg-path-simplify 0.4.3 → 0.4.4

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 (39) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +1 -0
  3. package/dist/svg-path-simplify.esm.js +1610 -495
  4. package/dist/svg-path-simplify.esm.min.js +2 -2
  5. package/dist/svg-path-simplify.js +1611 -494
  6. package/dist/svg-path-simplify.min.js +2 -2
  7. package/dist/svg-path-simplify.pathdata.esm.js +893 -456
  8. package/dist/svg-path-simplify.pathdata.esm.min.js +2 -2
  9. package/dist/svg-path-simplify.poly.cjs +9 -8
  10. package/index.html +58 -17
  11. package/package.json +1 -1
  12. package/src/constants.js +4 -0
  13. package/src/detect_input.js +47 -29
  14. package/src/index.js +8 -0
  15. package/src/pathData_simplify_cubic.js +26 -16
  16. package/src/pathData_simplify_revertToquadratics.js +0 -1
  17. package/src/pathSimplify-main.js +75 -20
  18. package/src/pathSimplify-only-pathdata.js +7 -2
  19. package/src/pathSimplify-presets.js +15 -4
  20. package/src/svg-getAttributes.js +4 -2
  21. package/src/svgii/convert_units.js +1 -1
  22. package/src/svgii/geometry.js +140 -2
  23. package/src/svgii/geometry_bbox_element.js +1 -1
  24. package/src/svgii/geometry_deduceRadius.js +116 -27
  25. package/src/svgii/geometry_length.js +17 -1
  26. package/src/svgii/pathData_analyze.js +18 -0
  27. package/src/svgii/pathData_convert.js +188 -88
  28. package/src/svgii/pathData_fix_directions.js +10 -18
  29. package/src/svgii/pathData_reorder.js +122 -16
  30. package/src/svgii/pathData_simplify_refineCorners.js +130 -35
  31. package/src/svgii/pathData_simplify_refine_round.js +420 -0
  32. package/src/svgii/rounding.js +79 -78
  33. package/src/svgii/svg_cleanup.js +68 -20
  34. package/src/svgii/svg_cleanup_convertPathLength.js +22 -15
  35. package/src/svgii/svg_cleanup_remove_els_and_atts.js +6 -1
  36. package/src/svgii/svg_el_parse_style_props.js +13 -10
  37. package/src/svgii/svg_validate.js +220 -0
  38. package/tests/testSVG.js +14 -1
  39. package/src/svgii/pathData_refine_round.js +0 -222
@@ -1,42 +1,92 @@
1
- import { getDeltaAngle, getDistance } from "./geometry";
1
+ import { checkLineIntersection, getDeltaAngle, getDistance, getDistManhattan, getSquareDistance } from "./geometry";
2
+ import { renderPoint } from "./visualize";
3
+ //import { arcToBezierResolved } from "./pathData_convert";
4
+ //import { renderPoint, renderPoly } from "./visualize";
2
5
 
3
- export function getArcFromPoly(pts) {
6
+ export function getArcFromPoly(pts, precise = false) {
4
7
  if (pts.length < 3) return false
5
8
 
6
9
  // Pick 3 well-spaced points
7
- let p1 = pts[0];
8
- let p2 = pts[Math.floor(pts.length / 2)];
9
- let p3 = pts[pts.length - 1];
10
+ let len = pts.length
11
+ let idx1 = Math.floor(len * 0.333)
12
+ let idx2 = Math.floor(len * 0.666)
13
+ let idx3 = Math.floor(len * 0.5)
10
14
 
11
- let x1 = p1.x, y1 = p1.y;
12
- let x2 = p2.x, y2 = p2.y;
13
- let x3 = p3.x, y3 = p3.y;
14
15
 
15
- let a = x1 - x2;
16
- let b = y1 - y2;
17
- let c = x1 - x3;
18
- let d = y1 - y3;
16
+ let p1 = pts[0];
17
+ let p2 = pts[idx3];
18
+ let p3 = pts[len - 1];
19
19
 
20
- let e = ((x1 * x1 - x2 * x2) + (y1 * y1 - y2 * y2)) / 2;
21
- let f = ((x1 * x1 - x3 * x3) + (y1 * y1 - y3 * y3)) / 2;
20
+ // Radius (use start point)
21
+ let pts1 = [p1, p2, p3];
22
+ let centroid = getPolyArcCentroid(pts1);
22
23
 
23
- let det = a * d - b * c;
24
+ let r = 0, deltaAngle = 0, startAngle = 0, endAngle = 0, angleData = {};
24
25
 
25
- if (Math.abs(det) < 1e-10) {
26
- console.warn("Points are collinear or numerically unstable");
27
- return false;
28
- }
29
26
 
30
- // find center of arc
31
- let cx = (d * e - b * f) / det;
32
- let cy = (-c * e + a * f) / det;
33
- let centroid = { x: cx, y: cy };
27
+ // check if radii are consistent
28
+ if (precise) {
29
+
30
+
31
+ /**
32
+ * check multiple centroids
33
+ * if the polyline can be expressed as
34
+ * an arc - all centroids should be close
35
+ */
36
+
37
+ if (len > 3) {
38
+ let centroid1 = getPolyArcCentroid([p1, pts[idx1], p3]);
39
+ let centroid2 = getPolyArcCentroid([p1, pts[idx2], p3]);
40
+
41
+ if (!centroid1 || !centroid2) return false;
42
+
43
+ //let dist0 = getDistManhattan(p1, p3)
44
+ let dist0 = getDistManhattan(centroid, p2)
45
+ let dist1 = getDistManhattan(centroid, centroid1)
46
+ let dist2 = getDistManhattan(centroid, centroid2)
47
+ let errorCentroid = (dist1 + dist2)
48
+
49
+ // centroids diverging too much
50
+ if (errorCentroid > dist0 * 0.05) {
51
+ //renderPoint(markers, centroid, 'magenta')
52
+ return false
53
+ }
54
+
55
+ }
56
+
57
+ let rSqMid = getSquareDistance(centroid, p2);
58
+
59
+ //check if radii are close enough
60
+ for (let i = 0; i < len; i++) {
61
+ let pt = pts[i]
62
+ let rSq = getSquareDistance(centroid, pt);
63
+ let error = Math.abs(rSqMid - rSq) / rSqMid
64
+
65
+ if (error > 0.0025) {
66
+ /*
67
+ console.log('error', error, len, idx1, idx2, idx3);
68
+ renderPoint(markers, centroid, 'orange')
69
+ renderPoint(markers, p1, 'green')
70
+ renderPoint(markers, p2)
71
+ renderPoint(markers, p3, 'purple')
72
+ */
73
+ return false;
74
+ }
75
+ }
76
+
77
+ // calculate proper radius
78
+ r = Math.sqrt(rSqMid);
79
+ angleData = getDeltaAngle(centroid, p1, p3);
80
+ ({ deltaAngle, startAngle, endAngle } = angleData);
81
+
82
+
83
+ } else {
84
+ r = getDistance(centroid, p1);
85
+ angleData = getDeltaAngle(centroid, p1, p3);
86
+ ({ deltaAngle, startAngle, endAngle } = angleData);
87
+ }
34
88
 
35
- // Radius (use start point)
36
- let r = getDistance(centroid, p1);
37
89
 
38
- let angleData = getDeltaAngle(centroid, p1, p3)
39
- let {deltaAngle, startAngle, endAngle} = angleData;
40
90
 
41
91
  return {
42
92
  centroid,
@@ -48,3 +98,42 @@ export function getArcFromPoly(pts) {
48
98
  }
49
99
 
50
100
 
101
+
102
+ export function getPolyArcCentroid(pts = []) {
103
+
104
+ pts = pts.filter(pt => pt !== undefined);
105
+ if (pts.length < 3) return false
106
+ //console.log(pts);
107
+
108
+ let p1 = pts[0];
109
+ let p2 = pts[Math.floor(pts.length / 2)];
110
+ let p3 = pts[pts.length - 1];
111
+
112
+ let x1 = p1.x, y1 = p1.y;
113
+ let x2 = p2.x, y2 = p2.y;
114
+ let x3 = p3.x, y3 = p3.y;
115
+
116
+ let a = x1 - x2;
117
+ let b = y1 - y2;
118
+ let c = x1 - x3;
119
+ let d = y1 - y3;
120
+
121
+ let e = ((x1 * x1 - x2 * x2) + (y1 * y1 - y2 * y2)) / 2;
122
+ let f = ((x1 * x1 - x3 * x3) + (y1 * y1 - y3 * y3)) / 2;
123
+
124
+ let det = a * d - b * c;
125
+
126
+ // colinear points
127
+ if (Math.abs(det) < 1e-10) {
128
+ return false;
129
+ }
130
+
131
+ // find center of arc
132
+ let cx = (d * e - b * f) / det;
133
+ let cy = (-c * e + a * f) / det;
134
+ let centroid = { x: cx, y: cy };
135
+ return centroid
136
+ }
137
+
138
+
139
+
@@ -1,4 +1,4 @@
1
- import { getDistance } from "./geometry";
1
+ import { getDistance, getDistManhattan } from "./geometry";
2
2
 
3
3
  // Legendre Gauss weight and abscissa values
4
4
  export const waArr_global = [];
@@ -176,6 +176,22 @@ export function getPolygonLength(pts=[], isPoly=false){
176
176
  return len
177
177
  }
178
178
 
179
+ export function getPolygonLengthManhattan(pts=[], isPoly=false){
180
+
181
+ let len = 0;
182
+ let l=pts.length;
183
+
184
+ for(let i=1; i<l; i++){
185
+ let p1 = pts[i-1]
186
+ let p2 = pts[i]
187
+ len += getDistManhattan(p1, p2)
188
+ }
189
+ if(isPoly){
190
+ len += getDistManhattan(pts[l-1], pts[0])
191
+ }
192
+ return len
193
+ }
194
+
179
195
 
180
196
  /**
181
197
  * Ramanujan approximation
@@ -67,6 +67,7 @@ export function analyzePathData(pathData = [], {
67
67
  let { type, values, p0, p, cp1 = null, cp2 = null, squareDist = 0, cptArea = 0, dimA = 0 } = com;
68
68
 
69
69
  //next command
70
+ let comPrev = pathData[c-2];
70
71
  let comN = pathData[c] || null;
71
72
 
72
73
 
@@ -93,6 +94,7 @@ export function analyzePathData(pathData = [], {
93
94
 
94
95
  // bezier types
95
96
  let isBezier = type === 'Q' || type === 'C';
97
+ let isArc = type === 'A';
96
98
  let isBezierN = comN && (comN.type === 'Q' || comN.type === 'C');
97
99
 
98
100
 
@@ -149,6 +151,22 @@ export function analyzePathData(pathData = [], {
149
151
  }
150
152
  }
151
153
 
154
+ // check extremes introduce by small arcs
155
+ else if(isArc && comN && ((comPrev.type==='C' || comPrev.type==='Q') || (comN.type==='C' || comN.type==='Q')) ){
156
+ let distN = comN ? comN.dimA : 0
157
+ let isShort = com.dimA < (comPrev.dimA + distN) * 0.1;
158
+ let smallRadius = com.values[0] === com.values[1] && (com.values[0] < 1)
159
+
160
+ if(isShort && smallRadius){
161
+ let bb = getPolyBBox([comPrev.p0, comN.p])
162
+ if(p.x>bb.right || p.x<bb.x || p.y<bb.y || p.y>bb.bottom){
163
+ hasExtremes = true;
164
+ //renderPoint(markers, p)
165
+ }
166
+ }
167
+
168
+ }
169
+
152
170
  if (hasExtremes) com.extreme = true
153
171
 
154
172
  // Corners and semi extremes
@@ -17,54 +17,10 @@ import { pathDataToD } from './pathData_stringify';
17
17
  import { roundPathData } from './rounding';
18
18
  import { renderPoint } from './visualize';
19
19
 
20
-
21
- export function parsePathDataNormalized(d,
22
- {
23
- // necessary for most calculations
24
- toAbsolute = true,
25
- toLonghands = true,
26
-
27
- // not necessary unless you need cubics only
28
- quadraticToCubic = false,
29
-
30
- // mostly a fallback if arc calculations fail
31
- arcToCubic = false,
32
- // arc to cubic precision - adds more segments for better precision
33
- arcAccuracy = 4,
34
- } = {}
35
- ) {
36
-
37
- // is already array
38
- let isArray = Array.isArray(d);
39
-
40
- // normalize native pathData to regular array
41
- let hasConstructor = isArray && typeof d[0] === 'object' && typeof d[0].constructor === 'function'
42
- /*
43
- if (hasConstructor) {
44
- d = d.map(com => { return { type: com.type, values: com.values } })
45
- console.log('hasConstructor', hasConstructor, (typeof d[0].constructor), d);
46
- }
47
- */
48
-
49
- let pathDataObj = isArray ? d : parsePathDataString(d);
50
-
51
- let { hasRelatives = true, hasShorthands = true, hasQuadratics = true, hasArcs = true } = pathDataObj;
52
- let pathData = hasConstructor ? pathDataObj : pathDataObj.pathData;
53
-
54
- //console.log('???quadraticToCubic', quadraticToCubic);
55
-
56
- // normalize
57
- pathData = normalizePathData(pathData,
58
- {
59
- toAbsolute, toLonghands, quadraticToCubic, arcToCubic, arcAccuracy,
60
- hasRelatives, hasShorthands, hasQuadratics, hasArcs
61
- },
62
- )
63
-
64
- return pathData;
65
- }
66
-
67
-
20
+ /**
21
+ * wrapper function for
22
+ * all path data conversion
23
+ */
68
24
  export function convertPathData(pathData, {
69
25
  toShorthands = true,
70
26
  toLonghands = false,
@@ -126,23 +82,25 @@ export function convertPathData(pathData, {
126
82
  //console.log(toShorthands, toRelative, decimals);
127
83
  if (hasQuadratics && quadraticToCubic) pathData = pathDataQuadraticToCubic(pathData);
128
84
 
129
- if(toMixed) toRelative = true
85
+ if (toMixed) toRelative = true
130
86
 
131
87
  // pre round - before relative conversion to minimize distortions
132
88
  if (decimals > -1 && toRelative) pathData = roundPathData(pathData, decimals);
133
89
 
134
90
  // clone absolute pathdata
135
- if(toMixed){
91
+ if (toMixed) {
136
92
  pathDataAbs = JSON.parse(JSON.stringify(pathData))
137
93
  }
138
94
 
139
95
  if (toRelative) pathData = pathDataToRelative(pathData);
96
+
97
+ // final rounding
140
98
  if (decimals > -1) pathData = roundPathData(pathData, decimals);
141
99
 
142
100
 
143
101
  // choose most compact commands: relative or absolute
144
- if(toMixed){
145
- for(let i=0; i<pathData.length; i++){
102
+ if (toMixed) {
103
+ for (let i = 0; i < pathData.length; i++) {
146
104
  let com = pathData[i]
147
105
  let comA = pathDataAbs[i]
148
106
  // compare Lengths
@@ -152,7 +110,7 @@ export function convertPathData(pathData, {
152
110
  let lenR = comStr.length;
153
111
  let lenA = comStrA.length;
154
112
 
155
- if(lenA<lenR){
113
+ if (lenA < lenR) {
156
114
  //console.log('absolute is shorter', comStrA, comStr);
157
115
  pathData[i] = pathDataAbs[i]
158
116
  }
@@ -162,6 +120,56 @@ export function convertPathData(pathData, {
162
120
  return pathData
163
121
  }
164
122
 
123
+
124
+
125
+
126
+ export function parsePathDataNormalized(d,
127
+ {
128
+ // necessary for most calculations
129
+ toAbsolute = true,
130
+ toLonghands = true,
131
+
132
+ // not necessary unless you need cubics only
133
+ quadraticToCubic = false,
134
+
135
+ // mostly a fallback if arc calculations fail
136
+ arcToCubic = false,
137
+ // arc to cubic precision - adds more segments for better precision
138
+ arcAccuracy = 4,
139
+ } = {}
140
+ ) {
141
+
142
+ // is already array
143
+ let isArray = Array.isArray(d);
144
+
145
+ // normalize native pathData to regular array
146
+ let hasConstructor = isArray && typeof d[0] === 'object' && typeof d[0].constructor === 'function'
147
+ /*
148
+ if (hasConstructor) {
149
+ d = d.map(com => { return { type: com.type, values: com.values } })
150
+ console.log('hasConstructor', hasConstructor, (typeof d[0].constructor), d);
151
+ }
152
+ */
153
+
154
+ let pathDataObj = isArray ? d : parsePathDataString(d);
155
+
156
+ let { hasRelatives = true, hasShorthands = true, hasQuadratics = true, hasArcs = true } = pathDataObj;
157
+ let pathData = hasConstructor ? pathDataObj : pathDataObj.pathData;
158
+
159
+ //console.log('???quadraticToCubic', quadraticToCubic);
160
+
161
+ // normalize
162
+ pathData = normalizePathData(pathData,
163
+ {
164
+ toAbsolute, toLonghands, quadraticToCubic, arcToCubic, arcAccuracy,
165
+ hasRelatives, hasShorthands, hasQuadratics, hasArcs
166
+ },
167
+ )
168
+
169
+ return pathData;
170
+ }
171
+
172
+
165
173
  /**
166
174
  *
167
175
  * @param {*} pathData
@@ -170,50 +178,100 @@ export function convertPathData(pathData, {
170
178
 
171
179
  export function optimizeArcPathData(pathData = []) {
172
180
 
173
- let remove =[]
181
+ //return pathData
174
182
 
175
- pathData.forEach((com, i) => {
183
+ let remove = []
184
+ let l = pathData.length;
185
+ let pathDataN = [];
186
+
187
+ for (let i = 0; i < l; i++) {
188
+ let com = pathData[i];
176
189
  let { type, values } = com;
177
- if (type === 'A') {
178
- let [rx, ry, largeArc, x, y] = [values[0], values[1], values[3], values[5], values[6]];
179
- let comPrev = pathData[i - 1]
180
- let [x0, y0] = [comPrev.values[comPrev.values.length - 2], comPrev.values[comPrev.values.length - 1]];
181
- let M = { x: x0, y: y0 };
182
- let p = { x, y };
183
- //largeArc=true
184
- //let pMid = {x: Math.abs(x-x0), y:Math.abs(y-y0)}
185
-
186
- if(rx===0 || ry===0){
187
- pathData[i]= null
188
- remove.push(i)
189
- //console.log('!!!');
190
- }
191
190
 
192
- // rx and ry are large enough
193
- if (rx >= 1 && (x === x0 || y === y0)) {
194
- let diff = Math.abs(rx - ry) / rx;
191
+ if (type !== 'A') {
192
+ pathDataN.push(com)
193
+ continue
194
+ }
195
195
 
196
- // rx~==ry
197
- if (diff < 0.01) {
198
196
 
199
- // test radius against mid point
200
- let pMid = interpolate(M, p, 0.5)
201
- let distM = getDistance(pMid, M)
202
- let rDiff = Math.abs(distM - rx) / rx
197
+ let [rx, ry, largeArc, x, y] = [values[0], values[1], values[3], values[5], values[6]];
198
+ let comPrev = pathData[i - 1]
199
+ let [x0, y0] = [comPrev.values[comPrev.values.length - 2], comPrev.values[comPrev.values.length - 1]];
200
+ let M = { x: x0, y: y0 };
201
+ let p = { x, y };
202
+ //largeArc=true
203
+ //let pMid = {x: Math.abs(x-x0), y:Math.abs(y-y0)}
203
204
 
204
- // half distance between mid and start point should be ~ equal
205
- if(rDiff<0.01){
206
- pathData[i].values[0] = 1;
207
- pathData[i].values[1] = 1;
208
- pathData[i].values[2] = 0;
209
- }
210
- }
205
+ if (rx === 0 || ry === 0) {
206
+ pathData[i] = null
207
+ remove.push(i)
208
+ }
209
+
210
+ // test for elliptic
211
+ let rat = rx / ry
212
+ let error = rx !== ry ? Math.abs(1 - rat) : 0
213
+
214
+ if (error > 0.01) {
215
+ //console.log('is elliptic');
216
+ pathDataN.push(com)
217
+ continue
218
+
219
+ }
220
+
221
+ // xAxis rotation is futile for circular arcs - reset
222
+ com.values[2] = 0
223
+
224
+ /**
225
+ * test semi circles
226
+ * rx and ry are large enough
227
+ */
228
+
229
+
230
+ // 1. horizontal or vertical
231
+ let thresh = getDistManhattan(M, p) * 0.001;
232
+ let diffX = Math.abs(x - x0)
233
+ let diffY = Math.abs(y - y0)
234
+
235
+ let isHorizontal = diffY < thresh
236
+ let isVertical = diffX < thresh
237
+
238
+
239
+ // minify rx and ry
240
+ if (isHorizontal || isVertical) {
241
+
242
+ // check if semi circle
243
+ let needsTrueR = isHorizontal ? rx*1.9 > diffX : ry*1.9 > diffY;
244
+
245
+ // is semicircle we can simplify rx
246
+ if (!needsTrueR) {
247
+ //console.log('needsTrueR', needsTrueR, diffX, rx, diffY, ry);
248
+ rx = rx >= 1 ? 1 : (rx > 0.5 ? 0.5 : rx);
211
249
  }
250
+
251
+ com.values[0] = rx
252
+ com.values[1] = rx
253
+ pathDataN.push(com)
254
+ continue
255
+
256
+
212
257
  }
213
- })
214
258
 
215
- if(remove.length) pathData = pathData.filter(Boolean)
216
- return pathData;
259
+ // 2. get true radius - if rx ~= diameter/distance we have a semicircle
260
+ let r = getDistance(M, p) * 0.5
261
+ error = rx / r
262
+ //console.log('rx', rx, r, error);
263
+
264
+ if (error < 0.5) {
265
+ rx = r >= 1 ? 1 : (r > 0.5 ? 0.5 : r);
266
+ }
267
+
268
+ com.values[0] = rx;
269
+ com.values[1] = rx;
270
+ pathDataN.push(com)
271
+
272
+ }
273
+
274
+ return pathDataN;
217
275
  }
218
276
 
219
277
 
@@ -281,6 +339,48 @@ export function normalizePathData(pathData = [],
281
339
  }
282
340
  */
283
341
 
342
+ export function convertSmallArcsToLinetos(pathData) {
343
+
344
+ let l = pathData.length;
345
+
346
+ // add fist command
347
+ let pathDataN = [pathData[0]]
348
+
349
+ for (let i = 1; i < l; i++) {
350
+ let com = pathData[i];
351
+ let comPrev = pathData[i - 1];
352
+ let comN = pathData[i + 1] || null;
353
+
354
+ if (!comN) {
355
+ pathDataN.push(com);
356
+ break
357
+ }
358
+
359
+ let { type, values, extreme = false, p0, p, dimA = 0 } = com;
360
+ // for short segment detection
361
+ let dimAN = comN.dimA;
362
+ let dimA0 = comPrev.dimA + dimA + dimAN;
363
+ let thresh = 0.05
364
+ let isShort = dimA < dimA0 * thresh;
365
+ //let isShortN = dimAN < dimA0 * thresh;
366
+
367
+ if (type === 'A' && isShort && values[0] < 1 && values[1] < 1) {
368
+
369
+ //renderPoint(markers, p0, 'green', '0.1%')
370
+ //renderPoint(markers, p, 'magenta', '0.1%')
371
+ com.type = 'L';
372
+ com.values = [p.x, p.y];
373
+ }
374
+
375
+ pathDataN.push(com)
376
+
377
+ }
378
+
379
+
380
+ return pathDataN;
381
+
382
+
383
+ }
284
384
 
285
385
 
286
386
 
@@ -54,33 +54,25 @@ export function fixPathDataDirections(pathDataArr = [], toClockwise = false) {
54
54
 
55
55
 
56
56
  // reverse paths
57
- for (let i = 0; i < l; i++) {
57
+ for (let i = 0; l && i < l; i++) {
58
58
 
59
59
  let poly = polys[i]
60
60
  let { cw, includedIn, includes } = poly
61
61
 
62
- // outer path direction to counter clockwise
63
- if (!includedIn.length && cw && !toClockwise
64
- || !includedIn.length && !cw && toClockwise
65
- ) {
66
- //console.log('reverse outer');
67
-
68
- pathDataArr[i].pathData = reversePathData(pathDataArr[i].pathData);
69
- polys[i].cw = polys[i].cw ? false : true
70
- cw = polys[i].cw
71
-
72
- }
62
+ let len = includes.length;
63
+ //console.log('try reverse', includes);
73
64
 
74
65
  // reverse inner sub paths
75
- for (let j = 0; j < includes.length; j++) {
66
+ for (let j = 0; len && j < len; j++) {
76
67
  let ind = includes[j];
77
68
  let child = polys[ind];
78
69
 
79
- if (child.cw === cw) {
80
- //console.log('reverse', child.cw, cw);
81
- pathDataArr[ind].pathData = reversePathData(pathDataArr[ind].pathData);
82
- polys[ind].cw = polys[ind].cw ? false : true
83
- }
70
+ // nothing to do
71
+ if (child.cw !== cw) continue
72
+
73
+ pathDataArr[ind].pathData = reversePathData(pathDataArr[ind].pathData);
74
+ polys[ind].cw = polys[ind].cw ? false : true
75
+
84
76
  }
85
77
  }
86
78