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
@@ -1,12 +1,14 @@
1
1
  import { detectInputType } from './detect_input';
2
- import { combineCubicPairs } from './pathData_simplify_cubic';
2
+ import { simplifyPathDataCubic } from './pathData_simplify_cubic';
3
3
  import { getPathDataVertices, pointAtT } from './svgii/geometry';
4
4
  import { getPolyBBox } from './svgii/geometry_bbox';
5
- import { analyzePathData, analyzePathData2 } from './svgii/pathData_analyze';
5
+ import { analyzePathData } from './svgii/pathData_analyze';
6
6
  import { combineArcs, convertPathData, cubicCommandToArc, revertCubicQuadratic } from './svgii/pathData_convert';
7
7
  import { parsePathDataNormalized } from './svgii/pathData_parse';
8
+ import { shapeElToPath } from './svgii/pathData_parse_els';
8
9
  import { pathDataRemoveColinear } from './svgii/pathData_remove_collinear';
9
- import { removeZeroLengthLinetos, removeZeroLengthLinetos_post } from './svgii/pathData_remove_zerolength';
10
+ import { removeOrphanedM } from './svgii/pathData_remove_orphaned';
11
+ import { removeZeroLengthLinetos } from './svgii/pathData_remove_zerolength';
10
12
  import { optimizeClosePath, pathDataToTopLeft } from './svgii/pathData_reorder';
11
13
  import { reversePathData } from './svgii/pathData_reverse';
12
14
  import { addExtremePoints, splitSubpaths } from './svgii/pathData_split';
@@ -15,8 +17,10 @@ import { pathDataToD } from './svgii/pathData_stringify';
15
17
  import { analyzePoly } from './svgii/poly_analyze';
16
18
  import { getCurvePathData } from './svgii/poly_to_pathdata';
17
19
  import { detectAccuracy } from './svgii/rounding';
20
+ import { refineAdjacentExtremes } from './svgii/pathData_simplify_refineExtremes';
18
21
  import { cleanUpSVG, removeEmptySVGEls, stringifySVG } from './svgii/svg_cleanup';
19
22
  import { renderPoint } from './svgii/visualize';
23
+ import { refineRoundedCorners } from './svgii/pathData_simplify_refineCorners';
20
24
 
