svg-path-simplify 0.0.2 → 0.0.5
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/LICENSE +339 -0
- package/README.md +33 -1
- package/dist/svg-path-simplify.esm.js +489 -131
- package/dist/svg-path-simplify.esm.min.js +1 -1
- package/dist/svg-path-simplify.js +489 -130
- package/dist/svg-path-simplify.min.js +1 -1
- package/dist/svg-path-simplify.node.js +489 -130
- package/dist/svg-path-simplify.node.min.js +1 -1
- package/index.html +17 -9
- package/package.json +4 -5
- package/src/detect_input.js +42 -0
- package/src/index.js +3 -0
- package/src/pathSimplify-main.js +198 -90
- package/src/svg_getViewbox.js +32 -0
- package/src/svgii/geometry.js +51 -4
- package/src/svgii/pathData_remove_collinear.js +31 -18
- package/src/svgii/pathData_reorder.js +50 -11
- package/src/svgii/pathData_stringify.js +11 -12
- package/src/svgii/rounding.js +14 -6
- package/src/svgii/svg_cleanup.js +7 -1
package/src/pathSimplify-main.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { detectInputType } from './detect_input';
|
|
1
2
|
import { combineCubicPairs } from './pathData_simplify_cubic';
|
|
2
3
|
import { getPathDataVertices, pointAtT } from './svgii/geometry';
|
|
3
4
|
import { getPolyBBox } from './svgii/geometry_bbox';
|
|
@@ -6,7 +7,7 @@ import { combineArcs, convertPathData, cubicCommandToArc, revertCubicQuadratic }
|
|
|
6
7
|
import { parsePathDataNormalized } from './svgii/pathData_parse';
|
|
7
8
|
import { pathDataRemoveColinear } from './svgii/pathData_remove_collinear';
|
|
8
9
|
import { removeZeroLengthLinetos } from './svgii/pathData_remove_zerolength';
|
|
9
|
-
import { pathDataToTopLeft } from './svgii/pathData_reorder';
|
|
10
|
+
import { optimizeClosePath, pathDataToTopLeft } from './svgii/pathData_reorder';
|
|
10
11
|
import { reversePathData } from './svgii/pathData_reverse';
|
|
11
12
|
import { addExtremePoints, splitSubpaths } from './svgii/pathData_split';
|
|
12
13
|
import { pathDataToD } from './svgii/pathData_stringify';
|
|
@@ -14,9 +15,10 @@ import { pathDataToPolyPlus } from './svgii/pathData_toPolygon';
|
|
|
14
15
|
import { analyzePoly } from './svgii/poly_analyze';
|
|
15
16
|
import { getCurvePathData } from './svgii/poly_to_pathdata';
|
|
16
17
|
import { detectAccuracy } from './svgii/rounding';
|
|
18
|
+
import { cleanUpSVG } from './svgii/svg_cleanup';
|
|
17
19
|
import { renderPoint } from './svgii/visualize';
|
|
18
20
|
|
|
19
|
-
export function svgPathSimplify(
|
|
21
|
+
export function svgPathSimplify(input = '', {
|
|
20
22
|
toAbsolute = true,
|
|
21
23
|
toRelative = true,
|
|
22
24
|
toShorthands = true,
|
|
@@ -45,147 +47,253 @@ export function svgPathSimplify(d = '', {
|
|
|
45
47
|
revertToQuadratics = true,
|
|
46
48
|
minifyD = 0,
|
|
47
49
|
tolerance = 1,
|
|
48
|
-
reverse = false
|
|
50
|
+
reverse = false,
|
|
51
|
+
|
|
52
|
+
// svg cleanup options
|
|
53
|
+
removeHidden = true,
|
|
54
|
+
removeUnused = true,
|
|
55
|
+
|
|
56
|
+
// return svg markup or object
|
|
57
|
+
getObject = false
|
|
58
|
+
|
|
49
59
|
} = {}) {
|
|
50
60
|
|
|
61
|
+
// clamp tolerance
|
|
62
|
+
tolerance = Math.max(0.1, tolerance);
|
|
63
|
+
|
|
64
|
+
let inputType = detectInputType(input);
|
|
51
65
|
|
|
52
|
-
let
|
|
66
|
+
let svg = '';
|
|
67
|
+
let svgSize = 0;
|
|
68
|
+
let svgSizeOpt = 0;
|
|
69
|
+
let compression = 0;
|
|
70
|
+
let report = {};
|
|
71
|
+
let d = '';
|
|
72
|
+
let mode = inputType === 'svgMarkup' ? 1 : 0;
|
|
53
73
|
|
|
54
|
-
|
|
55
|
-
let pathData = JSON.parse(JSON.stringify(pathDataO));
|
|
74
|
+
let paths = []
|
|
56
75
|
|
|
57
|
-
// count commands for evaluation
|
|
58
|
-
let comCount = pathDataO.length
|
|
59
76
|
|
|
60
77
|
/**
|
|
61
|
-
*
|
|
78
|
+
* normalize input
|
|
79
|
+
* switch mode
|
|
62
80
|
*/
|
|
63
|
-
let subPathArr = splitSubpaths(pathData);
|
|
64
81
|
|
|
65
|
-
//
|
|
66
|
-
|
|
82
|
+
// original size
|
|
83
|
+
svgSize = new Blob([input]).size;
|
|
67
84
|
|
|
68
|
-
|
|
85
|
+
// single path
|
|
86
|
+
if (!mode) {
|
|
87
|
+
if (inputType === 'pathDataString') {
|
|
88
|
+
d = input
|
|
89
|
+
} else if (inputType === 'polyString') {
|
|
90
|
+
d = 'M' + input
|
|
91
|
+
}
|
|
92
|
+
paths.push({ d, el: null })
|
|
93
|
+
}
|
|
94
|
+
// process svg
|
|
95
|
+
else {
|
|
96
|
+
//sanitize
|
|
97
|
+
let returnDom = true
|
|
98
|
+
svg = cleanUpSVG(input, { returnDom, removeHidden, removeUnused }
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
// collect paths
|
|
102
|
+
let pathEls = svg.querySelectorAll('path')
|
|
103
|
+
pathEls.forEach(path => {
|
|
104
|
+
paths.push({ d: path.getAttribute('d'), el: path })
|
|
105
|
+
})
|
|
106
|
+
}
|
|
69
107
|
|
|
70
|
-
|
|
71
|
-
|
|
108
|
+
//console.log(paths);
|
|
109
|
+
//console.log('inputType', inputType, 'mode', mode);
|
|
72
110
|
|
|
73
|
-
|
|
74
|
-
|
|
111
|
+
/**
|
|
112
|
+
* process all paths
|
|
113
|
+
*/
|
|
114
|
+
paths.forEach(path => {
|
|
115
|
+
let { d, el } = path;
|
|
75
116
|
|
|
76
|
-
|
|
77
|
-
|
|
117
|
+
let pathDataO = parsePathDataNormalized(d, { quadraticToCubic, toAbsolute, arcToCubic });
|
|
118
|
+
//console.log(pathDataO);
|
|
78
119
|
|
|
79
|
-
//
|
|
80
|
-
|
|
81
|
-
let tMin = 0, tMax = 1;
|
|
82
|
-
if (addExtremes) pathDataSub = addExtremePoints(pathDataSub, tMin, tMax)
|
|
120
|
+
// create clone for fallback
|
|
121
|
+
let pathData = JSON.parse(JSON.stringify(pathDataO));
|
|
83
122
|
|
|
123
|
+
// count commands for evaluation
|
|
124
|
+
let comCount = pathDataO.length
|
|
84
125
|
|
|
85
|
-
|
|
86
|
-
|
|
126
|
+
/**
|
|
127
|
+
* get sub paths
|
|
128
|
+
*/
|
|
129
|
+
let subPathArr = splitSubpaths(pathData);
|
|
87
130
|
|
|
88
|
-
//
|
|
89
|
-
|
|
131
|
+
// cleaned up pathData
|
|
132
|
+
let pathDataArrN = [];
|
|
90
133
|
|
|
91
|
-
|
|
92
|
-
let pathDataPlus = analyzePathData(pathDataSub);
|
|
134
|
+
for (let i = 0, l = subPathArr.length; i < l; i++) {
|
|
93
135
|
|
|
136
|
+
//let { pathData, bb } = subPathArr[i];
|
|
137
|
+
let pathDataSub = subPathArr[i];
|
|
94
138
|
|
|
95
|
-
|
|
96
|
-
|
|
139
|
+
// try simplification in reversed order
|
|
140
|
+
if (reverse) pathDataSub = reversePathData(pathDataSub);
|
|
97
141
|
|
|
142
|
+
// remove zero length linetos
|
|
143
|
+
if (removeColinear) pathDataSub = removeZeroLengthLinetos(pathDataSub)
|
|
98
144
|
|
|
145
|
+
// add extremes
|
|
146
|
+
//let tMin=0.2, tMax=0.8;
|
|
147
|
+
let tMin = 0, tMax = 1;
|
|
148
|
+
if (addExtremes) pathDataSub = addExtremePoints(pathDataSub, tMin, tMax)
|
|
99
149
|
|
|
100
150
|
|
|
151
|
+
// sort to top left
|
|
152
|
+
if (optimizeOrder) pathDataSub = pathDataToTopLeft(pathDataSub);
|
|
101
153
|
|
|
102
|
-
|
|
154
|
+
// remove colinear/flat
|
|
155
|
+
if (removeColinear) pathDataSub = pathDataRemoveColinear(pathDataSub, tolerance, flatBezierToLinetos);
|
|
103
156
|
|
|
157
|
+
// analyze pathdata to add info about signicant properties such as extremes, corners
|
|
158
|
+
let pathDataPlus = analyzePathData(pathDataSub);
|
|
104
159
|
|
|
105
160
|
|
|
106
|
-
|
|
107
|
-
|
|
161
|
+
// simplify beziers
|
|
162
|
+
let { pathData, bb, dimA } = pathDataPlus;
|
|
108
163
|
|
|
164
|
+
//let pathDataN = pathData;
|
|
109
165
|
|
|
166
|
+
//console.log(pathDataPlus);
|
|
110
167
|
|
|
111
|
-
|
|
112
|
-
if(cubicToArc){
|
|
168
|
+
pathData = simplifyBezier ? simplifyPathData(pathData, { simplifyBezier, keepInflections, keepExtremes, keepCorners, extrapolateDominant, revertToQuadratics, tolerance, reverse }) : pathData;
|
|
113
169
|
|
|
114
|
-
let thresh = 3;
|
|
115
170
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
171
|
+
// cubic to arcs
|
|
172
|
+
if (cubicToArc) {
|
|
173
|
+
|
|
174
|
+
let thresh = 3;
|
|
175
|
+
|
|
176
|
+
pathData.forEach((com, c) => {
|
|
177
|
+
let { type, values, p0, cp1 = null, cp2 = null, p = null } = com;
|
|
178
|
+
if (type === 'C') {
|
|
179
|
+
//console.log(com);
|
|
180
|
+
let comA = cubicCommandToArc(p0, cp1, cp2, p, thresh)
|
|
181
|
+
if (comA.isArc) pathData[c] = comA.com;
|
|
182
|
+
//if (comQ.type === 'Q') pathDataN[c] = comQ
|
|
183
|
+
}
|
|
184
|
+
})
|
|
125
185
|
|
|
126
|
-
|
|
127
|
-
|
|
186
|
+
// combine adjacent cubics
|
|
187
|
+
pathData = combineArcs(pathData)
|
|
128
188
|
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
// simplify to quadratics
|
|
193
|
+
if (revertToQuadratics) {
|
|
194
|
+
pathData.forEach((com, c) => {
|
|
195
|
+
let { type, values, p0, cp1 = null, cp2 = null, p = null } = com;
|
|
196
|
+
if (type === 'C') {
|
|
197
|
+
//console.log(com);
|
|
198
|
+
let comQ = revertCubicQuadratic(p0, cp1, cp2, p)
|
|
199
|
+
if (comQ.type === 'Q') pathData[c] = comQ
|
|
200
|
+
}
|
|
201
|
+
})
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// optimize close path
|
|
205
|
+
if(optimizeOrder) pathData=optimizeClosePath(pathData)
|
|
206
|
+
|
|
207
|
+
// update
|
|
208
|
+
pathDataArrN.push(pathData)
|
|
129
209
|
}
|
|
130
210
|
|
|
211
|
+
|
|
212
|
+
// flatten compound paths
|
|
213
|
+
pathData = pathDataArrN.flat();
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* detect accuracy
|
|
217
|
+
*/
|
|
218
|
+
if (autoAccuracy) {
|
|
219
|
+
decimals = detectAccuracy(pathData)
|
|
220
|
+
}
|
|
131
221
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
let comQ = revertCubicQuadratic(p0, cp1, cp2, p)
|
|
139
|
-
if (comQ.type === 'Q') pathDataN[c] = comQ
|
|
140
|
-
}
|
|
141
|
-
})
|
|
222
|
+
|
|
223
|
+
// optimize
|
|
224
|
+
let pathOptions = {
|
|
225
|
+
toRelative,
|
|
226
|
+
toShorthands,
|
|
227
|
+
decimals,
|
|
142
228
|
}
|
|
143
229
|
|
|
144
230
|
|
|
231
|
+
// optimize path data
|
|
232
|
+
pathData = convertPathData(pathData, pathOptions)
|
|
145
233
|
|
|
146
234
|
|
|
147
|
-
//
|
|
148
|
-
|
|
149
|
-
|
|
235
|
+
// remove zero-length segments introduced by rounding
|
|
236
|
+
let pathDataOpt = []
|
|
237
|
+
|
|
238
|
+
pathData.forEach((com, i) => {
|
|
239
|
+
let { type, values } = com;
|
|
240
|
+
if (type === 'l' || type === 'v' || type === 'h') {
|
|
241
|
+
let hasLength = type === 'l' ? (values.join('') !== '00') : values[0] !== 0
|
|
242
|
+
if (hasLength) pathDataOpt.push(com)
|
|
243
|
+
} else {
|
|
244
|
+
pathDataOpt.push(com)
|
|
245
|
+
}
|
|
246
|
+
})
|
|
150
247
|
|
|
248
|
+
pathData = pathDataOpt;
|
|
151
249
|
|
|
152
|
-
// merge pathdata
|
|
153
|
-
let pathDataFlat = pathDataArrN.flat();
|
|
154
250
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
*/
|
|
158
|
-
if (autoAccuracy) {
|
|
159
|
-
decimals = detectAccuracy(pathDataFlat)
|
|
160
|
-
}
|
|
251
|
+
// compare command count
|
|
252
|
+
let comCountS = pathData.length
|
|
161
253
|
|
|
254
|
+
let dOpt = pathDataToD(pathData, minifyD)
|
|
255
|
+
svgSizeOpt = new Blob([dOpt]).size;
|
|
256
|
+
//compression = +(100/svgSize * (svgSize - svgSizeOpt)).toFixed(2)
|
|
257
|
+
compression = +(100 / svgSize * (svgSizeOpt)).toFixed(2)
|
|
162
258
|
|
|
163
259
|
|
|
164
|
-
|
|
165
|
-
|
|
260
|
+
path.d = dOpt
|
|
261
|
+
path.report = {
|
|
262
|
+
original: comCount,
|
|
263
|
+
new: comCountS,
|
|
264
|
+
saved: comCount - comCountS,
|
|
265
|
+
compression,
|
|
266
|
+
decimals,
|
|
267
|
+
//success: comCountS < comCount
|
|
268
|
+
}
|
|
166
269
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
decimals,
|
|
172
|
-
}
|
|
270
|
+
// apply new path for svgs
|
|
271
|
+
if (el) el.setAttribute('d', dOpt)
|
|
272
|
+
|
|
273
|
+
});
|
|
173
274
|
|
|
275
|
+
// stringify new SVG
|
|
276
|
+
if (mode) {
|
|
277
|
+
svg = new XMLSerializer().serializeToString(svg);
|
|
278
|
+
svgSizeOpt = new Blob([svg]).size
|
|
279
|
+
//compression = +(100/svgSize * (svgSize-svgSizeOpt)).toFixed(2)
|
|
280
|
+
compression = +(100 / svgSize * (svgSizeOpt)).toFixed(2)
|
|
174
281
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
282
|
+
svgSize = +(svgSize / 1024).toFixed(3)
|
|
283
|
+
svgSizeOpt = +(svgSizeOpt / 1024).toFixed(3)
|
|
284
|
+
|
|
285
|
+
report = {
|
|
286
|
+
svgSize,
|
|
287
|
+
svgSizeOpt,
|
|
288
|
+
compression
|
|
289
|
+
}
|
|
178
290
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
new: comCountS,
|
|
182
|
-
saved: comCount - comCountS,
|
|
183
|
-
decimals,
|
|
184
|
-
success: comCountS < comCount
|
|
291
|
+
} else {
|
|
292
|
+
({ d, report } = paths[0]);
|
|
185
293
|
}
|
|
186
294
|
|
|
187
|
-
return { pathData, d: dOpt, report };
|
|
188
295
|
|
|
296
|
+
return !getObject ? (d ? d : svg) : { svg, d, report, inputType, mode };
|
|
189
297
|
|
|
190
298
|
}
|
|
191
299
|
|
|
@@ -221,9 +329,9 @@ function simplifyPathData(pathData, {
|
|
|
221
329
|
|
|
222
330
|
// cannot be combined as crossing extremes or corners
|
|
223
331
|
if (
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
332
|
+
(keepInflections && isDirChangeN) ||
|
|
333
|
+
(keepCorners && corner) ||
|
|
334
|
+
(!isDirChange && keepExtremes && extreme)
|
|
227
335
|
) {
|
|
228
336
|
//renderPoint(markers, p, 'red', '1%')
|
|
229
337
|
pathDataN.push(com)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* get viewBox
|
|
3
|
+
* either from explicit attribute or
|
|
4
|
+
* width and height attributes
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export function getViewBox(svg = null, round = false) {
|
|
8
|
+
|
|
9
|
+
// browser default
|
|
10
|
+
if (!svg) return { x: 0, y: 0, width: 300, height: 150 }
|
|
11
|
+
|
|
12
|
+
let style = window.getComputedStyle(svg);
|
|
13
|
+
|
|
14
|
+
// the baseVal API method also converts physical units to pixels/user-units
|
|
15
|
+
let w = svg.hasAttribute('width') ? svg.width.baseVal.value : parseFloat(style.width) || 300;
|
|
16
|
+
let h = svg.hasAttribute('height') ? svg.height.baseVal.value : parseFloat(style.height) || 150;
|
|
17
|
+
|
|
18
|
+
let viewBox = svg.getAttribute('viewBox') ? svg.viewBox.baseVal : { x: 0, y: 0, width: w, height: h };
|
|
19
|
+
|
|
20
|
+
// remove SVG constructor
|
|
21
|
+
let { x, y, width, height } = viewBox;
|
|
22
|
+
viewBox = { x, y, width, height };
|
|
23
|
+
|
|
24
|
+
// round to integers
|
|
25
|
+
if (round) {
|
|
26
|
+
for (let prop in viewBox) {
|
|
27
|
+
viewBox[prop] = Math.ceil(viewBox[prop]);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return viewBox
|
|
32
|
+
}
|
package/src/svgii/geometry.js
CHANGED
|
@@ -52,7 +52,7 @@ export function checkLineIntersection(p1, p2, p3, p4, exact = true) {
|
|
|
52
52
|
y: p1.y + (a * (p2.y - p1.y))
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
// console.log('intersectionPoint', intersectionPoint, p1, p2);
|
|
56
56
|
|
|
57
57
|
|
|
58
58
|
|
|
@@ -985,16 +985,63 @@ export function commandIsFlat(points, tolerance = 0.025) {
|
|
|
985
985
|
}
|
|
986
986
|
|
|
987
987
|
|
|
988
|
+
export function checkBezierFlatness(p0, cpts, p) {
|
|
988
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
|
+
|
|
1005
|
+
let cross1 = Math.abs(dx1 * dy2 - dy1 * dx2);
|
|
1006
|
+
|
|
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
|
+
}
|
|
989
1027
|
|
|
990
1028
|
/**
|
|
991
1029
|
* sloppy distance calculation
|
|
992
1030
|
* based on x/y differences
|
|
993
1031
|
*/
|
|
994
1032
|
export function getDistAv(pt1, pt2) {
|
|
995
|
-
|
|
996
|
-
let
|
|
1033
|
+
|
|
1034
|
+
let diffX = Math.abs(pt2.x - pt1.x);
|
|
1035
|
+
let diffY = Math.abs(pt2.y - pt1.y);
|
|
997
1036
|
let diff = (diffX + diffY) / 2;
|
|
1037
|
+
|
|
1038
|
+
/*
|
|
1039
|
+
let diffX = pt2.x - pt1.x;
|
|
1040
|
+
let diffY = pt2.y - pt1.y;
|
|
1041
|
+
let diff = Math.abs(diffX + diffY) / 2;
|
|
1042
|
+
*/
|
|
1043
|
+
|
|
1044
|
+
|
|
998
1045
|
return diff;
|
|
999
1046
|
}
|
|
1000
1047
|
|
|
@@ -1067,7 +1114,7 @@ export function reducePoints(points, maxPoints = 48) {
|
|
|
1067
1114
|
}
|
|
1068
1115
|
|
|
1069
1116
|
|
|
1070
|
-
export function mirrorCpts(cpt2_0, pt0, cpt2, pt1, outgoing = true, t=0.666) {
|
|
1117
|
+
export function mirrorCpts(cpt2_0, pt0, cpt2, pt1, outgoing = true, t = 0.666) {
|
|
1071
1118
|
|
|
1072
1119
|
// hypotenuse angle
|
|
1073
1120
|
let ang0 = getAngle(pt0, pt1, true);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getSquareDistance } from "./geometry.js";
|
|
1
|
+
import { checkBezierFlatness, getDistAv, getSquareDistance } from "./geometry.js";
|
|
2
2
|
import { getPolygonArea } from "./geometry_area.js";
|
|
3
3
|
import { renderPoint } from "./visualize.js";
|
|
4
4
|
|
|
@@ -17,49 +17,62 @@ export function pathDataRemoveColinear(pathData, tolerance = 1, flatBezierToLine
|
|
|
17
17
|
let comPrev = pathData[c - 1];
|
|
18
18
|
let com = pathData[c];
|
|
19
19
|
let comN = pathData[c + 1] || pathData[l - 1];
|
|
20
|
-
let p1 = comN.type === '
|
|
20
|
+
let p1 = comN.type.toLowerCase() === 'z' ? M : { x: comN.values[comN.values.length - 2], y: comN.values[comN.values.length - 1] }
|
|
21
21
|
|
|
22
22
|
let { type, values } = com;
|
|
23
23
|
let valsL = values.slice(-2)
|
|
24
24
|
p = type !== 'Z' ? { x: valsL[0], y: valsL[1] } : M;
|
|
25
25
|
|
|
26
|
-
let
|
|
27
|
-
[{ x: values[0], y: values[1] }, { x: values[2], y: values[3] }] :
|
|
28
|
-
(type === 'Q' ? [{ x: values[0], y: values[1] }] : []);
|
|
26
|
+
let area = getPolygonArea([p0, p, p1], true)
|
|
29
27
|
|
|
28
|
+
let distSquare = getSquareDistance(p0, p1)
|
|
29
|
+
let distMax = distSquare / 100 * tolerance
|
|
30
30
|
|
|
31
|
-
let
|
|
32
|
-
let
|
|
33
|
-
let distMax = distSquare / 500 * tolerance
|
|
31
|
+
let isFlat = area < distMax;
|
|
32
|
+
let isFlatBez = false;
|
|
34
33
|
|
|
35
|
-
let isFlat = area < distMax
|
|
36
|
-
|
|
37
|
-
if(!flatBezierToLinetos && type==='C') isFlat = false;
|
|
38
|
-
//let isFlat = flatBezierToLinetos && type === 'C' ? area < distMax : false
|
|
39
34
|
|
|
35
|
+
if (!flatBezierToLinetos && type === 'C') isFlat = false;
|
|
36
|
+
//let isFlat = flatBezierToLinetos && type === 'C' ? area < distMax : false
|
|
40
37
|
|
|
41
38
|
// convert flat beziers to linetos
|
|
42
|
-
if (flatBezierToLinetos && type === 'C') {
|
|
39
|
+
if (flatBezierToLinetos && (type === 'C' || type === 'Q')) {
|
|
40
|
+
|
|
41
|
+
let cpts = type === 'C' ?
|
|
42
|
+
[{ x: values[0], y: values[1] }, { x: values[2], y: values[3] }] :
|
|
43
|
+
(type === 'Q' ? [{ x: values[0], y: values[1] }] : []);
|
|
43
44
|
|
|
44
|
-
let areaBez = getPolygonArea([p0, ...cpts, p], true)
|
|
45
|
-
let isFlatBez = areaBez < distSquare / 1000
|
|
46
45
|
|
|
47
|
-
|
|
46
|
+
//let areaBez = getPolygonArea([p0, ...cpts, p], true)
|
|
47
|
+
|
|
48
|
+
isFlatBez = checkBezierFlatness(p0, cpts, p)
|
|
49
|
+
// console.log();
|
|
50
|
+
|
|
51
|
+
//isFlatBez = areaBez < distMax * 0.25
|
|
52
|
+
//console.log('isFlatBez', isFlatBez);
|
|
53
|
+
//isFlatBez = false
|
|
54
|
+
|
|
55
|
+
//&& comPrev.type !== 'C'
|
|
56
|
+
if (isFlatBez && c < l - 1 && comPrev.type !== 'C') {
|
|
57
|
+
type = "L"
|
|
48
58
|
com.type = "L"
|
|
49
59
|
com.values = valsL
|
|
60
|
+
|
|
61
|
+
//renderPoint(markers, p)
|
|
50
62
|
}
|
|
51
63
|
|
|
52
64
|
}
|
|
53
65
|
|
|
54
|
-
|
|
55
66
|
// update end point
|
|
56
67
|
p0 = p;
|
|
57
68
|
|
|
58
69
|
// colinear – exclude arcs (as always =) as semicircles won't have an area
|
|
59
|
-
if (
|
|
70
|
+
if ( isFlat && c < l - 1 && (type === 'L' || (flatBezierToLinetos && isFlatBez))) {
|
|
71
|
+
//console.log(area,distMax );
|
|
60
72
|
continue;
|
|
61
73
|
}
|
|
62
74
|
|
|
75
|
+
|
|
63
76
|
if (type === 'M') {
|
|
64
77
|
M = p
|
|
65
78
|
p0 = M
|