svg-path-simplify 0.2.6 → 0.3.0

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.
@@ -34,6 +34,8 @@ import { pathDataFromPoly } from './svgii/pathData_fromPoly';
34
34
  import { simplifyRDP } from './simplify_poly_RDP';
35
35
  import { harmonizeCubicCpts } from './pathData_simplify_harmonize_cpts';
36
36
  import { pathDataToPolygon } from './svgii/pathData_toPolygon';
37
+ import { pathDataLineToCubic } from './svgii/pathData_line_to_cubic';
38
+ import { fixPathDataDirections } from './svgii/pathData_fix_directions';
37
39
 
38
40
  //import { installDOMPolyfills } from './dom-polyfill';
39
41
 
@@ -66,6 +68,12 @@ export function svgPathSimplify(input = '', {
66
68
 
67
69
  refineExtremes = true,
68
70
  simplifyCorners = false,
71
+ removeDimensions = false,
72
+ removeIds = false,
73
+ removeClassNames = false,
74
+ omitNamespace = false,
75
+
76
+ fixDirections = false,
69
77
 
70
78
  keepExtremes = true,
71
79
  keepCorners = true,
@@ -80,7 +88,6 @@ export function svgPathSimplify(input = '', {
80
88
 
81
89
 
82
90
  removeOrphanSubpaths = false,
83
-
84
91
  simplifyRound = false,
85
92
 
86
93
  //svg scaling
@@ -90,32 +97,30 @@ export function svgPathSimplify(input = '', {
90
97
  alignToOrigin = false,
91
98
 
92
99
 
93
- // svg path optimizations
100
+ //svg path optimizations
94
101
  decimals = 3,
95
102
  autoAccuracy = true,
96
103
 
97
104
  minifyD = 0,
98
105
  tolerance = 1,
99
- reverse = false,
106
+ reversePath = false,
100
107
 
101
- // svg cleanup options
102
- cleanupSVGAtts=true,
108
+ //svg cleanup options
109
+ cleanupSVGAtts = true,
103
110
  removePrologue = true,
104
111
  stylesToAttributes = true,
105
112
  fixHref = true,
106
- removeNameSpaced=true,
107
- attributesToGroup=false,
113
+ removeNameSpaced = true,
114
+ attributesToGroup = false,
108
115
  mergePaths = false,
109
116
  removeHidden = true,
110
117
  removeUnused = true,
111
- shapesToPaths = true,
118
+ shapesToPaths = false,
119
+ lineToCubic = false,
112
120
 
113
121
  tMin = 0,
114
122
  tMax = 1,
115
123
 
116
- // redraw - for messed up paths
117
- redraw = false,
118
-
119
124
 
120
125
  } = {}) {
121
126
 
@@ -145,7 +150,6 @@ export function svgPathSimplify(input = '', {
145
150
  svgSize = input.length;
146
151
 
147
152
 
148
-
149
153
  /**
150
154
  * global bbox and viewBox for
151
155
  * path scaling
@@ -179,14 +183,18 @@ export function svgPathSimplify(input = '', {
179
183
  else {
180
184
  //sanitize
181
185
  let returnDom = true
182
- svg = cleanUpSVG(input, { cleanupSVGAtts, returnDom, removeHidden, removeUnused, removeNameSpaced, attributesToGroup, stylesToAttributes, removePrologue, fixHref ,mergePaths }
186
+ svg = cleanUpSVG(input, { removeIds, removeClassNames, removeDimensions, cleanupSVGAtts, returnDom, removeHidden, removeUnused, removeNameSpaced, stylesToAttributes, removePrologue, fixHref, mergePaths, shapesToPaths }
183
187
  );
184
188
 
185
189
  if (shapesToPaths) {
186
190
  let shapes = svg.querySelectorAll('polygon, polyline, line, rect, circle, ellipse');
187
191
  shapes.forEach(shape => {
188
192
  let path = shapeElToPath(shape);
193
+ //console.log('path', path.getAttribute('d'));
189
194
  shape.replaceWith(path)
195
+ //shape.parentNode.insertBefore(path, shape)
196
+ //shape.remove()
197
+ //shape = path
190
198
  })
191
199
  }
192
200
 
@@ -194,8 +202,6 @@ export function svgPathSimplify(input = '', {
194
202
  let pathEls = svg.querySelectorAll('path')
195
203
 
196
204
 
197
- //if(mergePaths){}
198
-
199
205
  pathEls.forEach(path => {
200
206
  paths.push({ d: path.getAttribute('d'), el: path })
201
207
  })
@@ -293,7 +299,6 @@ export function svgPathSimplify(input = '', {
293
299
  let pathDataSub = subPathArr[i];
294
300
 
295
301
 
296
-
297
302
  /**
298
303
  * convert cureves to polygon
299
304
  * flattening
@@ -361,23 +366,6 @@ export function svgPathSimplify(input = '', {
361
366
  // remove zero length linetos
362
367
  if (removeColinear || removeZeroLength) pathDataSub = removeZeroLengthLinetos(pathDataSub)
363
368
 
364
- /**
365
- * try to redraw messed up paths
366
- * based on significant points suchas
367
- * extremes, semi-extremes and corners
368
- */
369
- if (redraw) {
370
- addExtremes = true
371
- addSemiExtremes = true
372
- simplifyCorners = false
373
- keepCorners = true
374
- keepExtremes = true
375
- optimizeOrder = true
376
- //simplifyBezier = false
377
- tMin = 0
378
- tMax = 0
379
- }
380
-
381
369
 
382
370
  // sort to top left
383
371
  if (optimizeOrder) pathDataSub = pathDataToTopLeft(pathDataSub);
@@ -391,6 +379,12 @@ export function svgPathSimplify(input = '', {
391
379
 
392
380
 
393
381
 
382
+ // reverse
383
+ if(reversePath) {
384
+ pathDataSub = reversePathData(pathDataSub)
385
+ }
386
+
387
+
394
388
  // analyze pathdata to add info about signicant properties such as extremes, corners
395
389
  let pathDataPlus = analyzePathData(pathDataSub, {
396
390
  detectSemiExtremes: addSemiExtremes,
@@ -408,7 +402,7 @@ export function svgPathSimplify(input = '', {
408
402
  if (refineClosing) pathData = refineClosingCommand(pathData, { threshold: dimA * 0.001 })
409
403
 
410
404
 
411
- pathData = simplifyBezier ? simplifyPathDataCubic(pathData, { simplifyBezier, keepInflections, keepExtremes, keepCorners, extrapolateDominant, revertToQuadratics, tolerance, reverse }) : pathData;
405
+ pathData = simplifyBezier ? simplifyPathDataCubic(pathData, { simplifyBezier, keepInflections, keepExtremes, keepCorners, extrapolateDominant, revertToQuadratics, tolerance }) : pathData;
412
406
 
413
407
 
414
408
  // refine extremes
@@ -419,34 +413,6 @@ export function svgPathSimplify(input = '', {
419
413
  }
420
414
 
421
415
 
422
- /**
423
- * try redrawing
424
- */
425
-
426
- if (redraw) {
427
-
428
- //pathData = pathDataRemoveColinear(pathData, { tolerance, flatBezierToLinetos: false });
429
- /*
430
- pathData = addExtremePoints(pathData,
431
- { tMin: 0, tMax: 1, addExtremes: true, addSemiExtremes: true })
432
-
433
- pathData = analyzePathData(pathDataSub, {
434
- detectSemiExtremes: true,
435
- detectExtremes: true,
436
- }).pathData;
437
-
438
- */
439
-
440
-
441
- let thresholdEx = (bb.width + bb.height) * 0.1
442
- //pathData = refineAdjacentExtremes(pathData, { threshold: thresholdEx, tolerance })
443
- pathData = redrawPathData(pathData, { tolerance, threshold: dimA * 0.001 })
444
-
445
- //simplifyPathDataCubic(pathData, { simplifyBezier, keepInflections, keepExtremes, keepCorners, extrapolateDominant, revertToQuadratics, tolerance, reverse })
446
-
447
- }
448
-
449
-
450
416
  // cubic to arcs
451
417
  if (cubicToArc) pathData = pathDataCubicsToArc(pathData, { areaThreshold: 2.5 })
452
418
 
@@ -464,7 +430,7 @@ export function svgPathSimplify(input = '', {
464
430
  let threshold = (bb.width + bb.height) * 0.1
465
431
  pathData = refineRoundedCorners(pathData, { threshold, tolerance })
466
432
  }
467
-
433
+
468
434
  // refine round segment sequences
469
435
  if (simplifyRound) pathData = refineRoundSegments(pathData);
470
436
 
@@ -472,12 +438,14 @@ export function svgPathSimplify(input = '', {
472
438
  // simplify to quadratics
473
439
  if (revertToQuadratics) pathData = pathDataRevertCubicToQuadratic(pathData, tolerance);
474
440
 
441
+ if (lineToCubic) pathData = pathDataLineToCubic(pathData);
442
+
443
+
475
444
  // optimize close path
476
445
  if (optimizeOrder) pathData = optimizeClosePath(pathData, { autoClose })
477
446
 
447
+
478
448
  // update
479
- //pathDataFlat.push(...pathData)
480
- //subPathArr[i]=pathData
481
449
  pathDataPlusArr.push({ pathData, bb })
482
450
 
483
451
  } // end sup paths
@@ -500,25 +468,28 @@ export function svgPathSimplify(input = '', {
500
468
  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)
501
469
  }
502
470
 
471
+
472
+ // fix path directions
473
+ if (fixDirections) {
474
+ pathDataPlusArr = fixPathDataDirections(pathDataPlusArr);
475
+ }
476
+
477
+
503
478
  // flatten compound paths
504
479
  pathData = [];
505
480
  pathDataPlusArr.forEach(sub => {
506
481
  pathData.push(...sub.pathData)
507
482
  })
508
483
 
509
- //pathData = pathDataFlat;
510
- //pathData = subPathArr.flat();
511
- //console.log('pathData', pathData);
512
484
 
513
485
  if (autoAccuracy) {
514
486
  decimals = detectAccuracy(pathData)
515
487
  pathOptions.decimals = decimals
516
- //console.log('!decimals', decimals);
517
488
  }
518
489
 
519
490
 
520
491
  // collect for merged svg paths
521
- mergePaths= false
492
+ mergePaths = false
522
493
  if (el && mergePaths) {
523
494
  pathData_merged.push(...pathData)
524
495
  }
@@ -574,6 +545,7 @@ export function svgPathSimplify(input = '', {
574
545
  */
575
546
  if (mode) {
576
547
 
548
+
577
549
  if (pathData_merged.length) {
578
550
 
579
551
  // optimize path data
@@ -584,7 +556,6 @@ export function svgPathSimplify(input = '', {
584
556
 
585
557
  let dOpt = pathDataToD(pathData, minifyD)
586
558
 
587
-
588
559
  // apply new path for svgs
589
560
  paths[0].el.setAttribute('d', dOpt)
590
561
 
@@ -625,8 +596,19 @@ export function svgPathSimplify(input = '', {
625
596
  }
626
597
  }
627
598
 
599
+ // remove fill rules
600
+ if (fixDirections) {
601
+ let elsFill = svg.querySelectorAll('path[fill-rule], path[clip-rule]');
602
+ elsFill.forEach(el => {
603
+ el.removeAttribute('fill-rule')
604
+ el.removeAttribute('clip-rule')
605
+ })
606
+ }
607
+
608
+
609
+ svg = stringifySVG(svg, omitNamespace);
610
+
628
611
 
629
- svg = stringifySVG(svg);
630
612
  //svgSizeOpt = new Blob([svg]).size
631
613
  svgSizeOpt = svg.length;
632
614
  //compression = +(100/svgSize * (svgSize-svgSizeOpt)).toFixed(2)
@@ -0,0 +1,274 @@
1
+ import { detectInputType } from './detect_input';
2
+ import { simplifyPathDataCubic } from './pathData_simplify_cubic';
3
+ import { analyzePathData } from './svgii/pathData_analyze';
4
+ import { convertPathData } from './svgii/pathData_convert';
5
+ import { parsePathDataNormalized } from './svgii/pathData_parse';
6
+ import { pathDataRemoveColinear } from './svgii/pathData_remove_collinear';
7
+ import { removeOrphanedM } from './svgii/pathData_remove_orphaned';
8
+ import { removeZeroLengthLinetos } from './svgii/pathData_remove_zerolength';
9
+ import { optimizeClosePath, pathDataToTopLeft } from './svgii/pathData_reorder';
10
+ import { addExtremePoints, splitSubpaths } from './svgii/pathData_split';
11
+ import { pathDataToD } from './svgii/pathData_stringify';
12
+ //import { pathDataToPolyPlus, pathDataToPolySingle } from './svgii/pathData_toPolygon';
13
+ import { detectAccuracy } from './svgii/rounding';
14
+ import { refineAdjacentExtremes } from './svgii/pathData_simplify_refineExtremes';
15
+ import { refineRoundedCorners } from './svgii/pathData_simplify_refineCorners';
16
+ import { refineRoundSegments } from './svgii/pathData_refine_round';
17
+ import { refineClosingCommand } from './svgii/pathData_remove_short';
18
+ import { pathDataRevertCubicToQuadratic } from './pathData_simplify_revertToquadratics';
19
+ import { pathDataLineToCubic } from './svgii/pathData_line_to_cubic';
20
+
21
+ //import { installDOMPolyfills } from './dom-polyfill';
22
+
23
+ export function simplifyPathData(input = '', {
24
+
25
+ toAbsolute = true,
26
+ toRelative = true,
27
+ toShorthands = true,
28
+ //optimize = 0,
29
+
30
+ // not necessary unless you need cubics only
31
+ quadraticToCubic = true,
32
+
33
+ // mostly a fallback if arc calculations fail
34
+ arcToCubic = false,
35
+ cubicToArc = false,
36
+
37
+
38
+ simplifyBezier = true,
39
+ optimizeOrder = true,
40
+ autoClose = true,
41
+ removeZeroLength = true,
42
+ refineClosing = true,
43
+ removeColinear = true,
44
+ flatBezierToLinetos = true,
45
+ revertToQuadratics = true,
46
+
47
+ refineExtremes = true,
48
+ simplifyCorners = false,
49
+ fixDirections = false,
50
+
51
+ keepExtremes = true,
52
+ keepCorners = true,
53
+ keepInflections = false,
54
+ addExtremes = false,
55
+ addSemiExtremes = false,
56
+
57
+ harmonizeCpts = false,
58
+ toPolygon = false,
59
+ removeOrphanSubpaths = false,
60
+ simplifyRound = false,
61
+
62
+ //svg path optimizations
63
+ decimals = 3,
64
+ autoAccuracy = true,
65
+
66
+ minifyD = 0,
67
+ tolerance = 1,
68
+
69
+ lineToCubic = false,
70
+ // return svg markup or object
71
+ getObject = false,
72
+
73
+ } = {}) {
74
+
75
+
76
+ // clamp tolerance and scale
77
+ tolerance = Math.max(0.1, tolerance);
78
+
79
+ let compression = 0;
80
+ let report = {};
81
+ let d = '';
82
+
83
+ /**
84
+ * normalize input
85
+ * switch mode
86
+ */
87
+
88
+
89
+ /**
90
+ * global bbox and viewBox for
91
+ * path scaling
92
+ * sorting and cropping
93
+ */
94
+ let viewBox = { x: 0, y: 0, width: 0, height: 0 }
95
+ let bb_global = { x: 0, y: 0, width: 0, height: 0 }
96
+ let xArr = []
97
+ let yArr = []
98
+
99
+ // mode:0 – single path
100
+ let inputType = detectInputType(input)
101
+ if (inputType === 'pathDataString') {
102
+ d = input
103
+ } else if (inputType === 'polyString') {
104
+ d = 'M' + input
105
+ }
106
+ else if (inputType === 'pathData') {
107
+ d = input;
108
+ }else{
109
+ return false
110
+ }
111
+
112
+
113
+ /**
114
+ * process all paths
115
+ * try simplifications and removals
116
+ */
117
+
118
+ // SVG optimization options
119
+ let pathOptions = {
120
+ toRelative,
121
+ toShorthands,
122
+ decimals,
123
+ }
124
+
125
+
126
+ let pathData = parsePathDataNormalized(d, { quadraticToCubic, toAbsolute, arcToCubic });
127
+
128
+
129
+ // count commands for evaluation
130
+ let comCount = pathData.length
131
+
132
+ if (removeOrphanSubpaths) pathData = removeOrphanedM(pathData);
133
+
134
+
135
+ /**
136
+ * get sub paths
137
+ */
138
+ let subPathArr = splitSubpaths(pathData);
139
+ let lenSub = subPathArr.length;
140
+
141
+
142
+ // reset array
143
+ let pathDataPlusArr = []
144
+
145
+ // loop sub paths
146
+ for (let i = 0; i < lenSub; i++) {
147
+
148
+ //let { pathData, bb } = subPathArr[i];
149
+ let pathDataSub = subPathArr[i];
150
+
151
+
152
+ // remove zero length linetos
153
+ if (removeColinear || removeZeroLength) pathDataSub = removeZeroLengthLinetos(pathDataSub)
154
+
155
+
156
+ // sort to top left
157
+ if (optimizeOrder) pathDataSub = pathDataToTopLeft(pathDataSub);
158
+
159
+
160
+ // Preprocessing: remove colinear - ignore flat beziers (removed later)
161
+ if (removeColinear) pathDataSub = pathDataRemoveColinear(pathDataSub, { tolerance, flatBezierToLinetos: false });
162
+
163
+ if (addExtremes || addSemiExtremes) pathDataSub = addExtremePoints(pathDataSub,
164
+ { tMin, tMax, addExtremes, addSemiExtremes, angles: [30] })
165
+
166
+
167
+ // analyze pathdata to add info about signicant properties such as extremes, corners
168
+ let pathDataPlus = analyzePathData(pathDataSub, {
169
+ detectSemiExtremes: addSemiExtremes,
170
+ });
171
+
172
+
173
+ // simplify beziers
174
+ let { pathData, bb, dimA } = pathDataPlus;
175
+ xArr.push(bb.x, bb.x + bb.width)
176
+ yArr.push(bb.y, bb.y + bb.height)
177
+
178
+
179
+ if (refineClosing) pathData = refineClosingCommand(pathData, { threshold: dimA * 0.001 })
180
+
181
+ pathData = simplifyBezier ? simplifyPathDataCubic(pathData, { simplifyBezier, keepInflections, keepExtremes, keepCorners, revertToQuadratics, tolerance }) : pathData;
182
+
183
+
184
+ // refine extremes
185
+ if (refineExtremes) {
186
+ //let thresholdEx = (bb.width + bb.height) / 2 * 0.05
187
+ let thresholdEx = (bb.width + bb.height) * 0.05
188
+ pathData = refineAdjacentExtremes(pathData, { threshold: thresholdEx, tolerance })
189
+ }
190
+
191
+
192
+
193
+ // post processing: remove flat beziers
194
+ if (removeColinear && flatBezierToLinetos) {
195
+ pathData = pathDataRemoveColinear(pathData, { tolerance, flatBezierToLinetos });
196
+ }
197
+
198
+
199
+ // refine corners
200
+ if (simplifyCorners) {
201
+
202
+ let threshold = (bb.width + bb.height) * 0.1
203
+ pathData = refineRoundedCorners(pathData, { threshold, tolerance })
204
+ }
205
+
206
+ // refine round segment sequences
207
+ if (simplifyRound) pathData = refineRoundSegments(pathData);
208
+
209
+
210
+ // simplify to quadratics
211
+ if (revertToQuadratics) pathData = pathDataRevertCubicToQuadratic(pathData, tolerance);
212
+
213
+ if (lineToCubic) pathData = pathDataLineToCubic(pathData);
214
+
215
+
216
+ // optimize close path
217
+ if (optimizeOrder) pathData = optimizeClosePath(pathData, { autoClose })
218
+
219
+
220
+ // update
221
+ pathDataPlusArr.push({ pathData, bb })
222
+
223
+ } // end sup paths
224
+
225
+ // sort subpaths to top left
226
+ let xMin = Math.min(...xArr)
227
+ let yMin = Math.min(...yArr)
228
+ let xMax = Math.max(...xArr)
229
+ let yMax = Math.max(...yArr)
230
+
231
+ bb_global = { x: xMin, y: yMin, width: xMax - xMin, height: yMax - yMin }
232
+ let isPortrait = bb_global.height > bb_global.width;
233
+
234
+
235
+ // prefer top to bottom priority for portrait aspect ratios
236
+ if (optimizeOrder) {
237
+ 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)
238
+ }
239
+
240
+
241
+ // flatten compound paths
242
+ pathData = [];
243
+ pathDataPlusArr.forEach(sub => {
244
+ pathData.push(...sub.pathData)
245
+ })
246
+
247
+
248
+ if (autoAccuracy) {
249
+ decimals = detectAccuracy(pathData)
250
+ pathOptions.decimals = decimals
251
+ }
252
+
253
+
254
+ // optimize path data
255
+ pathData = convertPathData(pathData, pathOptions)
256
+
257
+ // remove zero-length segments introduced by rounding
258
+ pathData = removeZeroLengthLinetos(pathData);
259
+
260
+ let dOpt = pathDataToD(pathData, minifyD)
261
+
262
+ // remove custom properties
263
+ if(getObject){
264
+ pathData = pathData.map(com=>{return {type:com.type, values:com.values}});
265
+ }
266
+
267
+ return !getObject ? dOpt : pathData;
268
+
269
+ }
270
+
271
+
272
+
273
+
274
+
@@ -117,7 +117,7 @@ export function getDeltaAngle(centerPoint, startPoint, endPoint, largeArc = fals
117
117
  * http://jsfiddle.net/justin_c_rounds/Gd2S2/light/
118
118
  */
119
119
 
120
- export function checkLineIntersection(p1 = null, p2 = null, p3 = null, p4 = null, exact = true, debug = false) {
120
+ export function checkLineIntersection(p1 = null, p2 = null, p3 = null, p4 = null, exact = true, respectDirection = false, debug = false) {
121
121
  // 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
122
122
  let denominator, a, b, numerator1, numerator2;
123
123
  let intersectionPoint = {}
@@ -129,7 +129,9 @@ export function checkLineIntersection(p1 = null, p2 = null, p3 = null, p4 = null
129
129
 
130
130
  try {
131
131
  denominator = ((p4.y - p3.y) * (p2.x - p1.x)) - ((p4.x - p3.x) * (p2.y - p1.y));
132
- if (denominator == 0) {
132
+
133
+ // parallel or colinear
134
+ if (denominator === 0) {
133
135
  return false;
134
136
  }
135
137
  } catch {
@@ -151,7 +153,6 @@ export function checkLineIntersection(p1 = null, p2 = null, p3 = null, p4 = null
151
153
  y: p1.y + (a * (p2.y - p1.y))
152
154
  }
153
155
 
154
-
155
156
  let intersection = false;
156
157
  // if line1 is a segment and line2 is infinite, they intersect if:
157
158
  if ((a > 0 && a < 1) && (b > 0 && b < 1)) {
@@ -159,8 +160,14 @@ export function checkLineIntersection(p1 = null, p2 = null, p3 = null, p4 = null
159
160
  //console.log('line inters');
160
161
  }
161
162
 
163
+ // direction
164
+ if (!exact && respectDirection && ((a > 0 && b < 0) || (a < 0 && b > 0))) {
165
+ intersection = false;
166
+ return false
167
+ }
168
+
169
+
162
170
  if (exact && !intersection) {
163
- //console.log('no line inters');
164
171
  return false;
165
172
  }
166
173
 
@@ -170,6 +177,43 @@ export function checkLineIntersection(p1 = null, p2 = null, p3 = null, p4 = null
170
177
  };
171
178
 
172
179
 
180
+ /** Get relationship between a point and a polygon using ray-casting algorithm
181
+ * based on timepp's answer
182
+ * https://stackoverflow.com/questions/217578/how-can-i-determine-whether-a-2d-point-is-within-a-polygon#63436180
183
+ */
184
+ export function isPointInPolygon(pt, polygon, bb, skipBB = false) {
185
+ const between = (p, a, b) => (p >= a && p <= b) || (p <= a && p >= b);
186
+ let inside = false;
187
+
188
+ // not in bbox - quit || no bbox defined
189
+ if (!skipBB || !bb.bottom) {
190
+ if (bb.left > pt.x || bb.top > pt.y || bb.bottom < pt.y || bb.right < pt.x) {
191
+ return false;
192
+ }
193
+ }
194
+
195
+ let l=polygon.length;
196
+ for (let i = l - 1, j = 0; j < l; i = j, j++) {
197
+ const A = polygon[i];
198
+ const B = polygon[j];
199
+ // corner cases
200
+ if ((pt.x == A.x && pt.y == A.y) || (pt.x == B.x && pt.y == B.y))
201
+ return true;
202
+ if (A.y == B.y && pt.y == A.y && between(pt.x, A.x, B.x)) return true;
203
+ if (between(pt.y, A.y, B.y)) {
204
+ /**
205
+ * if pt inside the vertical range filter out "ray pass vertex" problem
206
+ * by treating the line a little lower
207
+ */
208
+ if ((pt.y == A.y && B.y >= A.y) || (pt.y == B.y && A.y >= B.y)) continue;
209
+ // calc cross product `ptA X ptB`, pt lays on left side of AB if c > 0
210
+ const c = (A.x - pt.x) * (B.y - pt.y) - (B.x - pt.x) * (A.y - pt.y);
211
+ if (c == 0) return true;
212
+ if (A.y < B.y == c > 0) inside = !inside;
213
+ }
214
+ }
215
+ return inside ? true : false;
216
+ }
173
217
 
174
218
 
175
219
 
@@ -1,5 +1,5 @@
1
1
  import { splitSubpaths } from './pathData_split.js';
2
- import { getAngle, bezierhasExtreme, getPathDataVertices, svgArcToCenterParam, getSquareDistance, getDistManhattan, isMultipleOf45, pointAtT, getTatAngles } from "./geometry.js";
2
+ import { getAngle, bezierhasExtreme, getPathDataVertices, svgArcToCenterParam, getSquareDistance, getDistManhattan, isMultipleOf45, pointAtT, getTatAngles, checkLineIntersection } from "./geometry.js";
3
3
  import { getPolygonArea, getPathArea } from './geometry_area.js';
4
4
  import { getPolyBBox } from './geometry_bbox.js';
5
5
  import { renderPoint, renderPath } from "./visualize.js";
@@ -86,6 +86,7 @@ export function analyzePathData(pathData = [], {
86
86
  // check flatness of command
87
87
  let toleranceFlat = 0.01;
88
88
  let thresholdLength = dimA * 0.1
89
+ let threshold = thresholdLength*0.01
89
90
  let areaThresh = squareDist * toleranceFlat;
90
91
  let isFlat = Math.abs(cptArea) < areaThresh;
91
92
 
@@ -108,10 +109,12 @@ export function analyzePathData(pathData = [], {
108
109
  let dx = type === 'C' ? Math.abs(com.cp2.x - com.p.x) : Math.abs(com.cp1.x - com.p.x)
109
110
  let dy = type === 'C' ? Math.abs(com.cp2.y - com.p.y) : Math.abs(com.cp1.y - com.p.y)
110
111
 
111
- let horizontal = dy === 0 && dx > 0
112
- let vertical = dx === 0 && dy > 0
112
+ let horizontal = (dy === 0 || dy<threshold ) && dx > 0
113
+ let vertical = (dx === 0 || dx<threshold ) && dy > 0
113
114
 
114
- if (horizontal || vertical) hasExtremes = true;
115
+ if (horizontal || vertical) {
116
+ hasExtremes = true;
117
+ }
115
118
 
116
119
  // is extreme relative to bounding box
117
120
  if ((p.x === left || p.y === top || p.x === right || p.y === bottom)) {
@@ -123,7 +126,7 @@ export function analyzePathData(pathData = [], {
123
126
  let couldHaveExtremes = bezierhasExtreme(null, commandPts)
124
127
  if (couldHaveExtremes) {
125
128
  let tArr = getTatAngles(commandPts)
126
- if (tArr.length && (tArr[0] > 0.15)) {
129
+ if (tArr.length && (tArr[0] > 0.2)) {
127
130
  hasExtremes = true;
128
131
  }
129
132
  }
@@ -183,7 +186,6 @@ export function analyzePathData(pathData = [], {
183
186
  }
184
187
  }
185
188
 
186
-
187
189
  //debug = true;
188
190
  if (debug) {
189
191
  //if (com.semiExtreme) renderPoint(markers, com.p, 'blue', '2%', '0.5')