21
25
  export function svgPathSimplify(input = '', {
22
26
 
@@ -38,15 +42,20 @@ export function svgPathSimplify(input = '', {
38
42
 
39
43
  simplifyBezier = true,
40
44
  optimizeOrder = true,
45
+ removeZeroLength = true,
41
46
  removeColinear = true,
42
47
  flatBezierToLinetos = true,
43
48
  revertToQuadratics = true,
44
49
 
50
+ refineExtremes = true,
51
+ refineCorners = false,
52
+
45
53
  keepExtremes = true,
46
54
  keepCorners = true,
47
55
  extrapolateDominant = true,
48
56
  keepInflections = false,
49
57
  addExtremes = false,
58
+ removeOrphanSubpaths = false,
50
59
 
51
60
 
52
61
  // svg path optimizations
@@ -61,6 +70,7 @@ export function svgPathSimplify(input = '', {
61
70
  mergePaths = false,
62
71
  removeHidden = true,
63
72
  removeUnused = true,
73
+ shapesToPaths = true,
64
74
 
65
75
 
66
76
  } = {}) {
@@ -77,34 +87,54 @@ export function svgPathSimplify(input = '', {
77
87
  let report = {};
78
88
  let d = '';
79
89
  let mode = inputType === 'svgMarkup' ? 1 : 0;
90
+ //console.log(inputType);
80
91
 
81
92
  let paths = []
82
93
 
83
-
84
94
  /**
85
95
  * normalize input
86
96
  * switch mode
87
97
  */
88
98
 
89
99
  // original size
90
- svgSize = new Blob([input]).size;
100
+ //svgSize = new Blob([input]).size;
101
+ svgSize = input.length;
102
+
91
103
 
92
- // single path
104
+ // mode:0 – single path
93
105
  if (!mode) {
94
106
  if (inputType === 'pathDataString') {
95
107
  d = input
96
108
  } else if (inputType === 'polyString') {
97
109
  d = 'M' + input
98
110
  }
111
+ else if (inputType === 'pathData') {
112
+ d = input;
113
+
114
+ // stringify to compare lengths
115
+ //let dStr = pathDataToD(d);
116
+ let dStr = d.map(com=>{return `${com.type} ${com.values.join(' ')}`}).join(' ') ;
117
+ svgSize = dStr.length;
118
+
119
+ }
120
+
99
121
  paths.push({ d, el: null })
100
122
  }
101
- // process svg
123
+ // mode:1 – process complete svg DOM
102
124
  else {
103
125
  //sanitize
104
126
  let returnDom = true
105
127
  svg = cleanUpSVG(input, { returnDom, removeHidden, removeUnused }
106
128
  );
107
129
 
130
+ if (shapesToPaths) {
131
+ let shapes = svg.querySelectorAll('polygon, polyline, line, rect, circle, ellipse');
132
+ shapes.forEach(shape => {
133
+ let path = shapeElToPath(shape);
134
+ shape.replaceWith(path)
135
+ })
136
+ }
137
+
108
138
  // collect paths
109
139
  let pathEls = svg.querySelectorAll('path')
110
140
  pathEls.forEach(path => {
@@ -112,11 +142,11 @@ export function svgPathSimplify(input = '', {
112
142
  })
113
143
  }
114
144
 
115
- //console.log(paths);
116
- //console.log('inputType', inputType, 'mode', mode);
145
+
117
146
 
118
147
  /**
119
148
  * process all paths
149
+ * try simplifications and removals
120
150
  */
121
151
 
122
152
  // SVG optimization options
@@ -129,36 +159,43 @@ export function svgPathSimplify(input = '', {
129
159
  // combinded path data for SVGs with mergePaths enabled
130
160
  let pathData_merged = [];
131
161
 
132
- paths.forEach(path => {
133
- let { d, el } = path;
162
+ for (let i = 0, l = paths.length; l && i < l; i++) {
134
163
 
135
- let pathDataO = parsePathDataNormalized(d, { quadraticToCubic, toAbsolute, arcToCubic });
136
- //console.log(pathDataO);
164
+ let path = paths[i];
165
+ let { d, el } = path;
137
166
 
138
- // create clone for fallback
139
- let pathData = JSON.parse(JSON.stringify(pathDataO));
167
+ let pathData = parsePathDataNormalized(d, { quadraticToCubic, toAbsolute, arcToCubic });
140
168
 
141
169
  // count commands for evaluation
142
- let comCount = pathDataO.length
170
+ let comCount = pathData.length
171
+
172
+
173
+ if (removeOrphanSubpaths) pathData = removeOrphanedM(pathData);
143
174
 
144
175
  /**
145
176
  * get sub paths
146
177
  */
147
178
  let subPathArr = splitSubpaths(pathData);
179
+ let lenSub = subPathArr.length;
180
+
148
181
 
149
182
  // cleaned up pathData
150
- let pathDataArrN = [];
183
+ //let pathDataArrN = new Array(lenSub);
184
+
185
+ // reset array
186
+ let pathDataFlat = []
187
+
188
+ for (let i = 0; i < lenSub; i++) {
151
189
 
152
- for (let i = 0, l = subPathArr.length; i < l; i++) {
153
190
 
154
191
  //let { pathData, bb } = subPathArr[i];
155
192
  let pathDataSub = subPathArr[i];
156
193
 
157
- // try simplification in reversed order
158
- if (reverse) pathDataSub = reversePathData(pathDataSub);
159
194
 
160
195
  // remove zero length linetos
161
- if (removeColinear) pathDataSub = removeZeroLengthLinetos(pathDataSub)
196
+ if (removeColinear || removeZeroLength) pathDataSub = removeZeroLengthLinetos(pathDataSub)
197
+ //console.log(removeColinear, removeZeroLength);
198
+
162
199
 
163
200
  // add extremes
164
201
  //let tMin=0.2, tMax=0.8;
@@ -169,8 +206,10 @@ export function svgPathSimplify(input = '', {
169
206
  // sort to top left
170
207
  if (optimizeOrder) pathDataSub = pathDataToTopLeft(pathDataSub);
171
208
 
172
- // remove colinear/flat
173
- if (removeColinear) pathDataSub = pathDataRemoveColinear(pathDataSub, tolerance, flatBezierToLinetos);
209
+
210
+ // Preprocessing: remove colinear - ignore flat beziers (removed later)
211
+ if (removeColinear) pathDataSub = pathDataRemoveColinear(pathDataSub, { tolerance, flatBezierToLinetos: false });
212
+
174
213
 
175
214
  // analyze pathdata to add info about signicant properties such as extremes, corners
176
215
  let pathDataPlus = analyzePathData(pathDataSub);
@@ -178,28 +217,29 @@ export function svgPathSimplify(input = '', {
178
217
 
179
218
  // simplify beziers
180
219
  let { pathData, bb, dimA } = pathDataPlus;
181
-
182
- //let pathDataN = pathData;
183
-
184
- //console.log(pathDataPlus);
185
-
186
220
  pathData = simplifyBezier ? simplifyPathDataCubic(pathData, { simplifyBezier, keepInflections, keepExtremes, keepCorners, extrapolateDominant, revertToQuadratics, tolerance, reverse }) : pathData;
187
221
 
222
+ // refine extremes
223
+ if (refineExtremes) {
224
+ let thresholdEx = (bb.width + bb.height) / 2 * 0.05
225
+ pathData = refineAdjacentExtremes(pathData, { threshold: thresholdEx, tolerance })
226
+ }
227
+
188
228
 
189
229
  // cubic to arcs
190
230
  if (cubicToArc) {
191
231
 
192
- let thresh = 3;
232
+ let thresh = 1;
193
233
 
194
- pathData.forEach((com, c) => {
234
+ for(let c=0, l=pathData.length; c<l; c++){
235
+ let com = pathData[c]
195
236
  let { type, values, p0, cp1 = null, cp2 = null, p = null } = com;
196
237
  if (type === 'C') {
197
238
  //console.log(com);
198
239
  let comA = cubicCommandToArc(p0, cp1, cp2, p, thresh)
199
240
  if (comA.isArc) pathData[c] = comA.com;
200
- //if (comQ.type === 'Q') pathDataN[c] = comQ
201
241
  }
202
- })
242
+ }
203
243
 
204
244
  // combine adjacent cubics
205
245
  pathData = combineArcs(pathData)
@@ -207,19 +247,30 @@ export function svgPathSimplify(input = '', {
207
247
  }
208
248
 
209
249
 
250
+ // post processing: remove flat beziers
251
+ if (removeColinear && flatBezierToLinetos) {
252
+ pathData = pathDataRemoveColinear(pathData, { tolerance, flatBezierToLinetos });
253
+ }
254
+
255
+
256
+ // refine corners
257
+ if(refineCorners){
258
+ let threshold = (bb.width + bb.height) / 2 * 0.1
259
+ pathData = refineRoundedCorners(pathData, { threshold, tolerance })
260
+ //console.log(refineCorners);
261
+ }
262
+
263
+
264
+
210
265
  // simplify to quadratics
211
266
  if (revertToQuadratics) {
212
- pathData.forEach((com, c) => {
267
+ for(let c=0, l=pathData.length; c<l; c++){
268
+ let com = pathData[c]
213
269
  let { type, values, p0, cp1 = null, cp2 = null, p = null } = com;
214
270
  if (type === 'C') {
215
271
  //console.log(com);
216
272
  let comQ = revertCubicQuadratic(p0, cp1, cp2, p)
217
273
  if (comQ.type === 'Q') {
218
- /*
219
- comQ.p0 = com.p0
220
- comQ.cp1 = {x:comQ.values[0], y:comQ.values[1]}
221
- comQ.p = com.p
222
- */
223
274
  comQ.extreme = com.extreme
224
275
  comQ.corner = com.corner
225
276
  comQ.dimA = com.dimA
@@ -227,25 +278,32 @@ export function svgPathSimplify(input = '', {
227
278
  pathData[c] = comQ
228
279
  }
229
280
  }
230
- })
281
+ }
231
282
  }
232
283
 
233
- //if (removeColinear) pathDataSub = pathDataRemoveColinear(pathDataSub, tolerance, flatBezierToLinetos);
234
284
 
235
285
  // optimize close path
236
286
  if (optimizeOrder) pathData = optimizeClosePath(pathData)
237
287
 
238
- // poly
239
- //let poly = pathDataToPolySingle(pathData, true)
240
- //console.log('poly', poly);
241
-
242
288
 
243
289
  // update
244
- pathDataArrN.push(pathData)
290
+ pathDataFlat.push(...pathData)
291
+ //pathDataArrN[i]=(pathData)
292
+
245
293
  }
246
294
 
295
+
247
296
  // flatten compound paths
248
- pathData = pathDataArrN.flat();
297
+ pathData = pathDataFlat;
298
+
299
+
300
+ //console.log('pathData', pathData);
301
+
302
+ if (autoAccuracy) {
303
+ decimals = detectAccuracy(pathData)
304
+ pathOptions.decimals = decimals
305
+ //console.log('!decimals', decimals);
306
+ }
249
307
 
250
308
 
251
309
  // collect for merged svg paths
@@ -255,26 +313,20 @@ export function svgPathSimplify(input = '', {
255
313
  // single output
256
314
  else {
257
315
 
258
- /**
259
- * detect accuracy
260
- */
261
- if (autoAccuracy) {
262
- decimals = detectAccuracy(pathData)
263
- }
264
-
265
316
  // optimize path data
266
317
  pathData = convertPathData(pathData, pathOptions)
267
318
 
268
319
  // remove zero-length segments introduced by rounding
269
- pathData = removeZeroLengthLinetos_post(pathData);
320
+ pathData = removeZeroLengthLinetos(pathData);
270
321
 
271
322
  // compare command count
272
323
  let comCountS = pathData.length
273
324
 
274
325
  let dOpt = pathDataToD(pathData, minifyD)
275
- svgSizeOpt = new Blob([dOpt]).size;
276
- compression = +(100 / svgSize * (svgSizeOpt)).toFixed(2)
326
+ //svgSizeOpt = new Blob([dOpt]).size;
327
+ svgSizeOpt = dOpt.length
277
328
 
329
+ compression = +(100 / svgSize * (svgSizeOpt)).toFixed(2)
278
330
 
279
331
  path.d = dOpt
280
332
  path.report = {
@@ -289,7 +341,7 @@ export function svgPathSimplify(input = '', {
289
341
  // apply new path for svgs
290
342
  if (el) el.setAttribute('d', dOpt)
291
343
  }
292
- });
344
+ };
293
345
 
294
346
  /**
295
347
  * stringify new SVG
@@ -297,12 +349,12 @@ export function svgPathSimplify(input = '', {
297
349
  if (mode) {
298
350
 
299
351
  if (pathData_merged.length) {
352
+
300
353
  // optimize path data
301
354
  let pathData = convertPathData(pathData_merged, pathOptions)
302
355
 
303
356
  // remove zero-length segments introduced by rounding
304
- pathData = removeZeroLengthLinetos_post(pathData);
305
-
357
+ pathData = removeZeroLengthLinetos(pathData);
306
358
 
307
359
  let dOpt = pathDataToD(pathData, minifyD)
308
360
 
@@ -310,7 +362,6 @@ export function svgPathSimplify(input = '', {
310
362
  // apply new path for svgs
311
363
  paths[0].el.setAttribute('d', dOpt)
312
364
 
313
-
314
365
  // remove other paths
315
366
  for (let i = 1; i < paths.length; i++) {
316
367
  let pathEl = paths[i].el
@@ -324,7 +375,8 @@ export function svgPathSimplify(input = '', {
324
375
 
325
376
 
326
377
  svg = stringifySVG(svg);
327
- svgSizeOpt = new Blob([svg]).size
378
+ //svgSizeOpt = new Blob([svg]).size
379
+ svgSizeOpt = svg.length;
328
380
  //compression = +(100/svgSize * (svgSize-svgSizeOpt)).toFixed(2)
329
381
  compression = +(100 / svgSize * (svgSizeOpt)).toFixed(2)
330
382
 
@@ -348,109 +400,5 @@ export function svgPathSimplify(input = '', {
348
400
 
349
401
 
350
402
 
351
- function simplifyPathDataCubic(pathData, {
352
- keepExtremes = true,
353
- keepInflections = true,
354
- keepCorners = true,
355
- extrapolateDominant = true,
356
- tolerance = 1,
357
- reverse = false
358
- } = {}) {
359
-
360
- let pathDataN = [pathData[0]];
361
-
362
- for (let i = 2, l = pathData.length; l && i <= l; i++) {
363
- let com = pathData[i - 1];
364
- let comN = i < l ? pathData[i] : null;
365
- let typeN = comN?.type || null;
366
- //let isCornerN = comN?.corner || null;
367
- //let isExtremeN = comN?.extreme || null;
368
- let isDirChange = com?.directionChange || null;
369
- let isDirChangeN = comN?.directionChange || null;
370
-
371
- let { type, values, p0, p, cp1 = null, cp2 = null, extreme = false, corner = false, dimA = 0 } = com;
372
-
373
- // count simplifications
374
- let success = 0;
375
-
376
- // next is also cubic
377
- if (type === 'C' && typeN === 'C') {
378
-
379
- // cannot be combined as crossing extremes or corners
380
- if (
381
- (keepInflections && isDirChangeN) ||
382
- (keepCorners && corner) ||
383
- (!isDirChange && keepExtremes && extreme)
384
- ) {
385
- //renderPoint(markers, p, 'red', '1%')
386
- pathDataN.push(com)
387
- }
388
-
389
- // try simplification
390
- else {
391
- //renderPoint(markers, p, 'magenta', '1%')
392
- let combined = combineCubicPairs(com, comN, extrapolateDominant, tolerance)
393
- let error = 0;
394
-
395
- // combining successful! try next segment
396
- if (combined.length === 1) {
397
- com = combined[0]
398
- let offset = 1;
399
-
400
- // add cumulative error to prevent distortions
401
- error += com.error;
402
- //console.log('!error', error);
403
-
404
- // find next candidates
405
- for (let n = i + 1; error < tolerance && n < l; n++) {
406
- let comN = pathData[n]
407
- if (comN.type !== 'C' ||
408
- (
409
- (keepInflections && comN.directionChange) ||
410
- (keepCorners && com.corner) ||
411
- (keepExtremes && com.extreme)
412
- )
413
- ) {
414
- break
415
- }
416
-
417
- let combined = combineCubicPairs(com, comN, extrapolateDominant, tolerance)
418
- if (combined.length === 1) {
419
- // add cumulative error to prevent distortions
420
- //console.log('combined', combined);
421
- error += combined[0].error * 0.5;
422
- offset++
423
- }
424
- com = combined[0]
425
- }
426
-
427
- //com.opt = true
428
- pathDataN.push(com)
429
-
430
- if (i < l) {
431
- i += offset
432
- }
433
-
434
- } else {
435
- pathDataN.push(com)
436
- }
437
- }
438
-
439
- } // end of bezier command
440
-
441
-
442
- // other commands
443
- else {
444
- pathDataN.push(com)
445
- }
446
-
447
- } // end command loop
448
-
449
- // reverse back
450
- if (reverse) pathDataN = reversePathData(pathDataN)
451
-
452
- return pathDataN
453
- }
454
-
455
403
 
456
404
 
@@ -23,19 +23,24 @@ export function getAngle(p1, p2, normalize = false) {
23
23
  * http://jsfiddle.net/justin_c_rounds/Gd2S2/light/
24
24
  */
25
25
 
26
- export function checkLineIntersection(p1, p2, p3, p4, exact = true) {
26
+ export function checkLineIntersection(p1=null, p2=null, p3=null, p4=null, exact = true, debug=false) {
27
27
  // 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
28
28
  let denominator, a, b, numerator1, numerator2;
29
29
  let intersectionPoint = {}
30
30
 
31
+ if(!p1 || !p2 || !p3 || !p4){
32
+ if(debug) console.warn('points missing');
33
+ return false
34
+ }
35
+
31
36
  try {
32
37
  denominator = ((p4.y - p3.y) * (p2.x - p1.x)) - ((p4.x - p3.x) * (p2.y - p1.y));
33
38
  if (denominator == 0) {
34
39
  return false;
35
40
  }
36
-
37
41
  } catch {
38
- console.log('!catch', p1, p2, 'p3:', p3, p4);
42
+ if(debug) console.warn('!catch', p1, p2, 'p3:', p3, 'p4:', p4);
43
+ return false
39
44
  }
40
45
 
41
46
  a = p1.y - p3.y;
@@ -52,9 +57,6 @@ export function checkLineIntersection(p1, p2, p3, p4, exact = true) {
52
57
  y: p1.y + (a * (p2.y - p1.y))
53
58
  }
54
59
 
55
- // console.log('intersectionPoint', intersectionPoint, p1, p2);
56
-
57
-
58
60
 
59
61
  let intersection = false;
60
62
  // if line1 is a segment and line2 is infinite, they intersect if:
@@ -874,156 +876,8 @@ export function intersectLines(p1, p2, p3, p4) {
874
876
 
875
877
 
876
878
 
877
- /**
878
- * check polygon flatness helper
879
- * basically a reduced shoelace algorithm
880
- */
881
- export function commandIsFlat0(points, tolerance = 0.025) {
882
-
883
-
884
- let xArr = points.map(pt => { return pt.x })
885
- let yArr = points.map(pt => { return pt.y })
886
-
887
- let xMin = Math.min(...xArr)
888
- let xMax = Math.max(...xArr)
889
- let yMin = Math.min(...yArr)
890
- let yMax = Math.max(...yArr)
891
- let w = xMax - xMin
892
- let h = yMax - yMin
893
-
894
-
895
- if (points.length < 3 || (w === 0 || h === 0)) {
896
- return { area: 0, flat: true, thresh: 0.0001, ratio: 0 };
897
- }
898
-
899
- tolerance = 0.5;
900
- let thresh = (w + h) * 0.5 * tolerance;
901
-
902
-
903
- //let thresh = tolerance;
904
- //console.log('w,h', w, h, thresh);
905
-
906
- let area = 0;
907
- for (let i = 0; i < points.length; i++) {
908
- let addX = points[i].x;
909
- let addY = points[i === points.length - 1 ? 0 : i + 1].y;
910
- let subX = points[i === points.length - 1 ? 0 : i + 1].x;
911
- let subY = points[i].y;
912
- area += addX * addY * 0.5 - subX * subY * 0.5;
913
- }
914
-
915
- //console.log('flatArea', area, points);
916
- area = +Math.abs(area).toFixed(9);
917
-
918
- let ratio = area / thresh;
919
- let isFlat = area === 0 ? true : (ratio < 0.15 ? true : false);
920
- //isFlat= false
921
-
922
- return { area: area, flat: isFlat, thresh: thresh, ratio: ratio };
923
- }
924
-
925
-
926
- export function commandIsFlat(points, tolerance = 0.025) {
927
-
928
- let p0 = points[0];
929
- let p = points[points.length - 1];
930
-
931
- let xArr = points.map(pt => { return pt.x })
932
- let yArr = points.map(pt => { return pt.y })
933
-
934
- let xMin = Math.min(...xArr)
935
- let xMax = Math.max(...xArr)
936
- let yMin = Math.min(...yArr)
937
- let yMax = Math.max(...yArr)
938
- let w = xMax - xMin
939
- let h = yMax - yMin
940
-
941
-
942
- if (points.length < 3 || (w === 0 || h === 0)) {
943
- return { area: 0, flat: true, thresh: 0.0001, ratio: 0 };
944
- }
945
-
946
- let squareDist = getSquareDistance(p0, p)
947
- let squareDist1 = getSquareDistance(p0, points[0])
948
- let squareDist2 = points.length > 3 ? getSquareDistance(p, points[1]) : squareDist1;
949
- let squareDistAvg = (squareDist1 + squareDist2) / 2
950
-
951
- tolerance = 0.5;
952
- let thresh = (w + h) * 0.5 * tolerance;
953
-
954
-
955
- //let thresh = tolerance;
956
- //console.log('w,h', w, h, thresh);
957
-
958
- let area = 0;
959
- for (let i = 0, l = points.length; i < l; i++) {
960
- let addX = points[i].x;
961
- let addY = points[i === points.length - 1 ? 0 : i + 1].y;
962
- let subX = points[i === points.length - 1 ? 0 : i + 1].x;
963
- let subY = points[i].y;
964
- area += addX * addY * 0.5 - subX * subY * 0.5;
965
- }
966
-
967
- //console.log('flatArea', area, points);
968
- area = +Math.abs(area).toFixed(9);
969
-
970
- let diff = Math.abs(area - squareDist);
971
- let areaDiff = Math.abs(100 - (100 / area * (area + diff)))
972
- let areaThresh = 1000
973
-
974
- //let ratio = area / (squareDistAvg/areaThresh);
975
- let ratio = area / (squareDistAvg);
976
-
977
-
978
- //let isFlat = area === 0 ? true : (ratio < 0.15 ? true : false);
979
- //let isFlat = area === 0 ? true : (area < squareDist/areaThresh ? true : false);
980
-
981
- let isFlat = area === 0 ? true : area < squareDistAvg / areaThresh;
982
-
983
-
984
- return { area: area, flat: isFlat, thresh: thresh, ratio: ratio, squareDist: squareDist, areaThresh: squareDist / areaThresh };
985
- }
986
-
987
-
988
- export function checkBezierFlatness(p0, cpts, p) {
989
-
990
- let isFlat = false;
991
-
992
- let isCubic = cpts.length===2;
993
-
994
- let cp1 = cpts[0]
995
- let cp2 = isCubic ? cpts[1] : cp1;
996
-
997
- if(p0.x===cp1.x && p0.y===cp1.y && p.x===cp2.x && p.y===cp2.y) return true;
998
-
999
- let dx1 = cp1.x - p0.x;
1000
- let dy1 = cp1.y - p0.y;
1001
-
1002
- let dx2 = p.x - cp2.x;
1003
- let dy2 = p.y - cp2.y;
1004
879
 
1005
- let cross1 = Math.abs(dx1 * dy2 - dy1 * dx2);
1006
880
 
1007
- if(!cross1) return true
1008
-
1009
- let dx0 = p.x - p0.x;
1010
- let dy0 = p.y - p0.y;
1011
- let cross0 = Math.abs(dx0 * dy1 - dy0 * dx1);
1012
-
1013
- if(!cross0) return true
1014
-
1015
- //let diff = Math.abs(cross0 - cross1)
1016
- //let rat0 = 1/cross0 * diff;
1017
- let rat = (cross0/cross1)
1018
-
1019
- if (rat<1.1 ) {
1020
- //console.log('cross', cross0, cross1, 'rat', rat, rat0, diff );
1021
- isFlat = true;
1022
- }
1023
-
1024
- return isFlat;
1025
-
1026
- }
1027
881
 
1028
882
  /**
1029
883
  * sloppy distance calculation
@@ -1041,7 +895,6 @@ export function getDistAv(pt1, pt2) {
1041
895
  let diff = Math.abs(diffX + diffY) / 2;
1042
896
  */
1043
897
 
1044
-
1045
898
  return diff;
1046
899
  }
1047
900