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
|
@@ -380,7 +380,14 @@
|
|
|
380
380
|
|
|
381
381
|
// get angle helper
|
|
382
382
|
function getAngle(p1, p2, normalize = false) {
|
|
383
|
-
let angle = atan2(p2.y - p1.y, p2.x - p1.x);
|
|
383
|
+
let angle = Math.atan2(p2.y - p1.y, p2.x - p1.x);
|
|
384
|
+
// normalize negative angles
|
|
385
|
+
if (normalize && angle < 0) angle += Math.PI * 2;
|
|
386
|
+
return angle
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
function getAngleFromDelta(dx, dy, normalize = false) {
|
|
390
|
+
let angle = Math.atan2(dy, dx);
|
|
384
391
|
// normalize negative angles
|
|
385
392
|
if (normalize && angle < 0) angle += Math.PI * 2;
|
|
386
393
|
return angle
|
|
@@ -476,7 +483,9 @@
|
|
|
476
483
|
// if we cast these lines infinitely in both directions, they intersect here:
|
|
477
484
|
intersectionPoint = {
|
|
478
485
|
x: p1.x + (a * (p2.x - p1.x)),
|
|
479
|
-
y: p1.y + (a * (p2.y - p1.y))
|
|
486
|
+
y: p1.y + (a * (p2.y - p1.y)),
|
|
487
|
+
t1: a,
|
|
488
|
+
t2: b
|
|
480
489
|
};
|
|
481
490
|
|
|
482
491
|
let intersection = false;
|
|
@@ -578,13 +587,13 @@
|
|
|
578
587
|
if (t === 0 && !shortCp1) {
|
|
579
588
|
pt.x = p0.x;
|
|
580
589
|
pt.y = p0.y;
|
|
581
|
-
pt.angle = getAngle(p0, cp1);
|
|
590
|
+
if (getTangent) pt.angle = getAngle(p0, cp1);
|
|
582
591
|
}
|
|
583
592
|
|
|
584
593
|
else if (t === 1 && !shortCp2) {
|
|
585
594
|
pt.x = p.x;
|
|
586
595
|
pt.y = p.y;
|
|
587
|
-
pt.angle = getAngle(cp2, p);
|
|
596
|
+
if (getTangent) pt.angle = getAngle(cp2, p);
|
|
588
597
|
}
|
|
589
598
|
|
|
590
599
|
else {
|
|
@@ -601,18 +610,38 @@
|
|
|
601
610
|
pt = interpolate(m3, m4, t);
|
|
602
611
|
|
|
603
612
|
// add angles
|
|
604
|
-
pt.angle = getAngle(m3, m4);
|
|
613
|
+
if (getTangent) pt.angle = getAngle(m3, m4);
|
|
614
|
+
|
|
615
|
+
/*
|
|
616
|
+
renderPoint(markers, m0, 'cyan')
|
|
617
|
+
renderPoint(markers, m3, 'magenta')
|
|
618
|
+
renderPoint(markers, pt, 'green')
|
|
619
|
+
renderPoint(markers, m4, 'cyan')
|
|
620
|
+
renderPoint(markers, m2, 'magenta')
|
|
621
|
+
*/
|
|
605
622
|
|
|
606
623
|
// add control points
|
|
607
|
-
if (getCpts)
|
|
624
|
+
if (getCpts) {
|
|
625
|
+
pt.cpts = [m1, m2, m3, m4];
|
|
626
|
+
pt.segments = [
|
|
627
|
+
{ p0, cp1: m0, cp2: m3, p: pt },
|
|
628
|
+
{ p0: pt, cp1: m4, cp2: m2, p }
|
|
629
|
+
];
|
|
630
|
+
}
|
|
608
631
|
} else {
|
|
609
632
|
m1 = interpolate(p0, cp1, t);
|
|
610
633
|
m2 = interpolate(cp1, p, t);
|
|
611
634
|
pt = interpolate(m1, m2, t);
|
|
612
|
-
pt.angle = getAngle(m1, m2);
|
|
635
|
+
if (getTangent) pt.angle = getAngle(m1, m2);
|
|
613
636
|
|
|
614
637
|
// add control points
|
|
615
|
-
if (getCpts)
|
|
638
|
+
if (getCpts) {
|
|
639
|
+
pt.cpts = [m1, m2];
|
|
640
|
+
pt.segments = [
|
|
641
|
+
{ p0, cp1: m1, p: pt },
|
|
642
|
+
{ p0: p, cp1: m2, p }
|
|
643
|
+
];
|
|
644
|
+
}
|
|
616
645
|
}
|
|
617
646
|
}
|
|
618
647
|
|
|
@@ -696,7 +725,7 @@
|
|
|
696
725
|
* get vertices from path command final on-path points
|
|
697
726
|
*/
|
|
698
727
|
|
|
699
|
-
function getPathDataVertices(pathData=[], includeCpts = false, decimals = -1) {
|
|
728
|
+
function getPathDataVertices(pathData = [], includeCpts = false, decimals = -1) {
|
|
700
729
|
let polyPoints = [];
|
|
701
730
|
|
|
702
731
|
pathData.forEach((com) => {
|
|
@@ -783,7 +812,8 @@
|
|
|
783
812
|
|
|
784
813
|
if (rx == 0 || ry == 0) {
|
|
785
814
|
// invalid arguments
|
|
786
|
-
|
|
815
|
+
console.warn("rx and ry can not be 0");
|
|
816
|
+
return arcData
|
|
787
817
|
}
|
|
788
818
|
|
|
789
819
|
/**
|
|
@@ -796,10 +826,10 @@
|
|
|
796
826
|
let s_phi = !phi ? 0 : Math.sin(phi);
|
|
797
827
|
let c_phi = !phi ? 1 : Math.cos(phi);
|
|
798
828
|
|
|
799
|
-
let hd_x = (x1 - x2)
|
|
800
|
-
let hd_y = (y1 - y2)
|
|
801
|
-
let hs_x = (x1 + x2)
|
|
802
|
-
let hs_y = (y1 + y2)
|
|
829
|
+
let hd_x = (x1 - x2) * 0.5;
|
|
830
|
+
let hd_y = (y1 - y2) * 0.5;
|
|
831
|
+
let hs_x = (x1 + x2) * 0.5;
|
|
832
|
+
let hs_y = (y1 + y2) * 0.5;
|
|
803
833
|
|
|
804
834
|
// F6.5.1
|
|
805
835
|
let x1_ = !phi ? hd_x : c_phi * hd_x + s_phi * hd_y;
|
|
@@ -821,10 +851,11 @@
|
|
|
821
851
|
let rxry = rx * ry;
|
|
822
852
|
let rxy1_ = rx * y1_;
|
|
823
853
|
let ryx1_ = ry * x1_;
|
|
824
|
-
let sum_of_sq = rxy1_
|
|
854
|
+
let sum_of_sq = rxy1_ * rxy1_ + ryx1_ * ryx1_; // sum of square
|
|
825
855
|
if (!sum_of_sq) {
|
|
856
|
+
console.warn("start point can not be same as end point");
|
|
857
|
+
return arcData
|
|
826
858
|
|
|
827
|
-
throw Error("start point can not be same as end point");
|
|
828
859
|
}
|
|
829
860
|
let coe = Math.sqrt(Math.abs((rxry * rxry - sum_of_sq) / sum_of_sq));
|
|
830
861
|
if (largeArc === sweep) {
|
|
@@ -973,7 +1004,11 @@
|
|
|
973
1004
|
|
|
974
1005
|
function getBezierExtremeT(pts, { addExtremes = true, addSemiExtremes = false } = {}) {
|
|
975
1006
|
let tArr = pts.length === 4 ? cubicBezierExtremeT(pts[0], pts[1], pts[2], pts[3], { addExtremes, addSemiExtremes }) : quadraticBezierExtremeT(pts[0], pts[1], pts[2], { addExtremes, addSemiExtremes });
|
|
976
|
-
|
|
1007
|
+
if (tArr.length) {
|
|
1008
|
+
tArr = tArr.map(t => +t.toFixed(9)).sort();
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
return tArr
|
|
977
1012
|
}
|
|
978
1013
|
|
|
979
1014
|
function getArcExtemes(p0, values) {
|
|
@@ -1303,13 +1338,6 @@
|
|
|
1303
1338
|
return tArr;
|
|
1304
1339
|
}
|
|
1305
1340
|
|
|
1306
|
-
function isMultipleOf45(angleRad, epsilon = 0.001) {
|
|
1307
|
-
let rad90 = Math.PI / 2;
|
|
1308
|
-
let rad45 = rad90 / 2;
|
|
1309
|
-
let isRightAngle = Math.abs(angleRad / rad90 - Math.round(angleRad / rad90)) < epsilon;
|
|
1310
|
-
return !isRightAngle ? Math.abs(angleRad / rad45 - Math.round(angleRad / rad45)) < epsilon : false;
|
|
1311
|
-
}
|
|
1312
|
-
|
|
1313
1341
|
function quadraticBezierExtremeT(p0, cp1, p, { addExtremes = true, addSemiExtremes = false } = {}) {
|
|
1314
1342
|
|
|
1315
1343
|
// rotate cpts for semi extremes
|
|
@@ -1373,6 +1401,11 @@
|
|
|
1373
1401
|
let dx = isArray ? p2[0] - p1[0] : (p2.x - p1.x);
|
|
1374
1402
|
let dy = isArray ? p2[1] - p1[1] : (p2.y - p1.y);
|
|
1375
1403
|
|
|
1404
|
+
/*
|
|
1405
|
+
let sqrt2 = 1.4142135623730951
|
|
1406
|
+
return dx===dy ? Math.abs(dx) * sqrt2 : Math.sqrt(dx * dx + dy * dy);
|
|
1407
|
+
*/
|
|
1408
|
+
|
|
1376
1409
|
return Math.sqrt(dx * dx + dy * dy);
|
|
1377
1410
|
}
|
|
1378
1411
|
|
|
@@ -1388,6 +1421,7 @@
|
|
|
1388
1421
|
* sloppy but fast
|
|
1389
1422
|
*/
|
|
1390
1423
|
function getDistManhattan(pt1, pt2) {
|
|
1424
|
+
|
|
1391
1425
|
let dx = Math.abs(pt2.x - pt1.x);
|
|
1392
1426
|
let dy = Math.abs(pt2.y - pt1.y);
|
|
1393
1427
|
return dx + dy;
|
|
@@ -1855,7 +1889,7 @@
|
|
|
1855
1889
|
|
|
1856
1890
|
let len = pathData.length;
|
|
1857
1891
|
let decimals = decimalsGlobal;
|
|
1858
|
-
let decimalsArc = decimals < 3 ? decimals +
|
|
1892
|
+
let decimalsArc = decimals < 3 ? decimals + 1 : decimals;
|
|
1859
1893
|
|
|
1860
1894
|
for (let c = 0; c < len; c++) {
|
|
1861
1895
|
let com = pathData[c];
|
|
@@ -1890,7 +1924,7 @@
|
|
|
1890
1924
|
}
|
|
1891
1925
|
}
|
|
1892
1926
|
|
|
1893
|
-
let dim_min = dims.sort();
|
|
1927
|
+
let dim_min = dims.sort((a,b)=>a-b);
|
|
1894
1928
|
let sliceIdx = Math.ceil(dim_min.length / 8);
|
|
1895
1929
|
dim_min = dim_min.slice(0, sliceIdx);
|
|
1896
1930
|
let minVal = dim_min.reduce((a, b) => a + b, 0) / sliceIdx;
|
|
@@ -1910,52 +1944,47 @@
|
|
|
1910
1944
|
let com = pathData[i];
|
|
1911
1945
|
let { type, values, p0 = null, p = null, dimA = 0 } = com;
|
|
1912
1946
|
|
|
1913
|
-
// use existing
|
|
1947
|
+
// use existing average dimension value or calculate
|
|
1914
1948
|
if (values.length && p && p0) {
|
|
1915
|
-
|
|
1916
1949
|
dimA = dimA ? dimA : getDistManhattan(p0, p);
|
|
1917
1950
|
|
|
1918
|
-
if (
|
|
1951
|
+
if (type === 'A') dimA *= 0.5;
|
|
1919
1952
|
|
|
1953
|
+
if (dimA) dims.push(+dimA.toFixed(8));
|
|
1920
1954
|
}
|
|
1921
1955
|
|
|
1922
1956
|
}
|
|
1923
1957
|
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1958
|
+
dims = dims.sort((a,b)=>a-b);
|
|
1959
|
+
let len = dims.length;
|
|
1960
|
+
let dim_mid = dims[Math.floor(len * 0.5)];
|
|
1927
1961
|
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1962
|
+
// smallest 25% of values
|
|
1963
|
+
let idx_q = Math.ceil(len * 0.25);
|
|
1964
|
+
let dims_min = dims.slice(0, idx_q);
|
|
1931
1965
|
|
|
1932
|
-
|
|
1933
|
-
|
|
1966
|
+
// average smallest values with mid value
|
|
1967
|
+
let dim_min = ((dims_min.reduce((a, b) => a + b, 0) / idx_q) + dim_mid) * 0.5;
|
|
1934
1968
|
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
// clamp
|
|
1939
|
-
return Math.min(Math.max(0, decimalsAuto), 8)
|
|
1940
|
-
|
|
1941
|
-
/*
|
|
1942
|
-
let dim_min = dims.sort()
|
|
1943
|
-
|
|
1944
|
-
let dim_mid = dim_min[Math.floor(dim_min.length*0.5)]
|
|
1969
|
+
let threshold = 75;
|
|
1970
|
+
let decimalsAuto = dim_min > threshold * 1.5 ? 0 : Math.floor(threshold / dim_min).toString().length;
|
|
1945
1971
|
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
let minVal = dim_min.reduce((a, b) => a + b, 0) / sliceIdx;
|
|
1972
|
+
// clamp
|
|
1973
|
+
return Math.min(Math.max(0, decimalsAuto), 8)
|
|
1949
1974
|
|
|
1950
|
-
|
|
1951
|
-
minVal = (minVal+dim_mid)*0.5
|
|
1975
|
+
}
|
|
1952
1976
|
|
|
1953
|
-
|
|
1954
|
-
|
|
1977
|
+
function roundPoly(poly = [], decimals = 3) {
|
|
1978
|
+
if (!poly.length || decimals < 0) return poly;
|
|
1979
|
+
poly = poly.map(pt => roundPoint(pt, decimals));
|
|
1980
|
+
return poly
|
|
1981
|
+
}
|
|
1955
1982
|
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1983
|
+
function roundPoint(pt = {}, decimals = 3) {
|
|
1984
|
+
if (pt.x === undefined || pt.y === undefined || decimals < 0) return pt;
|
|
1985
|
+
pt.x=roundTo(pt.x, decimals);
|
|
1986
|
+
pt.y=roundTo(pt.y, decimals);
|
|
1987
|
+
return pt
|
|
1959
1988
|
|
|
1960
1989
|
}
|
|
1961
1990
|
|
|
@@ -2733,7 +2762,7 @@
|
|
|
2733
2762
|
}
|
|
2734
2763
|
}
|
|
2735
2764
|
|
|
2736
|
-
let bbox = { x: xMin, y: yMin, width: xMax - xMin, height: yMax - yMin };
|
|
2765
|
+
let bbox = { x: xMin, y: yMin, right: xMax, width: xMax - xMin, bottom: yMax, height: yMax - yMin };
|
|
2737
2766
|
return bbox
|
|
2738
2767
|
}
|
|
2739
2768
|
|
|
@@ -2923,7 +2952,7 @@
|
|
|
2923
2952
|
* d attribute string
|
|
2924
2953
|
*/
|
|
2925
2954
|
|
|
2926
|
-
function pathDataToD(pathData, mode = 0) {
|
|
2955
|
+
function pathDataToD(pathData = [], mode = 0) {
|
|
2927
2956
|
|
|
2928
2957
|
mode = parseFloat(mode);
|
|
2929
2958
|
/*
|
|
@@ -2932,80 +2961,99 @@
|
|
|
2932
2961
|
1 = verbose
|
|
2933
2962
|
2 = beautify
|
|
2934
2963
|
*/
|
|
2964
|
+
|
|
2935
2965
|
let len = pathData.length;
|
|
2966
|
+
let d = '';
|
|
2936
2967
|
|
|
2937
|
-
|
|
2938
|
-
let
|
|
2939
|
-
|
|
2940
|
-
let separator_type = mode > 0.5 ? ' ' : '';
|
|
2968
|
+
// group same types
|
|
2969
|
+
let pathDataGrouped = mode >0.5 ? JSON.parse(JSON.stringify(pathData)) : [];
|
|
2970
|
+
let typePrev = 'M';
|
|
2941
2971
|
|
|
2942
|
-
|
|
2943
|
-
|
|
2972
|
+
if (mode < 1) {
|
|
2973
|
+
pathDataGrouped = [pathData[0]];
|
|
2944
2974
|
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
let
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2975
|
+
let idx = 0;
|
|
2976
|
+
|
|
2977
|
+
for (let i = 1; i < len; i++) {
|
|
2978
|
+
let com = pathData[i];
|
|
2979
|
+
let { type } = com;
|
|
2980
|
+
// decouple from object
|
|
2981
|
+
let values = [...com.values];
|
|
2982
|
+
|
|
2983
|
+
// new type
|
|
2984
|
+
if (type !== typePrev) {
|
|
2985
|
+
pathDataGrouped.push({type, values});
|
|
2986
|
+
idx++;
|
|
2987
|
+
} else {
|
|
2988
|
+
pathDataGrouped[idx].values.push(...values);
|
|
2989
|
+
}
|
|
2990
|
+
|
|
2991
|
+
// update type
|
|
2992
|
+
typePrev = type;
|
|
2958
2993
|
}
|
|
2994
|
+
}
|
|
2995
|
+
|
|
2996
|
+
// stringify grouped
|
|
2997
|
+
len = pathDataGrouped.length;
|
|
2998
|
+
let separator_type = mode < 1 ? '' : ' ';
|
|
2999
|
+
let separator_command = mode < 1 ? '' : (mode === 1 ? ' ' : `\n`);
|
|
3000
|
+
|
|
3001
|
+
typePrev = 'M';
|
|
2959
3002
|
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
: ((mode < 1) && com0.type === "M" && com.type === "L"
|
|
2964
|
-
? " "
|
|
2965
|
-
: com.type);
|
|
3003
|
+
for (let i = 0; i < len; i++) {
|
|
3004
|
+
let com = pathDataGrouped[i];
|
|
3005
|
+
let { type, values } = com;
|
|
2966
3006
|
|
|
2967
|
-
//
|
|
2968
|
-
|
|
3007
|
+
// we're always starting a path with absolute M!
|
|
3008
|
+
let omitType = mode < 1 && ((typePrev === 'M' && type === 'L') || (typePrev === 'm' && type === 'l'));
|
|
2969
3009
|
|
|
2970
|
-
|
|
3010
|
+
// add type
|
|
3011
|
+
if (!omitType) d += type + separator_type;
|
|
2971
3012
|
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
let isFloat = valStr.includes('.');
|
|
2976
|
-
let isSmallFloat = isFloat && Math.abs(val) < 1;
|
|
3013
|
+
// add values
|
|
3014
|
+
let wasSmallFloat = false;
|
|
3015
|
+
let separatorVal = ' ';
|
|
2977
3016
|
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
3017
|
+
for (let v = 0, vlen = values.length; vlen && v < vlen; v++) {
|
|
3018
|
+
let val = values[v];
|
|
3019
|
+
let valAbs = Math.abs(val);
|
|
3020
|
+
let valStr = val.toString();
|
|
3021
|
+
let isNegative = val < 0;
|
|
3022
|
+
let sign = isNegative ? '-' : '';
|
|
3023
|
+
let isSmallFloat = mode > 0.5 ? false : (val && valAbs < 1);
|
|
3024
|
+
let idxSub = isSmallFloat ? (isNegative ? 2 : 1) : 0;
|
|
2982
3025
|
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
3026
|
+
// we don't need whitespace for first value
|
|
3027
|
+
separatorVal = v === 0 || isNegative ? '' : ' ';
|
|
3028
|
+
|
|
3029
|
+
if (mode < 1) {
|
|
3030
|
+
// omit leading zero
|
|
3031
|
+
if (isSmallFloat) valStr = sign + valStr.substring(idxSub);
|
|
3032
|
+
|
|
3033
|
+
// omit whitespace for subsequent small floats
|
|
3034
|
+
separatorVal = (v === 0 && !omitType) || (wasSmallFloat && isSmallFloat) ?
|
|
3035
|
+
(!mode ? '' : (isNegative ? '' : ' '))
|
|
3036
|
+
: (isNegative ? '' : ' ');
|
|
2987
3037
|
|
|
2988
|
-
valsString += valStr;
|
|
2989
|
-
prevWasFloat = isSmallFloat;
|
|
2990
3038
|
}
|
|
2991
3039
|
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
3040
|
+
// omit separator between large Arc sweep and final x in minify mode
|
|
3041
|
+
if (!mode && (type === 'a' || type === 'A')) {
|
|
3042
|
+
let pos = (v % 7);
|
|
3043
|
+
if (pos > 3 && pos < 6) separatorVal = '';
|
|
3044
|
+
}
|
|
3045
|
+
|
|
3046
|
+
d += `${separatorVal}${valStr}`;
|
|
3047
|
+
wasSmallFloat = isSmallFloat;
|
|
3048
|
+
|
|
2996
3049
|
}
|
|
2997
3050
|
|
|
2998
|
-
|
|
2999
|
-
d +=
|
|
3000
|
-
|
|
3051
|
+
// add command separator
|
|
3052
|
+
if (mode) d += separator_command;
|
|
3053
|
+
|
|
3054
|
+
// update previous type
|
|
3055
|
+
typePrev = type;
|
|
3001
3056
|
|
|
3002
|
-
if (mode < 1) {
|
|
3003
|
-
d = d
|
|
3004
|
-
.replace(/[A-Za-z]0(?=\.)/g, m => m[0])
|
|
3005
|
-
.replace(/ 0\./g, " .") // Space before small decimals
|
|
3006
|
-
.replace(/ -/g, "-") // Remove space before negatives
|
|
3007
|
-
.replace(/-0\./g, "-.") // Remove leading zero from negative decimals
|
|
3008
|
-
.replace(/Z/g, "z"); // Convert uppercase 'Z' to lowercase
|
|
3009
3057
|
}
|
|
3010
3058
|
|
|
3011
3059
|
return d;
|
|
@@ -3013,39 +3061,26 @@
|
|
|
3013
3061
|
|
|
3014
3062
|
function getCombinedByDominant(com1, com2, maxDist = 0, tolerance = 1, debug = false) {
|
|
3015
3063
|
|
|
3016
|
-
// cubic Bézier derivative
|
|
3017
|
-
const cubicDerivative = (p0, p1, p2, p3, t) => {
|
|
3018
|
-
let mt = 1 - t;
|
|
3019
|
-
|
|
3020
|
-
return {
|
|
3021
|
-
x:
|
|
3022
|
-
3 * mt * mt * (p1.x - p0.x) +
|
|
3023
|
-
6 * mt * t * (p2.x - p1.x) +
|
|
3024
|
-
3 * t * t * (p3.x - p2.x),
|
|
3025
|
-
y:
|
|
3026
|
-
3 * mt * mt * (p1.y - p0.y) +
|
|
3027
|
-
6 * mt * t * (p2.y - p1.y) +
|
|
3028
|
-
3 * t * t * (p3.y - p2.y)
|
|
3029
|
-
};
|
|
3030
|
-
};
|
|
3031
|
-
|
|
3032
3064
|
// if combining fails return original commands
|
|
3033
3065
|
let commands = [com1, com2];
|
|
3034
3066
|
|
|
3035
3067
|
// detect dominant
|
|
3036
|
-
let dist1 =
|
|
3037
|
-
let dist2 =
|
|
3068
|
+
let dist1 = getDistManhattan(com1.p0, com1.p);
|
|
3069
|
+
let dist2 = getDistManhattan(com2.p0, com2.p);
|
|
3070
|
+
let thresh = (dist1 + dist2) * 0.5 * 0.075 * tolerance;
|
|
3038
3071
|
|
|
3039
|
-
|
|
3072
|
+
// take longer command
|
|
3073
|
+
let reverse = dist1 < dist2;
|
|
3040
3074
|
|
|
3041
3075
|
// backup original commands
|
|
3042
3076
|
let com1_o = JSON.parse(JSON.stringify(com1));
|
|
3043
3077
|
let com2_o = JSON.parse(JSON.stringify(com2));
|
|
3044
3078
|
|
|
3045
|
-
|
|
3079
|
+
// intersection of control tangents
|
|
3080
|
+
let ptI = checkLineIntersection(com1_o.p0, com1_o.cp1, com2_o.p, com2_o.cp2, false, true);
|
|
3046
3081
|
|
|
3082
|
+
// no intersection - we can't combine
|
|
3047
3083
|
if (!ptI) {
|
|
3048
|
-
|
|
3049
3084
|
return commands
|
|
3050
3085
|
}
|
|
3051
3086
|
|
|
@@ -3068,153 +3103,70 @@
|
|
|
3068
3103
|
com2 = com2_R;
|
|
3069
3104
|
}
|
|
3070
3105
|
|
|
3071
|
-
|
|
3072
|
-
let
|
|
3073
|
-
let
|
|
3074
|
-
let dot = (a, b) => a.x * b.x + a.y * b.y;
|
|
3075
|
-
|
|
3076
|
-
// estimate extrapolation parameter t0
|
|
3077
|
-
|
|
3078
|
-
let B0 = com2.p0;
|
|
3079
|
-
let D0 = cubicDerivative(
|
|
3080
|
-
com2.p0,
|
|
3081
|
-
com2.cp1,
|
|
3082
|
-
com2.cp2,
|
|
3083
|
-
com2.p,
|
|
3084
|
-
0
|
|
3085
|
-
);
|
|
3086
|
-
|
|
3087
|
-
let v = sub(com1.p0, B0);
|
|
3088
|
-
|
|
3089
|
-
// first-order projection onto tangent
|
|
3090
|
-
let t0 = dot(v, D0) / dot(D0, D0);
|
|
3091
|
-
|
|
3092
|
-
// refine with one Newton iteration (optional but cheap)
|
|
3093
|
-
let P = pointAtT([com2.p0, com2.cp1, com2.cp2, com2.p], t0);
|
|
3094
|
-
let dP = cubicDerivative(com2.p0, com2.cp1, com2.cp2, com2.p, t0);
|
|
3095
|
-
let r = sub(P, com1.p0);
|
|
3096
|
-
|
|
3097
|
-
t0 -= dot(r, dP) / dot(dP, dP);
|
|
3098
|
-
|
|
3099
|
-
// construct merged cubic over [t0, 1]
|
|
3100
|
-
let Q0 = pointAtT([com2.p0, com2.cp1, com2.cp2, com2.p], t0);
|
|
3101
|
-
let Q3 = com2.p;
|
|
3102
|
-
|
|
3103
|
-
let d0 = cubicDerivative(com2.p0, com2.cp1, com2.cp2, com2.p, t0);
|
|
3104
|
-
let d1 = cubicDerivative(com2.p0, com2.cp1, com2.cp2, com2.p, 1);
|
|
3105
|
-
|
|
3106
|
-
let scale = 1 - t0;
|
|
3107
|
-
|
|
3108
|
-
let Q1 = add(Q0, mul(d0, scale / 3));
|
|
3109
|
-
let Q2 = sub(Q3, mul(d1, scale / 3));
|
|
3110
|
-
|
|
3111
|
-
let result = {
|
|
3112
|
-
p0: Q0,
|
|
3113
|
-
cp1: Q1,
|
|
3114
|
-
cp2: Q2,
|
|
3115
|
-
p: Q3,
|
|
3116
|
-
t0
|
|
3117
|
-
};
|
|
3118
|
-
|
|
3119
|
-
if (reverse) {
|
|
3120
|
-
result = {
|
|
3121
|
-
p0: Q3,
|
|
3122
|
-
cp1: Q2,
|
|
3123
|
-
cp2: Q1,
|
|
3124
|
-
p: Q0,
|
|
3125
|
-
t0
|
|
3126
|
-
};
|
|
3127
|
-
}
|
|
3128
|
-
|
|
3129
|
-
let tMid = (1 - t0) * 0.5;
|
|
3130
|
-
|
|
3131
|
-
let ptM = pointAtT([result.p0, result.cp1, result.cp2, result.p], tMid, false, true);
|
|
3132
|
-
let seg1_cp2 = ptM.cpts[2];
|
|
3106
|
+
// etsimate t for extrapolation
|
|
3107
|
+
let PtI = checkLineIntersection(com1.cp2, com1.p, com2.p, com2.cp2, false, true);
|
|
3108
|
+
let cp1_I = interpolate(com1.p, PtI, 0.666);
|
|
3133
3109
|
|
|
3134
|
-
let
|
|
3135
|
-
let
|
|
3110
|
+
let dist1_2 = getDistManhattan(com1.cp2, com1.p);
|
|
3111
|
+
let dist2_2 = getDistManhattan(com1.cp2, cp1_I);
|
|
3112
|
+
let t = dist2_2 / dist1_2;
|
|
3136
3113
|
|
|
3137
|
-
|
|
3138
|
-
let
|
|
3114
|
+
// extrapolate
|
|
3115
|
+
let segs = pointAtT([com1.p0, com1.cp1, com1.cp2, com1.p], t, false, true).segments;
|
|
3139
3116
|
|
|
3140
|
-
|
|
3141
|
-
let cp_intersection = checkLineIntersection(com1_o.p0, cp1_2, com2_o.p, cp2_2, true);
|
|
3142
|
-
if (cp_intersection) {
|
|
3117
|
+
let seg = segs[0];
|
|
3143
3118
|
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
result.cp1 = cp1_2;
|
|
3148
|
-
result.cp2 = cp2_2;
|
|
3149
|
-
|
|
3150
|
-
// check distances between original starting point and extrapolated
|
|
3151
|
-
let dist3 = getDistAv(com1_o.p0, result.p0);
|
|
3152
|
-
let dist4 = getDistAv(com2_o.p, result.p);
|
|
3153
|
-
let dist5 = (dist3 + dist4);
|
|
3154
|
-
|
|
3155
|
-
// use original points
|
|
3156
|
-
result.p0 = com1_o.p0;
|
|
3157
|
-
result.p = com2_o.p;
|
|
3158
|
-
result.extreme = com2_o.extreme;
|
|
3159
|
-
result.corner = com2_o.corner;
|
|
3160
|
-
result.dimA = com2_o.dimA;
|
|
3161
|
-
result.directionChange = com2_o.directionChange;
|
|
3162
|
-
result.type = 'C';
|
|
3163
|
-
result.values = [result.cp1.x, result.cp1.y, result.cp2.x, result.cp2.y, result.p.x, result.p.y];
|
|
3119
|
+
// if it worked - points should be nearby
|
|
3120
|
+
let dist = getDistManhattan(seg.p, com2.p);
|
|
3164
3121
|
|
|
3165
|
-
//
|
|
3166
|
-
if (
|
|
3122
|
+
// if close enough adjust
|
|
3123
|
+
if (dist < thresh) {
|
|
3124
|
+
let angle = getAngle(seg.p, seg.cp2);
|
|
3125
|
+
let angle2 = getAngle(com2.p, com2.cp2);
|
|
3167
3126
|
|
|
3168
|
-
|
|
3169
|
-
let tTotal = 1 + Math.abs(t0);
|
|
3170
|
-
let tSplit = reverse ? 1 + t0 : Math.abs(t0);
|
|
3171
|
-
|
|
3172
|
-
let pO = pointAtT([com2_o.p0, com2_o.cp1, com2_o.cp2, com2_o.p], t0);
|
|
3173
|
-
*/
|
|
3127
|
+
let angleDiff = (angle2 - angle);
|
|
3174
3128
|
|
|
3175
|
-
//
|
|
3176
|
-
|
|
3129
|
+
// adjust cp angle
|
|
3130
|
+
seg.cp2 = rotatePoint(seg.cp2, seg.p.x, seg.p.y, angleDiff);
|
|
3131
|
+
let dist1 = getDistManhattan(seg.p, seg.cp2);
|
|
3177
3132
|
|
|
3178
|
-
|
|
3179
|
-
|
|
3133
|
+
// copy original final point coordinates
|
|
3134
|
+
seg.p = com2.p;
|
|
3180
3135
|
|
|
3181
|
-
|
|
3182
|
-
let
|
|
3136
|
+
// after rotation
|
|
3137
|
+
let dist2 = getDistManhattan(seg.p, seg.cp2);
|
|
3138
|
+
let scale = dist2 / dist1;
|
|
3183
3139
|
|
|
3184
|
-
//
|
|
3185
|
-
|
|
3140
|
+
// adjust tangent length
|
|
3141
|
+
seg.cp2 = interpolate(seg.p, seg.cp2, scale);
|
|
3186
3142
|
|
|
3187
|
-
|
|
3143
|
+
// reverse back
|
|
3144
|
+
if (reverse) {
|
|
3145
|
+
seg = {
|
|
3146
|
+
p0: seg.p,
|
|
3147
|
+
p: seg.p0,
|
|
3148
|
+
cp1: seg.cp2,
|
|
3149
|
+
cp2: seg.cp1,
|
|
3150
|
+
};
|
|
3188
3151
|
}
|
|
3189
3152
|
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3153
|
+
commands = [
|
|
3154
|
+
{
|
|
3155
|
+
type: 'C',
|
|
3156
|
+
values: [seg.cp1.x, seg.cp1.y, seg.cp2.x, seg.cp2.y, seg.p.x, seg.p.y],
|
|
3157
|
+
p0: seg.p0,
|
|
3158
|
+
cp1: seg.cp1,
|
|
3159
|
+
cp2: seg.cp2,
|
|
3160
|
+
p: seg.p,
|
|
3161
|
+
extreme: com2_o.extreme,
|
|
3162
|
+
corner: com2_o.corner,
|
|
3163
|
+
directionChange: com2_o.directionChange,
|
|
3164
|
+
dimA: getDistManhattan(seg.p0, seg.p),
|
|
3165
|
+
error: dist
|
|
3196
3166
|
|
|
3197
|
-
|
|
3198
|
-
let pathDataN = [
|
|
3199
|
-
{ type: 'M', values: [result.p0.x, result.p0.y] },
|
|
3200
|
-
{ type: 'C', values: [result.cp1.x, result.cp1.y, result.cp2.x, result.cp2.y, result.p.x, result.p.y] },
|
|
3167
|
+
}
|
|
3201
3168
|
];
|
|
3202
3169
|
|
|
3203
|
-
let areaN = getPathArea(pathDataN);
|
|
3204
|
-
let areaDiff = Math.abs(areaN / area0 - 1);
|
|
3205
|
-
|
|
3206
|
-
result.error = areaDiff * 5 * tolerance;
|
|
3207
|
-
|
|
3208
|
-
if (debug) {
|
|
3209
|
-
pathDataToD(pathDataN);
|
|
3210
|
-
|
|
3211
|
-
}
|
|
3212
|
-
|
|
3213
|
-
// success!!!
|
|
3214
|
-
if (areaDiff < 0.05 * tolerance) {
|
|
3215
|
-
commands = [result];
|
|
3216
|
-
|
|
3217
|
-
}
|
|
3218
3170
|
}
|
|
3219
3171
|
|
|
3220
3172
|
return commands
|
|
@@ -3337,11 +3289,12 @@
|
|
|
3337
3289
|
// quit if t is start
|
|
3338
3290
|
if (!t) return commands;
|
|
3339
3291
|
|
|
3292
|
+
// get averaged threshold
|
|
3340
3293
|
let distAv1 = getDistManhattan(com1.p0, com1.p);
|
|
3341
3294
|
let distAv2 = getDistManhattan(com2.p0, com2.p);
|
|
3342
3295
|
let distMin = Math.max(0, Math.min(distAv1, distAv2));
|
|
3343
3296
|
|
|
3344
|
-
let distScale = 0.
|
|
3297
|
+
let distScale = 0.075;
|
|
3345
3298
|
let maxDist = distMin * distScale * tolerance;
|
|
3346
3299
|
|
|
3347
3300
|
// get hypothetical combined command
|
|
@@ -3389,16 +3342,6 @@
|
|
|
3389
3342
|
|
|
3390
3343
|
if (error < maxDist) success = true;
|
|
3391
3344
|
|
|
3392
|
-
/*
|
|
3393
|
-
renderPoint(markers, ptM_seg1, 'cyan')
|
|
3394
|
-
renderPoint(markers, pt, 'orange', '1.5%', '1')
|
|
3395
|
-
renderPoint(markers, ptM_seg2, 'orange')
|
|
3396
|
-
|
|
3397
|
-
renderPoint(markers, com1.p, 'green')
|
|
3398
|
-
|
|
3399
|
-
renderPoint(markers, ptI_seg1, 'purple')
|
|
3400
|
-
*/
|
|
3401
|
-
|
|
3402
3345
|
}
|
|
3403
3346
|
|
|
3404
3347
|
} // end 1st try
|
|
@@ -3417,14 +3360,6 @@
|
|
|
3417
3360
|
comS.directionChange = com2.directionChange;
|
|
3418
3361
|
comS.corner = com2.corner;
|
|
3419
3362
|
|
|
3420
|
-
if (comS.extreme || comS.corner) ;
|
|
3421
|
-
|
|
3422
|
-
/*
|
|
3423
|
-
comS.extreme = com1.extreme;
|
|
3424
|
-
comS.directionChange = com1.directionChange;
|
|
3425
|
-
comS.corner = com1.corner;
|
|
3426
|
-
*/
|
|
3427
|
-
|
|
3428
3363
|
comS.values = [comS.cp1.x, comS.cp1.y, comS.cp2.x, comS.cp2.y, comS.p.x, comS.p.y];
|
|
3429
3364
|
|
|
3430
3365
|
// relative error
|
|
@@ -3483,7 +3418,9 @@
|
|
|
3483
3418
|
}
|
|
3484
3419
|
*/
|
|
3485
3420
|
|
|
3486
|
-
|
|
3421
|
+
let t = l1 / l3;
|
|
3422
|
+
|
|
3423
|
+
return t;
|
|
3487
3424
|
}
|
|
3488
3425
|
|
|
3489
3426
|
function commandIsFlat(points, {
|
|
@@ -3536,7 +3473,7 @@
|
|
|
3536
3473
|
detectDirection = true,
|
|
3537
3474
|
detectSemiExtremes = false,
|
|
3538
3475
|
debug = false,
|
|
3539
|
-
addSquareLength =
|
|
3476
|
+
addSquareLength = false,
|
|
3540
3477
|
addArea = true,
|
|
3541
3478
|
|
|
3542
3479
|
} = {}) {
|
|
@@ -3585,8 +3522,8 @@
|
|
|
3585
3522
|
let commandPts = (type === 'C' || type === 'Q') ?
|
|
3586
3523
|
(type === 'C' ? [p0, cp1, cp2, p] : [p0, cp1, p]) :
|
|
3587
3524
|
([p0, p]);
|
|
3588
|
-
|
|
3589
|
-
let threshold =
|
|
3525
|
+
|
|
3526
|
+
let threshold = dimA * 0.005;
|
|
3590
3527
|
|
|
3591
3528
|
// bezier types
|
|
3592
3529
|
let isBezier = type === 'Q' || type === 'C';
|
|
@@ -3599,7 +3536,7 @@
|
|
|
3599
3536
|
*/
|
|
3600
3537
|
let hasExtremes = false;
|
|
3601
3538
|
|
|
3602
|
-
if (isBezier) {
|
|
3539
|
+
if (detectExtremes && isBezier) {
|
|
3603
3540
|
|
|
3604
3541
|
let dx = type === 'C' ? Math.abs(com.cp2.x - com.p.x) : Math.abs(com.cp1.x - com.p.x);
|
|
3605
3542
|
let dy = type === 'C' ? Math.abs(com.cp2.y - com.p.y) : Math.abs(com.cp1.y - com.p.y);
|
|
@@ -3638,7 +3575,7 @@
|
|
|
3638
3575
|
}
|
|
3639
3576
|
|
|
3640
3577
|
// check extremes introduce by small arcs
|
|
3641
|
-
else if(isArc && comN && ((comPrev.type==='C' || comPrev.type==='Q') || (comN.type==='C' || comN.type==='Q')) ){
|
|
3578
|
+
else if(detectExtremes && isArc && comN && ((comPrev.type==='C' || comPrev.type==='Q') || (comN.type==='C' || comN.type==='Q')) ){
|
|
3642
3579
|
let distN = comN ? comN.dimA : 0;
|
|
3643
3580
|
let isShort = com.dimA < (comPrev.dimA + distN) * 0.1;
|
|
3644
3581
|
let smallRadius = com.values[0] === com.values[1] && (com.values[0] < 1);
|
|
@@ -3656,28 +3593,7 @@
|
|
|
3656
3593
|
if (hasExtremes) com.extreme = true;
|
|
3657
3594
|
|
|
3658
3595
|
// Corners and semi extremes
|
|
3659
|
-
if (isBezier && isBezierN) {
|
|
3660
|
-
|
|
3661
|
-
// semi extremes
|
|
3662
|
-
if (detectSemiExtremes && !com.extreme) {
|
|
3663
|
-
|
|
3664
|
-
let dx1 = Math.abs(p.x - cp2.x);
|
|
3665
|
-
let dy1 = Math.abs(p.y - cp2.y);
|
|
3666
|
-
let hasSemiExtreme = false;
|
|
3667
|
-
|
|
3668
|
-
// exclude extremes or small deltas
|
|
3669
|
-
if (dx1 && dy1 && dx1 > thresholdLength || dy1 > thresholdLength) {
|
|
3670
|
-
let ang1 = getAngle(cp2, p);
|
|
3671
|
-
let ang2 = getAngle(p, comN.cp1);
|
|
3672
|
-
|
|
3673
|
-
let ang3 = Math.abs(ang1 + ang2) / 2;
|
|
3674
|
-
hasSemiExtreme = isMultipleOf45(ang3);
|
|
3675
|
-
}
|
|
3676
|
-
|
|
3677
|
-
if (hasSemiExtreme) {
|
|
3678
|
-
com.semiExtreme = true;
|
|
3679
|
-
}
|
|
3680
|
-
}
|
|
3596
|
+
if (detectCorners && isBezier && isBezierN) {
|
|
3681
3597
|
|
|
3682
3598
|
/**
|
|
3683
3599
|
* Detect direction change points
|
|
@@ -4257,7 +4173,12 @@
|
|
|
4257
4173
|
if (hasShorthands && toLonghands) pathData = pathDataToLonghands(pathData);
|
|
4258
4174
|
|
|
4259
4175
|
// minify semicircle radii
|
|
4260
|
-
if (optimizeArcs)
|
|
4176
|
+
if (optimizeArcs) {
|
|
4177
|
+
pathData = optimizeArcPathData(pathData);
|
|
4178
|
+
} else {
|
|
4179
|
+
// get true absolute radii
|
|
4180
|
+
pathData = pathDataToTrueArcValues(pathData);
|
|
4181
|
+
}
|
|
4261
4182
|
|
|
4262
4183
|
if (toShorthands) pathData = pathDataToShorthands(pathData);
|
|
4263
4184
|
|
|
@@ -4347,9 +4268,42 @@
|
|
|
4347
4268
|
}
|
|
4348
4269
|
|
|
4349
4270
|
/**
|
|
4350
|
-
*
|
|
4351
|
-
*
|
|
4352
|
-
|
|
4271
|
+
* Converts minified arc
|
|
4272
|
+
* values to true rx and ry values
|
|
4273
|
+
*/
|
|
4274
|
+
function pathDataToTrueArcValues(pathData = []) {
|
|
4275
|
+
let l = pathData.length;
|
|
4276
|
+
let pathDataN = [pathData[0]];
|
|
4277
|
+
|
|
4278
|
+
for (let i = 1; i < l; i++) {
|
|
4279
|
+
let com = pathData[i];
|
|
4280
|
+
let comPrev = pathData[i-1];
|
|
4281
|
+
let { type, values } = com;
|
|
4282
|
+
|
|
4283
|
+
if (type === 'A') {
|
|
4284
|
+
|
|
4285
|
+
// previous commands final on-path point
|
|
4286
|
+
let [x1, y1] = comPrev.values.slice(-2);
|
|
4287
|
+
let [rx, ry, xAxisRotation, largeArc, sweep, x2, y2] = values;
|
|
4288
|
+
let arcData = svgArcToCenterParam(x1, y1, rx, ry, xAxisRotation, largeArc, sweep, x2, y2);
|
|
4289
|
+
|
|
4290
|
+
// set true arc values
|
|
4291
|
+
com.values[0] = arcData.rx;
|
|
4292
|
+
com.values[1] = arcData.ry;
|
|
4293
|
+
|
|
4294
|
+
}
|
|
4295
|
+
|
|
4296
|
+
pathDataN.push(com);
|
|
4297
|
+
|
|
4298
|
+
}
|
|
4299
|
+
return pathDataN;
|
|
4300
|
+
}
|
|
4301
|
+
|
|
4302
|
+
/**
|
|
4303
|
+
* Minify arc radii
|
|
4304
|
+
* for semi circles
|
|
4305
|
+
* returns smaller rx and ry
|
|
4306
|
+
* values
|
|
4353
4307
|
*/
|
|
4354
4308
|
|
|
4355
4309
|
function optimizeArcPathData(pathData = []) {
|
|
@@ -4406,7 +4360,7 @@
|
|
|
4406
4360
|
if (isHorizontal || isVertical) {
|
|
4407
4361
|
|
|
4408
4362
|
// check if semi circle
|
|
4409
|
-
let needsTrueR = isHorizontal ? rx*1.9 > diffX : ry*1.9 > diffY;
|
|
4363
|
+
let needsTrueR = isHorizontal ? rx * 1.9 > diffX : ry * 1.9 > diffY;
|
|
4410
4364
|
|
|
4411
4365
|
// is semicircle we can simplify rx
|
|
4412
4366
|
if (!needsTrueR) {
|
|
@@ -5102,8 +5056,8 @@
|
|
|
5102
5056
|
let phi = rotation ? rotation * TAU / 360 : 0;
|
|
5103
5057
|
let sinphi = phi ? Math.sin(phi) : 0;
|
|
5104
5058
|
let cosphi = phi ? Math.cos(phi) : 1;
|
|
5105
|
-
let pxp = cosphi * (p0.x - x)
|
|
5106
|
-
let pyp = -sinphi * (p0.x - x)
|
|
5059
|
+
let pxp = cosphi * (p0.x - x) *0.5 + sinphi * (p0.y - y) *0.5;
|
|
5060
|
+
let pyp = -sinphi * (p0.x - x) *0.5 + cosphi * (p0.y - y) *0.5;
|
|
5107
5061
|
|
|
5108
5062
|
if (pxp === 0 && pyp === 0) {
|
|
5109
5063
|
return []
|
|
@@ -5139,8 +5093,8 @@
|
|
|
5139
5093
|
|
|
5140
5094
|
let centerxp = radicant ? radicant * rx / ry * pyp : 0;
|
|
5141
5095
|
let centeryp = radicant ? radicant * -ry / rx * pxp : 0;
|
|
5142
|
-
let centerx = cosphi * centerxp - sinphi * centeryp + (p0.x + x)
|
|
5143
|
-
let centery = sinphi * centerxp + cosphi * centeryp + (p0.y + y)
|
|
5096
|
+
let centerx = cosphi * centerxp - sinphi * centeryp + (p0.x + x) * 0.5;
|
|
5097
|
+
let centery = sinphi * centerxp + cosphi * centeryp + (p0.y + y) * 0.5;
|
|
5144
5098
|
|
|
5145
5099
|
let vx1 = (pxp - centerxp) / rx;
|
|
5146
5100
|
let vy1 = (pyp - centeryp) / ry;
|
|
@@ -5162,15 +5116,17 @@
|
|
|
5162
5116
|
ang2 = vectorAngle(vx1, vy1, vx2, vy2);
|
|
5163
5117
|
|
|
5164
5118
|
if (sweepFlag === 0 && ang2 > 0) {
|
|
5165
|
-
|
|
5119
|
+
|
|
5120
|
+
ang2 -= TAU;
|
|
5166
5121
|
}
|
|
5167
5122
|
else if (sweepFlag === 1 && ang2 < 0) {
|
|
5168
|
-
|
|
5123
|
+
|
|
5124
|
+
ang2 += TAU;
|
|
5169
5125
|
}
|
|
5170
5126
|
|
|
5171
|
-
let ratio = +(Math.abs(ang2) / (TAU
|
|
5127
|
+
let ratio = +(Math.abs(ang2) / (TAU * 0.25)).toFixed(0) || 1;
|
|
5172
5128
|
|
|
5173
|
-
// increase segments for more
|
|
5129
|
+
// increase segments for more accurate length calculations
|
|
5174
5130
|
let segments = ratio * splitSegments;
|
|
5175
5131
|
ang2 /= segments;
|
|
5176
5132
|
let pathDataArc = [];
|
|
@@ -5182,7 +5138,8 @@
|
|
|
5182
5138
|
const k = 0.551785;
|
|
5183
5139
|
let a = ang2 === angle90 ? k :
|
|
5184
5140
|
(
|
|
5185
|
-
|
|
5141
|
+
|
|
5142
|
+
ang2 === -angle90 ? -k : 1.33333 * Math.tan(ang2 * 0.25)
|
|
5186
5143
|
);
|
|
5187
5144
|
|
|
5188
5145
|
let cos2 = ang2 ? Math.cos(ang2) : 1;
|
|
@@ -5229,7 +5186,7 @@
|
|
|
5229
5186
|
let comA = cubicCommandToArc(p0, cp1, cp2, p, areaThreshold);
|
|
5230
5187
|
let comAN = cubicCommandToArc(comN.p0, comN.cp1, comN.cp2, comN.p, areaThreshold);
|
|
5231
5188
|
|
|
5232
|
-
if (comA.isArc
|
|
5189
|
+
if (comA.isArc && comAN.isArc) {
|
|
5233
5190
|
|
|
5234
5191
|
let dist = getDistManhattan(p0, comN.p);
|
|
5235
5192
|
let maxDist = dist * 0.01;
|
|
@@ -5244,8 +5201,18 @@
|
|
|
5244
5201
|
let sweep = area < 0 ? 0 : 1;
|
|
5245
5202
|
|
|
5246
5203
|
if (vertical || horizontal) {
|
|
5204
|
+
|
|
5247
5205
|
rx = Math.min(rx, comAN.rx);
|
|
5248
5206
|
ry = Math.min(ry, comAN.ry);
|
|
5207
|
+
|
|
5208
|
+
let diffR = Math.abs(rx - ry) / rx;
|
|
5209
|
+
let isSemiCircle = diffR < 0.025;
|
|
5210
|
+
|
|
5211
|
+
if (isSemiCircle) {
|
|
5212
|
+
rx = rx > 1 ? 1 : Math.min(rx, ry);
|
|
5213
|
+
ry = rx;
|
|
5214
|
+
}
|
|
5215
|
+
|
|
5249
5216
|
pathData[c] = null;
|
|
5250
5217
|
pathData[c + 1].type = 'A';
|
|
5251
5218
|
pathData[c + 1].values = [rx, ry, 0, 0, sweep, comN.p.x, comN.p.y];
|
|
@@ -5268,33 +5235,61 @@
|
|
|
5268
5235
|
let arcSegArea = 0, isArc = false;
|
|
5269
5236
|
|
|
5270
5237
|
// check angles
|
|
5271
|
-
let
|
|
5272
|
-
let
|
|
5238
|
+
let dx1 = (cp1.x - p0.x);
|
|
5239
|
+
let dy1 = (cp1.y - p0.y);
|
|
5240
|
+
let dx2 = (cp2.x - p.x);
|
|
5241
|
+
let dy2 = (cp2.y - p.y);
|
|
5242
|
+
|
|
5243
|
+
let thresh = getDistManhattan(p0, p) * 0.001;
|
|
5244
|
+
|
|
5245
|
+
let isVertical1 = dx1 < thresh;
|
|
5246
|
+
let isVertical2 = dx2 < thresh;
|
|
5247
|
+
|
|
5248
|
+
let isHorizontal1 = dy1 < thresh;
|
|
5249
|
+
let isHorizontal2 = dy2 < thresh;
|
|
5250
|
+
|
|
5251
|
+
let isRightAngle = (isVertical1 || isVertical2) && (isHorizontal1 || isHorizontal2);
|
|
5252
|
+
|
|
5253
|
+
/*
|
|
5254
|
+
let angle1 = getAngleFromDelta(dx1, dy1, true);
|
|
5255
|
+
let angle2 = getAngleFromDelta(dx2, dy2, true);
|
|
5273
5256
|
let deltaAngle = Math.abs(angle1 - angle2) * 180 / Math.PI;
|
|
5274
5257
|
|
|
5275
5258
|
let angleDiff = Math.abs((deltaAngle % 180) - 90);
|
|
5276
5259
|
let isRightAngle = angleDiff < 3;
|
|
5260
|
+
*/
|
|
5277
5261
|
|
|
5278
5262
|
let rx = 0;
|
|
5279
5263
|
let ry = 0;
|
|
5280
|
-
let ptC;
|
|
5264
|
+
let ptC = p0;
|
|
5265
|
+
let r1 = 0, r2 = 0;
|
|
5281
5266
|
|
|
5282
5267
|
if (isRightAngle) {
|
|
5283
|
-
// point between cps
|
|
5284
5268
|
|
|
5285
|
-
|
|
5286
|
-
|
|
5287
|
-
|
|
5269
|
+
if (isHorizontal1 && isVertical2) {
|
|
5270
|
+
ptC = { x: p0.x, y: p.y };
|
|
5271
|
+
r1 = Math.abs(p.x-p0.x);
|
|
5272
|
+
r2 = Math.abs(p.y-p0.y);
|
|
5273
|
+
}
|
|
5274
|
+
else if (isHorizontal2 && isVertical1) {
|
|
5275
|
+
ptC = { x: p.x, y: p0.y };
|
|
5276
|
+
r2 = Math.abs(p0.x-p.x);
|
|
5277
|
+
r1 = Math.abs(p0.y-p.y);
|
|
5278
|
+
}
|
|
5279
|
+
|
|
5280
|
+
/*
|
|
5281
|
+
if (r1 && r2) {
|
|
5288
5282
|
|
|
5289
|
-
|
|
5290
|
-
ptC = checkLineIntersection(p0, cp1_r, p, cp2_r, false);
|
|
5283
|
+
}
|
|
5291
5284
|
|
|
5292
|
-
|
|
5285
|
+
*/
|
|
5293
5286
|
|
|
5294
|
-
if (
|
|
5287
|
+
if (r1 && r2) {
|
|
5295
5288
|
|
|
5289
|
+
/*
|
|
5296
5290
|
let r1 = getDistance(p0, pI);
|
|
5297
5291
|
let r2 = getDistance(p, pI);
|
|
5292
|
+
*/
|
|
5298
5293
|
|
|
5299
5294
|
let rMax = +Math.max(r1, r2).toFixed(8);
|
|
5300
5295
|
let rMin = +Math.min(r1, r2).toFixed(8);
|
|
@@ -5312,7 +5307,6 @@
|
|
|
5312
5307
|
let circular = (100 / rx * Math.abs(rx - ry)) < 5;
|
|
5313
5308
|
|
|
5314
5309
|
if (circular) {
|
|
5315
|
-
|
|
5316
5310
|
rx = rMax;
|
|
5317
5311
|
ry = rx;
|
|
5318
5312
|
}
|
|
@@ -5338,7 +5332,7 @@
|
|
|
5338
5332
|
arcSegArea = (Math.PI * (rx * ry)) / 4;
|
|
5339
5333
|
|
|
5340
5334
|
// subtract polygon between start, end and center point
|
|
5341
|
-
arcSegArea -= Math.abs(getPolygonArea([p0, p,
|
|
5335
|
+
arcSegArea -= Math.abs(getPolygonArea([p0, p, ptC]));
|
|
5342
5336
|
|
|
5343
5337
|
let areaDiff = getRelativeAreaDiff(comArea, arcSegArea);
|
|
5344
5338
|
|
|
@@ -6408,59 +6402,50 @@
|
|
|
6408
6402
|
pathData[pathData.length - 1].type.toLowerCase() === 'z';
|
|
6409
6403
|
|
|
6410
6404
|
for (let c = 1, l = pathData.length; c < l; c++) {
|
|
6411
|
-
|
|
6412
6405
|
let com = pathData[c];
|
|
6406
|
+
let { type, values } = com;
|
|
6413
6407
|
let comN = pathData[c + 1] || pathData[l - 1];
|
|
6414
|
-
|
|
6408
|
+
let valuesN = comN.values;
|
|
6415
6409
|
let p1 = comN.type.toLowerCase() === 'z' ? M : { x: comN.values[comN.values.length - 2], y: comN.values[comN.values.length - 1] };
|
|
6416
6410
|
|
|
6417
|
-
let { type, values } = com;
|
|
6418
6411
|
let valsL = values.slice(-2);
|
|
6419
6412
|
p = type !== 'Z' ? { x: valsL[0], y: valsL[1] } : M;
|
|
6420
6413
|
|
|
6414
|
+
let nextBezier = type!==comN.type && (comN.type==='C' || comN.type==='Q');
|
|
6415
|
+
|
|
6421
6416
|
let area = p1 ? getPolygonArea([p0, p, p1], true) : Infinity;
|
|
6422
6417
|
let distSquare = getSquareDistance(p0, p1);
|
|
6423
6418
|
let distMax = distSquare ? distSquare / 333 * tolerance : 0;
|
|
6424
6419
|
|
|
6425
6420
|
let isFlat = area < distMax;
|
|
6426
|
-
|
|
6427
6421
|
let isFlatBez = false;
|
|
6422
|
+
let cpts = [];
|
|
6428
6423
|
|
|
6429
|
-
|
|
6430
|
-
|
|
6431
|
-
|
|
6432
|
-
|
|
6433
|
-
|
|
6434
|
-
|
|
6435
|
-
|
|
6436
|
-
|
|
6437
|
-
let dx2 = Math.abs(p1.x - p.x)
|
|
6438
|
-
let dy2 = Math.abs(p1.y - p.y)
|
|
6439
|
-
|
|
6440
|
-
// zero length segments are flat
|
|
6441
|
-
let isZeroLength = (!dy1 && !dx1) || (!dy2 && !dx2)
|
|
6442
|
-
if (isZeroLength) isFlat = true;
|
|
6443
|
-
|
|
6444
|
-
// check cross products for colinearity
|
|
6445
|
-
if (!isFlat) {
|
|
6446
|
-
|
|
6447
|
-
let cross0 = Math.abs(dx0 * dy1 - dy0 * dx1);
|
|
6424
|
+
/**
|
|
6425
|
+
* type change
|
|
6426
|
+
* check flatness
|
|
6427
|
+
*/
|
|
6428
|
+
if (nextBezier) {
|
|
6429
|
+
cpts = comN.type === 'C' ?
|
|
6430
|
+
[{ x: valuesN[0], y: valuesN[1] }, { x: valuesN[2], y: valuesN[3] }] :
|
|
6431
|
+
(comN.type === 'Q' ? [{ x: valuesN[0], y: valuesN[1] }] : []);
|
|
6448
6432
|
|
|
6449
|
-
|
|
6433
|
+
isFlat = commandIsFlat([p0, ...cpts, p], { tolerance });
|
|
6450
6434
|
|
|
6451
|
-
|
|
6435
|
+
/*
|
|
6452
6436
|
|
|
6453
|
-
|
|
6437
|
+
if(!isFlatBez){
|
|
6438
|
+
pathDataN.push(com)
|
|
6439
|
+
continue
|
|
6454
6440
|
}
|
|
6455
|
-
|
|
6456
|
-
*/
|
|
6441
|
+
*/
|
|
6457
6442
|
|
|
6458
|
-
|
|
6443
|
+
}
|
|
6459
6444
|
|
|
6460
6445
|
// convert flat beziers to linetos
|
|
6461
6446
|
if (flatBezierToLinetos && (type === 'C' || type === 'Q')) {
|
|
6462
6447
|
|
|
6463
|
-
|
|
6448
|
+
cpts = type === 'C' ?
|
|
6464
6449
|
[{ x: values[0], y: values[1] }, { x: values[2], y: values[3] }] :
|
|
6465
6450
|
(type === 'Q' ? [{ x: values[0], y: values[1] }] : []);
|
|
6466
6451
|
|
|
@@ -6474,10 +6459,13 @@
|
|
|
6474
6459
|
}
|
|
6475
6460
|
}
|
|
6476
6461
|
|
|
6477
|
-
|
|
6462
|
+
/**
|
|
6463
|
+
* colinear = simplification success
|
|
6464
|
+
* exclude arcs (as always =)
|
|
6465
|
+
* as semicircles won't have an area
|
|
6466
|
+
*/
|
|
6478
6467
|
|
|
6479
6468
|
if (isFlat && c < l - 1 && comN.type !== 'A' && (type === 'L' || (flatBezierToLinetos && isFlatBez))) {
|
|
6480
|
-
|
|
6481
6469
|
continue;
|
|
6482
6470
|
}
|
|
6483
6471
|
|
|
@@ -6961,9 +6949,7 @@
|
|
|
6961
6949
|
if (comEx.length === 1) {
|
|
6962
6950
|
|
|
6963
6951
|
comEx = comEx[0];
|
|
6964
|
-
|
|
6965
6952
|
pathData[i + 1] = null;
|
|
6966
|
-
|
|
6967
6953
|
pathData[i + 2].values = [comEx.cp1.x, comEx.cp1.y, comEx.cp2.x, comEx.cp2.y, comEx.p.x, comEx.p.y];
|
|
6968
6954
|
pathData[i + 2].cp1 = comEx.cp1;
|
|
6969
6955
|
pathData[i + 2].cp2 = comEx.cp2;
|
|
@@ -7143,7 +7129,16 @@
|
|
|
7143
7129
|
return pts
|
|
7144
7130
|
}
|
|
7145
7131
|
|
|
7132
|
+
if (pts.length && typeof pts[0] === 'string') {
|
|
7133
|
+
pts = pts.map(pt => {
|
|
7134
|
+
return toPointArray(pt.split(/,| /).filter(Boolean).map(Number))
|
|
7135
|
+
});
|
|
7136
|
+
pts = pts.flat(2);
|
|
7137
|
+
|
|
7138
|
+
}
|
|
7139
|
+
|
|
7146
7140
|
if (flatten) pts = pts.flat(2);
|
|
7141
|
+
|
|
7147
7142
|
let poly = toArray ? polyPtsToArray(pts) : polyArrayToObject(pts);
|
|
7148
7143
|
return poly
|
|
7149
7144
|
}
|
|
@@ -8503,7 +8498,7 @@
|
|
|
8503
8498
|
styleProps.remove.push('transform');
|
|
8504
8499
|
|
|
8505
8500
|
// scale props like stroke width or dash-array
|
|
8506
|
-
styleProps = scaleProps(styleProps, { props: ['stroke-width', 'stroke-dasharray'], scale: scaleX });
|
|
8501
|
+
styleProps = scaleProps(styleProps, { props: ['stroke-width', 'stroke-dasharray', 'stroke-dashoffset'], scale: scaleX });
|
|
8507
8502
|
|
|
8508
8503
|
} else {
|
|
8509
8504
|
el.setAttribute('transform', transComponents.matrixAtt);
|
|
@@ -9050,6 +9045,38 @@
|
|
|
9050
9045
|
});
|
|
9051
9046
|
});
|
|
9052
9047
|
|
|
9048
|
+
/**
|
|
9049
|
+
* remove els and attributes
|
|
9050
|
+
*/
|
|
9051
|
+
|
|
9052
|
+
// remove meta
|
|
9053
|
+
if (!allowMeta) removeElements.push('meta', 'metadata', 'desc', 'title');
|
|
9054
|
+
|
|
9055
|
+
if (removeClassNames) {
|
|
9056
|
+
removeSVGAttributes.push('class');
|
|
9057
|
+
removeElAttributes.push('class');
|
|
9058
|
+
}
|
|
9059
|
+
|
|
9060
|
+
if (removeIds) {
|
|
9061
|
+
removeSVGAttributes.push('id');
|
|
9062
|
+
removeElAttributes.push('id');
|
|
9063
|
+
}
|
|
9064
|
+
|
|
9065
|
+
// remove hidden elements
|
|
9066
|
+
removeHiddenSvgEls(svg);
|
|
9067
|
+
|
|
9068
|
+
// remove SVG elements
|
|
9069
|
+
removeSvgEls(svg, { removeElements, removeNameSpaced });
|
|
9070
|
+
|
|
9071
|
+
// remove SVG attributes
|
|
9072
|
+
removeSvgAtts(svg, removeSVGAttributes);
|
|
9073
|
+
|
|
9074
|
+
// remove SVG child element attributes
|
|
9075
|
+
removeSvgChildAtts(svg, removeElAttributes);
|
|
9076
|
+
|
|
9077
|
+
// general cleanup
|
|
9078
|
+
if (cleanupSVGAtts) cleanupSVGAttributes(svg, { removeIds, removeClassNames, removeDimensions, stylesToAttributes, allowMeta, allowAriaAtts, allowDataAtts });
|
|
9079
|
+
|
|
9053
9080
|
// collect all elements' properties
|
|
9054
9081
|
let svgElProps = [];
|
|
9055
9082
|
let els = svg.querySelectorAll(`${renderedEls.join(', ')}`);
|
|
@@ -9070,14 +9097,15 @@
|
|
|
9070
9097
|
let styleProps = parseStylesProperties(el, propOptions);
|
|
9071
9098
|
let stylePropsFiltered = {};
|
|
9072
9099
|
|
|
9073
|
-
//
|
|
9074
|
-
|
|
9100
|
+
// reset remove array
|
|
9101
|
+
remove = [];
|
|
9075
9102
|
|
|
9076
|
-
|
|
9103
|
+
// convert pathLength before transforming
|
|
9104
|
+
if (convertTransforms || attributesToGroup) convertPathLength = true;
|
|
9077
9105
|
|
|
9106
|
+
if (convertPathLength) {
|
|
9078
9107
|
styleProps = convertPathLengthAtt(el, { styleProps });
|
|
9079
9108
|
remove = [...new Set([...remove, ...styleProps.remove])];
|
|
9080
|
-
|
|
9081
9109
|
}
|
|
9082
9110
|
|
|
9083
9111
|
// get parent styles
|
|
@@ -9127,41 +9155,11 @@
|
|
|
9127
9155
|
|
|
9128
9156
|
remove = [...new Set([...remove, ...styleProps.remove])];
|
|
9129
9157
|
|
|
9130
|
-
|
|
9131
|
-
* remove els and attributes
|
|
9132
|
-
*/
|
|
9158
|
+
|
|
9133
9159
|
|
|
9134
|
-
//
|
|
9135
|
-
if (
|
|
9136
|
-
|
|
9137
|
-
if (removeClassNames) {
|
|
9138
|
-
removeSVGAttributes.push('class');
|
|
9139
|
-
removeElAttributes.push('class');
|
|
9140
|
-
}
|
|
9141
|
-
|
|
9142
|
-
if (removeIds) {
|
|
9143
|
-
removeSVGAttributes.push('id');
|
|
9144
|
-
removeElAttributes.push('id');
|
|
9145
|
-
}
|
|
9146
|
-
|
|
9147
|
-
// remove hidden elements
|
|
9148
|
-
removeHiddenSvgEls(svg);
|
|
9149
|
-
|
|
9150
|
-
// remove SVG elements
|
|
9151
|
-
removeSvgEls(svg, { removeElements, removeNameSpaced });
|
|
9152
|
-
|
|
9153
|
-
// remove SVG attributes
|
|
9154
|
-
removeSvgAtts(svg, removeSVGAttributes);
|
|
9155
|
-
|
|
9156
|
-
// remove SVG child element attributes
|
|
9157
|
-
removeSvgChildAtts(svg, removeElAttributes);
|
|
9158
|
-
|
|
9159
|
-
// general cleanup
|
|
9160
|
-
if (cleanupSVGAtts) cleanupSVGAttributes(svg, { removeIds, removeClassNames, removeDimensions, stylesToAttributes, allowMeta, allowAriaAtts, allowDataAtts });
|
|
9161
|
-
|
|
9162
|
-
// all relative units to absolute
|
|
9163
|
-
if (toAbsoluteUnits) {
|
|
9164
|
-
normalizeTransforms = true;
|
|
9160
|
+
// all relative units to absolute
|
|
9161
|
+
if (toAbsoluteUnits) {
|
|
9162
|
+
normalizeTransforms = true;
|
|
9165
9163
|
|
|
9166
9164
|
/**
|
|
9167
9165
|
* apply consolidated
|
|
@@ -9202,7 +9200,6 @@
|
|
|
9202
9200
|
styleProps = setNormalizedTransformsToEl(el, { styleProps });
|
|
9203
9201
|
|
|
9204
9202
|
remove = [...new Set([...remove, ...styleProps.remove])];
|
|
9205
|
-
|
|
9206
9203
|
}
|
|
9207
9204
|
|
|
9208
9205
|
/**
|
|
@@ -9227,13 +9224,6 @@
|
|
|
9227
9224
|
*/
|
|
9228
9225
|
removeAtts(el, remove);
|
|
9229
9226
|
|
|
9230
|
-
/*
|
|
9231
|
-
for (let i = 0; i < remove.length; i++) {
|
|
9232
|
-
let att = remove[i];
|
|
9233
|
-
el.removeAttribute(att)
|
|
9234
|
-
}
|
|
9235
|
-
*/
|
|
9236
|
-
|
|
9237
9227
|
} // endof style processing
|
|
9238
9228
|
|
|
9239
9229
|
/**
|
|
@@ -10524,71 +10514,6 @@
|
|
|
10524
10514
|
return pathData
|
|
10525
10515
|
}
|
|
10526
10516
|
|
|
10527
|
-
function fixIntersectingCpts(pathData = []) {
|
|
10528
|
-
|
|
10529
|
-
let l = pathData.length;
|
|
10530
|
-
let p0 = { x: pathData[0].values[0], y: pathData[0].values[1] };
|
|
10531
|
-
let p = p0;
|
|
10532
|
-
|
|
10533
|
-
for (let i = 1; i < l; i++) {
|
|
10534
|
-
let com = pathData[i];
|
|
10535
|
-
let comPrev = pathData[i - 1] || null;
|
|
10536
|
-
let { type, values } = com;
|
|
10537
|
-
pathData[i + 1] ? pathData[i + 1] : null;
|
|
10538
|
-
|
|
10539
|
-
if (type === 'C') {
|
|
10540
|
-
let tx = 1.2;
|
|
10541
|
-
let cp1 = { x: values[0], y: values[1] };
|
|
10542
|
-
p = { x: values[4], y: values[5] };
|
|
10543
|
-
let cp2 = { x: values[2], y: values[3] };
|
|
10544
|
-
|
|
10545
|
-
interpolate(p0, cp1, tx );
|
|
10546
|
-
let cp2_ex = interpolate( p, cp2, tx);
|
|
10547
|
-
|
|
10548
|
-
comPrev.values.slice(-2);
|
|
10549
|
-
|
|
10550
|
-
// extend tangents
|
|
10551
|
-
|
|
10552
|
-
let ptI = checkLineIntersection(p0, cp1, p, cp2_ex, true, true);
|
|
10553
|
-
|
|
10554
|
-
|
|
10555
|
-
/*
|
|
10556
|
-
renderPoly(markers, [p0, cp1], 'orange', '0.75')
|
|
10557
|
-
renderPoly(markers, [p, cp2], 'blue', '0.75')
|
|
10558
|
-
renderPoly(markers, [p, cp2_ex], 'magenta', '0.75')
|
|
10559
|
-
|
|
10560
|
-
renderPoint(markers, p0, 'orange', '0.75')
|
|
10561
|
-
renderPoint(markers, cp1, 'magenta', '0.75')
|
|
10562
|
-
renderPoint(markers, cp1_ex, 'blue', '0.5')
|
|
10563
|
-
|
|
10564
|
-
renderPoint(markers, cp2, 'cyan', '0.75')
|
|
10565
|
-
*/
|
|
10566
|
-
|
|
10567
|
-
|
|
10568
|
-
|
|
10569
|
-
/*
|
|
10570
|
-
renderPoint(markers, p0, 'orange')
|
|
10571
|
-
renderPoint(markers, p, 'magenta', '0.5')
|
|
10572
|
-
*/
|
|
10573
|
-
|
|
10574
|
-
if (ptI) {
|
|
10575
|
-
let t = 0.666;
|
|
10576
|
-
cp1 = interpolate(p0, ptI, t);
|
|
10577
|
-
cp2 = interpolate(p, ptI, t);
|
|
10578
|
-
com.values = [cp1.x, cp1.y, cp2.x, cp2.y, p.x, p.y];
|
|
10579
|
-
|
|
10580
|
-
}
|
|
10581
|
-
|
|
10582
|
-
}
|
|
10583
|
-
|
|
10584
|
-
if (values.length) {
|
|
10585
|
-
p0 = p;
|
|
10586
|
-
}
|
|
10587
|
-
}
|
|
10588
|
-
|
|
10589
|
-
return pathData;
|
|
10590
|
-
}
|
|
10591
|
-
|
|
10592
10517
|
function simplifyPolyRDP(pts, {quality = 0.9, width = 0, height = 0}={}) {
|
|
10593
10518
|
|
|
10594
10519
|
/**
|
|
@@ -10783,860 +10708,817 @@
|
|
|
10783
10708
|
|
|
10784
10709
|
}
|
|
10785
10710
|
|
|
10786
|
-
function
|
|
10711
|
+
function refineAdjacentPolyExtremes(pts = []) {
|
|
10787
10712
|
|
|
10788
10713
|
let l = pts.length;
|
|
10789
|
-
let x = 0, y = 0;
|
|
10790
|
-
for (let i = 0; l && i < l; i++) {
|
|
10791
|
-
let pt = pts[i];
|
|
10792
|
-
x += pt.x;
|
|
10793
|
-
y += pt.y;
|
|
10794
|
-
}
|
|
10795
|
-
|
|
10796
|
-
let centroid = {x: x/l, y:y/l};
|
|
10797
|
-
return centroid
|
|
10798
|
-
|
|
10799
|
-
}
|
|
10800
10714
|
|
|
10801
|
-
|
|
10802
|
-
let
|
|
10803
|
-
let
|
|
10804
|
-
|
|
10805
|
-
for (let i = 1, l = pts.length; i < l; i++) {
|
|
10806
|
-
let pt1 = pts[i];
|
|
10807
|
-
let dist = getSquareDistance(pt1, centroid);
|
|
10808
|
-
|
|
10809
|
-
let diff = Math.abs(rSq-dist);
|
|
10810
|
-
let diffRel = diff/rSq;
|
|
10811
|
-
|
|
10812
|
-
if (diffRel > 0.05) {
|
|
10813
|
-
return false;
|
|
10814
|
-
}
|
|
10715
|
+
let { x, y, width, height, top, bottom, left, right } = getPolyBBox(pts);
|
|
10716
|
+
let threshShort = (width + height) * 0.05;
|
|
10717
|
+
let thresh = (width + height) * 0.001;
|
|
10815
10718
|
|
|
10816
|
-
|
|
10817
|
-
|
|
10818
|
-
}
|
|
10719
|
+
let pt0 = pts[0];
|
|
10720
|
+
let ptLast = pts[l - 1];
|
|
10819
10721
|
|
|
10820
|
-
|
|
10821
|
-
|
|
10822
|
-
|
|
10823
|
-
|
|
10824
|
-
|
|
10825
|
-
|
|
10826
|
-
} = {}) {
|
|
10722
|
+
/**
|
|
10723
|
+
* cleanup close path - almost vertical or horizontal
|
|
10724
|
+
* average start and end extremes
|
|
10725
|
+
*/
|
|
10726
|
+
let dx = Math.abs(ptLast.x - pt0.x);
|
|
10727
|
+
let dy = Math.abs(ptLast.y - pt0.y);
|
|
10827
10728
|
|
|
10828
|
-
|
|
10729
|
+
if (dy < threshShort || dx < threshShort) {
|
|
10829
10730
|
|
|
10830
|
-
|
|
10831
|
-
let bb0 = getPolyBBox(pts);
|
|
10731
|
+
if (pt0.isExtreme && !pt0.isCorner) {
|
|
10832
10732
|
|
|
10833
|
-
|
|
10834
|
-
|
|
10835
|
-
}
|
|
10733
|
+
let xAv = (pt0.x + ptLast.x) * 0.5;
|
|
10734
|
+
let yAv = (pt0.y + ptLast.y) * 0.5;
|
|
10836
10735
|
|
|
10837
|
-
|
|
10736
|
+
pt0.x = xAv;
|
|
10737
|
+
pt0.y = yAv;
|
|
10838
10738
|
|
|
10839
|
-
|
|
10840
|
-
|
|
10841
|
-
|
|
10842
|
-
let p1 = pts[i];
|
|
10843
|
-
let p2 = i < l - 1 ? pts[i + 1] : pts[l - 1];
|
|
10739
|
+
ptLast.x = xAv;
|
|
10740
|
+
ptLast.y = yAv;
|
|
10741
|
+
ptLast.isExtreme = true;
|
|
10844
10742
|
|
|
10845
|
-
|
|
10846
|
-
|
|
10847
|
-
|
|
10848
|
-
p1.idx = i;
|
|
10743
|
+
if (dy < thresh) {
|
|
10744
|
+
ptLast.tangentR.y = pt0.y;
|
|
10745
|
+
ptLast.tangentL.y = pt0.y;
|
|
10849
10746
|
|
|
10747
|
+
}
|
|
10748
|
+
if (dx < thresh) {
|
|
10749
|
+
ptLast.tangentR.x = pt0.x;
|
|
10750
|
+
ptLast.tangentL.x = pt0.x;
|
|
10751
|
+
}
|
|
10752
|
+
}
|
|
10850
10753
|
}
|
|
10851
10754
|
|
|
10852
|
-
for (let i =
|
|
10853
|
-
i >
|
|
10854
|
-
let p0 = i > 0 ? pts[i - 1] : pts[l - 1];
|
|
10755
|
+
for (let i = 1; i < l; i++) {
|
|
10756
|
+
i > 0 ? pts[i - 1] : pts[l - 1];
|
|
10855
10757
|
let p1 = pts[i];
|
|
10856
10758
|
let p2 = i < l - 1 ? pts[i + 1] : pts[l - 1];
|
|
10759
|
+
let dist = getDistManhattan(p1, p2);
|
|
10857
10760
|
|
|
10858
|
-
|
|
10859
|
-
|
|
10860
|
-
Math.abs(p0.area);
|
|
10861
|
-
let area1 = Math.abs(p1.area);
|
|
10862
|
-
Math.abs(p2.area);
|
|
10863
|
-
let isCorner = false;
|
|
10864
|
-
|
|
10865
|
-
let flat = !p1.area || area1 < thresh;
|
|
10866
|
-
|
|
10867
|
-
getDistManhattan(p1, p0);
|
|
10868
|
-
|
|
10869
|
-
/**
|
|
10870
|
-
* check extremes
|
|
10871
|
-
*/
|
|
10872
|
-
|
|
10873
|
-
let isExtreme = false;
|
|
10761
|
+
let { isHorizontal, isVertical, isCorner, isLong, isExtreme, isSemiExtreme, isDirChange } = p1;
|
|
10874
10762
|
|
|
10875
|
-
|
|
10876
|
-
if ((p1.x === bb0.left || p1.x === bb0.right || p1.y === bb0.top || p1.y === bb0.bottom)) {
|
|
10877
|
-
isExtreme = true;
|
|
10878
|
-
}
|
|
10763
|
+
let extremes = [];
|
|
10879
10764
|
|
|
10880
|
-
|
|
10881
|
-
|
|
10882
|
-
|
|
10765
|
+
/*
|
|
10766
|
+
if(isExtreme && p0.isCorner && !isLong && !isCorner){
|
|
10767
|
+
isExtreme= false
|
|
10768
|
+
p1.isExtreme = false
|
|
10769
|
+
p1.isHorizontal = false
|
|
10770
|
+
p1.isVertical = false
|
|
10883
10771
|
|
|
10884
|
-
|
|
10885
|
-
p0.isExtreme = true;
|
|
10886
|
-
isExtreme = true;
|
|
10772
|
+
continue;
|
|
10887
10773
|
}
|
|
10774
|
+
*/
|
|
10888
10775
|
|
|
10889
|
-
|
|
10890
|
-
|
|
10891
|
-
|
|
10892
|
-
|
|
10893
|
-
|
|
10894
|
-
|
|
10895
|
-
isExtreme = true;
|
|
10776
|
+
/*
|
|
10777
|
+
if(isExtreme && p2.isCorner && !isLong && !isCorner && dist<threshShort*0.5){
|
|
10778
|
+
isExtreme= false
|
|
10779
|
+
p1.isExtreme = false
|
|
10780
|
+
p1.isHorizontal = false
|
|
10781
|
+
p1.isVertical = false
|
|
10896
10782
|
|
|
10783
|
+
if(isVertical){
|
|
10784
|
+
p2.tangentL.x = p2.x
|
|
10785
|
+
}
|
|
10786
|
+
if(isHorizontal){
|
|
10787
|
+
p2.tangentL.y = p2.y
|
|
10788
|
+
}
|
|
10789
|
+
continue;
|
|
10897
10790
|
}
|
|
10791
|
+
*/
|
|
10898
10792
|
|
|
10899
|
-
|
|
10900
|
-
|
|
10901
|
-
*/
|
|
10902
|
-
let signChange = (p0.area < 0 && p1.area > 0) || (p0.area > 0 && p1.area < 0);
|
|
10903
|
-
let isDirChange = signChange && !flat && !p0.isDirChange;
|
|
10904
|
-
|
|
10905
|
-
/**
|
|
10906
|
-
* 3. corners
|
|
10907
|
-
*/
|
|
10908
|
-
|
|
10909
|
-
if (isExtreme) {
|
|
10910
|
-
|
|
10911
|
-
let delta = getDeltaAngle(p1, p2, p0);
|
|
10912
|
-
let { deltaAngleDeg } = delta;
|
|
10913
|
-
deltaAngleDeg = Math.abs(deltaAngleDeg);
|
|
10793
|
+
if (isExtreme && !isCorner && p2.isExtreme) {
|
|
10794
|
+
extremes.push(p1);
|
|
10914
10795
|
|
|
10915
|
-
let
|
|
10916
|
-
|
|
10796
|
+
for (let j = i + 1; j < l; j++) {
|
|
10797
|
+
let p2 = pts[j];
|
|
10798
|
+
dist = getDistManhattan(p1, p2);
|
|
10917
10799
|
|
|
10918
|
-
isCorner
|
|
10800
|
+
if (dist * 0.75 >= threshShort || p2.isCorner || p2.isDirChange) {
|
|
10801
|
+
break
|
|
10802
|
+
}
|
|
10803
|
+
if (p2.isExtreme && !p2.isDirChange && !p2.isCorner) {
|
|
10804
|
+
extremes.push(p2);
|
|
10805
|
+
}
|
|
10919
10806
|
}
|
|
10920
10807
|
|
|
10921
|
-
|
|
10808
|
+
if (extremes.length > 1) {
|
|
10922
10809
|
|
|
10923
|
-
|
|
10924
|
-
|
|
10925
|
-
if (debug) {
|
|
10810
|
+
// find best extreme according to angle
|
|
10811
|
+
let angleDiffMin = Infinity;
|
|
10926
10812
|
|
|
10927
|
-
|
|
10928
|
-
renderPoint(markers, p1, 'blue', '2%', '0.5')
|
|
10929
|
-
renderPoint(markers, p0, 'blue', '2%', '0.5')
|
|
10930
|
-
}
|
|
10813
|
+
let bestMatch = extremes[0];
|
|
10931
10814
|
|
|
10932
|
-
|
|
10933
|
-
|
|
10934
|
-
}
|
|
10815
|
+
|
|
10816
|
+
extremes.forEach(pt => {
|
|
10935
10817
|
|
|
10936
|
-
|
|
10937
|
-
|
|
10938
|
-
|
|
10818
|
+
let angle = Math.abs(getAngleFromDelta(pt.dx2, pt.dy2, false)) * rad2Deg;
|
|
10819
|
+
let angleDiff = angle > 160 ? Math.abs(180 - angle) : (angle > 60 ? Math.abs(90 - angle) : angle);
|
|
10820
|
+
pt.angle = angle;
|
|
10821
|
+
pt.angleDiff = angleDiff;
|
|
10939
10822
|
|
|
10940
|
-
|
|
10941
|
-
|
|
10942
|
-
|
|
10943
|
-
}
|
|
10944
|
-
*/
|
|
10823
|
+
if (angleDiff < angleDiffMin) {
|
|
10824
|
+
bestMatch = pt;
|
|
10825
|
+
angleDiffMin = angleDiff;
|
|
10945
10826
|
|
|
10946
|
-
|
|
10947
|
-
|
|
10948
|
-
p1.isHorizontal = isHorizontal;
|
|
10949
|
-
p1.isVertical = isVertical;
|
|
10950
|
-
p1.isDirChange = isDirChange;
|
|
10827
|
+
}
|
|
10828
|
+
});
|
|
10951
10829
|
|
|
10952
|
-
|
|
10830
|
+
let extremes2 = [];
|
|
10953
10831
|
|
|
10954
|
-
|
|
10955
|
-
let pts1 = [];
|
|
10956
|
-
let exclude = [];
|
|
10832
|
+
extremes.forEach((pt, i) => {
|
|
10957
10833
|
|
|
10958
|
-
|
|
10959
|
-
for (let i = 0; i < pts.length; i++) {
|
|
10960
|
-
let p = pts[i];
|
|
10961
|
-
let p1 = pts[i + 1] || null;
|
|
10962
|
-
let p2 = pts[i + 2] || null;
|
|
10834
|
+
let isBestMatch = pt === bestMatch;
|
|
10963
10835
|
|
|
10964
|
-
|
|
10836
|
+
if (isBestMatch) {
|
|
10965
10837
|
|
|
10966
|
-
|
|
10967
|
-
|
|
10968
|
-
|
|
10838
|
+
if (pt.isHorizontal) {
|
|
10839
|
+
pt.tangentL.y = pt.y;
|
|
10840
|
+
pt.tangentR.y = pt.y;
|
|
10841
|
+
}
|
|
10842
|
+
if (pt.isVertical) {
|
|
10843
|
+
pt.tangentL.x = pt.x;
|
|
10844
|
+
pt.tangentR.x = pt.x;
|
|
10845
|
+
}
|
|
10969
10846
|
|
|
10970
|
-
|
|
10971
|
-
extremes.push(p, p1);
|
|
10847
|
+
// renderPoint(markers, pt, 'green', '3%', '0.5')
|
|
10972
10848
|
|
|
10973
|
-
|
|
10974
|
-
|
|
10975
|
-
/*
|
|
10976
|
-
renderPoint(markers, p, 'green', '1%', '0.5')
|
|
10977
|
-
renderPoint(markers, p1, 'red', '1%', '0.5')
|
|
10978
|
-
renderPoint(markers, p2, 'blue', '1%', '0.5')
|
|
10979
|
-
*/
|
|
10980
|
-
}
|
|
10849
|
+
}
|
|
10850
|
+
else {
|
|
10981
10851
|
|
|
10982
|
-
|
|
10983
|
-
// average extreme
|
|
10852
|
+
if (bestMatch) {
|
|
10984
10853
|
|
|
10985
|
-
|
|
10986
|
-
|
|
10854
|
+
if (!isBestMatch && (pt.x === bestMatch.x || pt.y === bestMatch.y)) {
|
|
10855
|
+
extremes2.push(pt);
|
|
10856
|
+
}
|
|
10857
|
+
pt.isExtreme = false;
|
|
10858
|
+
pt.isHorizontal = false;
|
|
10859
|
+
pt.isVertical = false;
|
|
10860
|
+
}
|
|
10987
10861
|
|
|
10988
|
-
|
|
10989
|
-
p.y = y;
|
|
10862
|
+
}
|
|
10990
10863
|
|
|
10991
|
-
|
|
10992
|
-
}
|
|
10993
|
-
}
|
|
10864
|
+
});
|
|
10994
10865
|
|
|
10995
|
-
|
|
10996
|
-
|
|
10997
|
-
|
|
10866
|
+
// average coordinates
|
|
10867
|
+
if (extremes2.length) {
|
|
10868
|
+
bestMatch.x = (extremes2[0].x + bestMatch.x) * 0.5;
|
|
10869
|
+
bestMatch.y = (extremes2[0].y + bestMatch.y) * 0.5;
|
|
10998
10870
|
|
|
10999
|
-
|
|
10871
|
+
}
|
|
11000
10872
|
|
|
11001
|
-
|
|
11002
|
-
|
|
11003
|
-
let p0 = pts1[0];
|
|
11004
|
-
let pL = pts1[l2 - 1];
|
|
11005
|
-
let near0 = getDistManhattan(p0, pL) < thresh * 2;
|
|
11006
|
-
if (p0.isExtreme && pL.isExtreme && near0) {
|
|
11007
|
-
pL.x = p0.x;
|
|
11008
|
-
pL.y = p0.y;
|
|
10873
|
+
i += extremes.length;
|
|
10874
|
+
continue;
|
|
11009
10875
|
}
|
|
11010
10876
|
}
|
|
11011
10877
|
|
|
11012
|
-
pts = pts1;
|
|
11013
10878
|
}
|
|
11014
10879
|
|
|
11015
|
-
return pts
|
|
11016
10880
|
}
|
|
11017
10881
|
|
|
11018
|
-
function
|
|
11019
|
-
{ closed = true,
|
|
11020
|
-
keepCorners = true,
|
|
11021
|
-
keepExtremes = true,
|
|
11022
|
-
keepInflections = false
|
|
11023
|
-
} = {}
|
|
11024
|
-
) {
|
|
11025
|
-
let chunks = [[pts[0]]];
|
|
11026
|
-
|
|
11027
|
-
let idx = 0;
|
|
11028
|
-
let lastChunk = chunks[idx];
|
|
10882
|
+
function cleanupPolyKeypoints(pts = []) {
|
|
11029
10883
|
|
|
11030
10884
|
let l = pts.length;
|
|
11031
10885
|
|
|
11032
|
-
|
|
10886
|
+
getPolyBBox(pts);
|
|
10887
|
+
|
|
10888
|
+
pts[0];
|
|
10889
|
+
|
|
11033
10890
|
for (let i = 1; i < l; i++) {
|
|
11034
|
-
i > 0 ? pts[i] : pts[l - 1];
|
|
10891
|
+
i > 0 ? pts[i - 1] : pts[l - 1];
|
|
11035
10892
|
let p1 = pts[i];
|
|
11036
10893
|
i < l - 1 ? pts[i + 1] : pts[l - 1];
|
|
11037
10894
|
|
|
11038
|
-
|
|
11039
|
-
|
|
11040
|
-
if ((keepExtremes && p1.isExtreme || keepCorners && p1.isCorner )) {
|
|
11041
|
-
idx++;
|
|
11042
|
-
chunks.push([]);
|
|
11043
|
-
}
|
|
10895
|
+
let { isHorizontal, isVertical, isCorner, isLong, isExtreme, isSemiExtreme, isDirChange } = p1;
|
|
10896
|
+
let offset = 0;
|
|
11044
10897
|
|
|
11045
|
-
|
|
11046
|
-
lastChunk.push(p1);
|
|
11047
|
-
}
|
|
10898
|
+
if (!isSemiExtreme){
|
|
11048
10899
|
|
|
11049
|
-
|
|
10900
|
+
continue
|
|
10901
|
+
}
|
|
11050
10902
|
|
|
11051
|
-
|
|
11052
|
-
|
|
10903
|
+
if (isSemiExtreme || isExtreme) {
|
|
10904
|
+
let semiExtremes = isSemiExtreme ? [p1] : [];
|
|
11053
10905
|
|
|
11054
|
-
|
|
11055
|
-
|
|
11056
|
-
*
|
|
11057
|
-
*/
|
|
11058
|
-
function fitCurveSchneider(pts, {
|
|
11059
|
-
maxError = 0,
|
|
11060
|
-
adjustCpts = true,
|
|
11061
|
-
harmonize = true,
|
|
11062
|
-
keepCorners = true
|
|
11063
|
-
} = {}) {
|
|
10906
|
+
for (let j = i + 1; j < l; j++) {
|
|
10907
|
+
let p2 = pts[j];
|
|
11064
10908
|
|
|
11065
|
-
|
|
11066
|
-
|
|
11067
|
-
|
|
10909
|
+
if (!p2.isSemiExtreme || p2.isExtreme || p2.isCorner){
|
|
10910
|
+
break
|
|
10911
|
+
}
|
|
10912
|
+
semiExtremes.push(p2);
|
|
10913
|
+
}
|
|
11068
10914
|
|
|
11069
|
-
|
|
11070
|
-
if (pts.length === 2) {
|
|
11071
|
-
return [
|
|
11072
|
-
{ type: 'L', values: [pts[0].x, pts[0].y] },
|
|
11073
|
-
{ type: 'L', values: [pts[1].x, pts[1].y] }
|
|
11074
|
-
]
|
|
11075
|
-
}
|
|
10915
|
+
if (semiExtremes.length > 1) {
|
|
11076
10916
|
|
|
11077
|
-
|
|
10917
|
+
let semiExtremeMid = semiExtremes[Math.floor(semiExtremes.length*0.5)];
|
|
10918
|
+
let p1_1 = semiExtremes[0];
|
|
10919
|
+
let p2_1 = semiExtremes[semiExtremes.length - 1];
|
|
10920
|
+
let ptI = checkLineIntersection(p1_1, p1_1.tangentR, p2_1, p2_1.tangentL, false, true);
|
|
11078
10921
|
|
|
11079
|
-
|
|
11080
|
-
|
|
11081
|
-
|
|
11082
|
-
|
|
10922
|
+
semiExtremes.forEach(pt=>{
|
|
10923
|
+
pt.isSemiExtreme=false;
|
|
10924
|
+
});
|
|
10925
|
+
semiExtremeMid.isSemiExtreme=true;
|
|
11083
10926
|
|
|
11084
|
-
|
|
11085
|
-
|
|
11086
|
-
|
|
10927
|
+
// interpolate mid point
|
|
10928
|
+
if (ptI) {
|
|
10929
|
+
let pI_1 = interpolate(p1_1, ptI, 0.5);
|
|
10930
|
+
let pI_2 = interpolate(p2_1, ptI, 0.5);
|
|
10931
|
+
let pI_3 = interpolate(pI_2, pI_1, 0.5);
|
|
11087
10932
|
|
|
11088
|
-
|
|
10933
|
+
semiExtremeMid.x = pI_3.x;
|
|
10934
|
+
semiExtremeMid.y = pI_3.y;
|
|
10935
|
+
semiExtremeMid.tangentL = pI_1;
|
|
10936
|
+
semiExtremeMid.tangentR = pI_2;
|
|
11089
10937
|
|
|
11090
|
-
|
|
10938
|
+
i += offset;
|
|
10939
|
+
continue
|
|
10940
|
+
}
|
|
11091
10941
|
|
|
11092
|
-
|
|
10942
|
+
}
|
|
10943
|
+
}
|
|
10944
|
+
// find significant of same type
|
|
11093
10945
|
|
|
11094
|
-
|
|
11095
|
-
let com1 = pathData[0];
|
|
10946
|
+
}
|
|
11096
10947
|
|
|
11097
|
-
|
|
11098
|
-
|
|
10948
|
+
/*
|
|
10949
|
+
// update index
|
|
10950
|
+
ptsClean.forEach((pt, i) => {
|
|
10951
|
+
pt.idx = i
|
|
10952
|
+
})
|
|
10953
|
+
*/
|
|
11099
10954
|
|
|
11100
|
-
|
|
11101
|
-
let p1 = { x: pts[1].x, y: pts[1].y };
|
|
11102
|
-
let p2 = pts[2] ? { x: pts[2].x, y: pts[2].y } : null;
|
|
10955
|
+
return pts;
|
|
11103
10956
|
|
|
11104
|
-
|
|
11105
|
-
cp1 = { x: com1.values[0], y: com1.values[1] };
|
|
11106
|
-
cp1 = adjustTangentAngle(cp1, p0, p1, p2);
|
|
11107
|
-
com1.values[0] = cp1.x;
|
|
11108
|
-
com1.values[1] = cp1.y;
|
|
11109
|
-
}
|
|
10957
|
+
}
|
|
11110
10958
|
|
|
11111
|
-
|
|
11112
|
-
|
|
11113
|
-
|
|
10959
|
+
function adjustTangentAngle(cp, p0, p1, p2) {
|
|
10960
|
+
let ang1 = getAngle(p0, p1);
|
|
10961
|
+
let ang2 = getAngle(p0, p2);
|
|
10962
|
+
let angDiff = (ang2 - ang1);
|
|
11114
10963
|
|
|
11115
|
-
|
|
11116
|
-
|
|
11117
|
-
cp2 = adjustTangentAngle(cp2, pL, pL1, pL2);
|
|
11118
|
-
com2.values[2] = cp2.x;
|
|
11119
|
-
com2.values[3] = cp2.y;
|
|
11120
|
-
}
|
|
10964
|
+
let f = 0.666;
|
|
10965
|
+
f = 1;
|
|
11121
10966
|
|
|
11122
|
-
|
|
11123
|
-
|
|
11124
|
-
|
|
11125
|
-
harmonize = false;
|
|
11126
|
-
if (harmonize) {
|
|
11127
|
-
pathData = harmonizeCubicCptsThird([{ type: 'M', values: [pts[0].x, pts[0].y] },
|
|
11128
|
-
...pathData])
|
|
11129
|
-
pathData.shift()
|
|
11130
|
-
}
|
|
11131
|
-
*/
|
|
10967
|
+
cp = rotatePoint(cp, p0.x, p0.y, -angDiff * f);
|
|
10968
|
+
return cp
|
|
10969
|
+
}
|
|
11132
10970
|
|
|
11133
|
-
|
|
10971
|
+
function getTangents(pts = [], {
|
|
10972
|
+
x = 0,
|
|
10973
|
+
y = 0,
|
|
10974
|
+
width = 0,
|
|
10975
|
+
height = 0,
|
|
10976
|
+
debug = false,
|
|
10977
|
+
closed=false,
|
|
10978
|
+
} = {}) {
|
|
11134
10979
|
|
|
11135
|
-
|
|
11136
|
-
}
|
|
10980
|
+
let l = pts.length;
|
|
11137
10981
|
|
|
11138
|
-
|
|
11139
|
-
|
|
11140
|
-
|
|
11141
|
-
|
|
11142
|
-
*/
|
|
11143
|
-
function fitCubic(pts, leftTangent, rightTangent, error, keepCorners = false) {
|
|
10982
|
+
// bounding box of this sub poly
|
|
10983
|
+
if (!width || !height) {
|
|
10984
|
+
({ x, y, width, height } = getPolyBBox(pts));
|
|
10985
|
+
}
|
|
11144
10986
|
|
|
11145
|
-
|
|
11146
|
-
let bezCurve;
|
|
10987
|
+
// threshold for horizontal or vertical detection
|
|
11147
10988
|
|
|
11148
|
-
|
|
10989
|
+
for (let i = 0; i < l; i++) {
|
|
10990
|
+
let p0 = i > 0 ? pts[i - 1] : pts[l - 1];
|
|
10991
|
+
let p1 = pts[i];
|
|
10992
|
+
let p2 = i < l - 1 ? pts[i + 1] : pts[l - 1];
|
|
10993
|
+
let p3 = i < l - 1 ? pts[i + 2] : pts[l - 1];
|
|
11149
10994
|
|
|
11150
|
-
|
|
11151
|
-
let dist = getDistance(pts[0], pts[1], false) * 0.333;
|
|
11152
|
-
bezCurve = [pts[0], addArrays(pts[0], mulItems(leftTangent, dist)), addArrays(pts[1], mulItems(rightTangent, dist)), pts[1]];
|
|
11153
|
-
return [bezCurve];
|
|
11154
|
-
}
|
|
11155
|
-
*/
|
|
10995
|
+
let { isHorizontal, isVertical, isCorner, isLong, isExtreme, isSemiExtreme, isDirChange } = p1;
|
|
11156
10996
|
|
|
11157
|
-
|
|
11158
|
-
|
|
10997
|
+
// default
|
|
10998
|
+
let tangentL = { x: p1.x - p1.dx2 * 0.5, y: p1.y - p1.dy2 * 0.5 };
|
|
10999
|
+
let tangentR = { x: p1.x + p1.dx2 * 0.5, y: p1.y + p1.dy2 * 0.5 };
|
|
11159
11000
|
|
|
11160
|
-
|
|
11001
|
+
// average first tangent
|
|
11002
|
+
if(i===0){
|
|
11003
|
+
tangentR = adjustTangentAngle(p2, p1, p2, p3);
|
|
11004
|
+
}
|
|
11161
11005
|
|
|
11162
|
-
|
|
11163
|
-
|
|
11006
|
+
/**
|
|
11007
|
+
* add left and right tangents
|
|
11008
|
+
* for later curve fitting
|
|
11009
|
+
*/
|
|
11164
11010
|
|
|
11165
|
-
|
|
11166
|
-
|
|
11167
|
-
|
|
11168
|
-
|
|
11169
|
-
if (
|
|
11170
|
-
|
|
11171
|
-
|
|
11011
|
+
if (isHorizontal && !isCorner) {
|
|
11012
|
+
tangentL = { x: p1.x - p1.dx2*0.5, y: p1.y };
|
|
11013
|
+
tangentR = { x: p1.x + p1.dx2*0.5, y: p1.y };
|
|
11014
|
+
}
|
|
11015
|
+
else if (isVertical) {
|
|
11016
|
+
tangentL = { x: p1.x , y: p1.y - p1.dy2*0.5 };
|
|
11017
|
+
tangentR = { x: p1.x , y: p1.y + p1.dy2*0.5 };
|
|
11172
11018
|
}
|
|
11173
|
-
}
|
|
11174
11019
|
|
|
11175
|
-
|
|
11176
|
-
|
|
11177
|
-
|
|
11020
|
+
if (!isExtreme && p1.isLong) {
|
|
11021
|
+
tangentL = { x: p1.x - p1.dx*0.5, y: p1.y - p1.dy*0.5 };
|
|
11022
|
+
tangentR = { x: p1.x + p1.dx*0.5, y: p1.y + p1.dy*0.5 };
|
|
11023
|
+
}
|
|
11178
11024
|
|
|
11179
|
-
|
|
11025
|
+
/*
|
|
11180
11026
|
|
|
11181
|
-
|
|
11182
|
-
|
|
11183
|
-
|
|
11027
|
+
if (isDirChange && !isCorner && !isExtreme) {
|
|
11028
|
+
p1.tangentL = { x: p1.x-p1.dx2*0.5, y: p1.y-p1.dy2*0.5 }
|
|
11029
|
+
p1.tangentR = { x: p1.x+p1.dx2*0.5, y: p1.y+p1.dy2*0.5 }
|
|
11030
|
+
}
|
|
11031
|
+
*/
|
|
11184
11032
|
|
|
11185
|
-
|
|
11033
|
+
if (isCorner) {
|
|
11186
11034
|
|
|
11187
|
-
|
|
11035
|
+
tangentL = {x:p0.x, y:p0.y};
|
|
11036
|
+
tangentR = {x:p2.x, y:p2.y};
|
|
11188
11037
|
|
|
11189
|
-
let
|
|
11038
|
+
let p0_1 = pts[i - 2] ? pts[i - 2] : pts[l - 1];
|
|
11190
11039
|
|
|
11191
|
-
|
|
11192
|
-
maxError = _generateAndReport2[1];
|
|
11193
|
-
splitPoint = _generateAndReport2[2];
|
|
11040
|
+
let p2_1 = pts[i + 2] ? pts[i + 2] : pts[1];
|
|
11194
11041
|
|
|
11195
|
-
|
|
11196
|
-
|
|
11042
|
+
// adjust angle
|
|
11043
|
+
if (!p0.isCorner) {
|
|
11044
|
+
tangentL = adjustTangentAngle(p0, p1, p0, p0_1);
|
|
11197
11045
|
}
|
|
11198
11046
|
|
|
11199
|
-
|
|
11200
|
-
|
|
11201
|
-
if (errChange > .9999 && errChange < 1.0001) {
|
|
11202
|
-
break;
|
|
11203
|
-
}
|
|
11047
|
+
if (!p2.isCorner) {
|
|
11048
|
+
tangentR = adjustTangentAngle(tangentR, p1, p2, p2_1);
|
|
11204
11049
|
}
|
|
11205
11050
|
|
|
11206
|
-
|
|
11207
|
-
|
|
11051
|
+
/*
|
|
11052
|
+
renderPoint(markers, p0, 'darkblue', '0.75%', '0.5')
|
|
11053
|
+
// renderPoint(markers, p0_1, 'blue', '0.5%')
|
|
11054
|
+
renderPoint(markers, tangentL, 'blue', '0.5%', '0.5')
|
|
11055
|
+
renderPoint(markers, tangentR, 'blue', '0.5%', '0.5')
|
|
11056
|
+
*/
|
|
11057
|
+
|
|
11208
11058
|
}
|
|
11209
|
-
}
|
|
11210
11059
|
|
|
11211
|
-
|
|
11212
|
-
|
|
11060
|
+
p1.tangentL = tangentL;
|
|
11061
|
+
p1.tangentR = tangentR;
|
|
11062
|
+
|
|
11063
|
+
/*
|
|
11064
|
+
debug = true
|
|
11065
|
+
if(debug){
|
|
11066
|
+
if (isCorner || isSemiExtreme || isDirChange || isExtreme) {
|
|
11067
|
+
renderPoint(markers, p1.tangentL, 'darkred', '0.5%')
|
|
11068
|
+
renderPoint(markers, p1.tangentR, 'darkblue', '0.5%')
|
|
11069
|
+
}
|
|
11070
|
+
}
|
|
11071
|
+
*/
|
|
11213
11072
|
|
|
11214
|
-
if (centerVector.x === 0 && centerVector.y === 0) {
|
|
11215
|
-
centerVector = subtract(pts[splitPoint - 1], pts[splitPoint]);
|
|
11216
|
-
let _ref = { x: -centerVector.y, y: centerVector.x };
|
|
11217
|
-
centerVector.x = _ref.x;
|
|
11218
|
-
centerVector.y = _ref.y;
|
|
11219
11073
|
}
|
|
11220
11074
|
|
|
11221
|
-
|
|
11075
|
+
}
|
|
11222
11076
|
|
|
11223
|
-
|
|
11077
|
+
function getPolyCentroid(pts) {
|
|
11224
11078
|
|
|
11225
|
-
|
|
11079
|
+
let l = pts.length;
|
|
11080
|
+
let x = 0, y = 0;
|
|
11081
|
+
for (let i = 0; l && i < l; i++) {
|
|
11082
|
+
let pt = pts[i];
|
|
11083
|
+
x += pt.x;
|
|
11084
|
+
y += pt.y;
|
|
11085
|
+
}
|
|
11226
11086
|
|
|
11227
|
-
|
|
11228
|
-
|
|
11229
|
-
...fitCubic(pts.slice(splitPoint), fromCenterTangent, rightTangent, error, keepCorners)
|
|
11230
|
-
);
|
|
11087
|
+
let centroid = { x: x / l, y: y / l };
|
|
11088
|
+
return centroid
|
|
11231
11089
|
|
|
11232
|
-
return beziers;
|
|
11233
11090
|
}
|
|
11234
11091
|
|
|
11235
|
-
|
|
11236
|
-
|
|
11237
|
-
|
|
11238
|
-
function generateBezier(pts, parameters, leftTangent, rightTangent) {
|
|
11092
|
+
function detectRegularPolygon(pts, centroid = { x: 0, y: 0 }) {
|
|
11093
|
+
let rSq = getSquareDistance(pts[0], centroid);
|
|
11094
|
+
let isRegular = true;
|
|
11239
11095
|
|
|
11240
|
-
let
|
|
11096
|
+
for (let i = 1, l = pts.length; i < l; i++) {
|
|
11097
|
+
let pt1 = pts[i];
|
|
11098
|
+
let dist = getSquareDistance(pt1, centroid);
|
|
11241
11099
|
|
|
11242
|
-
|
|
11243
|
-
|
|
11244
|
-
let len = parameters.length;
|
|
11100
|
+
let diff = Math.abs(rSq - dist);
|
|
11101
|
+
let diffRel = diff / rSq;
|
|
11245
11102
|
|
|
11246
|
-
|
|
11247
|
-
|
|
11248
|
-
|
|
11249
|
-
a = A[i];
|
|
11103
|
+
if (diffRel > 0.05) {
|
|
11104
|
+
return false;
|
|
11105
|
+
}
|
|
11250
11106
|
|
|
11251
|
-
a[0] = mulItems(leftTangent, 3 * u * (ux * ux));
|
|
11252
|
-
a[1] = mulItems(rightTangent, 3 * ux * (u * u));
|
|
11253
11107
|
}
|
|
11108
|
+
return isRegular;
|
|
11109
|
+
}
|
|
11110
|
+
|
|
11111
|
+
function analyzePoly(pts, {
|
|
11112
|
+
x = 0,
|
|
11113
|
+
y = 0,
|
|
11114
|
+
width = 0,
|
|
11115
|
+
height = 0,
|
|
11116
|
+
debug = false
|
|
11117
|
+
} = {}) {
|
|
11254
11118
|
|
|
11255
|
-
let C = [[0, 0], [0, 0]];
|
|
11256
|
-
let X = [0, 0];
|
|
11257
11119
|
let l = pts.length;
|
|
11120
|
+
let left = x;
|
|
11121
|
+
let top = y;
|
|
11122
|
+
let right = x + width;
|
|
11123
|
+
let bottom = y + height;
|
|
11258
11124
|
|
|
11259
|
-
|
|
11260
|
-
|
|
11261
|
-
|
|
11125
|
+
if (!width || !height) {
|
|
11126
|
+
({ x, y, width, height, top, bottom, left, right } = getPolyBBox(pts));
|
|
11127
|
+
}
|
|
11262
11128
|
|
|
11263
|
-
|
|
11264
|
-
|
|
11265
|
-
C[1][0] += dot(a[0], a[1]);
|
|
11266
|
-
C[1][1] += dot(a[1], a[1]);
|
|
11129
|
+
// round
|
|
11130
|
+
[x, y, width, height, top, bottom, left, right] = [x, y, width, height, top, bottom, left, right].map(val => +val.toFixed(8));
|
|
11267
11131
|
|
|
11268
|
-
|
|
11132
|
+
// bounding box of this sub poly
|
|
11133
|
+
let bb0 = { x, y, top, left, width, height, right, bottom };
|
|
11269
11134
|
|
|
11270
|
-
|
|
11271
|
-
X[1] += dot(a[1], tmp);
|
|
11272
|
-
}
|
|
11135
|
+
let thresh = (width + height) * 0.01;
|
|
11273
11136
|
|
|
11274
|
-
|
|
11275
|
-
let
|
|
11276
|
-
let det_X_C1 = X[0] * C[1][1] - X[1] * C[0][1];
|
|
11137
|
+
// threshold for horizontal or vertical detection
|
|
11138
|
+
let thresh2 = thresh * 0.75;
|
|
11277
11139
|
|
|
11278
|
-
let
|
|
11279
|
-
let alpha_r = det_C0_C1 === 0 ? 0 : det_C0_X / det_C0_C1;
|
|
11280
|
-
let segLength = getDistance(firstPoint, lastPoint, false);
|
|
11281
|
-
let epsilon = 1.0e-6 * segLength;
|
|
11140
|
+
let dims = [];
|
|
11282
11141
|
|
|
11283
|
-
|
|
11142
|
+
/*
|
|
11143
|
+
pts.forEach(pt=>{
|
|
11144
|
+
renderPoint(markers, pt, 'red', '2.5%')
|
|
11145
|
+
})
|
|
11146
|
+
*/
|
|
11147
|
+
|
|
11148
|
+
/**
|
|
11149
|
+
* 1st run:
|
|
11150
|
+
* collect more details
|
|
11151
|
+
* area for sign change detection
|
|
11152
|
+
* deltas and distances
|
|
11153
|
+
*/
|
|
11154
|
+
for (let i = 0; i < l; i++) {
|
|
11155
|
+
let p0 = i > 0 ? pts[i - 1] : pts[l - 1];
|
|
11156
|
+
let p1 = pts[i];
|
|
11157
|
+
let p2 = i < l - 1 ? pts[i + 1] : pts[l - 1];
|
|
11158
|
+
|
|
11159
|
+
let area = getPolygonArea([p0, p1, p2], false);
|
|
11160
|
+
let dx = i > 0 ? +(p1.x - p0.x).toFixed(7) : 0;
|
|
11161
|
+
let dy = i > 0 ? +(p1.y - p0.y).toFixed(7) : 0;
|
|
11162
|
+
|
|
11163
|
+
let dx2 = +(p2.x - p0.x).toFixed(7);
|
|
11164
|
+
let dy2 = +(p2.y - p0.y).toFixed(7);
|
|
11165
|
+
|
|
11166
|
+
p1.area = area;
|
|
11167
|
+
p1.dist = i > 0 ? getDistManhattan(p0, p1) : 0;
|
|
11168
|
+
// add dist for long/short segment detection
|
|
11169
|
+
dims.push(p1.dist);
|
|
11170
|
+
p1.idx = i;
|
|
11171
|
+
p1.dx = dx;
|
|
11172
|
+
p1.dy = dy;
|
|
11173
|
+
p1.dx2 = dx2;
|
|
11174
|
+
p1.dy2 = dy2;
|
|
11284
11175
|
|
|
11285
|
-
bezCurve[1] = addArrays(firstPoint, mulItems(leftTangent, segLength * 0.333));
|
|
11286
|
-
bezCurve[2] = addArrays(lastPoint, mulItems(rightTangent, segLength * 0.333));
|
|
11287
|
-
} else {
|
|
11288
|
-
// First and last control pts of the Bezier curve
|
|
11289
|
-
bezCurve[1] = addArrays(firstPoint, mulItems(leftTangent, alpha_l));
|
|
11290
|
-
bezCurve[2] = addArrays(lastPoint, mulItems(rightTangent, alpha_r));
|
|
11291
11176
|
}
|
|
11292
11177
|
|
|
11293
|
-
|
|
11294
|
-
|
|
11178
|
+
/**
|
|
11179
|
+
* find average segment length
|
|
11180
|
+
* for long/short segment detection
|
|
11181
|
+
*/
|
|
11182
|
+
dims = dims.filter(Boolean).sort((a, b) => a - b);
|
|
11183
|
+
let lenD = dims.length;
|
|
11184
|
+
let dimMin = dims[0];
|
|
11185
|
+
dims[lenD - 1];
|
|
11295
11186
|
|
|
11296
|
-
|
|
11297
|
-
let
|
|
11187
|
+
let dimAv = dims.reduce((a, b) => a + b, 0) / lenD;
|
|
11188
|
+
let dimShort = (dimMin + dimAv) * 0.5;
|
|
11189
|
+
let dimLong = dimAv * 2;
|
|
11298
11190
|
|
|
11299
|
-
|
|
11300
|
-
|
|
11191
|
+
/*
|
|
11192
|
+
// round to adjust for minor deviations
|
|
11193
|
+
let idx_q = Math.ceil(lenD * 0.25);
|
|
11194
|
+
let dim_mid = dims[Math.floor(lenD * 0.5)]
|
|
11195
|
+
let dims_min = dims.slice(0, Math.floor(lenD * 0.25));
|
|
11196
|
+
let dim_min = ((dims_min.reduce((a, b) => a + b, 0) / idx_q) + dim_mid) * 0.5;
|
|
11301
11197
|
|
|
11302
|
-
|
|
11303
|
-
|
|
11198
|
+
let threshold = 75
|
|
11199
|
+
let decimalsAuto = dim_min > threshold * 1.5 ? 0 : Math.floor(threshold / dim_min).toString().length
|
|
11304
11200
|
|
|
11305
|
-
|
|
11306
|
-
|
|
11201
|
+
// clamp
|
|
11202
|
+
decimalsAuto = Math.min(Math.max(0, decimalsAuto), 8)
|
|
11307
11203
|
|
|
11308
|
-
|
|
11309
|
-
|
|
11310
|
-
|
|
11311
|
-
function reparameterize(bezier, pts, parameters) {
|
|
11312
|
-
return parameters.map((p, i) => {
|
|
11313
|
-
return newtonRaphsonRootFind(bezier, pts[i], p);
|
|
11314
|
-
});
|
|
11315
|
-
}
|
|
11316
|
-
/**
|
|
11317
|
-
* Use Newton-Raphson iteration to find better root.
|
|
11318
|
-
*/
|
|
11204
|
+
pts = roundPoly(pts, 2)
|
|
11205
|
+
console.log(pts);
|
|
11206
|
+
*/
|
|
11319
11207
|
|
|
11320
|
-
|
|
11321
|
-
|
|
11322
|
-
|
|
11323
|
-
|
|
11208
|
+
/**
|
|
11209
|
+
* analyze topology:
|
|
11210
|
+
* find significant commands:
|
|
11211
|
+
* extremes, inflections etc.
|
|
11212
|
+
*/
|
|
11213
|
+
for (let i = 0; i < l; i++) {
|
|
11324
11214
|
|
|
11325
|
-
|
|
11215
|
+
let p0 = i > 0 ? pts[i - 1] : pts[l - 1];
|
|
11216
|
+
let p1 = pts[i];
|
|
11217
|
+
let p2 = i < l - 1 ? pts[i + 1] : pts[l - 1];
|
|
11326
11218
|
|
|
11327
|
-
|
|
11219
|
+
// convert area to absolute for flatness checks
|
|
11220
|
+
let area1 = Math.abs(p1.area);
|
|
11221
|
+
let isCorner = false;
|
|
11222
|
+
let isSemiExtreme = false;
|
|
11223
|
+
let isShort = false;
|
|
11224
|
+
let isLong = false;
|
|
11328
11225
|
|
|
11329
|
-
|
|
11330
|
-
|
|
11226
|
+
/**
|
|
11227
|
+
* detect short or long
|
|
11228
|
+
*/
|
|
11229
|
+
if (p1.dist > dimLong) {
|
|
11230
|
+
isLong = true;
|
|
11231
|
+
}
|
|
11331
11232
|
|
|
11332
|
-
|
|
11333
|
-
|
|
11233
|
+
if (p1.dist < dimShort) {
|
|
11234
|
+
isShort = true;
|
|
11235
|
+
}
|
|
11334
11236
|
|
|
11335
|
-
|
|
11336
|
-
// This represents how much the error aligns with the tangent
|
|
11337
|
-
let numerator = dx * qp[0] + dy * qp[1];
|
|
11237
|
+
let flat = !p1.area || area1 < thresh;
|
|
11338
11238
|
|
|
11339
|
-
|
|
11340
|
-
|
|
11341
|
-
|
|
11239
|
+
/**
|
|
11240
|
+
* check extremes
|
|
11241
|
+
*/
|
|
11242
|
+
let isExtreme = false;
|
|
11342
11243
|
|
|
11343
|
-
|
|
11344
|
-
|
|
11244
|
+
// 1. total extreme
|
|
11245
|
+
let isTop = p1.y === bb0.top;
|
|
11246
|
+
let isBottom = p1.y === bb0.bottom;
|
|
11247
|
+
let isLeft = p1.x === bb0.left;
|
|
11248
|
+
let isRight = p1.x === bb0.right;
|
|
11345
11249
|
|
|
11346
|
-
|
|
11347
|
-
|
|
11250
|
+
if (isTop || isBottom || isLeft || isRight) {
|
|
11251
|
+
isExtreme = true;
|
|
11348
11252
|
|
|
11349
|
-
|
|
11253
|
+
}
|
|
11350
11254
|
|
|
11351
|
-
|
|
11352
|
-
|
|
11353
|
-
|
|
11255
|
+
// 1.2 horizontal or vertical
|
|
11256
|
+
/*
|
|
11257
|
+
let isHorizontal = isTop || isBottom || (p1.y === p0.y && p1.x !== p0.x);
|
|
11258
|
+
let isVertical = isLeft || isRight || (p1.x === p0.x && p1.y !== p0.y)
|
|
11354
11259
|
|
|
11355
|
-
|
|
11356
|
-
return u - numerator / denominator;
|
|
11357
|
-
}
|
|
11260
|
+
if ((isHorizontal || isVertical)) {
|
|
11358
11261
|
|
|
11359
|
-
|
|
11360
|
-
|
|
11361
|
-
*/
|
|
11362
|
-
function chordLengthParameterize(pts) {
|
|
11363
|
-
let u = [];
|
|
11364
|
-
let l = pts.length;
|
|
11365
|
-
let p0 = pts[0];
|
|
11366
|
-
let p = pts[1];
|
|
11367
|
-
let currU = 0;
|
|
11368
|
-
let prevU = 0;
|
|
11262
|
+
let diffX = Math.abs(p0.x - p1.x)
|
|
11263
|
+
let diffY = Math.abs(p0.y - p1.y)
|
|
11369
11264
|
|
|
11370
|
-
|
|
11371
|
-
|
|
11265
|
+
if (isLong) {
|
|
11266
|
+
}
|
|
11372
11267
|
|
|
11373
|
-
|
|
11374
|
-
|
|
11375
|
-
|
|
11268
|
+
if (isLong && (diffY < thresh2) && diffX > thresh) {
|
|
11269
|
+
p0.isExtreme = true;
|
|
11270
|
+
p0.isHorizontal = true;
|
|
11271
|
+
}
|
|
11272
|
+
else if (isLong && (diffX < thresh2) && diffY > thresh) {
|
|
11273
|
+
p0.isExtreme = true;
|
|
11274
|
+
p0.isVertical = true;
|
|
11275
|
+
}
|
|
11376
11276
|
|
|
11377
|
-
|
|
11378
|
-
|
|
11277
|
+
isExtreme = true
|
|
11278
|
+
}
|
|
11279
|
+
*/
|
|
11379
11280
|
|
|
11380
|
-
|
|
11381
|
-
|
|
11382
|
-
});
|
|
11281
|
+
let dx = Math.abs(p0.x - p1.x);
|
|
11282
|
+
let dy = Math.abs(p0.y - p1.y);
|
|
11383
11283
|
|
|
11384
|
-
|
|
11385
|
-
|
|
11386
|
-
|
|
11387
|
-
|
|
11388
|
-
*/
|
|
11389
|
-
function computeMaxError(pts, bez, parameters) {
|
|
11390
|
-
let dist,
|
|
11391
|
-
maxDist,
|
|
11392
|
-
splitPoint,
|
|
11393
|
-
i, point, t;
|
|
11284
|
+
let vh_thresh = thresh * 0.05;
|
|
11285
|
+
// vh_thresh = thresh * 0.25
|
|
11286
|
+
let isHorizontal = isTop || isBottom || (p1.y === p0.y && p1.x !== p0.x) || (dy <= vh_thresh);
|
|
11287
|
+
let isVertical = (isLeft || isRight || (p1.x === p0.x && p1.y !== p0.y) || (dx <= vh_thresh));
|
|
11394
11288
|
|
|
11395
|
-
|
|
11396
|
-
splitPoint = Math.floor(pts.length * 0.5);
|
|
11289
|
+
// renderPoint(markers, p1, 'red', '0.5%')
|
|
11397
11290
|
|
|
11398
|
-
|
|
11399
|
-
let l = pts.length;
|
|
11400
|
-
let ptOnPath = null;
|
|
11291
|
+
if (p1.y === p0.y) ;
|
|
11401
11292
|
|
|
11402
|
-
|
|
11403
|
-
point = pts[i];
|
|
11293
|
+
if ((isHorizontal || isVertical)) {
|
|
11404
11294
|
|
|
11405
|
-
|
|
11295
|
+
if (isLong && isHorizontal) {
|
|
11296
|
+
p0.isExtreme = true;
|
|
11297
|
+
p0.isHorizontal = true;
|
|
11406
11298
|
|
|
11407
|
-
|
|
11408
|
-
|
|
11299
|
+
}
|
|
11300
|
+
else if (isLong && isVertical) {
|
|
11301
|
+
p0.isExtreme = true;
|
|
11302
|
+
p0.isVertical = true;
|
|
11303
|
+
}
|
|
11409
11304
|
|
|
11410
|
-
|
|
11411
|
-
|
|
11412
|
-
v = subtract(pointAtT(bez, t), point);
|
|
11413
|
-
dist = v.x * v.x + v.y * v.y;
|
|
11414
|
-
*/
|
|
11305
|
+
isExtreme = true;
|
|
11306
|
+
}
|
|
11415
11307
|
|
|
11416
|
-
|
|
11308
|
+
// 1.3 is local or absolute extreme
|
|
11309
|
+
let bb = getPolyBBox([p0, p2]); // local bb
|
|
11310
|
+
let { left, right, top, bottom } = bb;
|
|
11311
|
+
|
|
11312
|
+
let extremeLocal = (p1.x < left || p1.x > right || p1.y < top || p1.y > bottom);
|
|
11313
|
+
if (!isExtreme && extremeLocal) {
|
|
11314
|
+
isExtreme = true;
|
|
11417
11315
|
|
|
11418
|
-
maxDist = dist;
|
|
11419
|
-
splitPoint = i;
|
|
11420
11316
|
}
|
|
11421
|
-
}
|
|
11422
11317
|
|
|
11423
|
-
|
|
11424
|
-
|
|
11318
|
+
/**
|
|
11319
|
+
* 2. sign changes
|
|
11320
|
+
*/
|
|
11321
|
+
let signChange = (p0.area < 0 && p1.area > 0) || (p0.area > 0 && p1.area < 0);
|
|
11322
|
+
let isDirChange = signChange && !flat && !p0.isDirChange && isLong;
|
|
11425
11323
|
|
|
11426
|
-
|
|
11427
|
-
|
|
11428
|
-
|
|
11429
|
-
let B_t_prev = bez[0];
|
|
11430
|
-
let sumLen = 0;
|
|
11324
|
+
/**
|
|
11325
|
+
* 3. corners
|
|
11326
|
+
*/
|
|
11431
11327
|
|
|
11432
|
-
|
|
11433
|
-
B_t_curr = pointAtT(bez, i / B_parts);
|
|
11434
|
-
sumLen += getDistance(B_t_curr, B_t_prev);
|
|
11435
|
-
B_t_dist.push(sumLen);
|
|
11436
|
-
B_t_prev = B_t_curr;
|
|
11437
|
-
}
|
|
11328
|
+
if (isExtreme) {
|
|
11438
11329
|
|
|
11439
|
-
|
|
11440
|
-
|
|
11441
|
-
|
|
11442
|
-
return B_t_dist;
|
|
11443
|
-
}
|
|
11444
|
-
function find_t(param, t_distMap, B_parts) {
|
|
11330
|
+
let delta = getDeltaAngle(p1, p2, p0);
|
|
11331
|
+
let { deltaAngleDeg } = delta;
|
|
11332
|
+
deltaAngleDeg = Math.abs(deltaAngleDeg);
|
|
11445
11333
|
|
|
11446
|
-
|
|
11447
|
-
|
|
11448
|
-
}
|
|
11449
|
-
if (param > 1) {
|
|
11450
|
-
return 1;
|
|
11451
|
-
}
|
|
11334
|
+
let isCornerDelta = deltaAngleDeg > 10 && deltaAngleDeg < 160;
|
|
11335
|
+
if (isCornerDelta) {
|
|
11452
11336
|
|
|
11453
|
-
|
|
11337
|
+
isCorner = true;
|
|
11454
11338
|
|
|
11455
|
-
|
|
11339
|
+
}
|
|
11456
11340
|
|
|
11457
|
-
if (param <= t_distMap[i]) {
|
|
11458
|
-
tMin = (i - 1) / B_parts;
|
|
11459
|
-
tMax = i / B_parts;
|
|
11460
|
-
lenMin = t_distMap[i - 1];
|
|
11461
|
-
lenMax = t_distMap[i];
|
|
11462
|
-
t = (param - lenMin) / (lenMax - lenMin) * (tMax - tMin) + tMin;
|
|
11463
|
-
break;
|
|
11464
11341
|
}
|
|
11465
|
-
}
|
|
11466
|
-
return t;
|
|
11467
|
-
}
|
|
11468
11342
|
|
|
11469
|
-
|
|
11343
|
+
if (isExtreme && !isCorner) {
|
|
11470
11344
|
|
|
11471
|
-
|
|
11472
|
-
|
|
11473
|
-
|
|
11345
|
+
if ((Math.abs(p1.dy2) < thresh2) && Math.abs(p1.dx2) > thresh) {
|
|
11346
|
+
isHorizontal = true;
|
|
11347
|
+
}
|
|
11348
|
+
else if (Math.abs(p1.dx2) < thresh2 && Math.abs(p1.dy2) > thresh) {
|
|
11349
|
+
isVertical = true;
|
|
11350
|
+
}
|
|
11351
|
+
}
|
|
11474
11352
|
|
|
11475
|
-
|
|
11476
|
-
|
|
11477
|
-
|
|
11353
|
+
/**
|
|
11354
|
+
* semi extremes
|
|
11355
|
+
* ~ 45deg tangent
|
|
11356
|
+
*/
|
|
11357
|
+
let diffX = Math.abs(p1.dx2);
|
|
11358
|
+
let diffY = Math.abs(p1.dy2);
|
|
11478
11359
|
|
|
11479
|
-
|
|
11480
|
-
|
|
11481
|
-
|
|
11482
|
-
|
|
11483
|
-
|
|
11360
|
+
let ratDelta = (diffX / diffY);
|
|
11361
|
+
|
|
11362
|
+
if (ratDelta > 0.8 && ratDelta <= 1.2) {
|
|
11363
|
+
isSemiExtreme = true;
|
|
11364
|
+
}
|
|
11484
11365
|
|
|
11485
|
-
|
|
11366
|
+
p1.isCorner = isCorner;
|
|
11367
|
+
p1.isExtreme = isExtreme;
|
|
11368
|
+
p1.isSemiExtreme = isSemiExtreme;
|
|
11369
|
+
p1.isLong = isLong;
|
|
11370
|
+
p1.isShort = isShort;
|
|
11486
11371
|
|
|
11487
|
-
|
|
11488
|
-
|
|
11372
|
+
p1.isHorizontal = isHorizontal;
|
|
11373
|
+
p1.isVertical = isVertical;
|
|
11374
|
+
p1.isDirChange = isDirChange;
|
|
11489
11375
|
|
|
11490
|
-
// flat line
|
|
11491
|
-
if (!polyArea && pts.length === 2) {
|
|
11492
|
-
polyArea = getSquareDistance(pts[0], pts[1]) * 0.01;
|
|
11493
11376
|
}
|
|
11494
11377
|
|
|
11495
|
-
|
|
11496
|
-
|
|
11378
|
+
// add tangents
|
|
11379
|
+
getTangents(pts, { x, y, width, height });
|
|
11497
11380
|
|
|
11498
|
-
|
|
11381
|
+
refineAdjacentPolyExtremes(pts);
|
|
11499
11382
|
|
|
11500
|
-
//
|
|
11501
|
-
|
|
11502
|
-
if (isBulged) {
|
|
11503
|
-
let ptMid = pts[Math.floor(l * 0.5)];
|
|
11504
|
-
let p = pts[l - 1];
|
|
11505
|
-
/*
|
|
11506
|
-
let cp1 = pointAtT([pts[0], ptMid], 0.666);
|
|
11507
|
-
let cp2 = pointAtT([p, ptMid], 0.666);
|
|
11383
|
+
// filter adjacent significant points
|
|
11384
|
+
cleanupPolyKeypoints(pts);
|
|
11508
11385
|
|
|
11509
|
-
|
|
11386
|
+
renderPolyTopology(pts);
|
|
11510
11387
|
|
|
11511
|
-
|
|
11388
|
+
return pts
|
|
11389
|
+
}
|
|
11512
11390
|
|
|
11513
|
-
|
|
11514
|
-
cp1 = pointAtT([bezCurve[0], bezCurve[3]], 0.333);
|
|
11515
|
-
cp2 = pointAtT([bezCurve[0], bezCurve[3]], 0.666);
|
|
11516
|
-
bezCurve = [bezCurve[0], cp1, cp2, bezCurve[3]]
|
|
11517
|
-
*/
|
|
11391
|
+
/*
|
|
11518
11392
|
|
|
11519
|
-
|
|
11520
|
-
bezierNew = bezCurve;
|
|
11521
|
-
}
|
|
11393
|
+
*/
|
|
11522
11394
|
|
|
11523
|
-
|
|
11524
|
-
|
|
11395
|
+
// just for visualization
|
|
11396
|
+
function renderPolyTopology(pts, showTangents = true) {
|
|
11525
11397
|
|
|
11526
|
-
|
|
11527
|
-
* Creates a vector of length 1 which shows the direction from B to A
|
|
11528
|
-
*/
|
|
11529
|
-
function createTangent(p1, p2) {
|
|
11530
|
-
// Returns unit vector pointing from B to A
|
|
11531
|
-
let dx = p1.x - p2.x;
|
|
11532
|
-
let dy = p1.y - p2.y;
|
|
11533
|
-
let length = Math.sqrt(dx * dx + dy * dy);
|
|
11398
|
+
let l = pts.length;
|
|
11534
11399
|
|
|
11535
|
-
|
|
11536
|
-
|
|
11537
|
-
|
|
11400
|
+
// render
|
|
11401
|
+
for (let i = 0; i < l; i++) {
|
|
11402
|
+
i > 0 ? pts[i - 1] : pts[l - 1];
|
|
11403
|
+
let p1 = pts[i];
|
|
11404
|
+
i < l - 1 ? pts[i + 1] : pts[l - 1];
|
|
11538
11405
|
|
|
11539
|
-
|
|
11540
|
-
|
|
11541
|
-
|
|
11406
|
+
if (p1.isDirChange) {
|
|
11407
|
+
renderPoint(markers, p1, 'orange', '1%', '0.75');
|
|
11408
|
+
}
|
|
11542
11409
|
|
|
11543
|
-
|
|
11544
|
-
|
|
11545
|
-
|
|
11546
|
-
}
|
|
11410
|
+
if (p1.isSemiExtreme) {
|
|
11411
|
+
renderPoint(markers, p1, 'red', '1%', '0.5');
|
|
11412
|
+
}
|
|
11547
11413
|
|
|
11548
|
-
|
|
11549
|
-
|
|
11550
|
-
|
|
11414
|
+
/*
|
|
11415
|
+
if (p1.isLong && (p1.isDirChange || p1.isExtreme || p1.isCorner || p1.isSemiExtreme)) {
|
|
11416
|
+
renderPoint(markers, p1, 'green', '1.5%', '0.5')
|
|
11417
|
+
}
|
|
11418
|
+
*/
|
|
11551
11419
|
|
|
11552
|
-
|
|
11553
|
-
|
|
11554
|
-
|
|
11420
|
+
if (p1.isDirChange) {
|
|
11421
|
+
renderPoint(markers, p1, 'green', '1.5%', '0.5');
|
|
11422
|
+
}
|
|
11555
11423
|
|
|
11556
|
-
|
|
11557
|
-
|
|
11558
|
-
|
|
11559
|
-
}
|
|
11424
|
+
if (p1.isExtreme) {
|
|
11425
|
+
renderPoint(markers, p1, 'cyan', '1%', '0.5');
|
|
11426
|
+
}
|
|
11560
11427
|
|
|
11561
|
-
|
|
11562
|
-
|
|
11563
|
-
|
|
11428
|
+
if (p1.isHorizontal) {
|
|
11429
|
+
renderPoint(markers, p1, 'blue', '1.5%', '0.25');
|
|
11430
|
+
}
|
|
11431
|
+
|
|
11432
|
+
if (p1.isVertical) {
|
|
11433
|
+
renderPoint(markers, p1, 'purple', '1.5%', '0.25');
|
|
11434
|
+
}
|
|
11435
|
+
|
|
11436
|
+
if (p1.isCorner) {
|
|
11437
|
+
renderPoint(markers, p1, 'magenta', '1%', '1');
|
|
11438
|
+
}
|
|
11439
|
+
|
|
11440
|
+
if (showTangents && (p1.isCorner || p1.isSemiExtreme || p1.isDirChange || p1.isExtreme)) {
|
|
11441
|
+
renderPoint(markers, p1.tangentL, 'darkred', '0.5%');
|
|
11442
|
+
renderPoint(markers, p1.tangentR, 'darkblue', '0.5%');
|
|
11443
|
+
|
|
11444
|
+
/*
|
|
11445
|
+
if (p1.isDirChange) {
|
|
11446
|
+
renderPoint(markers, p1.tangentL, 'darkred', '1.5%')
|
|
11447
|
+
renderPoint(markers, p1.tangentR, 'darkblue', '1.5%')
|
|
11448
|
+
}
|
|
11449
|
+
*/
|
|
11450
|
+
|
|
11451
|
+
}
|
|
11564
11452
|
|
|
11565
|
-
function zeros_Xx2x2(x) {
|
|
11566
|
-
let zs = [];
|
|
11567
|
-
while (x--) {
|
|
11568
|
-
zs.push([0, 0]);
|
|
11569
11453
|
}
|
|
11570
|
-
|
|
11454
|
+
|
|
11571
11455
|
}
|
|
11572
11456
|
|
|
11573
|
-
|
|
11574
|
-
|
|
11575
|
-
|
|
11576
|
-
|
|
11577
|
-
|
|
11578
|
-
|
|
11579
|
-
|
|
11580
|
-
let
|
|
11457
|
+
function getPolyChunks(pts,
|
|
11458
|
+
{ closed = true,
|
|
11459
|
+
keepCorners = true,
|
|
11460
|
+
keepExtremes = true,
|
|
11461
|
+
keepInflections = false
|
|
11462
|
+
} = {}
|
|
11463
|
+
) {
|
|
11464
|
+
let chunks = [[pts[0]]];
|
|
11581
11465
|
|
|
11582
|
-
|
|
11583
|
-
|
|
11584
|
-
6 * t * (p1.x - 2 * cp2.x + cp1.x);
|
|
11466
|
+
let idx = 0;
|
|
11467
|
+
let lastChunk = chunks[idx];
|
|
11585
11468
|
|
|
11586
|
-
|
|
11587
|
-
6 * t * (p1.y - 2 * cp2.y + cp1.y);
|
|
11469
|
+
let l = pts.length;
|
|
11588
11470
|
|
|
11589
|
-
|
|
11590
|
-
|
|
11591
|
-
|
|
11592
|
-
|
|
11471
|
+
// render
|
|
11472
|
+
for (let i = 1; i < l; i++) {
|
|
11473
|
+
i > 0 ? pts[i] : pts[l - 1];
|
|
11474
|
+
let p1 = pts[i];
|
|
11475
|
+
i < l - 1 ? pts[i + 1] : pts[l - 1];
|
|
11593
11476
|
|
|
11594
|
-
|
|
11595
|
-
|
|
11596
|
-
|
|
11477
|
+
// start new chunk
|
|
11478
|
+
// keepInflections && p1.isDirChange
|
|
11479
|
+
if ((keepExtremes && p1.isExtreme || keepCorners && p1.isCorner ||
|
|
11480
|
+
(keepInflections && p1.isDirChange && !p1.isExtreme && !p1.isCorner )
|
|
11481
|
+
)) {
|
|
11482
|
+
idx++;
|
|
11483
|
+
chunks.push([]);
|
|
11484
|
+
}
|
|
11485
|
+
|
|
11486
|
+
lastChunk = chunks[idx];
|
|
11487
|
+
lastChunk.push(p1);
|
|
11597
11488
|
}
|
|
11598
11489
|
|
|
11599
|
-
|
|
11600
|
-
}
|
|
11490
|
+
// test render
|
|
11601
11491
|
|
|
11602
|
-
|
|
11603
|
-
let ang1 = getAngle(p0, p1);
|
|
11604
|
-
let ang2 = getAngle(p0, p2);
|
|
11605
|
-
let angDiff = (ang2 - ang1);
|
|
11606
|
-
cp = rotatePoint(cp, p0.x, p0.y, -angDiff);
|
|
11607
|
-
return cp
|
|
11492
|
+
return chunks;
|
|
11608
11493
|
}
|
|
11609
11494
|
|
|
11610
|
-
|
|
11611
|
-
|
|
11612
|
-
|
|
11613
|
-
beziers.forEach(bez => {
|
|
11614
|
-
|
|
11615
|
-
let type = bez.length === 4 ? 'C' : (bez.length === 3 ? 'Q' : 'L');
|
|
11495
|
+
function removeCoincidingVertices(pts = []) {
|
|
11496
|
+
let l = pts.length;
|
|
11497
|
+
if (!l) return pts;
|
|
11616
11498
|
|
|
11617
|
-
|
|
11618
|
-
|
|
11619
|
-
let p = bez[bez.length - 1];
|
|
11499
|
+
let ptsN = [pts[0]];
|
|
11500
|
+
let pt1, pt2;
|
|
11620
11501
|
|
|
11621
|
-
|
|
11622
|
-
|
|
11623
|
-
|
|
11624
|
-
[cp1.x, cp1.y, p.x, p.y] :
|
|
11625
|
-
[p.x, p.y]
|
|
11626
|
-
);
|
|
11502
|
+
for (let i = 1; i < l; i++) {
|
|
11503
|
+
pt1 = pts[i - 1];
|
|
11504
|
+
pt2 = pts[i];
|
|
11627
11505
|
|
|
11628
|
-
|
|
11629
|
-
|
|
11630
|
-
|
|
11506
|
+
/**
|
|
11507
|
+
* 1. Skip zero-length segments
|
|
11508
|
+
*/
|
|
11509
|
+
if (pt1.x === pt2.x && pt1.y === pt2.y) {
|
|
11510
|
+
continue;
|
|
11511
|
+
}
|
|
11512
|
+
ptsN.push(pt2);
|
|
11513
|
+
}
|
|
11514
|
+
return ptsN
|
|
11631
11515
|
|
|
11632
|
-
return pathData
|
|
11633
11516
|
}
|
|
11634
11517
|
|
|
11635
|
-
function simplifyRC(pts, quality = 1, shiftStart = true) {
|
|
11636
|
-
|
|
11637
|
-
if (pts.length < 4) return pts;
|
|
11518
|
+
function simplifyRC(pts = [], quality = 1, shiftStart = true) {
|
|
11638
11519
|
|
|
11639
11520
|
let l = pts.length;
|
|
11521
|
+
if (l < 4) return pts;
|
|
11640
11522
|
|
|
11641
11523
|
// starting point
|
|
11642
11524
|
let M = pts[0];
|
|
@@ -11701,7 +11583,7 @@
|
|
|
11701
11583
|
let thresh = getSquareDistance(pt0, pt2) * 0.005;
|
|
11702
11584
|
|
|
11703
11585
|
// flat
|
|
11704
|
-
if (
|
|
11586
|
+
if (area <= thresh && i < l - 1) {
|
|
11705
11587
|
|
|
11706
11588
|
pt0 = pt1;
|
|
11707
11589
|
continue
|
|
@@ -11722,10 +11604,10 @@
|
|
|
11722
11604
|
}
|
|
11723
11605
|
|
|
11724
11606
|
// 1st and last are colinear
|
|
11725
|
-
let area0 = getPolygonArea([ptsSmp[1], M, ptsSmp[ptsSmp.length-1]], true);
|
|
11726
|
-
let thresh0 = getSquareDistance
|
|
11607
|
+
let area0 = getPolygonArea([ptsSmp[1], M, ptsSmp[ptsSmp.length - 1]], true);
|
|
11608
|
+
let thresh0 = getSquareDistance(ptsSmp[1], ptsSmp[ptsSmp.length - 1]) * 0.005;
|
|
11727
11609
|
// remove first point
|
|
11728
|
-
if(area0 < thresh0) ptsSmp.shift();
|
|
11610
|
+
if (area0 < thresh0) ptsSmp.shift();
|
|
11729
11611
|
|
|
11730
11612
|
return ptsSmp;
|
|
11731
11613
|
}
|
|
@@ -11744,6 +11626,7 @@
|
|
|
11744
11626
|
tolerance = 1,
|
|
11745
11627
|
simplifyRD = 1,
|
|
11746
11628
|
simplifyRDP = 1,
|
|
11629
|
+
isClosed = true,
|
|
11747
11630
|
} = {}) {
|
|
11748
11631
|
|
|
11749
11632
|
let polyPath = [];
|
|
@@ -11821,6 +11704,25 @@
|
|
|
11821
11704
|
|
|
11822
11705
|
// remove colinear
|
|
11823
11706
|
|
|
11707
|
+
keepExtremes = false;
|
|
11708
|
+
keepCorners = false;
|
|
11709
|
+
|
|
11710
|
+
keepExtremes = true;
|
|
11711
|
+
keepCorners = true;
|
|
11712
|
+
|
|
11713
|
+
// check if closed
|
|
11714
|
+
/*
|
|
11715
|
+
let bb = getPolyBBox(pts)
|
|
11716
|
+
let thresh = (bb.width+bb.height)*0.25
|
|
11717
|
+
let dist0 = getDistManhattan(pts[0], pts[pts.length-1])
|
|
11718
|
+
|
|
11719
|
+
*/
|
|
11720
|
+
|
|
11721
|
+
// copy 1st first to end
|
|
11722
|
+
if (isClosed) {
|
|
11723
|
+
pts.push(pts[0]);
|
|
11724
|
+
}
|
|
11725
|
+
|
|
11824
11726
|
// get topology of poly
|
|
11825
11727
|
let polyAnalyzed = !keepExtremes && !keepCorners ? pts : analyzePoly(pts, {
|
|
11826
11728
|
debug: false
|
|
@@ -11828,69 +11730,369 @@
|
|
|
11828
11730
|
});
|
|
11829
11731
|
|
|
11830
11732
|
// split into segment chunks
|
|
11831
|
-
|
|
11733
|
+
|
|
11734
|
+
let chunks = getPolyChunks(polyAnalyzed, { keepCorners, keepExtremes, keepInflections: true });
|
|
11832
11735
|
|
|
11833
11736
|
// Schneider curve fit
|
|
11834
11737
|
let threshold = width && height ? (width + height) / 2 * 0.004 * tolerance : 2.5;
|
|
11738
|
+
threshold = width && height ? (width + height) / 2 * 0.004 * tolerance : 2.5;
|
|
11835
11739
|
|
|
11836
|
-
|
|
11837
|
-
closed,
|
|
11838
|
-
tolerance: threshold,
|
|
11839
|
-
keepCorners,
|
|
11840
|
-
keepExtremes: true,
|
|
11841
|
-
});
|
|
11740
|
+
{
|
|
11842
11741
|
|
|
11843
|
-
|
|
11742
|
+
polyPath = simplifyPolyChunksTopology(chunks, {
|
|
11743
|
+
closed,
|
|
11744
|
+
tolerance: threshold,
|
|
11745
|
+
keepCorners,
|
|
11746
|
+
keepExtremes: true,
|
|
11747
|
+
});
|
|
11748
|
+
}
|
|
11844
11749
|
|
|
11845
11750
|
return polyPath;
|
|
11846
11751
|
}
|
|
11847
11752
|
|
|
11848
11753
|
/**
|
|
11849
|
-
*
|
|
11850
|
-
* to cubic beziers
|
|
11754
|
+
* topology based curve fit
|
|
11851
11755
|
*/
|
|
11852
|
-
|
|
11853
|
-
function simplifyPolyChunks(chunks = [], {
|
|
11756
|
+
function simplifyPolyChunksTopology(chunks = [], {
|
|
11854
11757
|
closed = true,
|
|
11855
11758
|
keepCorners = true,
|
|
11856
11759
|
tolerance = 1,
|
|
11857
11760
|
} = {}) {
|
|
11858
11761
|
|
|
11762
|
+
console.log(chunks);
|
|
11763
|
+
|
|
11859
11764
|
let l = chunks.length;
|
|
11860
11765
|
|
|
11861
11766
|
// new pathData
|
|
11767
|
+
|
|
11862
11768
|
let pathData = [{ type: 'M', values: [chunks[0][0].x, chunks[0][0].y] }];
|
|
11863
11769
|
|
|
11770
|
+
// loop chunks
|
|
11864
11771
|
for (let i = 0; i < l; i++) {
|
|
11865
11772
|
|
|
11773
|
+
let chunkPrev = i > 0 ? chunks[i - 1] : (closed ? chunks[l - 1] : null);
|
|
11866
11774
|
let chunk = chunks[i];
|
|
11867
|
-
let chunkN = chunks[i + 1] ? chunks[i + 1] : null;
|
|
11775
|
+
let chunkN = chunks[i + 1] ? chunks[i + 1] : (closed ? chunks[0] : null);
|
|
11868
11776
|
let segments = [];
|
|
11869
|
-
let chunklen = chunk.length;
|
|
11870
|
-
chunk[chunk.length - 1];
|
|
11871
11777
|
|
|
11872
11778
|
// add from next command
|
|
11873
11779
|
if (chunkN) {
|
|
11874
11780
|
chunk.push(chunkN[0]);
|
|
11875
11781
|
}
|
|
11876
11782
|
|
|
11877
|
-
|
|
11878
|
-
|
|
11879
|
-
|
|
11880
|
-
|
|
11783
|
+
let chunklen = chunk.length;
|
|
11784
|
+
let hasInflection = false;
|
|
11785
|
+
let segments_1 = [], segments_2 = [], segments_3 = [];
|
|
11786
|
+
let segsRequired = 3;
|
|
11787
|
+
|
|
11788
|
+
// 1st point
|
|
11789
|
+
let p1 = chunk[0];
|
|
11790
|
+
// last point in chunk
|
|
11791
|
+
let p2 = chunk[chunklen - 1];
|
|
11792
|
+
|
|
11793
|
+
// nothing to simplify - lineto
|
|
11794
|
+
|
|
11795
|
+
// if (chunklen < 2 || (chunklen === 2 && (chunk[1].isExtreme || i===l-1 && !closed) )) {
|
|
11796
|
+
|
|
11797
|
+
if (chunklen < 2 || (chunklen === 2 && (chunk[1].isExtreme))) {
|
|
11798
|
+
|
|
11799
|
+
if (chunklen === 2) {
|
|
11800
|
+
segsRequired = 2;
|
|
11801
|
+
segments_2 = [
|
|
11802
|
+
{
|
|
11803
|
+
type: 'L',
|
|
11804
|
+
values: [p1.x, p1.y],
|
|
11805
|
+
p0: chunkPrev,
|
|
11806
|
+
p: p1,
|
|
11807
|
+
},
|
|
11808
|
+
{
|
|
11809
|
+
type: 'L',
|
|
11810
|
+
values: [chunk[1].x, chunk[1].y],
|
|
11811
|
+
p0: p1,
|
|
11812
|
+
p: p2,
|
|
11813
|
+
}
|
|
11814
|
+
];
|
|
11815
|
+
} else {
|
|
11816
|
+
segsRequired = 1;
|
|
11817
|
+
segments_1 = [
|
|
11818
|
+
{
|
|
11819
|
+
type: 'L',
|
|
11820
|
+
values: [p1.x, p1.y],
|
|
11821
|
+
p0: chunkPrev,
|
|
11822
|
+
p: p2,
|
|
11823
|
+
},
|
|
11824
|
+
];
|
|
11825
|
+
}
|
|
11881
11826
|
|
|
11882
11827
|
} else {
|
|
11883
|
-
|
|
11884
|
-
|
|
11885
|
-
|
|
11828
|
+
|
|
11829
|
+
// point before inflection
|
|
11830
|
+
let p3 = chunk[chunklen - 2];
|
|
11831
|
+
|
|
11832
|
+
chunk.filter(pt => pt.isExtreme);
|
|
11833
|
+
let semiExtremes = chunk.filter(pt => pt.isSemiExtreme);
|
|
11834
|
+
chunk.filter(pt => pt.isCorner);
|
|
11835
|
+
let inflections = chunk.filter(pt => pt.isDirChange && !pt.isCorner && !pt.isExtreme);
|
|
11836
|
+
hasInflection = inflections.length && inflections[0] !== p1;
|
|
11837
|
+
|
|
11838
|
+
let idxMid = Math.floor(chunklen * 0.5);
|
|
11839
|
+
let pMid = semiExtremes.length ? semiExtremes[Math.floor(semiExtremes.length * 0.5)] : chunk[idxMid];
|
|
11840
|
+
|
|
11841
|
+
let dist0 = getDistManhattan(p1, p3);
|
|
11842
|
+
let dist1 = getDistManhattan(pMid, p3);
|
|
11843
|
+
let dist2 = getDistManhattan(pMid, p1);
|
|
11844
|
+
let thresh = dist0 * 0.25;
|
|
11845
|
+
let shortMidSegment = dist1 < thresh || dist2 < thresh;
|
|
11846
|
+
|
|
11847
|
+
/**
|
|
11848
|
+
* we have 3 modes
|
|
11849
|
+
* 1 segment: only 1 segment between extremes/corners
|
|
11850
|
+
* 2 segments: semiextreme/mid in between
|
|
11851
|
+
* 3 segments: inflection
|
|
11852
|
+
*/
|
|
11853
|
+
|
|
11854
|
+
segsRequired = shortMidSegment ? (!hasInflection ? 1 : 2) : (hasInflection ? 3 : 2);
|
|
11855
|
+
|
|
11856
|
+
let cp1_1 = p1.tangentR;
|
|
11857
|
+
let cp2_1 = pMid.tangentL;
|
|
11858
|
+
let p_1 = pMid;
|
|
11859
|
+
|
|
11860
|
+
// renderPoint(markers, pMid, 'orange', '2%')
|
|
11861
|
+
|
|
11862
|
+
let cp2_2 = p2.tangentL;
|
|
11863
|
+
let cp1_2 = pMid.tangentR;
|
|
11864
|
+
let p_2 = p2;
|
|
11865
|
+
|
|
11866
|
+
let cp1_3 = null;
|
|
11867
|
+
let cp2_3 = null;
|
|
11868
|
+
let p_3 = null;
|
|
11869
|
+
|
|
11870
|
+
// general extrapolation
|
|
11871
|
+
let t = 0.666;
|
|
11872
|
+
let ptI_1 = null, ptI_2 = null, ptI_3 = null;
|
|
11873
|
+
|
|
11874
|
+
// 1 segment
|
|
11875
|
+
ptI_1 = checkLineIntersection(p1, p1.tangentR, p2, p2.tangentL, false, true);
|
|
11876
|
+
if (ptI_1) {
|
|
11877
|
+
cp1_1 = interpolate(p1, ptI_1, t);
|
|
11878
|
+
cp2_1 = interpolate(p2, ptI_1, t);
|
|
11879
|
+
p_1 = p2;
|
|
11880
|
+
|
|
11881
|
+
segments_1 = [
|
|
11882
|
+
|
|
11883
|
+
{
|
|
11884
|
+
type: 'C',
|
|
11885
|
+
values: [cp1_1.x, cp1_1.y, cp2_1.x, cp2_1.y, p_1.x, p_1.y],
|
|
11886
|
+
p0: p1,
|
|
11887
|
+
cp1: cp1_1,
|
|
11888
|
+
cp2: cp2_1,
|
|
11889
|
+
p: p_1,
|
|
11890
|
+
}
|
|
11891
|
+
];
|
|
11892
|
+
|
|
11893
|
+
}
|
|
11894
|
+
|
|
11895
|
+
// 2 segments
|
|
11896
|
+
ptI_1 = checkLineIntersection(p1, p1.tangentR, pMid, pMid.tangentL, false, true);
|
|
11897
|
+
ptI_2 = checkLineIntersection(p2, p2.tangentL, pMid, pMid.tangentR, false, true);
|
|
11898
|
+
|
|
11899
|
+
if (ptI_1 && ptI_2) {
|
|
11900
|
+
cp1_1 = interpolate(p1, ptI_1, t);
|
|
11901
|
+
cp2_1 = interpolate(pMid, ptI_1, t);
|
|
11902
|
+
p_1 = pMid;
|
|
11903
|
+
|
|
11904
|
+
cp1_2 = interpolate(pMid, ptI_2, t);
|
|
11905
|
+
cp2_2 = interpolate(p2, ptI_2, t);
|
|
11906
|
+
p_2 = p2;
|
|
11907
|
+
|
|
11908
|
+
segments_2 = [
|
|
11909
|
+
|
|
11910
|
+
{
|
|
11911
|
+
type: 'C',
|
|
11912
|
+
values: [cp1_1.x, cp1_1.y, cp2_1.x, cp2_1.y, p_1.x, p_1.y],
|
|
11913
|
+
p0: p1,
|
|
11914
|
+
cp1: cp1_1,
|
|
11915
|
+
cp2: cp2_1,
|
|
11916
|
+
p: p_1,
|
|
11917
|
+
isExtreme: p_1.isExtreme
|
|
11918
|
+
},
|
|
11919
|
+
{
|
|
11920
|
+
type: 'C',
|
|
11921
|
+
values: [cp1_2.x, cp1_2.y, cp2_2.x, cp2_2.y, p_2.x, p_2.y],
|
|
11922
|
+
p0: p_1,
|
|
11923
|
+
cp1: cp1_2,
|
|
11924
|
+
cp2: cp2_2,
|
|
11925
|
+
p: p_2,
|
|
11926
|
+
isExtreme: p_2.isExtreme
|
|
11927
|
+
|
|
11928
|
+
},
|
|
11929
|
+
];
|
|
11930
|
+
|
|
11931
|
+
}
|
|
11932
|
+
|
|
11933
|
+
// 3 segments
|
|
11934
|
+
|
|
11935
|
+
if (hasInflection) {
|
|
11936
|
+
|
|
11937
|
+
// get pt between dir change and mid
|
|
11938
|
+
let idx_3_4 = Math.floor(chunklen * 0.75);
|
|
11939
|
+
p3 = chunk[idx_3_4];
|
|
11940
|
+
ptI_3 = checkLineIntersection(p3, p3.tangentR, p2, p2.tangentL, false, false);
|
|
11941
|
+
|
|
11942
|
+
if (ptI_3) {
|
|
11943
|
+
let tangentR_beforeDirChange = interpolate(p3, ptI_3, t);
|
|
11944
|
+
|
|
11945
|
+
// extend right tangent
|
|
11946
|
+
p3.tangentR.x = tangentR_beforeDirChange.x;
|
|
11947
|
+
p3.tangentR.y = tangentR_beforeDirChange.y;
|
|
11948
|
+
|
|
11949
|
+
// extend dir change tangent
|
|
11950
|
+
let tangentL_dirChange = interpolate(p2, ptI_3, t);
|
|
11951
|
+
p2.tangentL.x = tangentL_dirChange.x;
|
|
11952
|
+
p2.tangentL.y = tangentL_dirChange.y;
|
|
11953
|
+
} else {
|
|
11954
|
+
|
|
11955
|
+
if (p3 === p2) {
|
|
11956
|
+
|
|
11957
|
+
idx_3_4 = Math.floor(chunklen * 0.3);
|
|
11958
|
+
p3 = chunk[idx_3_4];
|
|
11959
|
+
|
|
11960
|
+
}
|
|
11961
|
+
checkLineIntersection(p3, p3.tangentR, p2, p2.tangentL, false, false);
|
|
11962
|
+
|
|
11963
|
+
cp1_1 = interpolate(p1, p1.tangentR, 1.333);
|
|
11964
|
+
cp2_1 = interpolate(p2, p2.tangentL, 1.333);
|
|
11965
|
+
|
|
11966
|
+
segments_3 = [
|
|
11967
|
+
{
|
|
11968
|
+
type: 'C',
|
|
11969
|
+
values: [cp1_1.x, cp1_1.y, cp2_1.x, cp2_1.y, p2.x, p2.y],
|
|
11970
|
+
p0: p1,
|
|
11971
|
+
cp1: cp1_1,
|
|
11972
|
+
cp2: cp2_1,
|
|
11973
|
+
p: p2,
|
|
11974
|
+
isExtreme: p2.isExtreme
|
|
11975
|
+
|
|
11976
|
+
},
|
|
11977
|
+
];
|
|
11978
|
+
|
|
11979
|
+
/*
|
|
11980
|
+
let tangentR_beforeDirChange = interpolate(p3, ptI_3, t)
|
|
11981
|
+
|
|
11982
|
+
// extend right tangent
|
|
11983
|
+
p3.tangentR.x = tangentR_beforeDirChange.x
|
|
11984
|
+
p3.tangentR.y = tangentR_beforeDirChange.y
|
|
11985
|
+
|
|
11986
|
+
let tangentL_dirChange = interpolate(p2, ptI_3, t)
|
|
11987
|
+
p2.tangentL.x = tangentL_dirChange.x
|
|
11988
|
+
p2.tangentL.y = tangentL_dirChange.y
|
|
11989
|
+
*/
|
|
11990
|
+
|
|
11991
|
+
pathDataToD([{ type: 'M', values: [p1.x, p1.y] }, ...segments_3]);
|
|
11992
|
+
|
|
11993
|
+
}
|
|
11994
|
+
|
|
11995
|
+
cp1_3 = p3.tangentR;
|
|
11996
|
+
cp2_3 = p2.tangentL;
|
|
11997
|
+
p_3 = p2;
|
|
11998
|
+
|
|
11999
|
+
ptI_1 = checkLineIntersection(p1, p1.tangentR, pMid, pMid.tangentL, false, true);
|
|
12000
|
+
ptI_2 = checkLineIntersection(pMid, pMid.tangentR, p3, p3.tangentL, false, true);
|
|
12001
|
+
|
|
12002
|
+
if (ptI_1 && ptI_2 && ptI_3) {
|
|
12003
|
+
|
|
12004
|
+
cp1_1 = interpolate(p1, ptI_1, t);
|
|
12005
|
+
cp2_1 = interpolate(pMid, ptI_1, t);
|
|
12006
|
+
p_1 = pMid;
|
|
12007
|
+
|
|
12008
|
+
cp1_2 = interpolate(pMid, ptI_2, t);
|
|
12009
|
+
cp2_2 = interpolate(p3, ptI_2, t);
|
|
12010
|
+
p_2 = p3;
|
|
12011
|
+
|
|
12012
|
+
segments_3 = [
|
|
12013
|
+
{
|
|
12014
|
+
type: 'C',
|
|
12015
|
+
values: [cp1_1.x, cp1_1.y, cp2_1.x, cp2_1.y, p_1.x, p_1.y],
|
|
12016
|
+
p0: p1,
|
|
12017
|
+
cp1: cp1_1,
|
|
12018
|
+
cp2: cp2_1,
|
|
12019
|
+
p: p_1,
|
|
12020
|
+
isExtreme: p_1.isExtreme
|
|
12021
|
+
|
|
12022
|
+
},
|
|
12023
|
+
{
|
|
12024
|
+
type: 'C',
|
|
12025
|
+
values: [cp1_2.x, cp1_2.y, cp2_2.x, cp2_2.y, p_2.x, p_2.y],
|
|
12026
|
+
p0: p_1,
|
|
12027
|
+
cp1: cp1_2,
|
|
12028
|
+
cp2: cp2_2,
|
|
12029
|
+
p: p_2,
|
|
12030
|
+
},
|
|
12031
|
+
{
|
|
12032
|
+
type: 'C',
|
|
12033
|
+
values: [cp1_3.x, cp1_3.y, cp2_3.x, cp2_3.y, p_3.x, p_3.y],
|
|
12034
|
+
p0: p_2,
|
|
12035
|
+
cp1: cp1_3,
|
|
12036
|
+
cp2: cp2_3,
|
|
12037
|
+
p: p_3,
|
|
12038
|
+
isExtreme: p_3.isExtreme
|
|
12039
|
+
|
|
12040
|
+
}
|
|
12041
|
+
];
|
|
12042
|
+
|
|
12043
|
+
pathDataToD([{ type: 'M', values: [p1.x, p1.y] }, ...segments_3]);
|
|
12044
|
+
|
|
12045
|
+
}
|
|
12046
|
+
|
|
12047
|
+
}
|
|
12048
|
+
|
|
12049
|
+
}
|
|
12050
|
+
|
|
12051
|
+
if (segsRequired === 1) {
|
|
12052
|
+
segments = segments_1;
|
|
12053
|
+
} else if (segsRequired === 2 && segments_2.length) {
|
|
12054
|
+
segments = segments_2;
|
|
12055
|
+
}
|
|
12056
|
+
else ;
|
|
12057
|
+
|
|
12058
|
+
segments = segments_3.length ? segments_3 : segments_2;
|
|
12059
|
+
/*
|
|
12060
|
+
if (simplify && !isLinetoSeg && segments.length > 1) {
|
|
12061
|
+
|
|
12062
|
+
let com1 = segments[0]
|
|
12063
|
+
let com2 = segments[1]
|
|
12064
|
+
|
|
12065
|
+
tolerance = 1.1
|
|
12066
|
+
let combined = combineCubicPairs(com1, com2, { tolerance })
|
|
12067
|
+
|
|
12068
|
+
let error = 0;
|
|
12069
|
+
let comsSimp =[]
|
|
12070
|
+
|
|
12071
|
+
console.log('!!!combined', segments.length, combined);
|
|
12072
|
+
|
|
12073
|
+
// success
|
|
12074
|
+
if (combined.length === 1) {
|
|
12075
|
+
|
|
12076
|
+
if(segments.length === 2){
|
|
12077
|
+
segments = combined
|
|
12078
|
+
}
|
|
12079
|
+
|
|
12080
|
+
let com = combined[0]
|
|
12081
|
+
}
|
|
12082
|
+
|
|
11886
12083
|
}
|
|
12084
|
+
*/
|
|
11887
12085
|
|
|
11888
12086
|
// remove first segment to connect to last segment
|
|
11889
12087
|
pathData.push(...segments);
|
|
11890
12088
|
|
|
11891
12089
|
}
|
|
11892
12090
|
|
|
11893
|
-
if (closed)
|
|
12091
|
+
if (closed) {
|
|
12092
|
+
pathData.push({ type: 'Z', values: [] });
|
|
12093
|
+
}
|
|
12094
|
+
|
|
12095
|
+
// refine extremes
|
|
11894
12096
|
return pathData
|
|
11895
12097
|
|
|
11896
12098
|
}
|
|
@@ -11901,13 +12103,18 @@
|
|
|
11901
12103
|
*/
|
|
11902
12104
|
function pathDataToPolygonOpt(pathData, {
|
|
11903
12105
|
precisionPoly = 1,
|
|
11904
|
-
autoAccuracy=false,
|
|
11905
|
-
polyFormat='
|
|
11906
|
-
decimals
|
|
11907
|
-
simplifyRD=1,
|
|
11908
|
-
|
|
12106
|
+
autoAccuracy = false,
|
|
12107
|
+
polyFormat = 'object',
|
|
12108
|
+
decimals = -1,
|
|
12109
|
+
simplifyRD = 1,
|
|
12110
|
+
simplifyRDP = 1,
|
|
11909
12111
|
} = {}) {
|
|
11910
12112
|
|
|
12113
|
+
pathData = convertPathData(pathData, {toAbsolute:true, toLonghands:true, arcToCubic:true});
|
|
12114
|
+
pathData = addExtremePoints(pathData);
|
|
12115
|
+
|
|
12116
|
+
pathData = getPathDataVerbose(pathData);
|
|
12117
|
+
|
|
11911
12118
|
let l = pathData.length;
|
|
11912
12119
|
let M = { x: pathData[0].values[0], y: pathData[0].values[1] };
|
|
11913
12120
|
let p0 = M;
|
|
@@ -11934,7 +12141,7 @@
|
|
|
11934
12141
|
let pts2 = [pts[0]];
|
|
11935
12142
|
|
|
11936
12143
|
// adjustments for very small or large paths
|
|
11937
|
-
dims = dims.filter(Boolean).sort();
|
|
12144
|
+
dims = dims.filter(Boolean).sort((a,b)=>a-b);
|
|
11938
12145
|
let dimMax = dims[dims.length - 1];
|
|
11939
12146
|
|
|
11940
12147
|
let scale = dimMax > 2 && dimMax < 25 ? 1 : (20 / dimMax);
|
|
@@ -11973,31 +12180,33 @@
|
|
|
11973
12180
|
}
|
|
11974
12181
|
|
|
11975
12182
|
// simplify polygon
|
|
11976
|
-
if(simplifyRD>0){
|
|
11977
|
-
pts2 = simplifyPolyRD(pts2, {quality:simplifyRD});
|
|
12183
|
+
if (simplifyRD > 0) {
|
|
12184
|
+
pts2 = simplifyPolyRD(pts2, { quality: simplifyRD });
|
|
11978
12185
|
}
|
|
11979
12186
|
|
|
11980
|
-
if(simplifyRDP>0){
|
|
11981
|
-
pts2 = simplifyPolyRDP(pts2, {quality:simplifyRDP});
|
|
12187
|
+
if (simplifyRDP > 0) {
|
|
12188
|
+
pts2 = simplifyPolyRDP(pts2, { quality: simplifyRDP });
|
|
11982
12189
|
}
|
|
11983
12190
|
|
|
11984
|
-
|
|
11985
|
-
pathData = pathDataPoly;
|
|
11986
|
-
|
|
11987
|
-
if(autoAccuracy){
|
|
12191
|
+
if (autoAccuracy) {
|
|
11988
12192
|
decimals = detectAccuracyPoly(pts);
|
|
11989
12193
|
}
|
|
11990
12194
|
|
|
11991
|
-
let poly = decimals
|
|
12195
|
+
let poly = decimals > -1 ? pts2.map(pt => { return { x: roundTo(pt.x, decimals), y: roundTo(pt.y, decimals) } }) : pts2.map(pt => { return { x: pt.x, y: pt.y } });
|
|
11992
12196
|
|
|
11993
|
-
|
|
12197
|
+
pathDataPoly = pathDataFromPoly(poly);
|
|
12198
|
+
pathData = pathDataPoly;
|
|
12199
|
+
|
|
12200
|
+
if (polyFormat === 'array') {
|
|
11994
12201
|
poly = poly.map(pt => { return [pt.x, pt.y] });
|
|
11995
12202
|
}
|
|
11996
|
-
else if(polyFormat==='string'){
|
|
12203
|
+
else if (polyFormat === 'string') {
|
|
11997
12204
|
poly = poly.map(pt => { return [pt.x, pt.y].join(',') }).flat().join(' ');
|
|
11998
12205
|
}
|
|
11999
12206
|
|
|
12000
|
-
|
|
12207
|
+
let d= pathDataToD(pathData);
|
|
12208
|
+
|
|
12209
|
+
return { pathData, poly, d }
|
|
12001
12210
|
|
|
12002
12211
|
}
|
|
12003
12212
|
|
|
@@ -12238,6 +12447,7 @@
|
|
|
12238
12447
|
// polygon
|
|
12239
12448
|
toPolygon: false,
|
|
12240
12449
|
smoothPoly: false,
|
|
12450
|
+
isClosed:true,
|
|
12241
12451
|
polyFormat: 'object',
|
|
12242
12452
|
precisionPoly: 1,
|
|
12243
12453
|
simplifyRD: 0,
|
|
@@ -12534,6 +12744,122 @@
|
|
|
12534
12744
|
}
|
|
12535
12745
|
*/
|
|
12536
12746
|
|
|
12747
|
+
function SlickVGObj(props = {}) {
|
|
12748
|
+
|
|
12749
|
+
Object.assign(this, props);
|
|
12750
|
+
}
|
|
12751
|
+
|
|
12752
|
+
SlickVGObj.prototype.getD = function () {
|
|
12753
|
+
let d = this.d;
|
|
12754
|
+
return d;
|
|
12755
|
+
};
|
|
12756
|
+
|
|
12757
|
+
SlickVGObj.prototype.getSvg = function () {
|
|
12758
|
+
let svg = this.svg;
|
|
12759
|
+
|
|
12760
|
+
if (!svg) {
|
|
12761
|
+
let xArr = [];
|
|
12762
|
+
let yArr = [];
|
|
12763
|
+
let d = this.d;
|
|
12764
|
+
let pathDataPlusArr = this.pathDataPlusArr || [];
|
|
12765
|
+
pathDataPlusArr.forEach(path => {
|
|
12766
|
+
path.forEach(sub => {
|
|
12767
|
+
let { pathData } = sub;
|
|
12768
|
+
let bb = getPathDataBBox(pathData);
|
|
12769
|
+
let { x, y, right, bottom } = bb;
|
|
12770
|
+
xArr.push(x, right);
|
|
12771
|
+
yArr.push(y, bottom);
|
|
12772
|
+
});
|
|
12773
|
+
});
|
|
12774
|
+
|
|
12775
|
+
let x = Math.min(...xArr);
|
|
12776
|
+
let right = Math.max(...xArr);
|
|
12777
|
+
let y = Math.min(...yArr);
|
|
12778
|
+
let bottom = Math.max(...yArr);
|
|
12779
|
+
let width = right - x;
|
|
12780
|
+
let height = bottom - y;
|
|
12781
|
+
|
|
12782
|
+
svg = `<svg xmlns="${svgNs}" viewBox="${[x, y, width, height].join(' ')}"><path d="${d}"/></svg>`;
|
|
12783
|
+
|
|
12784
|
+
}
|
|
12785
|
+
return svg;
|
|
12786
|
+
};
|
|
12787
|
+
|
|
12788
|
+
/**
|
|
12789
|
+
* retrieve poly
|
|
12790
|
+
* formats: points, array, string, pathData, d,
|
|
12791
|
+
*/
|
|
12792
|
+
SlickVGObj.prototype.getPoly = function (options = {}
|
|
12793
|
+
) {
|
|
12794
|
+
|
|
12795
|
+
options = {
|
|
12796
|
+
...{
|
|
12797
|
+
precisionPoly: 1,
|
|
12798
|
+
simplifyRDP: 0,
|
|
12799
|
+
simplifyRD: 0,
|
|
12800
|
+
autoAccuracy: true,
|
|
12801
|
+
decimals: 3,
|
|
12802
|
+
format: 'object'
|
|
12803
|
+
},
|
|
12804
|
+
...options
|
|
12805
|
+
};
|
|
12806
|
+
|
|
12807
|
+
let { precisionPoly, simplifyRDP, simplifyRD, autoAccuracy, decimals, format } = options;
|
|
12808
|
+
|
|
12809
|
+
let polyFormat = format;
|
|
12810
|
+
|
|
12811
|
+
let polys = this.polys;
|
|
12812
|
+
if (!polys.length) {
|
|
12813
|
+
let pathDataPlusArr = this.pathDataPlusArr || [];
|
|
12814
|
+
let poly = [];
|
|
12815
|
+
let polyPaths = [];
|
|
12816
|
+
let dPoly = '';
|
|
12817
|
+
pathDataPlusArr.forEach(path => {
|
|
12818
|
+
path.forEach(sub => {
|
|
12819
|
+
let { pathData } = sub;
|
|
12820
|
+
|
|
12821
|
+
let polyData = pathDataToPolygonOpt(pathData, {
|
|
12822
|
+
precisionPoly,
|
|
12823
|
+
autoAccuracy,
|
|
12824
|
+
decimals,
|
|
12825
|
+
simplifyRD,
|
|
12826
|
+
simplifyRDP,
|
|
12827
|
+
polyFormat
|
|
12828
|
+
});
|
|
12829
|
+
|
|
12830
|
+
dPoly += polyData.d;
|
|
12831
|
+
poly.push(polyData.poly);
|
|
12832
|
+
polyPaths.push(polyData.pathData);
|
|
12833
|
+
|
|
12834
|
+
});
|
|
12835
|
+
});
|
|
12836
|
+
|
|
12837
|
+
if (polyFormat === 'object' || polyFormat === 'array' || polyFormat === 'string') {
|
|
12838
|
+
polys = poly;
|
|
12839
|
+
}
|
|
12840
|
+
else if (polyFormat === 'pathData') {
|
|
12841
|
+
polys = polyPaths.flat();
|
|
12842
|
+
}
|
|
12843
|
+
|
|
12844
|
+
else if (polyFormat === 'd') {
|
|
12845
|
+
polys = dPoly;
|
|
12846
|
+
}
|
|
12847
|
+
|
|
12848
|
+
}
|
|
12849
|
+
return polys;
|
|
12850
|
+
};
|
|
12851
|
+
|
|
12852
|
+
/*
|
|
12853
|
+
export function PathLengthObject(props = {}) {
|
|
12854
|
+
Object.assign(this, props);
|
|
12855
|
+
}
|
|
12856
|
+
*/
|
|
12857
|
+
|
|
12858
|
+
function SlickVG(input = '', settings = {}) {
|
|
12859
|
+
settings.getObject = true;
|
|
12860
|
+
return svgPathSimplify(input, settings)
|
|
12861
|
+
}
|
|
12862
|
+
|
|
12537
12863
|
function svgPathSimplify(input = '', settings = {}) {
|
|
12538
12864
|
|
|
12539
12865
|
let preset = settings['preset'] !== undefined && settings['preset'] ? settings['preset'] : null;
|
|
@@ -12545,7 +12871,7 @@
|
|
|
12545
12871
|
...settings
|
|
12546
12872
|
};
|
|
12547
12873
|
|
|
12548
|
-
let { getObject = false, removeComments, removeOffCanvas, unGroup, mergePaths, removeElements, removeDimensions, removeIds, removeClassNames, omitNamespace, cleanUpStrokes, addViewBox, addDimensions, removePrologue, removeHidden, removeUnused, cleanupDefs, cleanupClip, cleanupSVGAtts, removeNameSpaced, removeNameSpacedAtts, attributesToGroup, minifyRgbColors, stylesToAttributes, fixHref, legacyHref, allowMeta, allowDataAtts, allowAriaAtts, removeSVGAttributes, removeElAttributes, shapesToPaths, shapeConvert, convertShapes, simplifyBezier, optimizeOrder, autoClose, removeZeroLength, refineClosing, removeColinear, flatBezierToLinetos, revertToQuadratics, refineExtremes, simplifyCorners, fixDirections, keepExtremes, keepCorners, keepInflections, addExtremes, reversePath, toAbsolute, toRelative, toMixed, toShorthands, toLonghands, quadraticToCubic, arcToCubic, cubicToArc, lineToCubic, decimals, autoAccuracy, minifyD, tolerance, toPolygon, smoothPoly, polyFormat, precisionPoly, simplifyRD, simplifyRDP, harmonizeCpts, removeOrphanSubpaths, simplifyRound, simplifyQuadraticCorners, scale, scaleTo, crop, alignToOrigin, convertTransforms, keepSmaller, splitCompound, convertPathLength, toAbsoluteUnits } = settings;
|
|
12874
|
+
let { getObject = false, removeComments, removeOffCanvas, unGroup, mergePaths, removeElements, removeDimensions, removeIds, removeClassNames, omitNamespace, cleanUpStrokes, addViewBox, addDimensions, removePrologue, removeHidden, removeUnused, cleanupDefs, cleanupClip, cleanupSVGAtts, removeNameSpaced, removeNameSpacedAtts, attributesToGroup, minifyRgbColors, stylesToAttributes, fixHref, legacyHref, allowMeta, allowDataAtts, allowAriaAtts, removeSVGAttributes, removeElAttributes, shapesToPaths, shapeConvert, convertShapes, simplifyBezier, optimizeOrder, autoClose, removeZeroLength, refineClosing, removeColinear, flatBezierToLinetos, revertToQuadratics, refineExtremes, simplifyCorners, fixDirections, keepExtremes, keepCorners, keepInflections, addExtremes, reversePath, toAbsolute, toRelative, toMixed, toShorthands, toLonghands, quadraticToCubic, arcToCubic, cubicToArc, lineToCubic, decimals, autoAccuracy, minifyD, tolerance, toPolygon, smoothPoly, polyFormat, isClosed, precisionPoly, simplifyRD, simplifyRDP, harmonizeCpts, removeOrphanSubpaths, simplifyRound, simplifyQuadraticCorners, scale, scaleTo, crop, alignToOrigin, convertTransforms, keepSmaller, splitCompound, convertPathLength, toAbsoluteUnits } = settings;
|
|
12549
12875
|
|
|
12550
12876
|
// clamp tolerance and scale
|
|
12551
12877
|
tolerance = Math.max(0.1, tolerance);
|
|
@@ -12556,6 +12882,10 @@
|
|
|
12556
12882
|
settings.convertTransforms = true;
|
|
12557
12883
|
}
|
|
12558
12884
|
|
|
12885
|
+
if (shapeConvert === 'toShapes' || shapeConvert === 'shapesToPaths') {
|
|
12886
|
+
keepSmaller = false;
|
|
12887
|
+
}
|
|
12888
|
+
|
|
12559
12889
|
/**
|
|
12560
12890
|
* intercept
|
|
12561
12891
|
* invalid inputs
|
|
@@ -12733,6 +13063,8 @@
|
|
|
12733
13063
|
toRelative,
|
|
12734
13064
|
toMixed,
|
|
12735
13065
|
toShorthands,
|
|
13066
|
+
// return true arc radii or minified/parametrized
|
|
13067
|
+
optimizeArcs: minifyD < 1,
|
|
12736
13068
|
decimals,
|
|
12737
13069
|
};
|
|
12738
13070
|
|
|
@@ -12744,7 +13076,6 @@
|
|
|
12744
13076
|
let pathDataPlusArr = [];
|
|
12745
13077
|
let path = paths[i];
|
|
12746
13078
|
let { d, el } = path;
|
|
12747
|
-
let isPoly = false;
|
|
12748
13079
|
|
|
12749
13080
|
// disable reordering for elements with stroke dash-array
|
|
12750
13081
|
if (el && (el.hasAttribute('stroke-dasharray') || el.hasAttribute('stroke-dashoffset'))) {
|
|
@@ -12791,7 +13122,7 @@
|
|
|
12791
13122
|
// count commands for evaluation
|
|
12792
13123
|
comCount += pathData.length;
|
|
12793
13124
|
|
|
12794
|
-
if (
|
|
13125
|
+
if (removeOrphanSubpaths) pathData = removeOrphanedM(pathData);
|
|
12795
13126
|
|
|
12796
13127
|
/**
|
|
12797
13128
|
* get sub paths
|
|
@@ -12805,8 +13136,9 @@
|
|
|
12805
13136
|
let pathDataSub = subPathArr[i];
|
|
12806
13137
|
let poly = [];
|
|
12807
13138
|
let coms = Array.from(new Set(pathDataSub.map(com => com.type))).join('');
|
|
12808
|
-
isPoly = !(/[acqts]/gi).test(coms);
|
|
12809
|
-
|
|
13139
|
+
let isPoly = !(/[acqts]/gi).test(coms);
|
|
13140
|
+
|
|
13141
|
+
let closed = (/z/gi).test(coms);
|
|
12810
13142
|
|
|
12811
13143
|
if (isPoly && !mode) {
|
|
12812
13144
|
|
|
@@ -12823,7 +13155,6 @@
|
|
|
12823
13155
|
|
|
12824
13156
|
}
|
|
12825
13157
|
|
|
12826
|
-
toPolygon = false;
|
|
12827
13158
|
pathDataSub = pathDataFromPoly(poly, closed);
|
|
12828
13159
|
|
|
12829
13160
|
}
|
|
@@ -12832,26 +13163,56 @@
|
|
|
12832
13163
|
* convert curves to polygon
|
|
12833
13164
|
* flattening
|
|
12834
13165
|
*/
|
|
12835
|
-
|
|
13166
|
+
|
|
13167
|
+
if (toPolygon) {
|
|
12836
13168
|
simplifyBezier = false;
|
|
12837
13169
|
smoothPoly = false;
|
|
12838
13170
|
harmonizeCpts = false;
|
|
12839
13171
|
|
|
12840
|
-
|
|
13172
|
+
/**
|
|
13173
|
+
* if pathdata is already polygon- pass through
|
|
13174
|
+
* otherwise create precise polygon by curve splitting
|
|
13175
|
+
* */
|
|
12841
13176
|
|
|
12842
|
-
|
|
12843
|
-
|
|
12844
|
-
|
|
13177
|
+
if (!isPoly) {
|
|
13178
|
+
pathDataSub = getPathDataVerbose(pathDataSub);
|
|
13179
|
+
let polyData = pathDataToPolygonOpt(pathDataSub, {
|
|
13180
|
+
precisionPoly,
|
|
13181
|
+
autoAccuracy,
|
|
13182
|
+
polyFormat,
|
|
12845
13183
|
|
|
12846
|
-
|
|
12847
|
-
|
|
12848
|
-
|
|
13184
|
+
simplifyRD,
|
|
13185
|
+
simplifyRDP
|
|
13186
|
+
});
|
|
13187
|
+
|
|
13188
|
+
poly = polyData.poly;
|
|
13189
|
+
pathDataSub = polyData.pathData;
|
|
13190
|
+
isPoly = true;
|
|
13191
|
+
|
|
13192
|
+
}
|
|
13193
|
+
|
|
13194
|
+
polys.push(poly);
|
|
13195
|
+
|
|
13196
|
+
}
|
|
13197
|
+
|
|
13198
|
+
// harmonize cpts
|
|
13199
|
+
// if (harmonizeCpts) pathDataSub = harmonizeCubicCpts(pathDataSub)
|
|
12849
13200
|
|
|
12850
|
-
|
|
12851
|
-
isPoly = true;
|
|
13201
|
+
if (smoothPoly) {
|
|
12852
13202
|
|
|
13203
|
+
removeZeroLength=true;
|
|
13204
|
+
optimizeOrder=true;
|
|
12853
13205
|
}
|
|
12854
13206
|
|
|
13207
|
+
// remove zero length linetos
|
|
13208
|
+
if (removeColinear || removeZeroLength) pathDataSub = removeZeroLengthLinetos(pathDataSub);
|
|
13209
|
+
|
|
13210
|
+
// sort to top left
|
|
13211
|
+
if (optimizeOrder) pathDataSub = pathDataToTopLeft(pathDataSub);
|
|
13212
|
+
|
|
13213
|
+
// Preprocessing: remove colinear - ignore flat beziers (removed later)
|
|
13214
|
+
if (removeColinear) pathDataSub = pathDataRemoveColinear(pathDataSub, { tolerance, flatBezierToLinetos: false });
|
|
13215
|
+
|
|
12855
13216
|
/**
|
|
12856
13217
|
* poly to beziers via
|
|
12857
13218
|
* Philip J. Schneider's
|
|
@@ -12860,12 +13221,18 @@
|
|
|
12860
13221
|
if (smoothPoly) {
|
|
12861
13222
|
|
|
12862
13223
|
if (isPoly) {
|
|
12863
|
-
|
|
13224
|
+
/*
|
|
13225
|
+
pathDataSub = pathDataToTopLeft(pathDataSub)
|
|
13226
|
+
pathDataSub = removeZeroLengthLinetos(pathDataSub)
|
|
13227
|
+
pathDataSub = pathDataRemoveColinear(pathDataSub, { tolerance, flatBezierToLinetos: true });
|
|
13228
|
+
*/
|
|
13229
|
+
|
|
12864
13230
|
let poly = getPathDataVertices(pathDataSub);
|
|
12865
13231
|
|
|
12866
13232
|
// options for poly simplification
|
|
12867
13233
|
let optionsPoly = {
|
|
12868
|
-
|
|
13234
|
+
|
|
13235
|
+
denoise: 0,
|
|
12869
13236
|
tolerance,
|
|
12870
13237
|
width: bb_poly.width,
|
|
12871
13238
|
height: bb_poly.height,
|
|
@@ -12877,26 +13244,15 @@
|
|
|
12877
13244
|
closed,
|
|
12878
13245
|
simplifyRD,
|
|
12879
13246
|
simplifyRDP,
|
|
13247
|
+
isClosed,
|
|
12880
13248
|
};
|
|
12881
13249
|
|
|
12882
13250
|
pathDataSub = simplifyPolygonToPathData(poly, optionsPoly);
|
|
12883
13251
|
// flag as non poly as we're smoothing to curves
|
|
12884
|
-
|
|
13252
|
+
isPoly = false;
|
|
12885
13253
|
}
|
|
12886
13254
|
}
|
|
12887
13255
|
|
|
12888
|
-
// harmonize cpts
|
|
12889
|
-
// if (harmonizeCpts) pathDataSub = harmonizeCubicCpts(pathDataSub)
|
|
12890
|
-
|
|
12891
|
-
// remove zero length linetos
|
|
12892
|
-
if (removeColinear || removeZeroLength) pathDataSub = removeZeroLengthLinetos(pathDataSub);
|
|
12893
|
-
|
|
12894
|
-
// sort to top left
|
|
12895
|
-
if (optimizeOrder) pathDataSub = pathDataToTopLeft(pathDataSub);
|
|
12896
|
-
|
|
12897
|
-
// Preprocessing: remove colinear - ignore flat beziers (removed later)
|
|
12898
|
-
if (removeColinear) pathDataSub = pathDataRemoveColinear(pathDataSub, { tolerance, flatBezierToLinetos: false });
|
|
12899
|
-
|
|
12900
13256
|
let tMin = 0, tMax = 1;
|
|
12901
13257
|
if (addExtremes) pathDataSub = addExtremePoints(pathDataSub,
|
|
12902
13258
|
{ tMin, tMax, addExtremes, angles: [30] });
|
|
@@ -12919,11 +13275,13 @@
|
|
|
12919
13275
|
pathDataPlus.bb = getPolyBBox(getPathDataVertices(pathDataCubic));
|
|
12920
13276
|
}
|
|
12921
13277
|
pathDataPlus.dimA = pathDataPlus.bb.width + pathDataPlus.bb.height;
|
|
13278
|
+
|
|
12922
13279
|
pathDataPlus.pathData = getPathDataVerbose(pathDataSub, {
|
|
12923
13280
|
addSquareLength: false,
|
|
12924
13281
|
addArea: false,
|
|
12925
13282
|
addAverageDim: false
|
|
12926
13283
|
});
|
|
13284
|
+
|
|
12927
13285
|
}
|
|
12928
13286
|
|
|
12929
13287
|
// simplify beziers
|
|
@@ -12934,6 +13292,12 @@
|
|
|
12934
13292
|
|
|
12935
13293
|
if (refineClosing) pathData = refineClosingCommand(pathData, { threshold: dimA * 0.001 });
|
|
12936
13294
|
|
|
13295
|
+
// refine round segment sequences
|
|
13296
|
+
if (simplifyRound) {
|
|
13297
|
+
pathData = refineRoundSegments(pathData);
|
|
13298
|
+
pathData = simplifyAdjacentRound(pathData);
|
|
13299
|
+
}
|
|
13300
|
+
|
|
12937
13301
|
pathData = simplifyBezier ? simplifyPathDataCubic(pathData, { simplifyBezier, keepInflections, keepExtremes, keepCorners, revertToQuadratics, tolerance }) : pathData;
|
|
12938
13302
|
|
|
12939
13303
|
// refine extremes
|
|
@@ -12957,12 +13321,6 @@
|
|
|
12957
13321
|
pathData = refineRoundedCorners(pathData, { threshold, tolerance, simplifyQuadraticCorners });
|
|
12958
13322
|
}
|
|
12959
13323
|
|
|
12960
|
-
// refine round segment sequences
|
|
12961
|
-
if (simplifyRound) {
|
|
12962
|
-
pathData = refineRoundSegments(pathData);
|
|
12963
|
-
pathData = simplifyAdjacentRound(pathData);
|
|
12964
|
-
}
|
|
12965
|
-
|
|
12966
13324
|
// simplify to quadratics
|
|
12967
13325
|
if (revertToQuadratics) pathData = pathDataRevertCubicToQuadratic(pathData, tolerance);
|
|
12968
13326
|
|
|
@@ -12979,6 +13337,8 @@
|
|
|
12979
13337
|
|
|
12980
13338
|
}
|
|
12981
13339
|
|
|
13340
|
+
// offset path
|
|
13341
|
+
|
|
12982
13342
|
// update
|
|
12983
13343
|
pathDataPlusArr.push({ pathData, bb });
|
|
12984
13344
|
|
|
@@ -13038,17 +13398,18 @@
|
|
|
13038
13398
|
}
|
|
13039
13399
|
|
|
13040
13400
|
// add simplified poly - if not populated by toPoly conversion
|
|
13401
|
+
/*
|
|
13041
13402
|
if (isPoly) {
|
|
13042
13403
|
|
|
13043
13404
|
pathDataPlusArr.forEach(sub => {
|
|
13044
|
-
let poly = getPathDataVertices(sub.pathData, false, decimals)
|
|
13405
|
+
let poly = getPathDataVertices(sub.pathData, false, decimals)
|
|
13045
13406
|
if (polyFormat === 'array') {
|
|
13046
|
-
poly = polyPtsToArray(poly)
|
|
13407
|
+
poly = polyPtsToArray(poly)
|
|
13047
13408
|
}
|
|
13048
|
-
polys.push(poly)
|
|
13049
|
-
})
|
|
13050
|
-
|
|
13409
|
+
polys.push(poly)
|
|
13410
|
+
})
|
|
13051
13411
|
}
|
|
13412
|
+
*/
|
|
13052
13413
|
|
|
13053
13414
|
// split into sub paths - returns svg with multiple paths
|
|
13054
13415
|
if (splitCompound && !mode && pathDataPlusArr.length > 1) {
|
|
@@ -13147,7 +13508,6 @@
|
|
|
13147
13508
|
svg = stringifySVG(svg, { omitNamespace, removeComments, format: minifyD });
|
|
13148
13509
|
|
|
13149
13510
|
svgSizeOpt = svg.length;
|
|
13150
|
-
|
|
13151
13511
|
compression = +(100 / svgSize * (svgSizeOpt)).toFixed(2);
|
|
13152
13512
|
|
|
13153
13513
|
svgSize = +(svgSize / 1024).toFixed(3);
|
|
@@ -13173,15 +13533,52 @@
|
|
|
13173
13533
|
({ d, report } = paths[0]);
|
|
13174
13534
|
}
|
|
13175
13535
|
|
|
13176
|
-
|
|
13177
|
-
|
|
13178
|
-
|
|
13536
|
+
// sanitize poly output
|
|
13537
|
+
if (polys.length) {
|
|
13538
|
+
|
|
13539
|
+
// round point data
|
|
13540
|
+
polys.forEach((poly, i) => {
|
|
13541
|
+
|
|
13542
|
+
if (polyFormat === 'string') poly = normalizePoly(poly);
|
|
13543
|
+
|
|
13544
|
+
poly = roundPoly(poly, decimals);
|
|
13545
|
+
// remove coinciding points
|
|
13546
|
+
polys[i] = removeCoincidingVertices(poly);
|
|
13547
|
+
});
|
|
13548
|
+
|
|
13549
|
+
if (polys.length === 1) {
|
|
13550
|
+
polys = polys[0];
|
|
13551
|
+
}
|
|
13552
|
+
|
|
13553
|
+
if (polyFormat === 'string') {
|
|
13554
|
+
|
|
13555
|
+
polys = normalizePoly(polys, { toArray: true, flatten: true });
|
|
13556
|
+
|
|
13557
|
+
polys = polys.flat().join(' ');
|
|
13558
|
+
}
|
|
13179
13559
|
|
|
13180
|
-
if (polyFormat === 'string' && polys.length) {
|
|
13181
|
-
polys = polys.flat().map(pt => `${pt.x},${pt.y}`).join(' ');
|
|
13182
13560
|
}
|
|
13183
13561
|
|
|
13184
|
-
|
|
13562
|
+
// create object
|
|
13563
|
+
let svgObj = new SlickVGObj({ svg, d, polys, report, pathDataPlusArr: pathDataPlusArr_global, inputType, dOriginal });
|
|
13564
|
+
|
|
13565
|
+
/*
|
|
13566
|
+
let d2 = svgObj.getD()
|
|
13567
|
+
console.log('d2', d2);
|
|
13568
|
+
|
|
13569
|
+
let svg2 = svgObj.getSvg()
|
|
13570
|
+
console.log('svg2', svg2);
|
|
13571
|
+
|
|
13572
|
+
let format = 'd'
|
|
13573
|
+
format = 'pathData'
|
|
13574
|
+
format = 'd'
|
|
13575
|
+
format = 'points'
|
|
13576
|
+
format = 'array'
|
|
13577
|
+
let poly2 = svgObj.getPoly({ format })
|
|
13578
|
+
console.log('poly2', 'format', format, poly2);
|
|
13579
|
+
*/
|
|
13580
|
+
|
|
13581
|
+
return !getObject ? (d ? d : svg) : svgObj;
|
|
13185
13582
|
|
|
13186
13583
|
}
|
|
13187
13584
|
|
|
@@ -13190,6 +13587,7 @@
|
|
|
13190
13587
|
// IIFE
|
|
13191
13588
|
if (typeof window !== 'undefined') {
|
|
13192
13589
|
window.svgPathSimplify = svgPathSimplify;
|
|
13590
|
+
window.SlickVG = SlickVG;
|
|
13193
13591
|
window.getElementTransform = getElementTransform;
|
|
13194
13592
|
window.validateSVG = validateSVG;
|
|
13195
13593
|
window.detectInputType = detectInputType;
|
|
@@ -13199,6 +13597,7 @@
|
|
|
13199
13597
|
}
|
|
13200
13598
|
|
|
13201
13599
|
exports.PI = PI$1;
|
|
13600
|
+
exports.SlickVG = SlickVG;
|
|
13202
13601
|
exports.abs = abs$1;
|
|
13203
13602
|
exports.acos = acos$1;
|
|
13204
13603
|
exports.asin = asin$1;
|