svg-path-simplify 0.1.2 → 0.2.1
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 +10 -0
- package/dist/svg-path-simplify.esm.js +3935 -1441
- package/dist/svg-path-simplify.esm.min.js +13 -1
- package/dist/svg-path-simplify.js +3953 -1459
- package/dist/svg-path-simplify.min.js +13 -1
- package/dist/svg-path-simplify.min.js.gz +0 -0
- package/dist/svg-path-simplify.poly.cjs +0 -1
- package/index.html +69 -31
- package/package.json +5 -9
- package/src/constants.js +3 -0
- package/src/index-node.js +0 -1
- package/src/index-poly.js +0 -1
- package/src/index.js +26 -0
- package/src/pathData_simplify_cubic.js +75 -46
- package/src/pathData_simplify_cubicsToArcs.js +566 -0
- package/src/pathData_simplify_harmonize_cpts.js +170 -0
- package/src/pathData_simplify_revertToquadratics.js +21 -0
- package/src/pathSimplify-main.js +274 -61
- package/src/poly-fit-curve-schneider.js +570 -0
- package/src/simplify_poly_RDP.js +146 -0
- package/src/simplify_poly_radial_distance.js +100 -0
- package/src/svg_getViewbox.js +28 -15
- package/src/svgii/geometry.js +389 -63
- package/src/svgii/geometry_area.js +2 -1
- package/src/svgii/pathData_analyze.js +259 -212
- package/src/svgii/pathData_convert.js +91 -663
- package/src/svgii/pathData_fromPoly.js +12 -0
- package/src/svgii/pathData_parse.js +90 -89
- package/src/svgii/pathData_parse_els.js +3 -0
- package/src/svgii/pathData_parse_fontello.js +449 -0
- package/src/svgii/pathData_remove_collinear.js +44 -37
- package/src/svgii/pathData_reorder.js +2 -1
- package/src/svgii/pathData_simplify_redraw.js +343 -0
- package/src/svgii/pathData_simplify_refineCorners.js +18 -9
- package/src/svgii/pathData_simplify_refineExtremes.js +19 -78
- package/src/svgii/pathData_split.js +42 -45
- package/src/svgii/pathData_toPolygon.js +130 -4
- package/src/svgii/pathData_transform_scale.js +51 -0
- package/src/svgii/poly_analyze.js +470 -14
- package/src/svgii/poly_to_pathdata.js +224 -19
- package/src/svgii/rounding.js +55 -112
- package/src/svgii/svg_cleanup.js +13 -1
- package/src/svgii/visualize.js +8 -3
- package/{debug.cjs → tests/debug.cjs} +3 -0
- package/{testSVG.js → tests/testSVG.js} +1 -1
- /package/{test.js → tests/test.js} +0 -0
|
@@ -0,0 +1,566 @@
|
|
|
1
|
+
import { checkLineIntersection, getAngle, getDistManhattan, getDistance, rotatePoint } from "./svgii/geometry";
|
|
2
|
+
import { getPathArea, getPolygonArea, getRelativeAreaDiff } from "./svgii/geometry_area";
|
|
3
|
+
//import { cubicCommandToArc } from "./svgii/pathData_convert";
|
|
4
|
+
|
|
5
|
+
export function pathDataCubicsToArc(pathData, { areaThreshold = 2.5 } = {}) {
|
|
6
|
+
|
|
7
|
+
for (let c = 0, l = pathData.length; c < l; c++) {
|
|
8
|
+
let com = pathData[c]
|
|
9
|
+
let comN = pathData[c + 1] || null
|
|
10
|
+
let { type, values, p0, cp1 = null, cp2 = null, p = null } = com;
|
|
11
|
+
|
|
12
|
+
if (type === 'C' && comN && comN.type === 'C') {
|
|
13
|
+
let comA = cubicCommandToArc(p0, cp1, cp2, p, areaThreshold)
|
|
14
|
+
let comAN = cubicCommandToArc(comN.p0, comN.cp1, comN.cp2, comN.p, areaThreshold)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
if (comA.isArc && comAN.isArc) {
|
|
18
|
+
|
|
19
|
+
let dist = getDistManhattan(p0, comN.p);
|
|
20
|
+
let maxDist = dist * 0.01;
|
|
21
|
+
let dx = Math.abs(comN.p.x - p0.x)
|
|
22
|
+
let dy = Math.abs(comN.p.y - p0.y)
|
|
23
|
+
|
|
24
|
+
let horizontal = dy < maxDist && dx > maxDist;
|
|
25
|
+
let vertical = dx < maxDist && dy > maxDist;
|
|
26
|
+
//console.log(comA, comAN, horizontal, vertical);
|
|
27
|
+
|
|
28
|
+
let { rx, ry } = comA;
|
|
29
|
+
let area = getPolygonArea([p0, p, comN.p])
|
|
30
|
+
let sweep = area < 0 ? 0 : 1;
|
|
31
|
+
|
|
32
|
+
if (vertical || horizontal) {
|
|
33
|
+
rx = Math.min(rx, comAN.rx)
|
|
34
|
+
ry = Math.min(ry, comAN.ry)
|
|
35
|
+
pathData[c] = null;
|
|
36
|
+
pathData[c + 1].type = 'A';
|
|
37
|
+
pathData[c + 1].values = [rx, ry, 0, 0, sweep, comN.p.x, comN.p.y];
|
|
38
|
+
continue
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
pathData = pathData.filter(Boolean)
|
|
45
|
+
|
|
46
|
+
return pathData
|
|
47
|
+
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
export function cubicCommandToArc(p0, cp1, cp2, p, tolerance = 7.5) {
|
|
53
|
+
|
|
54
|
+
//console.log(p0, cp1, cp2, p, segArea );
|
|
55
|
+
let com = { type: 'C', values: [cp1.x, cp1.y, cp2.x, cp2.y, p.x, p.y] };
|
|
56
|
+
//let pathDataChunk = [{ type: 'M', values: [p0.x, p0.y] }, com];
|
|
57
|
+
|
|
58
|
+
let arcSegArea = 0, isArc = false
|
|
59
|
+
|
|
60
|
+
// check angles
|
|
61
|
+
let angle1 = getAngle(p0, cp1, true);
|
|
62
|
+
let angle2 = getAngle(p, cp2, true);
|
|
63
|
+
let deltaAngle = Math.abs(angle1 - angle2) * 180 / Math.PI;
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
let angleDiff = Math.abs((deltaAngle % 180) - 90);
|
|
67
|
+
let isRightAngle = angleDiff < 3;
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
let rx = 0
|
|
71
|
+
let ry = 0
|
|
72
|
+
let ptC;
|
|
73
|
+
|
|
74
|
+
if (isRightAngle) {
|
|
75
|
+
// point between cps
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
// center point
|
|
79
|
+
let cp1_r = rotatePoint(cp1, p0.x, p0.y, (Math.PI * -0.5))
|
|
80
|
+
let cp2_r = rotatePoint(cp2, p.x, p.y, (Math.PI * 0.5))
|
|
81
|
+
|
|
82
|
+
// assumed centroid
|
|
83
|
+
ptC = checkLineIntersection(p0, cp1_r, p, cp2_r, false)
|
|
84
|
+
//renderPoint(markers, ptC, 'red', '1%', '0.5' )
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
let pI = checkLineIntersection(p0, cp1, p, cp2, false);
|
|
90
|
+
|
|
91
|
+
if (pI) {
|
|
92
|
+
|
|
93
|
+
let r1 = getDistance(p0, pI);
|
|
94
|
+
let r2 = getDistance(p, pI);
|
|
95
|
+
|
|
96
|
+
let rMax = +Math.max(r1, r2).toFixed(8);
|
|
97
|
+
let rMin = +Math.min(r1, r2).toFixed(8);
|
|
98
|
+
|
|
99
|
+
rx = rMin
|
|
100
|
+
ry = rMax
|
|
101
|
+
|
|
102
|
+
let arcArea = getPolygonArea([p0, cp1, cp2, p])
|
|
103
|
+
let sweep = arcArea < 0 ? 0 : 1;
|
|
104
|
+
|
|
105
|
+
let w = Math.abs(p.x - p0.x);
|
|
106
|
+
let h = Math.abs(p.y - p0.y);
|
|
107
|
+
let landscape = w > h;
|
|
108
|
+
|
|
109
|
+
let circular = (100 / rx * Math.abs(rx - ry)) < 5;
|
|
110
|
+
|
|
111
|
+
if (circular) {
|
|
112
|
+
//rx = (rx+ry)/2
|
|
113
|
+
rx = rMax
|
|
114
|
+
ry = rx;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (landscape) {
|
|
118
|
+
//console.log('landscape', w, h);
|
|
119
|
+
rx = rMax
|
|
120
|
+
ry = rMin
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
// get original cubic area
|
|
125
|
+
let comO = [
|
|
126
|
+
{ type: 'M', values: [p0.x, p0.y] },
|
|
127
|
+
{ type: 'C', values: [cp1.x, cp1.y, cp2.x, cp2.y, p.x, p.y] }
|
|
128
|
+
];
|
|
129
|
+
|
|
130
|
+
let comArea = getPathArea(comO);
|
|
131
|
+
|
|
132
|
+
// new arc command
|
|
133
|
+
let comArc = { type: 'A', values: [rx, ry, 0, 0, sweep, p.x, p.y] };
|
|
134
|
+
|
|
135
|
+
// calculate arc seg area
|
|
136
|
+
arcSegArea = (Math.PI * (rx * ry)) / 4
|
|
137
|
+
|
|
138
|
+
// subtract polygon between start, end and center point
|
|
139
|
+
arcSegArea -= Math.abs(getPolygonArea([p0, p, pI]))
|
|
140
|
+
|
|
141
|
+
let areaDiff = getRelativeAreaDiff(comArea, arcSegArea);
|
|
142
|
+
|
|
143
|
+
if (areaDiff < tolerance) {
|
|
144
|
+
isArc = true;
|
|
145
|
+
com = comArc;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return { com: com, isArc, area: arcSegArea, rx, ry, centroid: ptC }
|
|
152
|
+
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
/*
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
// combine adjacent arcs
|
|
161
|
+
|
|
162
|
+
export function combineArcs(pathData) {
|
|
163
|
+
|
|
164
|
+
let arcSeq = [[]]
|
|
165
|
+
let ind = 0
|
|
166
|
+
let arcIndices = [[]];
|
|
167
|
+
let p0 = { x: pathData[0].values[0], y: pathData[0].values[1] }, p;
|
|
168
|
+
|
|
169
|
+
for (let i = 0, len = pathData.length; i < len; i++) {
|
|
170
|
+
let com = pathData[i];
|
|
171
|
+
let { type, values } = com;
|
|
172
|
+
|
|
173
|
+
if (type === 'A') {
|
|
174
|
+
|
|
175
|
+
let comPrev = pathData[i - 1];
|
|
176
|
+
|
|
177
|
+
// previous p0 values might not be correct anymore due to cubic simplification
|
|
178
|
+
let valsL = comPrev.values.slice(-2);
|
|
179
|
+
p0 = { x: valsL[0], y: valsL[1] };
|
|
180
|
+
|
|
181
|
+
let [rx, ry, xAxisRotation, largeArc, sweep, x, y] = values;
|
|
182
|
+
|
|
183
|
+
// check if arc is circular
|
|
184
|
+
let circular = (100 / rx * Math.abs(rx - ry)) < 5;
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
//add p0
|
|
188
|
+
p = { x: values[5], y: values[6] }
|
|
189
|
+
com.p0 = p0;
|
|
190
|
+
com.p = p;
|
|
191
|
+
com.circular = circular;
|
|
192
|
+
|
|
193
|
+
let comNext = pathData[i + 1];
|
|
194
|
+
|
|
195
|
+
//add first
|
|
196
|
+
if (!arcSeq[ind].length && comNext && comNext.type === 'A') {
|
|
197
|
+
arcSeq[ind].push(com)
|
|
198
|
+
arcIndices[ind].push(i)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (comNext && comNext.type === 'A') {
|
|
202
|
+
let [rx1, ry1, xAxisRotation0, largeArc, sweep, x, y] = comNext.values;
|
|
203
|
+
let diffRx = rx != rx1 ? 100 / rx * Math.abs(rx - rx1) : 0
|
|
204
|
+
let diffRy = ry != ry1 ? 100 / ry * Math.abs(ry - ry1) : 0
|
|
205
|
+
//let diff = (diffRx + diffRy) / 2
|
|
206
|
+
//let circular2 = (100 / rx1 * Math.abs(rx1 - ry1)) < 5;
|
|
207
|
+
|
|
208
|
+
p = { x: comNext.values[5], y: comNext.values[6] }
|
|
209
|
+
comNext.p0 = p0;
|
|
210
|
+
comNext.p = p;
|
|
211
|
+
|
|
212
|
+
// add if radii are almost same
|
|
213
|
+
if (diffRx < 5 && diffRy < 5) {
|
|
214
|
+
//console.log(rx, rx1, ry, ry1, 'diff:',diff, 'circular', circular, circular2);
|
|
215
|
+
arcSeq[ind].push(comNext)
|
|
216
|
+
arcIndices[ind].push(i + 1)
|
|
217
|
+
} else {
|
|
218
|
+
|
|
219
|
+
// start new segment
|
|
220
|
+
arcSeq.push([])
|
|
221
|
+
arcIndices.push([])
|
|
222
|
+
ind++
|
|
223
|
+
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
else {
|
|
228
|
+
//arcSeq[ind].push(com)
|
|
229
|
+
//arcIndices[ind].push(i - 1)
|
|
230
|
+
arcSeq.push([])
|
|
231
|
+
arcIndices.push([])
|
|
232
|
+
ind++
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (!arcIndices.length) return pathData;
|
|
238
|
+
|
|
239
|
+
arcSeq = arcSeq.filter(item => item.length)
|
|
240
|
+
arcIndices = arcIndices.filter(item => item.length)
|
|
241
|
+
//console.log('combine arcs:', arcSeq, arcIndices);
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
// Process in reverse to avoid index shifting
|
|
245
|
+
for (let i = arcSeq.length - 1; i >= 0; i--) {
|
|
246
|
+
const seq = arcSeq[i];
|
|
247
|
+
const start = arcIndices[i][0];
|
|
248
|
+
const len = seq.length;
|
|
249
|
+
|
|
250
|
+
// Average radii to prevent distortions
|
|
251
|
+
let rxA = 0, ryA = 0;
|
|
252
|
+
seq.forEach(({ values }) => {
|
|
253
|
+
const [rx, ry] = values;
|
|
254
|
+
rxA += rx;
|
|
255
|
+
ryA += ry;
|
|
256
|
+
});
|
|
257
|
+
rxA /= len;
|
|
258
|
+
ryA /= len;
|
|
259
|
+
|
|
260
|
+
// Correct near-circular arcs
|
|
261
|
+
//console.log('seq', seq);
|
|
262
|
+
|
|
263
|
+
//let rDiff = 100 / rxA * Math.abs(rxA - ryA);
|
|
264
|
+
//let circular = rDiff < 5;
|
|
265
|
+
|
|
266
|
+
// check if arc is circular
|
|
267
|
+
let circular = (100 / rxA * Math.abs(rxA - ryA)) < 5;
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
if (circular) {
|
|
271
|
+
// average radii
|
|
272
|
+
rxA = (rxA + ryA) / 2;
|
|
273
|
+
ryA = rxA;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
let comPrev = pathData[start - 1]
|
|
277
|
+
let comPrevVals = comPrev.values.slice(-2)
|
|
278
|
+
let M = { type: 'M', values: [comPrevVals[0], comPrevVals[1]] }
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
if (len === 4) {
|
|
282
|
+
//console.log('4 arcs');
|
|
283
|
+
|
|
284
|
+
let [rx, ry, xAxisRotation, largeArc, sweep, x1, y1] = seq[1].values;
|
|
285
|
+
let [, , , , , x2, y2] = seq[3].values;
|
|
286
|
+
|
|
287
|
+
let xDiff = Math.abs(x2 - x1);
|
|
288
|
+
let yDiff = Math.abs(y2 - y1);
|
|
289
|
+
let horizontal = xDiff > yDiff;
|
|
290
|
+
|
|
291
|
+
if (circular) {
|
|
292
|
+
let adjustY = !horizontal ? rxA * 2 : 0;
|
|
293
|
+
|
|
294
|
+
// simplify radii
|
|
295
|
+
rxA = 1;
|
|
296
|
+
ryA = 1;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
let com1 = { type: 'A', values: [rxA, ryA, xAxisRotation, largeArc, sweep, x1, y1] };
|
|
300
|
+
let com2 = { type: 'A', values: [rxA, ryA, xAxisRotation, largeArc, sweep, x2, y2] };
|
|
301
|
+
|
|
302
|
+
// This now correctly replaces the original 4 arc commands with 2
|
|
303
|
+
pathData.splice(start, len, com1, com2);
|
|
304
|
+
//console.log(com1, com2);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
else if (len === 3) {
|
|
308
|
+
//console.log('3 arcs');
|
|
309
|
+
let [rx, ry, xAxisRotation, largeArc, sweep, x1, y1] = seq[0].values;
|
|
310
|
+
let [rx2, ry2, , , , x2, y2] = seq[2].values;
|
|
311
|
+
|
|
312
|
+
// must be large arc
|
|
313
|
+
largeArc = 1;
|
|
314
|
+
let com1 = { type: 'A', values: [rxA, ryA, xAxisRotation, largeArc, sweep, x2, y2] };
|
|
315
|
+
|
|
316
|
+
// replace
|
|
317
|
+
pathData.splice(start, len, com1);
|
|
318
|
+
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
else if (len === 2) {
|
|
323
|
+
//console.log('2 arcs');
|
|
324
|
+
let [rx, ry, xAxisRotation, largeArc, sweep, x1, y1] = seq[0].values;
|
|
325
|
+
let [rx2, ry2, , , , x2, y2] = seq[1].values;
|
|
326
|
+
|
|
327
|
+
// if circular or non-elliptic xAxisRotation has no effect
|
|
328
|
+
if (circular) {
|
|
329
|
+
rxA = 1;
|
|
330
|
+
ryA = 1;
|
|
331
|
+
xAxisRotation = 0;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// check if arc is already ideal
|
|
335
|
+
let { p0, p } = seq[0];
|
|
336
|
+
let [p0_1, p_1] = [seq[1].p0, seq[1].p];
|
|
337
|
+
|
|
338
|
+
if (p0.x !== p_1.x || p0.y !== p_1.y) {
|
|
339
|
+
|
|
340
|
+
let com1 = { type: 'A', values: [rxA, ryA, xAxisRotation, largeArc, sweep, x2, y2] };
|
|
341
|
+
|
|
342
|
+
// replace
|
|
343
|
+
pathData.splice(start, len, com1);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
else {
|
|
348
|
+
//console.log('single arc');
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
return pathData
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
//cubics to arcs old
|
|
358
|
+
|
|
359
|
+
export function combineCubicsToArcs(pathData = [], {
|
|
360
|
+
threshold = 0,
|
|
361
|
+
} = {}) {
|
|
362
|
+
|
|
363
|
+
let l = pathData.length;
|
|
364
|
+
let pathDataN = [pathData[0]];
|
|
365
|
+
|
|
366
|
+
for (let i = 1; i < l; i++) {
|
|
367
|
+
let com = pathData[i];
|
|
368
|
+
let { type, cp1 = null, cp2 = null, p0, p } = com;
|
|
369
|
+
let comP = pathData[i - 1];
|
|
370
|
+
let comN = pathData[i + 1] ? pathData[i + 1] : null;
|
|
371
|
+
let comN2 = pathData[i + 2] ? pathData[i + 2] : null;
|
|
372
|
+
|
|
373
|
+
if (type === 'C' && comN && comN.type === 'C') {
|
|
374
|
+
|
|
375
|
+
let thresh = getDistAv(p0, p) * 0.02;
|
|
376
|
+
//thresh = getDistAv(p0, p) * 10000;
|
|
377
|
+
|
|
378
|
+
let dx1 = Math.abs(p0.x - cp1.x)
|
|
379
|
+
let dy1 = Math.abs(p0.y - cp1.y)
|
|
380
|
+
|
|
381
|
+
let isHorizontal1 = dy1 < thresh;
|
|
382
|
+
let isVertical1 = dx1 < thresh;
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
let dx2 = Math.abs(comN.p0.x - comN.cp1.x)
|
|
386
|
+
let dy2 = Math.abs(comN.p0.y - comN.cp1.y)
|
|
387
|
+
|
|
388
|
+
let isHorizontal2 = dy2 < thresh;
|
|
389
|
+
let isVertical2 = dx2 < thresh;
|
|
390
|
+
|
|
391
|
+
//console.log(isHorizontal1, isVertical1);
|
|
392
|
+
|
|
393
|
+
// check angles
|
|
394
|
+
let angleDiff1 = (isHorizontal1 || isVertical1) ? 0 : Infinity;
|
|
395
|
+
let angleDiff2 = (isHorizontal2 || isVertical2) ? 0 : Infinity;
|
|
396
|
+
|
|
397
|
+
if (!isHorizontal1 && !isVertical1) {
|
|
398
|
+
//console.log('get angles', isHorizontal1, isVertical1);
|
|
399
|
+
let angle1 = getAngle(p0, cp1, true);
|
|
400
|
+
let angle2 = getAngle(p, cp2, true);
|
|
401
|
+
let deltaAngle = Math.abs(angle1 - angle2) * 180 / Math.PI;
|
|
402
|
+
angleDiff1 = Math.abs((deltaAngle % 180) - 90);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
if (!isHorizontal2 && !isVertical2) {
|
|
406
|
+
//console.log('get angles', isHorizontal1, isVertical1);
|
|
407
|
+
let angle1 = getAngle(p0, cp1, true);
|
|
408
|
+
let angle2 = getAngle(p, cp2, true);
|
|
409
|
+
let deltaAngle = Math.abs(angle1 - angle2) * 180 / Math.PI;
|
|
410
|
+
angleDiff2 = Math.abs((deltaAngle % 180) - 90);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
let isRightAngle1 = angleDiff1 < 3;
|
|
415
|
+
let isRightAngle2 = angleDiff2 < 3;
|
|
416
|
+
|
|
417
|
+
let centroids = [];
|
|
418
|
+
let poly = [];
|
|
419
|
+
let rArr = []
|
|
420
|
+
let largeArc = 0;
|
|
421
|
+
|
|
422
|
+
// final on path point
|
|
423
|
+
let p_a = p
|
|
424
|
+
|
|
425
|
+
// 2 possible candidates - test radius
|
|
426
|
+
if (isRightAngle1 && isRightAngle2) {
|
|
427
|
+
//renderPoint(markers, com.p)
|
|
428
|
+
|
|
429
|
+
let pI = checkLineIntersection(p0, cp1, p, cp2, false);
|
|
430
|
+
let r1 = getDistance(p0, pI);
|
|
431
|
+
let r2 = getDistance(p, pI);
|
|
432
|
+
let rDiff1 = Math.abs(r1 - r2)
|
|
433
|
+
//let r = r1
|
|
434
|
+
|
|
435
|
+
rArr.push(r1, r2)
|
|
436
|
+
|
|
437
|
+
poly.push(p0, p)
|
|
438
|
+
p_a = p
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
// 2 commands can be combined – similar radii
|
|
442
|
+
if (rDiff1 < thresh) {
|
|
443
|
+
|
|
444
|
+
//renderPoint(markers, com.p)
|
|
445
|
+
|
|
446
|
+
// add to polygon for sweep
|
|
447
|
+
poly.push(comN.p)
|
|
448
|
+
|
|
449
|
+
// update final point
|
|
450
|
+
p_a = comN.p
|
|
451
|
+
|
|
452
|
+
// approximate/average final center point for final radius
|
|
453
|
+
let cp1_r = rotatePoint(cp1, p0.x, p0.y, (Math.PI * -0.5))
|
|
454
|
+
let cp2_r = rotatePoint(cp2, p.x, p.y, (Math.PI * 0.5))
|
|
455
|
+
|
|
456
|
+
let cp1_r2 = rotatePoint(comN.cp1, comN.p0.x, comN.p0.y, (Math.PI * -0.5))
|
|
457
|
+
let cp2_r2 = rotatePoint(comN.cp2, comN.p.x, comN.p.y, (Math.PI * 0.5))
|
|
458
|
+
|
|
459
|
+
// assumed centroid
|
|
460
|
+
let ptC = checkLineIntersection(p0, cp1_r, p, cp2_r, false)
|
|
461
|
+
let ptC2 = checkLineIntersection(comN.p0, cp1_r2, comN.p, cp2_r2, false)
|
|
462
|
+
let distC = ptC && ptC2 ? getDistAv(ptC, ptC2) : Infinity
|
|
463
|
+
|
|
464
|
+
|
|
465
|
+
// 2 commands can definitely be combined
|
|
466
|
+
if (distC < thresh) {
|
|
467
|
+
//renderPoint(markers, ptC, 'cyan', '1.2%', '0.5')
|
|
468
|
+
//renderPoint(markers, ptC2, 'magenta', '0.5%', '0.5')
|
|
469
|
+
|
|
470
|
+
// add to centroid array
|
|
471
|
+
centroids.push(ptC, ptC2)
|
|
472
|
+
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
if (comN2 && comN2.type === 'C') {
|
|
477
|
+
|
|
478
|
+
let cp1_r3 = rotatePoint(comN2.cp1, comN2.p0.x, comN2.p0.y, (Math.PI * -0.5))
|
|
479
|
+
let cp2_r3 = rotatePoint(comN2.cp2, comN2.p.x, comN2.p.y, (Math.PI * 0.5))
|
|
480
|
+
let ptC3 = checkLineIntersection(comN2.p0, cp1_r3, comN2.p, cp2_r3, false)
|
|
481
|
+
|
|
482
|
+
let distC2 = ptC && ptC3 ? getDistAv(ptC, ptC3) : Infinity
|
|
483
|
+
|
|
484
|
+
// can be combined with 3rd command
|
|
485
|
+
if (distC2 < thresh) {
|
|
486
|
+
//renderPoint(markers, ptC3, 'green', '2%', '0.3')
|
|
487
|
+
|
|
488
|
+
let r3 = getDistance(ptC3, comN2.p)
|
|
489
|
+
rArr.push(r3)
|
|
490
|
+
|
|
491
|
+
// update final point
|
|
492
|
+
p_a = comN2.p
|
|
493
|
+
poly.push(p, comN2.p)
|
|
494
|
+
|
|
495
|
+
largeArc = 1;
|
|
496
|
+
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
//console.log(rDiff1, r, r1, r2);
|
|
500
|
+
|
|
501
|
+
} else {
|
|
502
|
+
pathDataN.push(com)
|
|
503
|
+
continue
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
|
|
509
|
+
// create new arc command
|
|
510
|
+
if (poly.length > 1) {
|
|
511
|
+
|
|
512
|
+
// get average radius
|
|
513
|
+
//rArr = rArr.sort()
|
|
514
|
+
let rA = Math.max(...rArr)
|
|
515
|
+
rA = rArr[0]
|
|
516
|
+
|
|
517
|
+
let centroidA;
|
|
518
|
+
let xArr = centroids.map(pt => pt.x)
|
|
519
|
+
let yArr = centroids.map(pt => pt.y)
|
|
520
|
+
|
|
521
|
+
centroidA = {
|
|
522
|
+
x: (xArr.reduce((a, b) => a + b, 0)) / centroids.length,
|
|
523
|
+
y: (yArr.reduce((a, b) => a + b, 0)) / centroids.length
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
//console.log(xArr, centroidA);
|
|
527
|
+
|
|
528
|
+
//rA = getDistance(p0, centroids[0])
|
|
529
|
+
|
|
530
|
+
rA = getDistance(p0, centroidA)
|
|
531
|
+
let rA2 = getDistance(p, centroidA)
|
|
532
|
+
//rA = (rA+rA2) /2
|
|
533
|
+
//rA = Math.min(rA,rA2)
|
|
534
|
+
|
|
535
|
+
// rA = ((Math.min(...rArr) * 2 + Math.max(...rArr)) ) / 3
|
|
536
|
+
//console.log(rArr, rA);
|
|
537
|
+
|
|
538
|
+
let area = getPolygonArea(poly, false)
|
|
539
|
+
let sweep = area < 0 ? 0 : 1;
|
|
540
|
+
|
|
541
|
+
let comA = { type: 'A', values: [rA, rA, 0, largeArc, sweep, p_a.x, p_a.y], p0, p: p_a }
|
|
542
|
+
|
|
543
|
+
console.log('comA', comA);
|
|
544
|
+
|
|
545
|
+
pathDataN.push(comA)
|
|
546
|
+
|
|
547
|
+
i += rArr.length - 1;
|
|
548
|
+
//i++
|
|
549
|
+
continue
|
|
550
|
+
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// test angles
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
pathDataN.push(com)
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
let d = pathDataToD(pathDataN)
|
|
560
|
+
console.log(d);
|
|
561
|
+
|
|
562
|
+
console.log('pathDataN', pathDataN);
|
|
563
|
+
return pathDataN
|
|
564
|
+
|
|
565
|
+
}
|
|
566
|
+
*/
|