svg-path-simplify 0.4.5 → 0.4.6
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/CHANGELOG.md +9 -0
- package/README.md +8 -0
- package/dist/svg-path-simplify.esm.js +1617 -1219
- package/dist/svg-path-simplify.esm.min.js +2 -2
- package/dist/svg-path-simplify.js +1617 -1218
- package/dist/svg-path-simplify.min.js +2 -2
- package/dist/svg-path-simplify.pathdata.esm.js +293 -665
- package/dist/svg-path-simplify.pathdata.esm.min.js +2 -2
- package/dist/svg-path-simplify.poly.cjs +8 -9
- package/drawing.svg +62 -0
- package/index.html +11 -5
- package/package.json +1 -1
- package/src/index.js +3 -1
- package/src/pathData_get_intersections.js +777 -0
- package/src/pathData_offset.js +201 -0
- package/src/pathData_simplify_cubic.js +14 -34
- package/src/pathData_simplify_cubic_extrapolate.js +147 -8
- package/src/pathData_simplify_cubicsToArcs.js +65 -21
- package/src/pathSimplify-main.js +256 -59
- package/src/pathSimplify-presets.js +1 -0
- package/src/poly-fit-curve-schneider.js +171 -132
- package/src/poly-fit-curve-schneider_check_bulge.js +131 -0
- package/src/poly-offset.js +133 -0
- package/src/simplify_poly_RC.js +29 -7
- package/src/svgii/geometry.js +130 -27
- package/src/svgii/geometry_bbox.js +1 -1
- package/src/svgii/pathData_analyze.js +6 -28
- package/src/svgii/pathData_convert.js +63 -15
- package/src/svgii/pathData_remove_collinear.js +32 -36
- package/src/svgii/pathData_simplify_refineExtremes.js +1 -3
- package/src/svgii/pathData_stringify.js +132 -6
- package/src/svgii/pathData_toPolygon.js +33 -23
- package/src/svgii/poly_analyze.js +272 -125
- package/src/svgii/poly_analyze_cleanup.js +311 -0
- package/src/svgii/poly_analyze_getTangents.js +125 -0
- package/src/svgii/poly_analyze_get_chunks.js +3 -1
- package/src/svgii/poly_normalize.js +12 -0
- package/src/svgii/poly_to_pathdata.js +477 -8
- package/src/svgii/rounding.js +29 -39
- package/src/svgii/svg_cleanup.js +42 -54
- package/src/svgii/svg_cleanup_normalize_transforms.js +1 -1
- package/src/svgii/visualize.js +33 -2
- package/v/0.4.5/index.html +1040 -0
- package/v/0.4.5/svg-path-simplify.js +13227 -0
|
@@ -18,6 +18,8 @@ import { simplifyRC } from "../simplify_poly_RC";
|
|
|
18
18
|
import { getPolygonArea } from "./geometry_area";
|
|
19
19
|
import { pathDataToD } from "./pathData_stringify";
|
|
20
20
|
import { fixIntersectingCpts } from "../pathData_simplify_harmonize_cpts";
|
|
21
|
+
import { convertPathData } from "./pathData_convert";
|
|
22
|
+
import { combineCubicPairs } from "../pathData_simplify_cubic";
|
|
21
23
|
|
|
22
24
|
|
|
23
25
|
|
|
@@ -35,6 +37,7 @@ export function simplifyPolygonToPathData(pts, {
|
|
|
35
37
|
tolerance = 1,
|
|
36
38
|
simplifyRD = 1,
|
|
37
39
|
simplifyRDP = 1,
|
|
40
|
+
isClosed = true,
|
|
38
41
|
} = {}) {
|
|
39
42
|
|
|
40
43
|
|
|
@@ -121,6 +124,28 @@ export function simplifyPolygonToPathData(pts, {
|
|
|
121
124
|
// remove colinear
|
|
122
125
|
//pts = simplifyRC(pts)
|
|
123
126
|
|
|
127
|
+
keepExtremes = false;
|
|
128
|
+
keepCorners = false;
|
|
129
|
+
|
|
130
|
+
keepExtremes = true;
|
|
131
|
+
keepCorners = true;
|
|
132
|
+
|
|
133
|
+
// check if closed
|
|
134
|
+
/*
|
|
135
|
+
let bb = getPolyBBox(pts)
|
|
136
|
+
let thresh = (bb.width+bb.height)*0.25
|
|
137
|
+
let dist0 = getDistManhattan(pts[0], pts[pts.length-1])
|
|
138
|
+
|
|
139
|
+
//let isClosed = dist0<=thresh;
|
|
140
|
+
//isClosed = false;
|
|
141
|
+
//console.log('isClosed', isClosed, 'dist0', dist0, thresh);
|
|
142
|
+
*/
|
|
143
|
+
|
|
144
|
+
// copy 1st first to end
|
|
145
|
+
if (isClosed) {
|
|
146
|
+
pts.push(pts[0])
|
|
147
|
+
}
|
|
148
|
+
|
|
124
149
|
// get topology of poly
|
|
125
150
|
let polyAnalyzed = !keepExtremes && !keepCorners ? pts : analyzePoly(pts, {
|
|
126
151
|
debug: false
|
|
@@ -131,27 +156,471 @@ export function simplifyPolygonToPathData(pts, {
|
|
|
131
156
|
//console.log(polyAnalyzed, polyAnalyzed2);
|
|
132
157
|
|
|
133
158
|
// split into segment chunks
|
|
134
|
-
let chunks = !keepExtremes && !keepCorners ? [polyAnalyzed] : getPolyChunks(polyAnalyzed, { keepCorners, keepExtremes, keepInflections });
|
|
159
|
+
//let chunks = !keepExtremes && !keepCorners ? [polyAnalyzed] : getPolyChunks(polyAnalyzed, { keepCorners, keepExtremes, keepInflections });
|
|
160
|
+
|
|
161
|
+
let chunks = getPolyChunks(polyAnalyzed, { keepCorners, keepExtremes, keepInflections: true });
|
|
135
162
|
|
|
136
163
|
|
|
137
164
|
// Schneider curve fit
|
|
138
165
|
let threshold = width && height ? (width + height) / 2 * 0.004 * tolerance : 2.5
|
|
166
|
+
threshold = width && height ? (width + height) / 2 * 0.004 * tolerance : 2.5
|
|
167
|
+
// console.log('tolerance', tolerance, threshold);
|
|
168
|
+
|
|
139
169
|
|
|
140
170
|
//threshold = 2
|
|
171
|
+
let useToplogy = true
|
|
172
|
+
|
|
173
|
+
if (useToplogy) {
|
|
174
|
+
|
|
175
|
+
polyPath = simplifyPolyChunksTopology(chunks, {
|
|
176
|
+
closed,
|
|
177
|
+
tolerance: threshold,
|
|
178
|
+
keepCorners,
|
|
179
|
+
keepExtremes: true,
|
|
180
|
+
});
|
|
181
|
+
} else {
|
|
182
|
+
|
|
183
|
+
polyPath = simplifyPolyChunks(chunks, {
|
|
184
|
+
closed,
|
|
185
|
+
tolerance: threshold,
|
|
186
|
+
keepCorners,
|
|
187
|
+
keepExtremes: true,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
|
|
141
191
|
|
|
142
|
-
polyPath = simplifyPolyChunks(chunks, {
|
|
143
|
-
closed,
|
|
144
|
-
tolerance: threshold,
|
|
145
|
-
keepCorners,
|
|
146
|
-
keepExtremes: true,
|
|
147
|
-
});
|
|
148
192
|
|
|
149
|
-
|
|
193
|
+
|
|
194
|
+
//polyPath = fixIntersectingCpts(polyPath);
|
|
150
195
|
|
|
151
196
|
return polyPath;
|
|
152
197
|
}
|
|
153
198
|
|
|
154
199
|
|
|
200
|
+
/**
|
|
201
|
+
* topology based curve fit
|
|
202
|
+
*/
|
|
203
|
+
export function simplifyPolyChunksTopology(chunks = [], {
|
|
204
|
+
closed = true,
|
|
205
|
+
keepCorners = true,
|
|
206
|
+
tolerance = 1,
|
|
207
|
+
} = {}) {
|
|
208
|
+
|
|
209
|
+
console.log(chunks);
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
let l = chunks.length;
|
|
213
|
+
|
|
214
|
+
// new pathData
|
|
215
|
+
//let M = {x:chunks[0][0].x}
|
|
216
|
+
let pathData = [{ type: 'M', values: [chunks[0][0].x, chunks[0][0].y] }]
|
|
217
|
+
|
|
218
|
+
// loop chunks
|
|
219
|
+
for (let i = 0; i < l; i++) {
|
|
220
|
+
|
|
221
|
+
let chunkPrev = i > 0 ? chunks[i - 1] : (closed ? chunks[l - 1] : null);
|
|
222
|
+
let chunk = chunks[i];
|
|
223
|
+
let chunkN = chunks[i + 1] ? chunks[i + 1] : (closed ? chunks[0] : null)
|
|
224
|
+
let segments = []
|
|
225
|
+
|
|
226
|
+
// add from next command
|
|
227
|
+
if (chunkN) {
|
|
228
|
+
chunk.push(chunkN[0])
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
let chunklen = chunk.length
|
|
232
|
+
let hasInflection = false;
|
|
233
|
+
let segments_1 = [], segments_2 = [], segments_3 = [];
|
|
234
|
+
let segsRequired = 3;
|
|
235
|
+
|
|
236
|
+
// 1st point
|
|
237
|
+
let p1 = chunk[0]
|
|
238
|
+
// last point in chunk
|
|
239
|
+
let p2 = chunk[chunklen - 1]
|
|
240
|
+
let isLinetoSeg = false
|
|
241
|
+
|
|
242
|
+
// nothing to simplify - lineto
|
|
243
|
+
|
|
244
|
+
// if (chunklen < 2 || (chunklen === 2 && (chunk[1].isExtreme || i===l-1 && !closed) )) {
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
if (chunklen < 2 || (chunklen === 2 && (chunk[1].isExtreme))) {
|
|
249
|
+
//segments_1 = chunk.map(com => { return { type: 'L', values: [com.x, com.y] } })
|
|
250
|
+
|
|
251
|
+
isLinetoSeg = true
|
|
252
|
+
|
|
253
|
+
if (chunklen === 2) {
|
|
254
|
+
segsRequired = 2;
|
|
255
|
+
segments_2 = [
|
|
256
|
+
{
|
|
257
|
+
type: 'L',
|
|
258
|
+
values: [p1.x, p1.y],
|
|
259
|
+
p0: chunkPrev,
|
|
260
|
+
p: p1,
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
type: 'L',
|
|
264
|
+
values: [chunk[1].x, chunk[1].y],
|
|
265
|
+
p0: p1,
|
|
266
|
+
p: p2,
|
|
267
|
+
}
|
|
268
|
+
]
|
|
269
|
+
} else {
|
|
270
|
+
segsRequired = 1;
|
|
271
|
+
segments_1 = [
|
|
272
|
+
{
|
|
273
|
+
type: 'L',
|
|
274
|
+
values: [p1.x, p1.y],
|
|
275
|
+
p0: chunkPrev,
|
|
276
|
+
p: p2,
|
|
277
|
+
},
|
|
278
|
+
]
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
} else {
|
|
282
|
+
//renderPoint(markers, chunk[0], 'green')
|
|
283
|
+
|
|
284
|
+
// point before inflection
|
|
285
|
+
let p3 = chunk[chunklen - 2];
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
let extremes = chunk.filter(pt => pt.isExtreme)
|
|
289
|
+
let semiExtremes = chunk.filter(pt => pt.isSemiExtreme)
|
|
290
|
+
let corners = chunk.filter(pt => pt.isCorner)
|
|
291
|
+
let inflections = chunk.filter(pt => pt.isDirChange && !pt.isCorner && !pt.isExtreme)
|
|
292
|
+
hasInflection = inflections.length && inflections[0] !== p1
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
let idxMid = Math.floor(chunklen * 0.5)
|
|
296
|
+
let pMid = semiExtremes.length ? semiExtremes[Math.floor(semiExtremes.length * 0.5)] : chunk[idxMid]
|
|
297
|
+
|
|
298
|
+
//check if mid is close to end
|
|
299
|
+
let dist0 = getDistManhattan(p1, p3)
|
|
300
|
+
let dist1 = getDistManhattan(pMid, p3)
|
|
301
|
+
let dist2 = getDistManhattan(pMid, p1)
|
|
302
|
+
let thresh = dist0 * 0.25
|
|
303
|
+
let shortMidSegment = dist1 < thresh || dist2 < thresh
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* we have 3 modes
|
|
308
|
+
* 1 segment: only 1 segment between extremes/corners
|
|
309
|
+
* 2 segments: semiextreme/mid in between
|
|
310
|
+
* 3 segments: inflection
|
|
311
|
+
*/
|
|
312
|
+
|
|
313
|
+
segsRequired = shortMidSegment ? (!hasInflection ? 1 : 2) : (hasInflection ? 3 : 2)
|
|
314
|
+
|
|
315
|
+
//if(shortMidSegment){renderPoint(markers, pMid, 'red', '2%')}
|
|
316
|
+
let cp1_1 = p1.tangentR;
|
|
317
|
+
let cp2_1 = pMid.tangentL;
|
|
318
|
+
let p_1 = pMid
|
|
319
|
+
|
|
320
|
+
// renderPoint(markers, pMid, 'orange', '2%')
|
|
321
|
+
|
|
322
|
+
let cp2_2 = p2.tangentL;
|
|
323
|
+
let cp1_2 = pMid.tangentR;
|
|
324
|
+
let p_2 = p2
|
|
325
|
+
|
|
326
|
+
let cp1_3 = null
|
|
327
|
+
let cp2_3 = null
|
|
328
|
+
let p_3 = null
|
|
329
|
+
|
|
330
|
+
// general extrapolation
|
|
331
|
+
let t = 0.666;
|
|
332
|
+
let d = ''
|
|
333
|
+
let ptI_1 = null, ptI_2 = null, ptI_3 = null;
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
// 1 segment
|
|
337
|
+
ptI_1 = checkLineIntersection(p1, p1.tangentR, p2, p2.tangentL, false, true)
|
|
338
|
+
if (ptI_1) {
|
|
339
|
+
cp1_1 = interpolate(p1, ptI_1, t)
|
|
340
|
+
cp2_1 = interpolate(p2, ptI_1, t)
|
|
341
|
+
p_1 = p2;
|
|
342
|
+
|
|
343
|
+
segments_1 = [
|
|
344
|
+
//{type: 'M', values: [p1.x, p1.y]},
|
|
345
|
+
{
|
|
346
|
+
type: 'C',
|
|
347
|
+
values: [cp1_1.x, cp1_1.y, cp2_1.x, cp2_1.y, p_1.x, p_1.y],
|
|
348
|
+
p0: p1,
|
|
349
|
+
cp1: cp1_1,
|
|
350
|
+
cp2: cp2_1,
|
|
351
|
+
p: p_1,
|
|
352
|
+
}
|
|
353
|
+
];
|
|
354
|
+
|
|
355
|
+
//d = pathDataToD( [{ type: 'M', values: [p1.x, p1.y] },...segments_1])
|
|
356
|
+
//renderPath(markers, d)
|
|
357
|
+
//console.log(d);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
// 2 segments
|
|
362
|
+
ptI_1 = checkLineIntersection(p1, p1.tangentR, pMid, pMid.tangentL, false, true)
|
|
363
|
+
ptI_2 = checkLineIntersection(p2, p2.tangentL, pMid, pMid.tangentR, false, true)
|
|
364
|
+
|
|
365
|
+
if (ptI_1 && ptI_2) {
|
|
366
|
+
cp1_1 = interpolate(p1, ptI_1, t)
|
|
367
|
+
cp2_1 = interpolate(pMid, ptI_1, t)
|
|
368
|
+
p_1 = pMid
|
|
369
|
+
|
|
370
|
+
cp1_2 = interpolate(pMid, ptI_2, t)
|
|
371
|
+
cp2_2 = interpolate(p2, ptI_2, t)
|
|
372
|
+
p_2 = p2
|
|
373
|
+
|
|
374
|
+
segments_2 = [
|
|
375
|
+
//{type: 'M', values: [p1.x, p1.y]},
|
|
376
|
+
{
|
|
377
|
+
type: 'C',
|
|
378
|
+
values: [cp1_1.x, cp1_1.y, cp2_1.x, cp2_1.y, p_1.x, p_1.y],
|
|
379
|
+
p0: p1,
|
|
380
|
+
cp1: cp1_1,
|
|
381
|
+
cp2: cp2_1,
|
|
382
|
+
p: p_1,
|
|
383
|
+
isExtreme: p_1.isExtreme
|
|
384
|
+
},
|
|
385
|
+
{
|
|
386
|
+
type: 'C',
|
|
387
|
+
values: [cp1_2.x, cp1_2.y, cp2_2.x, cp2_2.y, p_2.x, p_2.y],
|
|
388
|
+
p0: p_1,
|
|
389
|
+
cp1: cp1_2,
|
|
390
|
+
cp2: cp2_2,
|
|
391
|
+
p: p_2,
|
|
392
|
+
isExtreme: p_2.isExtreme
|
|
393
|
+
|
|
394
|
+
//isExtreme: true
|
|
395
|
+
},
|
|
396
|
+
];
|
|
397
|
+
|
|
398
|
+
//d = pathDataToD( [{ type: 'M', values: [p1.x, p1.y] },...segments_2])
|
|
399
|
+
//renderPath(markers, d)
|
|
400
|
+
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
// 3 segments
|
|
405
|
+
//hasInflection=true
|
|
406
|
+
if (hasInflection) {
|
|
407
|
+
|
|
408
|
+
// get pt between dir change and mid
|
|
409
|
+
let idx_3_4 = Math.floor(chunklen * 0.75)
|
|
410
|
+
p3 = chunk[idx_3_4]
|
|
411
|
+
ptI_3 = checkLineIntersection(p3, p3.tangentR, p2, p2.tangentL, false, false)
|
|
412
|
+
|
|
413
|
+
if (ptI_3) {
|
|
414
|
+
let tangentR_beforeDirChange = interpolate(p3, ptI_3, t)
|
|
415
|
+
|
|
416
|
+
// extend right tangent
|
|
417
|
+
p3.tangentR.x = tangentR_beforeDirChange.x
|
|
418
|
+
p3.tangentR.y = tangentR_beforeDirChange.y
|
|
419
|
+
|
|
420
|
+
// extend dir change tangent
|
|
421
|
+
let tangentL_dirChange = interpolate(p2, ptI_3, t)
|
|
422
|
+
p2.tangentL.x = tangentL_dirChange.x
|
|
423
|
+
p2.tangentL.y = tangentL_dirChange.y
|
|
424
|
+
} else {
|
|
425
|
+
|
|
426
|
+
if (p3 === p2) {
|
|
427
|
+
|
|
428
|
+
idx_3_4 = Math.floor(chunklen * 0.3)
|
|
429
|
+
p3 = chunk[idx_3_4]
|
|
430
|
+
|
|
431
|
+
//renderPoint(markers, p2, 'purple', '3%')
|
|
432
|
+
//renderPoint(markers, p3, 'orange', '2%')
|
|
433
|
+
|
|
434
|
+
}
|
|
435
|
+
let ptI_4 = checkLineIntersection(p3, p3.tangentR, p2, p2.tangentL, false, false)
|
|
436
|
+
//renderPoint(markers, ptI_4, 'orange', '2%')
|
|
437
|
+
//renderPoint(markers, p3, 'blue', '1%')
|
|
438
|
+
//renderPoint(markers, p1, 'magenta', '2%')
|
|
439
|
+
|
|
440
|
+
//let ptI_4 = checkLineIntersection(p3, p3.tangentL, p1, p1.tangentR, false, false)
|
|
441
|
+
//renderPoint(markers, ptI_4, 'blue', '1%')
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
//cp1_1 = interpolate(p1, ptI_4, t )
|
|
445
|
+
//cp2_1 = interpolate(p1, ptI_4, t )
|
|
446
|
+
|
|
447
|
+
cp1_1 = interpolate(p1, p1.tangentR, 1.333)
|
|
448
|
+
cp2_1 = interpolate(p2, p2.tangentL, 1.333)
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
segments_3 = [
|
|
452
|
+
{
|
|
453
|
+
type: 'C',
|
|
454
|
+
values: [cp1_1.x, cp1_1.y, cp2_1.x, cp2_1.y, p2.x, p2.y],
|
|
455
|
+
p0: p1,
|
|
456
|
+
cp1: cp1_1,
|
|
457
|
+
cp2: cp2_1,
|
|
458
|
+
p: p2,
|
|
459
|
+
isExtreme: p2.isExtreme
|
|
460
|
+
//isExtreme: true
|
|
461
|
+
},
|
|
462
|
+
];
|
|
463
|
+
|
|
464
|
+
|
|
465
|
+
//let ptI_4 = checkLineIntersection(p2, p2.tangentL, p2, p2.tangentL, false, false)
|
|
466
|
+
//renderPoint(markers, ptI_3, 'orange', '2%')
|
|
467
|
+
|
|
468
|
+
/*
|
|
469
|
+
let tangentR_beforeDirChange = interpolate(p3, ptI_3, t)
|
|
470
|
+
|
|
471
|
+
// extend right tangent
|
|
472
|
+
p3.tangentR.x = tangentR_beforeDirChange.x
|
|
473
|
+
p3.tangentR.y = tangentR_beforeDirChange.y
|
|
474
|
+
|
|
475
|
+
let tangentL_dirChange = interpolate(p2, ptI_3, t)
|
|
476
|
+
p2.tangentL.x = tangentL_dirChange.x
|
|
477
|
+
p2.tangentL.y = tangentL_dirChange.y
|
|
478
|
+
*/
|
|
479
|
+
|
|
480
|
+
d = pathDataToD([{ type: 'M', values: [p1.x, p1.y] }, ...segments_3])
|
|
481
|
+
//renderPath(markers, d)
|
|
482
|
+
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
cp1_3 = p3.tangentR
|
|
488
|
+
cp2_3 = p2.tangentL
|
|
489
|
+
p_3 = p2
|
|
490
|
+
|
|
491
|
+
ptI_1 = checkLineIntersection(p1, p1.tangentR, pMid, pMid.tangentL, false, true)
|
|
492
|
+
ptI_2 = checkLineIntersection(pMid, pMid.tangentR, p3, p3.tangentL, false, true)
|
|
493
|
+
|
|
494
|
+
if (!ptI_3) {
|
|
495
|
+
//console.log(chunk, pMid, pMid.tangentR, 'p3', p3, p3.tangentL);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
if (ptI_1 && ptI_2 && ptI_3) {
|
|
499
|
+
|
|
500
|
+
cp1_1 = interpolate(p1, ptI_1, t)
|
|
501
|
+
cp2_1 = interpolate(pMid, ptI_1, t)
|
|
502
|
+
p_1 = pMid
|
|
503
|
+
|
|
504
|
+
cp1_2 = interpolate(pMid, ptI_2, t)
|
|
505
|
+
cp2_2 = interpolate(p3, ptI_2, t)
|
|
506
|
+
p_2 = p3
|
|
507
|
+
|
|
508
|
+
segments_3 = [
|
|
509
|
+
{
|
|
510
|
+
type: 'C',
|
|
511
|
+
values: [cp1_1.x, cp1_1.y, cp2_1.x, cp2_1.y, p_1.x, p_1.y],
|
|
512
|
+
p0: p1,
|
|
513
|
+
cp1: cp1_1,
|
|
514
|
+
cp2: cp2_1,
|
|
515
|
+
p: p_1,
|
|
516
|
+
isExtreme: p_1.isExtreme
|
|
517
|
+
//isExtreme: true
|
|
518
|
+
},
|
|
519
|
+
{
|
|
520
|
+
type: 'C',
|
|
521
|
+
values: [cp1_2.x, cp1_2.y, cp2_2.x, cp2_2.y, p_2.x, p_2.y],
|
|
522
|
+
p0: p_1,
|
|
523
|
+
cp1: cp1_2,
|
|
524
|
+
cp2: cp2_2,
|
|
525
|
+
p: p_2,
|
|
526
|
+
},
|
|
527
|
+
{
|
|
528
|
+
type: 'C',
|
|
529
|
+
values: [cp1_3.x, cp1_3.y, cp2_3.x, cp2_3.y, p_3.x, p_3.y],
|
|
530
|
+
p0: p_2,
|
|
531
|
+
cp1: cp1_3,
|
|
532
|
+
cp2: cp2_3,
|
|
533
|
+
p: p_3,
|
|
534
|
+
isExtreme: p_3.isExtreme
|
|
535
|
+
//isExtreme: true
|
|
536
|
+
}
|
|
537
|
+
];
|
|
538
|
+
|
|
539
|
+
d = pathDataToD([{ type: 'M', values: [p1.x, p1.y] }, ...segments_3])
|
|
540
|
+
//renderPath(markers, d)
|
|
541
|
+
|
|
542
|
+
|
|
543
|
+
} else {
|
|
544
|
+
//console.log('incomplete', ptI_1, ptI_2, ptI_3);
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
//console.log('chunk', chunk, extremes, semiExtremes, corners, inflections);
|
|
550
|
+
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
if (segsRequired === 1) {
|
|
554
|
+
segments = segments_1
|
|
555
|
+
} else if (segsRequired === 2 && segments_2.length) {
|
|
556
|
+
segments = segments_2
|
|
557
|
+
}
|
|
558
|
+
else {
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
segments = segments_3.length ? segments_3 : segments_2
|
|
562
|
+
//segments = segments_2.length ? segments_2 : segments_3
|
|
563
|
+
//segments = segments_3
|
|
564
|
+
|
|
565
|
+
|
|
566
|
+
/**
|
|
567
|
+
* try simplification
|
|
568
|
+
*/
|
|
569
|
+
let simplify = false
|
|
570
|
+
/*
|
|
571
|
+
if (simplify && !isLinetoSeg && segments.length > 1) {
|
|
572
|
+
|
|
573
|
+
let com1 = segments[0]
|
|
574
|
+
let com2 = segments[1]
|
|
575
|
+
|
|
576
|
+
tolerance = 1.1
|
|
577
|
+
let combined = combineCubicPairs(com1, com2, { tolerance })
|
|
578
|
+
//let error = combined[0].error;
|
|
579
|
+
let error = 0;
|
|
580
|
+
let comsSimp =[]
|
|
581
|
+
|
|
582
|
+
console.log('!!!combined', segments.length, combined);
|
|
583
|
+
|
|
584
|
+
// success
|
|
585
|
+
if (combined.length === 1) {
|
|
586
|
+
|
|
587
|
+
if(segments.length === 2){
|
|
588
|
+
segments = combined
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
let com = combined[0]
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
}
|
|
595
|
+
*/
|
|
596
|
+
|
|
597
|
+
// remove first segment to connect to last segment
|
|
598
|
+
pathData.push(...segments)
|
|
599
|
+
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
|
|
603
|
+
|
|
604
|
+
if (closed) {
|
|
605
|
+
pathData.push({ type: 'Z', values: [] })
|
|
606
|
+
}
|
|
607
|
+
//console.log('!!!pathData from poly', pathData);
|
|
608
|
+
|
|
609
|
+
|
|
610
|
+
//pathData2 = convertPathData(pathData2, { toRelative: true, decimals: 5 });
|
|
611
|
+
//let d = pathDataToD(pathData2);
|
|
612
|
+
//renderPath(markers, d, 'green', '0.75%', '0.5')
|
|
613
|
+
//console.log(d);
|
|
614
|
+
|
|
615
|
+
|
|
616
|
+
// refine extremes
|
|
617
|
+
return pathData
|
|
618
|
+
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
|
|
622
|
+
|
|
623
|
+
|
|
155
624
|
/**
|
|
156
625
|
* convert polygon chunks
|
|
157
626
|
* to cubic beziers
|
package/src/svgii/rounding.js
CHANGED
|
@@ -19,7 +19,7 @@ export function roundPathData(pathData, decimalsGlobal = -1) {
|
|
|
19
19
|
|
|
20
20
|
let len = pathData.length;
|
|
21
21
|
let decimals = decimalsGlobal
|
|
22
|
-
let decimalsArc = decimals < 3 ? decimals +
|
|
22
|
+
let decimalsArc = decimals < 3 ? decimals + 1 : decimals
|
|
23
23
|
//decimalsArc = decimals
|
|
24
24
|
//console.log(decimalsArc);
|
|
25
25
|
|
|
@@ -64,7 +64,7 @@ export function detectAccuracyPoly(pts) {
|
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
let dim_min = dims.sort()
|
|
67
|
+
let dim_min = dims.sort((a,b)=>a-b)
|
|
68
68
|
let sliceIdx = Math.ceil(dim_min.length / 8);
|
|
69
69
|
dim_min = dim_min.slice(0, sliceIdx);
|
|
70
70
|
let minVal = dim_min.reduce((a, b) => a + b, 0) / sliceIdx;
|
|
@@ -90,65 +90,55 @@ export function detectAccuracy(pathData) {
|
|
|
90
90
|
let com = pathData[i];
|
|
91
91
|
let { type, values, p0 = null, p = null, dimA = 0 } = com;
|
|
92
92
|
|
|
93
|
-
// use existing
|
|
93
|
+
// use existing average dimension value or calculate
|
|
94
94
|
if (values.length && p && p0) {
|
|
95
|
-
//console.log(com);
|
|
96
95
|
dimA = dimA ? dimA : getDistManhattan(p0, p);
|
|
97
96
|
|
|
98
|
-
//
|
|
99
|
-
|
|
97
|
+
//who would have thought we need an exception for arcs?
|
|
98
|
+
if (type === 'A') dimA *= 0.5
|
|
100
99
|
|
|
101
100
|
if (dimA) dims.push(+dimA.toFixed(8));
|
|
102
|
-
//if (dimA) dims.push(dimA);
|
|
103
101
|
if (dimA && dimA < minDim) minDim = dimA;
|
|
104
|
-
//if (dimA && dimA > maxDim) maxDim = dimA;
|
|
105
102
|
}
|
|
106
103
|
|
|
107
104
|
}
|
|
108
105
|
|
|
109
106
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
// smallest 25% of values
|
|
115
|
-
let idx_q = Math.ceil(len*0.25);
|
|
116
|
-
let dims_min = dims.slice(0, idx_q);
|
|
117
|
-
|
|
118
|
-
// average smallest values with mid value
|
|
119
|
-
let dim_min = ((dims_min.reduce((a, b) => a + b, 0) / idx_q) + dim_mid) * 0.5;
|
|
120
|
-
|
|
107
|
+
dims = dims.sort((a,b)=>a-b)
|
|
108
|
+
let len = dims.length;
|
|
109
|
+
let dim_mid = dims[Math.floor(len * 0.5)]
|
|
121
110
|
|
|
122
|
-
|
|
123
|
-
|
|
111
|
+
// smallest 25% of values
|
|
112
|
+
let idx_q = Math.ceil(len * 0.25);
|
|
113
|
+
let dims_min = dims.slice(0, idx_q);
|
|
124
114
|
|
|
125
|
-
|
|
126
|
-
|
|
115
|
+
// average smallest values with mid value
|
|
116
|
+
let dim_min = ((dims_min.reduce((a, b) => a + b, 0) / idx_q) + dim_mid) * 0.5;
|
|
127
117
|
|
|
128
118
|
|
|
119
|
+
let threshold = 75
|
|
120
|
+
let decimalsAuto = dim_min > threshold * 1.5 ? 0 : Math.floor(threshold / dim_min).toString().length
|
|
129
121
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
//console.log('dim_min', dim_min);
|
|
133
|
-
|
|
134
|
-
let dim_mid = dim_min[Math.floor(dim_min.length*0.5)]
|
|
122
|
+
// clamp
|
|
123
|
+
return Math.min(Math.max(0, decimalsAuto), 8)
|
|
135
124
|
|
|
136
|
-
|
|
137
|
-
dim_min = dim_min.slice(0, sliceIdx);
|
|
138
|
-
let minVal = dim_min.reduce((a, b) => a + b, 0) / sliceIdx;
|
|
125
|
+
}
|
|
139
126
|
|
|
140
|
-
// average with mid value
|
|
141
|
-
minVal = (minVal+dim_mid)*0.5
|
|
142
|
-
//console.log('minVal', minVal, dim_mid);
|
|
143
127
|
|
|
144
128
|
|
|
145
|
-
|
|
146
|
-
|
|
129
|
+
export function roundPoly(poly = [], decimals = 3) {
|
|
130
|
+
if (!poly.length || decimals < 0) return poly;
|
|
131
|
+
poly = poly.map(pt => roundPoint(pt, decimals));
|
|
132
|
+
return poly
|
|
133
|
+
}
|
|
147
134
|
|
|
148
|
-
// clamp
|
|
149
|
-
return Math.min(Math.max(0, decimalsAuto), 8)
|
|
150
|
-
*/
|
|
151
135
|
|
|
136
|
+
export function roundPoint(pt = {}, decimals = 3) {
|
|
137
|
+
if (pt.x === undefined || pt.y === undefined || decimals < 0) return pt;
|
|
138
|
+
pt.x=roundTo(pt.x, decimals)
|
|
139
|
+
pt.y=roundTo(pt.y, decimals)
|
|
140
|
+
return pt
|
|
141
|
+
//return { x: roundTo(pt.x, decimals), y: roundTo(pt.y, decimals) }
|
|
152
142
|
}
|
|
153
143
|
|
|
154
144
|
/**
|