svg-path-simplify 0.0.8 → 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.
- package/README.md +25 -5
- package/dist/svg-path-simplify.esm.js +576 -494
- package/dist/svg-path-simplify.esm.min.js +1 -1
- package/dist/svg-path-simplify.js +576 -494
- package/dist/svg-path-simplify.min.js +1 -1
- package/dist/svg-path-simplify.node.js +576 -494
- package/dist/svg-path-simplify.node.min.js +1 -1
- package/index.html +86 -29
- package/package.json +1 -1
- package/src/detect_input.js +17 -10
- package/src/index.js +3 -0
- package/src/pathData_simplify_cubic.js +113 -106
- package/src/pathData_simplify_cubic_extrapolate.js +25 -11
- package/src/pathSimplify-main.js +89 -182
- package/src/svgii/geometry_flatness.js +29 -36
- package/src/svgii/pathData_analyze.js +4 -0
- package/src/svgii/pathData_convert.js +26 -17
- package/src/svgii/pathData_interpolate.js +65 -0
- package/src/svgii/pathData_parse.js +25 -9
- package/src/svgii/pathData_parse_els.js +18 -12
- package/src/svgii/pathData_remove_collinear.js +31 -28
- package/src/svgii/pathData_remove_orphaned.js +5 -4
- package/src/svgii/pathData_remove_zerolength.js +8 -4
- package/src/svgii/pathData_reorder.js +6 -2
- package/src/svgii/pathData_simplify_refineCorners.js +160 -0
- package/src/svgii/{simplify_refineExtremes.js → pathData_simplify_refineExtremes.js} +78 -43
- package/src/svgii/pathData_split.js +42 -15
- package/src/svgii/pathData_stringify.js +3 -12
- package/src/svgii/rounding.js +16 -14
- package/src/svgii/svg_cleanup.js +1 -1
- package/src/pathData_simplify_cubic_arr.js +0 -50
- package/src/svgii/simplify.js +0 -248
- package/src/svgii/simplify_bezier.js +0 -470
- package/src/svgii/simplify_linetos.js +0 -93
package/src/pathSimplify-main.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { detectInputType } from './detect_input';
|
|
2
|
-
import {
|
|
2
|
+
import { simplifyPathDataCubic } from './pathData_simplify_cubic';
|
|
3
3
|
import { getPathDataVertices, pointAtT } from './svgii/geometry';
|
|
4
4
|
import { getPolyBBox } from './svgii/geometry_bbox';
|
|
5
5
|
import { analyzePathData } from './svgii/pathData_analyze';
|
|
@@ -17,9 +17,10 @@ import { pathDataToD } from './svgii/pathData_stringify';
|
|
|
17
17
|
import { analyzePoly } from './svgii/poly_analyze';
|
|
18
18
|
import { getCurvePathData } from './svgii/poly_to_pathdata';
|
|
19
19
|
import { detectAccuracy } from './svgii/rounding';
|
|
20
|
-
import { refineAdjacentExtremes } from './svgii/
|
|
20
|
+
import { refineAdjacentExtremes } from './svgii/pathData_simplify_refineExtremes';
|
|
21
21
|
import { cleanUpSVG, removeEmptySVGEls, stringifySVG } from './svgii/svg_cleanup';
|
|
22
22
|
import { renderPoint } from './svgii/visualize';
|
|
23
|
+
import { refineRoundedCorners } from './svgii/pathData_simplify_refineCorners';
|
|
23
24
|
|
|
24
25
|
export function svgPathSimplify(input = '', {
|
|
25
26
|
|
|
@@ -41,11 +42,14 @@ export function svgPathSimplify(input = '', {
|
|
|
41
42
|
|
|
42
43
|
simplifyBezier = true,
|
|
43
44
|
optimizeOrder = true,
|
|
45
|
+
removeZeroLength = true,
|
|
44
46
|
removeColinear = true,
|
|
45
47
|
flatBezierToLinetos = true,
|
|
46
48
|
revertToQuadratics = true,
|
|
47
49
|
|
|
48
50
|
refineExtremes = true,
|
|
51
|
+
refineCorners = false,
|
|
52
|
+
|
|
49
53
|
keepExtremes = true,
|
|
50
54
|
keepCorners = true,
|
|
51
55
|
extrapolateDominant = true,
|
|
@@ -83,37 +87,49 @@ export function svgPathSimplify(input = '', {
|
|
|
83
87
|
let report = {};
|
|
84
88
|
let d = '';
|
|
85
89
|
let mode = inputType === 'svgMarkup' ? 1 : 0;
|
|
90
|
+
//console.log(inputType);
|
|
86
91
|
|
|
87
92
|
let paths = []
|
|
88
93
|
|
|
89
|
-
|
|
90
94
|
/**
|
|
91
95
|
* normalize input
|
|
92
96
|
* switch mode
|
|
93
97
|
*/
|
|
94
98
|
|
|
95
99
|
// original size
|
|
96
|
-
svgSize = new Blob([input]).size;
|
|
100
|
+
//svgSize = new Blob([input]).size;
|
|
101
|
+
svgSize = input.length;
|
|
102
|
+
|
|
97
103
|
|
|
98
|
-
// single path
|
|
104
|
+
// mode:0 – single path
|
|
99
105
|
if (!mode) {
|
|
100
106
|
if (inputType === 'pathDataString') {
|
|
101
107
|
d = input
|
|
102
108
|
} else if (inputType === 'polyString') {
|
|
103
109
|
d = 'M' + input
|
|
104
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
|
+
|
|
105
121
|
paths.push({ d, el: null })
|
|
106
122
|
}
|
|
107
|
-
// process svg
|
|
123
|
+
// mode:1 – process complete svg DOM
|
|
108
124
|
else {
|
|
109
125
|
//sanitize
|
|
110
126
|
let returnDom = true
|
|
111
127
|
svg = cleanUpSVG(input, { returnDom, removeHidden, removeUnused }
|
|
112
128
|
);
|
|
113
129
|
|
|
114
|
-
if(shapesToPaths){
|
|
130
|
+
if (shapesToPaths) {
|
|
115
131
|
let shapes = svg.querySelectorAll('polygon, polyline, line, rect, circle, ellipse');
|
|
116
|
-
shapes.forEach(shape=>{
|
|
132
|
+
shapes.forEach(shape => {
|
|
117
133
|
let path = shapeElToPath(shape);
|
|
118
134
|
shape.replaceWith(path)
|
|
119
135
|
})
|
|
@@ -126,11 +142,11 @@ export function svgPathSimplify(input = '', {
|
|
|
126
142
|
})
|
|
127
143
|
}
|
|
128
144
|
|
|
129
|
-
|
|
130
|
-
//console.log('inputType', inputType, 'mode', mode);
|
|
145
|
+
|
|
131
146
|
|
|
132
147
|
/**
|
|
133
148
|
* process all paths
|
|
149
|
+
* try simplifications and removals
|
|
134
150
|
*/
|
|
135
151
|
|
|
136
152
|
// SVG optimization options
|
|
@@ -143,44 +159,43 @@ export function svgPathSimplify(input = '', {
|
|
|
143
159
|
// combinded path data for SVGs with mergePaths enabled
|
|
144
160
|
let pathData_merged = [];
|
|
145
161
|
|
|
146
|
-
paths.
|
|
162
|
+
for (let i = 0, l = paths.length; l && i < l; i++) {
|
|
163
|
+
|
|
164
|
+
let path = paths[i];
|
|
147
165
|
let { d, el } = path;
|
|
148
166
|
|
|
149
|
-
|
|
150
|
-
let pathDataO = parsePathDataNormalized(d, { quadraticToCubic, toAbsolute, arcToCubic });
|
|
151
|
-
//console.log('pathDataO', pathDataO);
|
|
167
|
+
let pathData = parsePathDataNormalized(d, { quadraticToCubic, toAbsolute, arcToCubic });
|
|
152
168
|
|
|
153
169
|
// count commands for evaluation
|
|
154
|
-
let comCount =
|
|
170
|
+
let comCount = pathData.length
|
|
155
171
|
|
|
156
172
|
|
|
157
|
-
|
|
158
|
-
//let pathData = JSON.parse(JSON.stringify(pathDataO));
|
|
159
|
-
let pathData = pathDataO;
|
|
160
|
-
//let t1 = performance.now() - t0;
|
|
161
|
-
//console.log('t1', t1);
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
if(removeOrphanSubpaths) pathData = removeOrphanedM(pathData);
|
|
173
|
+
if (removeOrphanSubpaths) pathData = removeOrphanedM(pathData);
|
|
165
174
|
|
|
166
175
|
/**
|
|
167
176
|
* get sub paths
|
|
168
177
|
*/
|
|
169
178
|
let subPathArr = splitSubpaths(pathData);
|
|
179
|
+
let lenSub = subPathArr.length;
|
|
180
|
+
|
|
170
181
|
|
|
171
182
|
// cleaned up pathData
|
|
172
|
-
let pathDataArrN =
|
|
183
|
+
//let pathDataArrN = new Array(lenSub);
|
|
184
|
+
|
|
185
|
+
// reset array
|
|
186
|
+
let pathDataFlat = []
|
|
187
|
+
|
|
188
|
+
for (let i = 0; i < lenSub; i++) {
|
|
173
189
|
|
|
174
|
-
for (let i = 0, l = subPathArr.length; i < l; i++) {
|
|
175
190
|
|
|
176
191
|
//let { pathData, bb } = subPathArr[i];
|
|
177
192
|
let pathDataSub = subPathArr[i];
|
|
178
193
|
|
|
179
|
-
// try simplification in reversed order
|
|
180
|
-
if (reverse) pathDataSub = reversePathData(pathDataSub);
|
|
181
194
|
|
|
182
195
|
// remove zero length linetos
|
|
183
|
-
if (removeColinear) pathDataSub = removeZeroLengthLinetos(pathDataSub)
|
|
196
|
+
if (removeColinear || removeZeroLength) pathDataSub = removeZeroLengthLinetos(pathDataSub)
|
|
197
|
+
//console.log(removeColinear, removeZeroLength);
|
|
198
|
+
|
|
184
199
|
|
|
185
200
|
// add extremes
|
|
186
201
|
//let tMin=0.2, tMax=0.8;
|
|
@@ -191,8 +206,10 @@ export function svgPathSimplify(input = '', {
|
|
|
191
206
|
// sort to top left
|
|
192
207
|
if (optimizeOrder) pathDataSub = pathDataToTopLeft(pathDataSub);
|
|
193
208
|
|
|
194
|
-
|
|
195
|
-
|
|
209
|
+
|
|
210
|
+
// Preprocessing: remove colinear - ignore flat beziers (removed later)
|
|
211
|
+
if (removeColinear) pathDataSub = pathDataRemoveColinear(pathDataSub, { tolerance, flatBezierToLinetos: false });
|
|
212
|
+
|
|
196
213
|
|
|
197
214
|
// analyze pathdata to add info about signicant properties such as extremes, corners
|
|
198
215
|
let pathDataPlus = analyzePathData(pathDataSub);
|
|
@@ -200,37 +217,29 @@ export function svgPathSimplify(input = '', {
|
|
|
200
217
|
|
|
201
218
|
// simplify beziers
|
|
202
219
|
let { pathData, bb, dimA } = pathDataPlus;
|
|
203
|
-
|
|
204
|
-
//let pathDataN = pathData;
|
|
205
|
-
|
|
206
|
-
//console.log(pathDataPlus);
|
|
207
|
-
//let t0=performance.now()
|
|
208
220
|
pathData = simplifyBezier ? simplifyPathDataCubic(pathData, { simplifyBezier, keepInflections, keepExtremes, keepCorners, extrapolateDominant, revertToQuadratics, tolerance, reverse }) : pathData;
|
|
209
|
-
//let t1=performance.now() - t0;
|
|
210
|
-
//console.log('t1', t1);
|
|
211
|
-
|
|
212
221
|
|
|
213
222
|
// refine extremes
|
|
214
|
-
if(refineExtremes){
|
|
223
|
+
if (refineExtremes) {
|
|
215
224
|
let thresholdEx = (bb.width + bb.height) / 2 * 0.05
|
|
216
|
-
pathData = refineAdjacentExtremes(pathData, {threshold:thresholdEx, tolerance})
|
|
225
|
+
pathData = refineAdjacentExtremes(pathData, { threshold: thresholdEx, tolerance })
|
|
217
226
|
}
|
|
218
227
|
|
|
219
228
|
|
|
220
229
|
// cubic to arcs
|
|
221
230
|
if (cubicToArc) {
|
|
222
231
|
|
|
223
|
-
let thresh =
|
|
232
|
+
let thresh = 1;
|
|
224
233
|
|
|
225
|
-
|
|
234
|
+
for(let c=0, l=pathData.length; c<l; c++){
|
|
235
|
+
let com = pathData[c]
|
|
226
236
|
let { type, values, p0, cp1 = null, cp2 = null, p = null } = com;
|
|
227
237
|
if (type === 'C') {
|
|
228
238
|
//console.log(com);
|
|
229
239
|
let comA = cubicCommandToArc(p0, cp1, cp2, p, thresh)
|
|
230
240
|
if (comA.isArc) pathData[c] = comA.com;
|
|
231
|
-
//if (comQ.type === 'Q') pathDataN[c] = comQ
|
|
232
241
|
}
|
|
233
|
-
}
|
|
242
|
+
}
|
|
234
243
|
|
|
235
244
|
// combine adjacent cubics
|
|
236
245
|
pathData = combineArcs(pathData)
|
|
@@ -238,22 +247,30 @@ export function svgPathSimplify(input = '', {
|
|
|
238
247
|
}
|
|
239
248
|
|
|
240
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
|
+
}
|
|
241
262
|
|
|
242
263
|
|
|
243
264
|
|
|
244
265
|
// simplify to quadratics
|
|
245
266
|
if (revertToQuadratics) {
|
|
246
|
-
|
|
267
|
+
for(let c=0, l=pathData.length; c<l; c++){
|
|
268
|
+
let com = pathData[c]
|
|
247
269
|
let { type, values, p0, cp1 = null, cp2 = null, p = null } = com;
|
|
248
270
|
if (type === 'C') {
|
|
249
271
|
//console.log(com);
|
|
250
272
|
let comQ = revertCubicQuadratic(p0, cp1, cp2, p)
|
|
251
273
|
if (comQ.type === 'Q') {
|
|
252
|
-
/*
|
|
253
|
-
comQ.p0 = com.p0
|
|
254
|
-
comQ.cp1 = {x:comQ.values[0], y:comQ.values[1]}
|
|
255
|
-
comQ.p = com.p
|
|
256
|
-
*/
|
|
257
274
|
comQ.extreme = com.extreme
|
|
258
275
|
comQ.corner = com.corner
|
|
259
276
|
comQ.dimA = com.dimA
|
|
@@ -261,26 +278,32 @@ export function svgPathSimplify(input = '', {
|
|
|
261
278
|
pathData[c] = comQ
|
|
262
279
|
}
|
|
263
280
|
}
|
|
264
|
-
}
|
|
281
|
+
}
|
|
265
282
|
}
|
|
266
283
|
|
|
267
|
-
//if (removeColinear) pathDataSub = pathDataRemoveColinear(pathDataSub, tolerance, flatBezierToLinetos);
|
|
268
|
-
|
|
269
284
|
|
|
270
285
|
// optimize close path
|
|
271
286
|
if (optimizeOrder) pathData = optimizeClosePath(pathData)
|
|
272
287
|
|
|
273
|
-
// poly
|
|
274
|
-
//let poly = pathDataToPolySingle(pathData, true)
|
|
275
|
-
//console.log('poly', poly);
|
|
276
|
-
|
|
277
288
|
|
|
278
289
|
// update
|
|
279
|
-
|
|
290
|
+
pathDataFlat.push(...pathData)
|
|
291
|
+
//pathDataArrN[i]=(pathData)
|
|
292
|
+
|
|
280
293
|
}
|
|
281
294
|
|
|
295
|
+
|
|
282
296
|
// flatten compound paths
|
|
283
|
-
pathData =
|
|
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
|
+
}
|
|
284
307
|
|
|
285
308
|
|
|
286
309
|
// collect for merged svg paths
|
|
@@ -290,17 +313,6 @@ export function svgPathSimplify(input = '', {
|
|
|
290
313
|
// single output
|
|
291
314
|
else {
|
|
292
315
|
|
|
293
|
-
/**
|
|
294
|
-
* detect accuracy
|
|
295
|
-
*/
|
|
296
|
-
if (autoAccuracy) {
|
|
297
|
-
decimals = detectAccuracy(pathData)
|
|
298
|
-
pathOptions.decimals = decimals
|
|
299
|
-
//console.log('!decimals', decimals);
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
//console.log('autoAccuracy', autoAccuracy, decimals);
|
|
303
|
-
|
|
304
316
|
// optimize path data
|
|
305
317
|
pathData = convertPathData(pathData, pathOptions)
|
|
306
318
|
|
|
@@ -311,9 +323,10 @@ export function svgPathSimplify(input = '', {
|
|
|
311
323
|
let comCountS = pathData.length
|
|
312
324
|
|
|
313
325
|
let dOpt = pathDataToD(pathData, minifyD)
|
|
314
|
-
svgSizeOpt = new Blob([dOpt]).size;
|
|
315
|
-
|
|
326
|
+
//svgSizeOpt = new Blob([dOpt]).size;
|
|
327
|
+
svgSizeOpt = dOpt.length
|
|
316
328
|
|
|
329
|
+
compression = +(100 / svgSize * (svgSizeOpt)).toFixed(2)
|
|
317
330
|
|
|
318
331
|
path.d = dOpt
|
|
319
332
|
path.report = {
|
|
@@ -328,7 +341,7 @@ export function svgPathSimplify(input = '', {
|
|
|
328
341
|
// apply new path for svgs
|
|
329
342
|
if (el) el.setAttribute('d', dOpt)
|
|
330
343
|
}
|
|
331
|
-
}
|
|
344
|
+
};
|
|
332
345
|
|
|
333
346
|
/**
|
|
334
347
|
* stringify new SVG
|
|
@@ -336,21 +349,19 @@ export function svgPathSimplify(input = '', {
|
|
|
336
349
|
if (mode) {
|
|
337
350
|
|
|
338
351
|
if (pathData_merged.length) {
|
|
352
|
+
|
|
339
353
|
// optimize path data
|
|
340
354
|
let pathData = convertPathData(pathData_merged, pathOptions)
|
|
341
355
|
|
|
342
356
|
// remove zero-length segments introduced by rounding
|
|
343
|
-
//pathData = removeZeroLengthLinetos_post(pathData);
|
|
344
357
|
pathData = removeZeroLengthLinetos(pathData);
|
|
345
358
|
|
|
346
|
-
|
|
347
359
|
let dOpt = pathDataToD(pathData, minifyD)
|
|
348
360
|
|
|
349
361
|
|
|
350
362
|
// apply new path for svgs
|
|
351
363
|
paths[0].el.setAttribute('d', dOpt)
|
|
352
364
|
|
|
353
|
-
|
|
354
365
|
// remove other paths
|
|
355
366
|
for (let i = 1; i < paths.length; i++) {
|
|
356
367
|
let pathEl = paths[i].el
|
|
@@ -364,7 +375,8 @@ export function svgPathSimplify(input = '', {
|
|
|
364
375
|
|
|
365
376
|
|
|
366
377
|
svg = stringifySVG(svg);
|
|
367
|
-
svgSizeOpt = new Blob([svg]).size
|
|
378
|
+
//svgSizeOpt = new Blob([svg]).size
|
|
379
|
+
svgSizeOpt = svg.length;
|
|
368
380
|
//compression = +(100/svgSize * (svgSize-svgSizeOpt)).toFixed(2)
|
|
369
381
|
compression = +(100 / svgSize * (svgSizeOpt)).toFixed(2)
|
|
370
382
|
|
|
@@ -388,110 +400,5 @@ export function svgPathSimplify(input = '', {
|
|
|
388
400
|
|
|
389
401
|
|
|
390
402
|
|
|
391
|
-
function simplifyPathDataCubic(pathData, {
|
|
392
|
-
keepExtremes = true,
|
|
393
|
-
keepInflections = true,
|
|
394
|
-
keepCorners = true,
|
|
395
|
-
extrapolateDominant = true,
|
|
396
|
-
tolerance = 1,
|
|
397
|
-
reverse = false
|
|
398
|
-
} = {}) {
|
|
399
|
-
|
|
400
|
-
let pathDataN = [pathData[0]];
|
|
401
|
-
|
|
402
|
-
for (let i = 2, l = pathData.length; l && i <= l; i++) {
|
|
403
|
-
let com = pathData[i - 1];
|
|
404
|
-
let comN = i < l ? pathData[i] : null;
|
|
405
|
-
let typeN = comN?.type || null;
|
|
406
|
-
//let isCornerN = comN?.corner || null;
|
|
407
|
-
//let isExtremeN = comN?.extreme || null;
|
|
408
|
-
let isDirChange = com?.directionChange || null;
|
|
409
|
-
let isDirChangeN = comN?.directionChange || null;
|
|
410
|
-
|
|
411
|
-
let { type, values, p0, p, cp1 = null, cp2 = null, extreme = false, corner = false, dimA = 0 } = com;
|
|
412
|
-
|
|
413
|
-
// count simplifications
|
|
414
|
-
let success = 0;
|
|
415
|
-
|
|
416
|
-
// next is also cubic
|
|
417
|
-
if (type === 'C' && typeN === 'C') {
|
|
418
|
-
|
|
419
|
-
// cannot be combined as crossing extremes or corners
|
|
420
|
-
if (
|
|
421
|
-
(keepInflections && isDirChangeN) ||
|
|
422
|
-
(keepCorners && corner) ||
|
|
423
|
-
(!isDirChange && keepExtremes && extreme)
|
|
424
|
-
) {
|
|
425
|
-
//renderPoint(markers, p, 'red', '1%')
|
|
426
|
-
pathDataN.push(com)
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
// try simplification
|
|
430
|
-
else {
|
|
431
|
-
//renderPoint(markers, p, 'magenta', '1%')
|
|
432
|
-
let combined = combineCubicPairs(com, comN, extrapolateDominant, tolerance)
|
|
433
|
-
let error = 0;
|
|
434
|
-
|
|
435
|
-
// combining successful! try next segment
|
|
436
|
-
if (combined.length === 1) {
|
|
437
|
-
com = combined[0]
|
|
438
|
-
let offset = 1;
|
|
439
|
-
|
|
440
|
-
// add cumulative error to prevent distortions
|
|
441
|
-
error += com.error;
|
|
442
|
-
//console.log('!error', error);
|
|
443
|
-
|
|
444
|
-
// find next candidates
|
|
445
|
-
for (let n = i + 1; error < tolerance && n < l; n++) {
|
|
446
|
-
let comN = pathData[n]
|
|
447
|
-
if (comN.type !== 'C' ||
|
|
448
|
-
(
|
|
449
|
-
(keepInflections && comN.directionChange) ||
|
|
450
|
-
(keepCorners && com.corner) ||
|
|
451
|
-
(keepExtremes && com.extreme)
|
|
452
|
-
)
|
|
453
|
-
) {
|
|
454
|
-
break
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
let combined = combineCubicPairs(com, comN, extrapolateDominant, tolerance)
|
|
458
|
-
if (combined.length === 1) {
|
|
459
|
-
// add cumulative error to prevent distortions
|
|
460
|
-
//console.log('combined', combined);
|
|
461
|
-
error += combined[0].error * 0.5;
|
|
462
|
-
//error += combined[0].error * 1;
|
|
463
|
-
offset++
|
|
464
|
-
}
|
|
465
|
-
com = combined[0]
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
//com.opt = true
|
|
469
|
-
pathDataN.push(com)
|
|
470
|
-
|
|
471
|
-
if (i < l) {
|
|
472
|
-
i += offset
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
} else {
|
|
476
|
-
pathDataN.push(com)
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
} // end of bezier command
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
// other commands
|
|
484
|
-
else {
|
|
485
|
-
pathDataN.push(com)
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
} // end command loop
|
|
489
|
-
|
|
490
|
-
// reverse back
|
|
491
|
-
if (reverse) pathDataN = reversePathData(pathDataN)
|
|
492
|
-
|
|
493
|
-
return pathDataN
|
|
494
|
-
}
|
|
495
|
-
|
|
496
403
|
|
|
497
404
|
|
|
@@ -1,53 +1,46 @@
|
|
|
1
1
|
import { getSquareDistance } from "./geometry";
|
|
2
2
|
import { getPolygonArea } from "./geometry_area";
|
|
3
3
|
|
|
4
|
-
export function commandIsFlat(points,
|
|
4
|
+
export function commandIsFlat(points, {
|
|
5
|
+
tolerance = 1,
|
|
6
|
+
debug=false
|
|
7
|
+
} = {}) {
|
|
8
|
+
|
|
9
|
+
let isFlat=false;
|
|
10
|
+
let report = {
|
|
11
|
+
flat:true,
|
|
12
|
+
steepness:0
|
|
13
|
+
}
|
|
5
14
|
|
|
6
15
|
let p0 = points[0];
|
|
7
16
|
let p = points[points.length - 1];
|
|
8
17
|
|
|
9
|
-
let
|
|
10
|
-
let
|
|
11
|
-
|
|
12
|
-
let xMin = Math.min(...xArr)
|
|
13
|
-
let xMax = Math.max(...xArr)
|
|
14
|
-
let yMin = Math.min(...yArr)
|
|
15
|
-
let yMax = Math.max(...yArr)
|
|
16
|
-
let w = xMax - xMin
|
|
17
|
-
let h = yMax - yMin
|
|
18
|
+
let xSet = new Set([...points.map(pt => +pt.x.toFixed(8))])
|
|
19
|
+
let ySet = new Set([...points.map(pt => +pt.y.toFixed(8))])
|
|
18
20
|
|
|
19
21
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
22
|
+
// must be flat
|
|
23
|
+
if(xSet.size===1 || ySet.size===1) return !debug ? true : report;
|
|
23
24
|
|
|
24
25
|
let squareDist = getSquareDistance(p0, p)
|
|
25
|
-
let
|
|
26
|
-
let squareDist2 = points.length > 3 ? getSquareDistance(p, points[1]) : squareDist1;
|
|
27
|
-
let squareDistAvg = (squareDist1 + squareDist2) / 2
|
|
28
|
-
|
|
29
|
-
tolerance = 0.5;
|
|
30
|
-
let thresh = (w + h) * 0.5 * tolerance;
|
|
31
|
-
|
|
32
|
-
//let thresh = tolerance;
|
|
26
|
+
let threshold = squareDist / 1000 * tolerance
|
|
33
27
|
let area = getPolygonArea(points, true)
|
|
34
28
|
|
|
29
|
+
// flat enough
|
|
30
|
+
if(area < threshold) isFlat = true;
|
|
35
31
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
let ratio = area / (squareDistAvg);
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
let isFlat = area === 0 ? true : area < squareDistAvg / areaThresh;
|
|
45
|
-
|
|
32
|
+
if(debug){
|
|
33
|
+
report.flat = isFlat;
|
|
34
|
+
//report.steepness = area/threshold
|
|
35
|
+
report.steepness = area/squareDist*10
|
|
36
|
+
}
|
|
46
37
|
|
|
47
|
-
return
|
|
38
|
+
return !debug ? isFlat : report;
|
|
48
39
|
}
|
|
49
40
|
|
|
50
41
|
|
|
42
|
+
|
|
43
|
+
// deprecated
|
|
51
44
|
export function checkBezierFlatness(p0, cpts, p) {
|
|
52
45
|
|
|
53
46
|
let isFlat = false;
|
|
@@ -75,13 +68,13 @@ export function checkBezierFlatness(p0, cpts, p) {
|
|
|
75
68
|
|
|
76
69
|
if (!cross0) return true
|
|
77
70
|
|
|
78
|
-
let area = getPolygonArea([p0
|
|
71
|
+
let area = getPolygonArea([p0, ...cpts, p], true)
|
|
79
72
|
let dist1 = getSquareDistance(p0, p)
|
|
80
|
-
let thresh = dist1/200;
|
|
73
|
+
let thresh = dist1 / 200;
|
|
81
74
|
|
|
82
75
|
|
|
83
|
-
|
|
84
|
-
isFlat = area<thresh;
|
|
76
|
+
// if(area<thresh) return true;
|
|
77
|
+
isFlat = area < thresh;
|
|
85
78
|
//console.log('area', area, thresh, isFlat);
|
|
86
79
|
|
|
87
80
|
|
|
@@ -112,6 +112,7 @@ export function analyzePathData(pathData = []) {
|
|
|
112
112
|
com.directionChange = false;
|
|
113
113
|
com.closePath = false;
|
|
114
114
|
com.dimA = 0;
|
|
115
|
+
//com.flat = false;
|
|
115
116
|
|
|
116
117
|
|
|
117
118
|
/**
|
|
@@ -181,14 +182,17 @@ export function analyzePathData(pathData = []) {
|
|
|
181
182
|
commandPts.push(p);
|
|
182
183
|
|
|
183
184
|
/*
|
|
185
|
+
//let commandFlatness = commandIsFlat(commandPts);
|
|
184
186
|
let commandFlatness = commandIsFlat(commandPts);
|
|
185
187
|
isFlat = commandFlatness.flat;
|
|
186
188
|
com.flat = isFlat;
|
|
187
189
|
|
|
188
190
|
if (isFlat) {
|
|
189
191
|
com.extreme = false;
|
|
192
|
+
//renderPoint(markers, p, 'red', '1%', '0.5')
|
|
190
193
|
}
|
|
191
194
|
*/
|
|
195
|
+
|
|
192
196
|
}
|
|
193
197
|
|
|
194
198
|
/**
|