svg-path-simplify 0.4.5 → 0.4.7
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 +14 -0
- package/README.md +24 -14
- package/dist/svg-path-simplify.esm.js +1620 -1216
- package/dist/svg-path-simplify.esm.min.js +2 -2
- package/dist/svg-path-simplify.js +1620 -1215
- package/dist/svg-path-simplify.min.js +2 -2
- package/dist/svg-path-simplify.pathdata.esm.js +298 -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 +259 -60
- 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 +70 -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
|
@@ -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 = []) {
|
|
@@ -4367,6 +4321,11 @@
|
|
|
4367
4321
|
|
|
4368
4322
|
let [rx, ry, largeArc, x, y] = [values[0], values[1], values[3], values[5], values[6]];
|
|
4369
4323
|
let comPrev = pathData[i - 1];
|
|
4324
|
+
|
|
4325
|
+
// force absolute
|
|
4326
|
+
rx = Math.abs(rx);
|
|
4327
|
+
ry = Math.abs(ry);
|
|
4328
|
+
|
|
4370
4329
|
let [x0, y0] = [comPrev.values[comPrev.values.length - 2], comPrev.values[comPrev.values.length - 1]];
|
|
4371
4330
|
let M = { x: x0, y: y0 };
|
|
4372
4331
|
let p = { x, y };
|
|
@@ -4406,7 +4365,7 @@
|
|
|
4406
4365
|
if (isHorizontal || isVertical) {
|
|
4407
4366
|
|
|
4408
4367
|
// check if semi circle
|
|
4409
|
-
let needsTrueR = isHorizontal ? rx*1.9 > diffX : ry*1.9 > diffY;
|
|
4368
|
+
let needsTrueR = isHorizontal ? rx * 1.9 > diffX : ry * 1.9 > diffY;
|
|
4410
4369
|
|
|
4411
4370
|
// is semicircle we can simplify rx
|
|
4412
4371
|
if (!needsTrueR) {
|
|
@@ -5102,8 +5061,8 @@
|
|
|
5102
5061
|
let phi = rotation ? rotation * TAU / 360 : 0;
|
|
5103
5062
|
let sinphi = phi ? Math.sin(phi) : 0;
|
|
5104
5063
|
let cosphi = phi ? Math.cos(phi) : 1;
|
|
5105
|
-
let pxp = cosphi * (p0.x - x)
|
|
5106
|
-
let pyp = -sinphi * (p0.x - x)
|
|
5064
|
+
let pxp = cosphi * (p0.x - x) *0.5 + sinphi * (p0.y - y) *0.5;
|
|
5065
|
+
let pyp = -sinphi * (p0.x - x) *0.5 + cosphi * (p0.y - y) *0.5;
|
|
5107
5066
|
|
|
5108
5067
|
if (pxp === 0 && pyp === 0) {
|
|
5109
5068
|
return []
|
|
@@ -5139,8 +5098,8 @@
|
|
|
5139
5098
|
|
|
5140
5099
|
let centerxp = radicant ? radicant * rx / ry * pyp : 0;
|
|
5141
5100
|
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)
|
|
5101
|
+
let centerx = cosphi * centerxp - sinphi * centeryp + (p0.x + x) * 0.5;
|
|
5102
|
+
let centery = sinphi * centerxp + cosphi * centeryp + (p0.y + y) * 0.5;
|
|
5144
5103
|
|
|
5145
5104
|
let vx1 = (pxp - centerxp) / rx;
|
|
5146
5105
|
let vy1 = (pyp - centeryp) / ry;
|
|
@@ -5162,15 +5121,17 @@
|
|
|
5162
5121
|
ang2 = vectorAngle(vx1, vy1, vx2, vy2);
|
|
5163
5122
|
|
|
5164
5123
|
if (sweepFlag === 0 && ang2 > 0) {
|
|
5165
|
-
|
|
5124
|
+
|
|
5125
|
+
ang2 -= TAU;
|
|
5166
5126
|
}
|
|
5167
5127
|
else if (sweepFlag === 1 && ang2 < 0) {
|
|
5168
|
-
|
|
5128
|
+
|
|
5129
|
+
ang2 += TAU;
|
|
5169
5130
|
}
|
|
5170
5131
|
|
|
5171
|
-
let ratio = +(Math.abs(ang2) / (TAU
|
|
5132
|
+
let ratio = +(Math.abs(ang2) / (TAU * 0.25)).toFixed(0) || 1;
|
|
5172
5133
|
|
|
5173
|
-
// increase segments for more
|
|
5134
|
+
// increase segments for more accurate length calculations
|
|
5174
5135
|
let segments = ratio * splitSegments;
|
|
5175
5136
|
ang2 /= segments;
|
|
5176
5137
|
let pathDataArc = [];
|
|
@@ -5182,7 +5143,8 @@
|
|
|
5182
5143
|
const k = 0.551785;
|
|
5183
5144
|
let a = ang2 === angle90 ? k :
|
|
5184
5145
|
(
|
|
5185
|
-
|
|
5146
|
+
|
|
5147
|
+
ang2 === -angle90 ? -k : 1.33333 * Math.tan(ang2 * 0.25)
|
|
5186
5148
|
);
|
|
5187
5149
|
|
|
5188
5150
|
let cos2 = ang2 ? Math.cos(ang2) : 1;
|
|
@@ -5229,7 +5191,7 @@
|
|
|
5229
5191
|
let comA = cubicCommandToArc(p0, cp1, cp2, p, areaThreshold);
|
|
5230
5192
|
let comAN = cubicCommandToArc(comN.p0, comN.cp1, comN.cp2, comN.p, areaThreshold);
|
|
5231
5193
|
|
|
5232
|
-
if (comA.isArc
|
|
5194
|
+
if (comA.isArc && comAN.isArc) {
|
|
5233
5195
|
|
|
5234
5196
|
let dist = getDistManhattan(p0, comN.p);
|
|
5235
5197
|
let maxDist = dist * 0.01;
|
|
@@ -5244,8 +5206,18 @@
|
|
|
5244
5206
|
let sweep = area < 0 ? 0 : 1;
|
|
5245
5207
|
|
|
5246
5208
|
if (vertical || horizontal) {
|
|
5209
|
+
|
|
5247
5210
|
rx = Math.min(rx, comAN.rx);
|
|
5248
5211
|
ry = Math.min(ry, comAN.ry);
|
|
5212
|
+
|
|
5213
|
+
let diffR = Math.abs(rx - ry) / rx;
|
|
5214
|
+
let isSemiCircle = diffR < 0.025;
|
|
5215
|
+
|
|
5216
|
+
if (isSemiCircle) {
|
|
5217
|
+
rx = rx > 1 ? 1 : Math.min(rx, ry);
|
|
5218
|
+
ry = rx;
|
|
5219
|
+
}
|
|
5220
|
+
|
|
5249
5221
|
pathData[c] = null;
|
|
5250
5222
|
pathData[c + 1].type = 'A';
|
|
5251
5223
|
pathData[c + 1].values = [rx, ry, 0, 0, sweep, comN.p.x, comN.p.y];
|
|
@@ -5268,33 +5240,61 @@
|
|
|
5268
5240
|
let arcSegArea = 0, isArc = false;
|
|
5269
5241
|
|
|
5270
5242
|
// check angles
|
|
5271
|
-
let
|
|
5272
|
-
let
|
|
5243
|
+
let dx1 = (cp1.x - p0.x);
|
|
5244
|
+
let dy1 = (cp1.y - p0.y);
|
|
5245
|
+
let dx2 = (cp2.x - p.x);
|
|
5246
|
+
let dy2 = (cp2.y - p.y);
|
|
5247
|
+
|
|
5248
|
+
let thresh = getDistManhattan(p0, p) * 0.001;
|
|
5249
|
+
|
|
5250
|
+
let isVertical1 = dx1 < thresh;
|
|
5251
|
+
let isVertical2 = dx2 < thresh;
|
|
5252
|
+
|
|
5253
|
+
let isHorizontal1 = dy1 < thresh;
|
|
5254
|
+
let isHorizontal2 = dy2 < thresh;
|
|
5255
|
+
|
|
5256
|
+
let isRightAngle = (isVertical1 || isVertical2) && (isHorizontal1 || isHorizontal2);
|
|
5257
|
+
|
|
5258
|
+
/*
|
|
5259
|
+
let angle1 = getAngleFromDelta(dx1, dy1, true);
|
|
5260
|
+
let angle2 = getAngleFromDelta(dx2, dy2, true);
|
|
5273
5261
|
let deltaAngle = Math.abs(angle1 - angle2) * 180 / Math.PI;
|
|
5274
5262
|
|
|
5275
5263
|
let angleDiff = Math.abs((deltaAngle % 180) - 90);
|
|
5276
5264
|
let isRightAngle = angleDiff < 3;
|
|
5265
|
+
*/
|
|
5277
5266
|
|
|
5278
5267
|
let rx = 0;
|
|
5279
5268
|
let ry = 0;
|
|
5280
|
-
let ptC;
|
|
5269
|
+
let ptC = p0;
|
|
5270
|
+
let r1 = 0, r2 = 0;
|
|
5281
5271
|
|
|
5282
5272
|
if (isRightAngle) {
|
|
5283
|
-
// point between cps
|
|
5284
5273
|
|
|
5285
|
-
|
|
5286
|
-
|
|
5287
|
-
|
|
5274
|
+
if (isHorizontal1 && isVertical2) {
|
|
5275
|
+
ptC = { x: p0.x, y: p.y };
|
|
5276
|
+
r1 = Math.abs(p.x-p0.x);
|
|
5277
|
+
r2 = Math.abs(p.y-p0.y);
|
|
5278
|
+
}
|
|
5279
|
+
else if (isHorizontal2 && isVertical1) {
|
|
5280
|
+
ptC = { x: p.x, y: p0.y };
|
|
5281
|
+
r2 = Math.abs(p0.x-p.x);
|
|
5282
|
+
r1 = Math.abs(p0.y-p.y);
|
|
5283
|
+
}
|
|
5284
|
+
|
|
5285
|
+
/*
|
|
5286
|
+
if (r1 && r2) {
|
|
5288
5287
|
|
|
5289
|
-
|
|
5290
|
-
ptC = checkLineIntersection(p0, cp1_r, p, cp2_r, false);
|
|
5288
|
+
}
|
|
5291
5289
|
|
|
5292
|
-
|
|
5290
|
+
*/
|
|
5293
5291
|
|
|
5294
|
-
if (
|
|
5292
|
+
if (r1 && r2) {
|
|
5295
5293
|
|
|
5294
|
+
/*
|
|
5296
5295
|
let r1 = getDistance(p0, pI);
|
|
5297
5296
|
let r2 = getDistance(p, pI);
|
|
5297
|
+
*/
|
|
5298
5298
|
|
|
5299
5299
|
let rMax = +Math.max(r1, r2).toFixed(8);
|
|
5300
5300
|
let rMin = +Math.min(r1, r2).toFixed(8);
|
|
@@ -5312,7 +5312,6 @@
|
|
|
5312
5312
|
let circular = (100 / rx * Math.abs(rx - ry)) < 5;
|
|
5313
5313
|
|
|
5314
5314
|
if (circular) {
|
|
5315
|
-
|
|
5316
5315
|
rx = rMax;
|
|
5317
5316
|
ry = rx;
|
|
5318
5317
|
}
|
|
@@ -5338,7 +5337,7 @@
|
|
|
5338
5337
|
arcSegArea = (Math.PI * (rx * ry)) / 4;
|
|
5339
5338
|
|
|
5340
5339
|
// subtract polygon between start, end and center point
|
|
5341
|
-
arcSegArea -= Math.abs(getPolygonArea([p0, p,
|
|
5340
|
+
arcSegArea -= Math.abs(getPolygonArea([p0, p, ptC]));
|
|
5342
5341
|
|
|
5343
5342
|
let areaDiff = getRelativeAreaDiff(comArea, arcSegArea);
|
|
5344
5343
|
|
|
@@ -6408,59 +6407,50 @@
|
|
|
6408
6407
|
pathData[pathData.length - 1].type.toLowerCase() === 'z';
|
|
6409
6408
|
|
|
6410
6409
|
for (let c = 1, l = pathData.length; c < l; c++) {
|
|
6411
|
-
|
|
6412
6410
|
let com = pathData[c];
|
|
6411
|
+
let { type, values } = com;
|
|
6413
6412
|
let comN = pathData[c + 1] || pathData[l - 1];
|
|
6414
|
-
|
|
6413
|
+
let valuesN = comN.values;
|
|
6415
6414
|
let p1 = comN.type.toLowerCase() === 'z' ? M : { x: comN.values[comN.values.length - 2], y: comN.values[comN.values.length - 1] };
|
|
6416
6415
|
|
|
6417
|
-
let { type, values } = com;
|
|
6418
6416
|
let valsL = values.slice(-2);
|
|
6419
6417
|
p = type !== 'Z' ? { x: valsL[0], y: valsL[1] } : M;
|
|
6420
6418
|
|
|
6419
|
+
let nextBezier = type!==comN.type && (comN.type==='C' || comN.type==='Q');
|
|
6420
|
+
|
|
6421
6421
|
let area = p1 ? getPolygonArea([p0, p, p1], true) : Infinity;
|
|
6422
6422
|
let distSquare = getSquareDistance(p0, p1);
|
|
6423
6423
|
let distMax = distSquare ? distSquare / 333 * tolerance : 0;
|
|
6424
6424
|
|
|
6425
6425
|
let isFlat = area < distMax;
|
|
6426
|
-
|
|
6427
6426
|
let isFlatBez = false;
|
|
6427
|
+
let cpts = [];
|
|
6428
6428
|
|
|
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);
|
|
6429
|
+
/**
|
|
6430
|
+
* type change
|
|
6431
|
+
* check flatness
|
|
6432
|
+
*/
|
|
6433
|
+
if (nextBezier) {
|
|
6434
|
+
cpts = comN.type === 'C' ?
|
|
6435
|
+
[{ x: valuesN[0], y: valuesN[1] }, { x: valuesN[2], y: valuesN[3] }] :
|
|
6436
|
+
(comN.type === 'Q' ? [{ x: valuesN[0], y: valuesN[1] }] : []);
|
|
6448
6437
|
|
|
6449
|
-
|
|
6438
|
+
isFlat = commandIsFlat([p0, ...cpts, p], { tolerance });
|
|
6450
6439
|
|
|
6451
|
-
|
|
6440
|
+
/*
|
|
6452
6441
|
|
|
6453
|
-
|
|
6442
|
+
if(!isFlatBez){
|
|
6443
|
+
pathDataN.push(com)
|
|
6444
|
+
continue
|
|
6454
6445
|
}
|
|
6455
|
-
|
|
6456
|
-
*/
|
|
6446
|
+
*/
|
|
6457
6447
|
|
|
6458
|
-
|
|
6448
|
+
}
|
|
6459
6449
|
|
|
6460
6450
|
// convert flat beziers to linetos
|
|
6461
6451
|
if (flatBezierToLinetos && (type === 'C' || type === 'Q')) {
|
|
6462
6452
|
|
|
6463
|
-
|
|
6453
|
+
cpts = type === 'C' ?
|
|
6464
6454
|
[{ x: values[0], y: values[1] }, { x: values[2], y: values[3] }] :
|
|
6465
6455
|
(type === 'Q' ? [{ x: values[0], y: values[1] }] : []);
|
|
6466
6456
|
|
|
@@ -6474,10 +6464,13 @@
|
|
|
6474
6464
|
}
|
|
6475
6465
|
}
|
|
6476
6466
|
|
|
6477
|
-
|
|
6467
|
+
/**
|
|
6468
|
+
* colinear = simplification success
|
|
6469
|
+
* exclude arcs (as always =)
|
|
6470
|
+
* as semicircles won't have an area
|
|
6471
|
+
*/
|
|
6478
6472
|
|
|
6479
6473
|
if (isFlat && c < l - 1 && comN.type !== 'A' && (type === 'L' || (flatBezierToLinetos && isFlatBez))) {
|
|
6480
|
-
|
|
6481
6474
|
continue;
|
|
6482
6475
|
}
|
|
6483
6476
|
|
|
@@ -6961,9 +6954,7 @@
|
|
|
6961
6954
|
if (comEx.length === 1) {
|
|
6962
6955
|
|
|
6963
6956
|
comEx = comEx[0];
|
|
6964
|
-
|
|
6965
6957
|
pathData[i + 1] = null;
|
|
6966
|
-
|
|
6967
6958
|
pathData[i + 2].values = [comEx.cp1.x, comEx.cp1.y, comEx.cp2.x, comEx.cp2.y, comEx.p.x, comEx.p.y];
|
|
6968
6959
|
pathData[i + 2].cp1 = comEx.cp1;
|
|
6969
6960
|
pathData[i + 2].cp2 = comEx.cp2;
|
|
@@ -7143,7 +7134,16 @@
|
|
|
7143
7134
|
return pts
|
|
7144
7135
|
}
|
|
7145
7136
|
|
|
7137
|
+
if (pts.length && typeof pts[0] === 'string') {
|
|
7138
|
+
pts = pts.map(pt => {
|
|
7139
|
+
return toPointArray(pt.split(/,| /).filter(Boolean).map(Number))
|
|
7140
|
+
});
|
|
7141
|
+
pts = pts.flat(2);
|
|
7142
|
+
|
|
7143
|
+
}
|
|
7144
|
+
|
|
7146
7145
|
if (flatten) pts = pts.flat(2);
|
|
7146
|
+
|
|
7147
7147
|
let poly = toArray ? polyPtsToArray(pts) : polyArrayToObject(pts);
|
|
7148
7148
|
return poly
|
|
7149
7149
|
}
|
|
@@ -8503,7 +8503,7 @@
|
|
|
8503
8503
|
styleProps.remove.push('transform');
|
|
8504
8504
|
|
|
8505
8505
|
// scale props like stroke width or dash-array
|
|
8506
|
-
styleProps = scaleProps(styleProps, { props: ['stroke-width', 'stroke-dasharray'], scale: scaleX });
|
|
8506
|
+
styleProps = scaleProps(styleProps, { props: ['stroke-width', 'stroke-dasharray', 'stroke-dashoffset'], scale: scaleX });
|
|
8507
8507
|
|
|
8508
8508
|
} else {
|
|
8509
8509
|
el.setAttribute('transform', transComponents.matrixAtt);
|
|
@@ -9050,6 +9050,38 @@
|
|
|
9050
9050
|
});
|
|
9051
9051
|
});
|
|
9052
9052
|
|
|
9053
|
+
/**
|
|
9054
|
+
* remove els and attributes
|
|
9055
|
+
*/
|
|
9056
|
+
|
|
9057
|
+
// remove meta
|
|
9058
|
+
if (!allowMeta) removeElements.push('meta', 'metadata', 'desc', 'title');
|
|
9059
|
+
|
|
9060
|
+
if (removeClassNames) {
|
|
9061
|
+
removeSVGAttributes.push('class');
|
|
9062
|
+
removeElAttributes.push('class');
|
|
9063
|
+
}
|
|
9064
|
+
|
|
9065
|
+
if (removeIds) {
|
|
9066
|
+
removeSVGAttributes.push('id');
|
|
9067
|
+
removeElAttributes.push('id');
|
|
9068
|
+
}
|
|
9069
|
+
|
|
9070
|
+
// remove hidden elements
|
|
9071
|
+
removeHiddenSvgEls(svg);
|
|
9072
|
+
|
|
9073
|
+
// remove SVG elements
|
|
9074
|
+
removeSvgEls(svg, { removeElements, removeNameSpaced });
|
|
9075
|
+
|
|
9076
|
+
// remove SVG attributes
|
|
9077
|
+
removeSvgAtts(svg, removeSVGAttributes);
|
|
9078
|
+
|
|
9079
|
+
// remove SVG child element attributes
|
|
9080
|
+
removeSvgChildAtts(svg, removeElAttributes);
|
|
9081
|
+
|
|
9082
|
+
// general cleanup
|
|
9083
|
+
if (cleanupSVGAtts) cleanupSVGAttributes(svg, { removeIds, removeClassNames, removeDimensions, stylesToAttributes, allowMeta, allowAriaAtts, allowDataAtts });
|
|
9084
|
+
|
|
9053
9085
|
// collect all elements' properties
|
|
9054
9086
|
let svgElProps = [];
|
|
9055
9087
|
let els = svg.querySelectorAll(`${renderedEls.join(', ')}`);
|
|
@@ -9070,14 +9102,15 @@
|
|
|
9070
9102
|
let styleProps = parseStylesProperties(el, propOptions);
|
|
9071
9103
|
let stylePropsFiltered = {};
|
|
9072
9104
|
|
|
9073
|
-
//
|
|
9074
|
-
|
|
9105
|
+
// reset remove array
|
|
9106
|
+
remove = [];
|
|
9075
9107
|
|
|
9076
|
-
|
|
9108
|
+
// convert pathLength before transforming
|
|
9109
|
+
if (convertTransforms || attributesToGroup) convertPathLength = true;
|
|
9077
9110
|
|
|
9111
|
+
if (convertPathLength) {
|
|
9078
9112
|
styleProps = convertPathLengthAtt(el, { styleProps });
|
|
9079
9113
|
remove = [...new Set([...remove, ...styleProps.remove])];
|
|
9080
|
-
|
|
9081
9114
|
}
|
|
9082
9115
|
|
|
9083
9116
|
// get parent styles
|
|
@@ -9127,37 +9160,7 @@
|
|
|
9127
9160
|
|
|
9128
9161
|
remove = [...new Set([...remove, ...styleProps.remove])];
|
|
9129
9162
|
|
|
9130
|
-
|
|
9131
|
-
* remove els and attributes
|
|
9132
|
-
*/
|
|
9133
|
-
|
|
9134
|
-
// remove meta
|
|
9135
|
-
if (!allowMeta) removeElements.push('meta', 'metadata', 'desc', 'title');
|
|
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 });
|
|
9163
|
+
|
|
9161
9164
|
|
|
9162
9165
|
// all relative units to absolute
|
|
9163
9166
|
if (toAbsoluteUnits) {
|
|
@@ -9202,7 +9205,6 @@
|
|
|
9202
9205
|
styleProps = setNormalizedTransformsToEl(el, { styleProps });
|
|
9203
9206
|
|
|
9204
9207
|
remove = [...new Set([...remove, ...styleProps.remove])];
|
|
9205
|
-
|
|
9206
9208
|
}
|
|
9207
9209
|
|
|
9208
9210
|
/**
|
|
@@ -9227,13 +9229,6 @@
|
|
|
9227
9229
|
*/
|
|
9228
9230
|
removeAtts(el, remove);
|
|
9229
9231
|
|
|
9230
|
-
/*
|
|
9231
|
-
for (let i = 0; i < remove.length; i++) {
|
|
9232
|
-
let att = remove[i];
|
|
9233
|
-
el.removeAttribute(att)
|
|
9234
|
-
}
|
|
9235
|
-
*/
|
|
9236
|
-
|
|
9237
9232
|
} // endof style processing
|
|
9238
9233
|
|
|
9239
9234
|
/**
|
|
@@ -10524,71 +10519,6 @@
|
|
|
10524
10519
|
return pathData
|
|
10525
10520
|
}
|
|
10526
10521
|
|
|
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
10522
|
function simplifyPolyRDP(pts, {quality = 0.9, width = 0, height = 0}={}) {
|
|
10593
10523
|
|
|
10594
10524
|
/**
|
|
@@ -10783,860 +10713,817 @@
|
|
|
10783
10713
|
|
|
10784
10714
|
}
|
|
10785
10715
|
|
|
10786
|
-
function
|
|
10716
|
+
function refineAdjacentPolyExtremes(pts = []) {
|
|
10787
10717
|
|
|
10788
10718
|
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
|
-
|
|
10801
|
-
function detectRegularPolygon(pts, centroid={x:0, y:0}) {
|
|
10802
|
-
let rSq = getSquareDistance(pts[0], centroid);
|
|
10803
|
-
let isRegular = true;
|
|
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
10719
|
|
|
10812
|
-
|
|
10813
|
-
|
|
10814
|
-
|
|
10720
|
+
let { x, y, width, height, top, bottom, left, right } = getPolyBBox(pts);
|
|
10721
|
+
let threshShort = (width + height) * 0.05;
|
|
10722
|
+
let thresh = (width + height) * 0.001;
|
|
10815
10723
|
|
|
10816
|
-
|
|
10817
|
-
|
|
10818
|
-
}
|
|
10724
|
+
let pt0 = pts[0];
|
|
10725
|
+
let ptLast = pts[l - 1];
|
|
10819
10726
|
|
|
10820
|
-
|
|
10821
|
-
|
|
10822
|
-
|
|
10823
|
-
|
|
10824
|
-
|
|
10825
|
-
|
|
10826
|
-
} = {}) {
|
|
10727
|
+
/**
|
|
10728
|
+
* cleanup close path - almost vertical or horizontal
|
|
10729
|
+
* average start and end extremes
|
|
10730
|
+
*/
|
|
10731
|
+
let dx = Math.abs(ptLast.x - pt0.x);
|
|
10732
|
+
let dy = Math.abs(ptLast.y - pt0.y);
|
|
10827
10733
|
|
|
10828
|
-
|
|
10734
|
+
if (dy < threshShort || dx < threshShort) {
|
|
10829
10735
|
|
|
10830
|
-
|
|
10831
|
-
let bb0 = getPolyBBox(pts);
|
|
10736
|
+
if (pt0.isExtreme && !pt0.isCorner) {
|
|
10832
10737
|
|
|
10833
|
-
|
|
10834
|
-
|
|
10835
|
-
}
|
|
10738
|
+
let xAv = (pt0.x + ptLast.x) * 0.5;
|
|
10739
|
+
let yAv = (pt0.y + ptLast.y) * 0.5;
|
|
10836
10740
|
|
|
10837
|
-
|
|
10741
|
+
pt0.x = xAv;
|
|
10742
|
+
pt0.y = yAv;
|
|
10838
10743
|
|
|
10839
|
-
|
|
10840
|
-
|
|
10841
|
-
|
|
10842
|
-
let p1 = pts[i];
|
|
10843
|
-
let p2 = i < l - 1 ? pts[i + 1] : pts[l - 1];
|
|
10744
|
+
ptLast.x = xAv;
|
|
10745
|
+
ptLast.y = yAv;
|
|
10746
|
+
ptLast.isExtreme = true;
|
|
10844
10747
|
|
|
10845
|
-
|
|
10846
|
-
|
|
10847
|
-
|
|
10848
|
-
p1.idx = i;
|
|
10748
|
+
if (dy < thresh) {
|
|
10749
|
+
ptLast.tangentR.y = pt0.y;
|
|
10750
|
+
ptLast.tangentL.y = pt0.y;
|
|
10849
10751
|
|
|
10752
|
+
}
|
|
10753
|
+
if (dx < thresh) {
|
|
10754
|
+
ptLast.tangentR.x = pt0.x;
|
|
10755
|
+
ptLast.tangentL.x = pt0.x;
|
|
10756
|
+
}
|
|
10757
|
+
}
|
|
10850
10758
|
}
|
|
10851
10759
|
|
|
10852
|
-
for (let i =
|
|
10853
|
-
i >
|
|
10854
|
-
let p0 = i > 0 ? pts[i - 1] : pts[l - 1];
|
|
10760
|
+
for (let i = 1; i < l; i++) {
|
|
10761
|
+
i > 0 ? pts[i - 1] : pts[l - 1];
|
|
10855
10762
|
let p1 = pts[i];
|
|
10856
10763
|
let p2 = i < l - 1 ? pts[i + 1] : pts[l - 1];
|
|
10764
|
+
let dist = getDistManhattan(p1, p2);
|
|
10857
10765
|
|
|
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;
|
|
10766
|
+
let { isHorizontal, isVertical, isCorner, isLong, isExtreme, isSemiExtreme, isDirChange } = p1;
|
|
10874
10767
|
|
|
10875
|
-
|
|
10876
|
-
if ((p1.x === bb0.left || p1.x === bb0.right || p1.y === bb0.top || p1.y === bb0.bottom)) {
|
|
10877
|
-
isExtreme = true;
|
|
10878
|
-
}
|
|
10768
|
+
let extremes = [];
|
|
10879
10769
|
|
|
10880
|
-
|
|
10881
|
-
|
|
10882
|
-
|
|
10770
|
+
/*
|
|
10771
|
+
if(isExtreme && p0.isCorner && !isLong && !isCorner){
|
|
10772
|
+
isExtreme= false
|
|
10773
|
+
p1.isExtreme = false
|
|
10774
|
+
p1.isHorizontal = false
|
|
10775
|
+
p1.isVertical = false
|
|
10883
10776
|
|
|
10884
|
-
|
|
10885
|
-
p0.isExtreme = true;
|
|
10886
|
-
isExtreme = true;
|
|
10777
|
+
continue;
|
|
10887
10778
|
}
|
|
10779
|
+
*/
|
|
10888
10780
|
|
|
10889
|
-
|
|
10890
|
-
|
|
10891
|
-
|
|
10892
|
-
|
|
10893
|
-
|
|
10894
|
-
|
|
10895
|
-
isExtreme = true;
|
|
10781
|
+
/*
|
|
10782
|
+
if(isExtreme && p2.isCorner && !isLong && !isCorner && dist<threshShort*0.5){
|
|
10783
|
+
isExtreme= false
|
|
10784
|
+
p1.isExtreme = false
|
|
10785
|
+
p1.isHorizontal = false
|
|
10786
|
+
p1.isVertical = false
|
|
10896
10787
|
|
|
10788
|
+
if(isVertical){
|
|
10789
|
+
p2.tangentL.x = p2.x
|
|
10790
|
+
}
|
|
10791
|
+
if(isHorizontal){
|
|
10792
|
+
p2.tangentL.y = p2.y
|
|
10793
|
+
}
|
|
10794
|
+
continue;
|
|
10897
10795
|
}
|
|
10796
|
+
*/
|
|
10898
10797
|
|
|
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);
|
|
10798
|
+
if (isExtreme && !isCorner && p2.isExtreme) {
|
|
10799
|
+
extremes.push(p1);
|
|
10914
10800
|
|
|
10915
|
-
let
|
|
10916
|
-
|
|
10801
|
+
for (let j = i + 1; j < l; j++) {
|
|
10802
|
+
let p2 = pts[j];
|
|
10803
|
+
dist = getDistManhattan(p1, p2);
|
|
10917
10804
|
|
|
10918
|
-
isCorner
|
|
10805
|
+
if (dist * 0.75 >= threshShort || p2.isCorner || p2.isDirChange) {
|
|
10806
|
+
break
|
|
10807
|
+
}
|
|
10808
|
+
if (p2.isExtreme && !p2.isDirChange && !p2.isCorner) {
|
|
10809
|
+
extremes.push(p2);
|
|
10810
|
+
}
|
|
10919
10811
|
}
|
|
10920
10812
|
|
|
10921
|
-
|
|
10813
|
+
if (extremes.length > 1) {
|
|
10922
10814
|
|
|
10923
|
-
|
|
10924
|
-
|
|
10925
|
-
if (debug) {
|
|
10815
|
+
// find best extreme according to angle
|
|
10816
|
+
let angleDiffMin = Infinity;
|
|
10926
10817
|
|
|
10927
|
-
|
|
10928
|
-
renderPoint(markers, p1, 'blue', '2%', '0.5')
|
|
10929
|
-
renderPoint(markers, p0, 'blue', '2%', '0.5')
|
|
10930
|
-
}
|
|
10818
|
+
let bestMatch = extremes[0];
|
|
10931
10819
|
|
|
10932
|
-
|
|
10933
|
-
|
|
10934
|
-
}
|
|
10820
|
+
|
|
10821
|
+
extremes.forEach(pt => {
|
|
10935
10822
|
|
|
10936
|
-
|
|
10937
|
-
|
|
10938
|
-
|
|
10823
|
+
let angle = Math.abs(getAngleFromDelta(pt.dx2, pt.dy2, false)) * rad2Deg;
|
|
10824
|
+
let angleDiff = angle > 160 ? Math.abs(180 - angle) : (angle > 60 ? Math.abs(90 - angle) : angle);
|
|
10825
|
+
pt.angle = angle;
|
|
10826
|
+
pt.angleDiff = angleDiff;
|
|
10939
10827
|
|
|
10940
|
-
|
|
10941
|
-
|
|
10942
|
-
|
|
10943
|
-
}
|
|
10944
|
-
*/
|
|
10828
|
+
if (angleDiff < angleDiffMin) {
|
|
10829
|
+
bestMatch = pt;
|
|
10830
|
+
angleDiffMin = angleDiff;
|
|
10945
10831
|
|
|
10946
|
-
|
|
10947
|
-
|
|
10948
|
-
p1.isHorizontal = isHorizontal;
|
|
10949
|
-
p1.isVertical = isVertical;
|
|
10950
|
-
p1.isDirChange = isDirChange;
|
|
10832
|
+
}
|
|
10833
|
+
});
|
|
10951
10834
|
|
|
10952
|
-
|
|
10835
|
+
let extremes2 = [];
|
|
10953
10836
|
|
|
10954
|
-
|
|
10955
|
-
let pts1 = [];
|
|
10956
|
-
let exclude = [];
|
|
10837
|
+
extremes.forEach((pt, i) => {
|
|
10957
10838
|
|
|
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;
|
|
10839
|
+
let isBestMatch = pt === bestMatch;
|
|
10963
10840
|
|
|
10964
|
-
|
|
10841
|
+
if (isBestMatch) {
|
|
10965
10842
|
|
|
10966
|
-
|
|
10967
|
-
|
|
10968
|
-
|
|
10843
|
+
if (pt.isHorizontal) {
|
|
10844
|
+
pt.tangentL.y = pt.y;
|
|
10845
|
+
pt.tangentR.y = pt.y;
|
|
10846
|
+
}
|
|
10847
|
+
if (pt.isVertical) {
|
|
10848
|
+
pt.tangentL.x = pt.x;
|
|
10849
|
+
pt.tangentR.x = pt.x;
|
|
10850
|
+
}
|
|
10969
10851
|
|
|
10970
|
-
|
|
10971
|
-
extremes.push(p, p1);
|
|
10852
|
+
// renderPoint(markers, pt, 'green', '3%', '0.5')
|
|
10972
10853
|
|
|
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
|
-
}
|
|
10854
|
+
}
|
|
10855
|
+
else {
|
|
10981
10856
|
|
|
10982
|
-
|
|
10983
|
-
// average extreme
|
|
10857
|
+
if (bestMatch) {
|
|
10984
10858
|
|
|
10985
|
-
|
|
10986
|
-
|
|
10859
|
+
if (!isBestMatch && (pt.x === bestMatch.x || pt.y === bestMatch.y)) {
|
|
10860
|
+
extremes2.push(pt);
|
|
10861
|
+
}
|
|
10862
|
+
pt.isExtreme = false;
|
|
10863
|
+
pt.isHorizontal = false;
|
|
10864
|
+
pt.isVertical = false;
|
|
10865
|
+
}
|
|
10987
10866
|
|
|
10988
|
-
|
|
10989
|
-
p.y = y;
|
|
10867
|
+
}
|
|
10990
10868
|
|
|
10991
|
-
|
|
10992
|
-
}
|
|
10993
|
-
}
|
|
10869
|
+
});
|
|
10994
10870
|
|
|
10995
|
-
|
|
10996
|
-
|
|
10997
|
-
|
|
10871
|
+
// average coordinates
|
|
10872
|
+
if (extremes2.length) {
|
|
10873
|
+
bestMatch.x = (extremes2[0].x + bestMatch.x) * 0.5;
|
|
10874
|
+
bestMatch.y = (extremes2[0].y + bestMatch.y) * 0.5;
|
|
10998
10875
|
|
|
10999
|
-
|
|
10876
|
+
}
|
|
11000
10877
|
|
|
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;
|
|
10878
|
+
i += extremes.length;
|
|
10879
|
+
continue;
|
|
11009
10880
|
}
|
|
11010
10881
|
}
|
|
11011
10882
|
|
|
11012
|
-
pts = pts1;
|
|
11013
10883
|
}
|
|
11014
10884
|
|
|
11015
|
-
return pts
|
|
11016
10885
|
}
|
|
11017
10886
|
|
|
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];
|
|
10887
|
+
function cleanupPolyKeypoints(pts = []) {
|
|
11029
10888
|
|
|
11030
10889
|
let l = pts.length;
|
|
11031
10890
|
|
|
11032
|
-
|
|
10891
|
+
getPolyBBox(pts);
|
|
10892
|
+
|
|
10893
|
+
pts[0];
|
|
10894
|
+
|
|
11033
10895
|
for (let i = 1; i < l; i++) {
|
|
11034
|
-
i > 0 ? pts[i] : pts[l - 1];
|
|
10896
|
+
i > 0 ? pts[i - 1] : pts[l - 1];
|
|
11035
10897
|
let p1 = pts[i];
|
|
11036
10898
|
i < l - 1 ? pts[i + 1] : pts[l - 1];
|
|
11037
10899
|
|
|
11038
|
-
|
|
11039
|
-
|
|
11040
|
-
if ((keepExtremes && p1.isExtreme || keepCorners && p1.isCorner )) {
|
|
11041
|
-
idx++;
|
|
11042
|
-
chunks.push([]);
|
|
11043
|
-
}
|
|
10900
|
+
let { isHorizontal, isVertical, isCorner, isLong, isExtreme, isSemiExtreme, isDirChange } = p1;
|
|
10901
|
+
let offset = 0;
|
|
11044
10902
|
|
|
11045
|
-
|
|
11046
|
-
lastChunk.push(p1);
|
|
11047
|
-
}
|
|
10903
|
+
if (!isSemiExtreme){
|
|
11048
10904
|
|
|
11049
|
-
|
|
10905
|
+
continue
|
|
10906
|
+
}
|
|
11050
10907
|
|
|
11051
|
-
|
|
11052
|
-
|
|
10908
|
+
if (isSemiExtreme || isExtreme) {
|
|
10909
|
+
let semiExtremes = isSemiExtreme ? [p1] : [];
|
|
11053
10910
|
|
|
11054
|
-
|
|
11055
|
-
|
|
11056
|
-
*
|
|
11057
|
-
*/
|
|
11058
|
-
function fitCurveSchneider(pts, {
|
|
11059
|
-
maxError = 0,
|
|
11060
|
-
adjustCpts = true,
|
|
11061
|
-
harmonize = true,
|
|
11062
|
-
keepCorners = true
|
|
11063
|
-
} = {}) {
|
|
10911
|
+
for (let j = i + 1; j < l; j++) {
|
|
10912
|
+
let p2 = pts[j];
|
|
11064
10913
|
|
|
11065
|
-
|
|
11066
|
-
|
|
11067
|
-
|
|
10914
|
+
if (!p2.isSemiExtreme || p2.isExtreme || p2.isCorner){
|
|
10915
|
+
break
|
|
10916
|
+
}
|
|
10917
|
+
semiExtremes.push(p2);
|
|
10918
|
+
}
|
|
11068
10919
|
|
|
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
|
-
}
|
|
10920
|
+
if (semiExtremes.length > 1) {
|
|
11076
10921
|
|
|
11077
|
-
|
|
10922
|
+
let semiExtremeMid = semiExtremes[Math.floor(semiExtremes.length*0.5)];
|
|
10923
|
+
let p1_1 = semiExtremes[0];
|
|
10924
|
+
let p2_1 = semiExtremes[semiExtremes.length - 1];
|
|
10925
|
+
let ptI = checkLineIntersection(p1_1, p1_1.tangentR, p2_1, p2_1.tangentL, false, true);
|
|
11078
10926
|
|
|
11079
|
-
|
|
11080
|
-
|
|
11081
|
-
|
|
11082
|
-
|
|
10927
|
+
semiExtremes.forEach(pt=>{
|
|
10928
|
+
pt.isSemiExtreme=false;
|
|
10929
|
+
});
|
|
10930
|
+
semiExtremeMid.isSemiExtreme=true;
|
|
11083
10931
|
|
|
11084
|
-
|
|
11085
|
-
|
|
11086
|
-
|
|
10932
|
+
// interpolate mid point
|
|
10933
|
+
if (ptI) {
|
|
10934
|
+
let pI_1 = interpolate(p1_1, ptI, 0.5);
|
|
10935
|
+
let pI_2 = interpolate(p2_1, ptI, 0.5);
|
|
10936
|
+
let pI_3 = interpolate(pI_2, pI_1, 0.5);
|
|
11087
10937
|
|
|
11088
|
-
|
|
10938
|
+
semiExtremeMid.x = pI_3.x;
|
|
10939
|
+
semiExtremeMid.y = pI_3.y;
|
|
10940
|
+
semiExtremeMid.tangentL = pI_1;
|
|
10941
|
+
semiExtremeMid.tangentR = pI_2;
|
|
11089
10942
|
|
|
11090
|
-
|
|
10943
|
+
i += offset;
|
|
10944
|
+
continue
|
|
10945
|
+
}
|
|
11091
10946
|
|
|
11092
|
-
|
|
10947
|
+
}
|
|
10948
|
+
}
|
|
10949
|
+
// find significant of same type
|
|
11093
10950
|
|
|
11094
|
-
|
|
11095
|
-
let com1 = pathData[0];
|
|
10951
|
+
}
|
|
11096
10952
|
|
|
11097
|
-
|
|
11098
|
-
|
|
10953
|
+
/*
|
|
10954
|
+
// update index
|
|
10955
|
+
ptsClean.forEach((pt, i) => {
|
|
10956
|
+
pt.idx = i
|
|
10957
|
+
})
|
|
10958
|
+
*/
|
|
11099
10959
|
|
|
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;
|
|
10960
|
+
return pts;
|
|
11103
10961
|
|
|
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
|
-
}
|
|
10962
|
+
}
|
|
11110
10963
|
|
|
11111
|
-
|
|
11112
|
-
|
|
11113
|
-
|
|
10964
|
+
function adjustTangentAngle(cp, p0, p1, p2) {
|
|
10965
|
+
let ang1 = getAngle(p0, p1);
|
|
10966
|
+
let ang2 = getAngle(p0, p2);
|
|
10967
|
+
let angDiff = (ang2 - ang1);
|
|
11114
10968
|
|
|
11115
|
-
|
|
11116
|
-
|
|
11117
|
-
cp2 = adjustTangentAngle(cp2, pL, pL1, pL2);
|
|
11118
|
-
com2.values[2] = cp2.x;
|
|
11119
|
-
com2.values[3] = cp2.y;
|
|
11120
|
-
}
|
|
10969
|
+
let f = 0.666;
|
|
10970
|
+
f = 1;
|
|
11121
10971
|
|
|
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
|
-
*/
|
|
10972
|
+
cp = rotatePoint(cp, p0.x, p0.y, -angDiff * f);
|
|
10973
|
+
return cp
|
|
10974
|
+
}
|
|
11132
10975
|
|
|
11133
|
-
|
|
10976
|
+
function getTangents(pts = [], {
|
|
10977
|
+
x = 0,
|
|
10978
|
+
y = 0,
|
|
10979
|
+
width = 0,
|
|
10980
|
+
height = 0,
|
|
10981
|
+
debug = false,
|
|
10982
|
+
closed=false,
|
|
10983
|
+
} = {}) {
|
|
11134
10984
|
|
|
11135
|
-
|
|
11136
|
-
}
|
|
10985
|
+
let l = pts.length;
|
|
11137
10986
|
|
|
11138
|
-
|
|
11139
|
-
|
|
11140
|
-
|
|
11141
|
-
|
|
11142
|
-
*/
|
|
11143
|
-
function fitCubic(pts, leftTangent, rightTangent, error, keepCorners = false) {
|
|
10987
|
+
// bounding box of this sub poly
|
|
10988
|
+
if (!width || !height) {
|
|
10989
|
+
({ x, y, width, height } = getPolyBBox(pts));
|
|
10990
|
+
}
|
|
11144
10991
|
|
|
11145
|
-
|
|
11146
|
-
let bezCurve;
|
|
10992
|
+
// threshold for horizontal or vertical detection
|
|
11147
10993
|
|
|
11148
|
-
|
|
10994
|
+
for (let i = 0; i < l; i++) {
|
|
10995
|
+
let p0 = i > 0 ? pts[i - 1] : pts[l - 1];
|
|
10996
|
+
let p1 = pts[i];
|
|
10997
|
+
let p2 = i < l - 1 ? pts[i + 1] : pts[l - 1];
|
|
10998
|
+
let p3 = i < l - 1 ? pts[i + 2] : pts[l - 1];
|
|
11149
10999
|
|
|
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
|
-
*/
|
|
11000
|
+
let { isHorizontal, isVertical, isCorner, isLong, isExtreme, isSemiExtreme, isDirChange } = p1;
|
|
11156
11001
|
|
|
11157
|
-
|
|
11158
|
-
|
|
11002
|
+
// default
|
|
11003
|
+
let tangentL = { x: p1.x - p1.dx2 * 0.5, y: p1.y - p1.dy2 * 0.5 };
|
|
11004
|
+
let tangentR = { x: p1.x + p1.dx2 * 0.5, y: p1.y + p1.dy2 * 0.5 };
|
|
11159
11005
|
|
|
11160
|
-
|
|
11006
|
+
// average first tangent
|
|
11007
|
+
if(i===0){
|
|
11008
|
+
tangentR = adjustTangentAngle(p2, p1, p2, p3);
|
|
11009
|
+
}
|
|
11161
11010
|
|
|
11162
|
-
|
|
11163
|
-
|
|
11011
|
+
/**
|
|
11012
|
+
* add left and right tangents
|
|
11013
|
+
* for later curve fitting
|
|
11014
|
+
*/
|
|
11164
11015
|
|
|
11165
|
-
|
|
11166
|
-
|
|
11167
|
-
|
|
11168
|
-
|
|
11169
|
-
if (
|
|
11170
|
-
|
|
11171
|
-
|
|
11016
|
+
if (isHorizontal && !isCorner) {
|
|
11017
|
+
tangentL = { x: p1.x - p1.dx2*0.5, y: p1.y };
|
|
11018
|
+
tangentR = { x: p1.x + p1.dx2*0.5, y: p1.y };
|
|
11019
|
+
}
|
|
11020
|
+
else if (isVertical) {
|
|
11021
|
+
tangentL = { x: p1.x , y: p1.y - p1.dy2*0.5 };
|
|
11022
|
+
tangentR = { x: p1.x , y: p1.y + p1.dy2*0.5 };
|
|
11172
11023
|
}
|
|
11173
|
-
}
|
|
11174
11024
|
|
|
11175
|
-
|
|
11176
|
-
|
|
11177
|
-
|
|
11025
|
+
if (!isExtreme && p1.isLong) {
|
|
11026
|
+
tangentL = { x: p1.x - p1.dx*0.5, y: p1.y - p1.dy*0.5 };
|
|
11027
|
+
tangentR = { x: p1.x + p1.dx*0.5, y: p1.y + p1.dy*0.5 };
|
|
11028
|
+
}
|
|
11178
11029
|
|
|
11179
|
-
|
|
11030
|
+
/*
|
|
11180
11031
|
|
|
11181
|
-
|
|
11182
|
-
|
|
11183
|
-
|
|
11032
|
+
if (isDirChange && !isCorner && !isExtreme) {
|
|
11033
|
+
p1.tangentL = { x: p1.x-p1.dx2*0.5, y: p1.y-p1.dy2*0.5 }
|
|
11034
|
+
p1.tangentR = { x: p1.x+p1.dx2*0.5, y: p1.y+p1.dy2*0.5 }
|
|
11035
|
+
}
|
|
11036
|
+
*/
|
|
11184
11037
|
|
|
11185
|
-
|
|
11038
|
+
if (isCorner) {
|
|
11186
11039
|
|
|
11187
|
-
|
|
11040
|
+
tangentL = {x:p0.x, y:p0.y};
|
|
11041
|
+
tangentR = {x:p2.x, y:p2.y};
|
|
11188
11042
|
|
|
11189
|
-
let
|
|
11043
|
+
let p0_1 = pts[i - 2] ? pts[i - 2] : pts[l - 1];
|
|
11190
11044
|
|
|
11191
|
-
|
|
11192
|
-
maxError = _generateAndReport2[1];
|
|
11193
|
-
splitPoint = _generateAndReport2[2];
|
|
11045
|
+
let p2_1 = pts[i + 2] ? pts[i + 2] : pts[1];
|
|
11194
11046
|
|
|
11195
|
-
|
|
11196
|
-
|
|
11047
|
+
// adjust angle
|
|
11048
|
+
if (!p0.isCorner) {
|
|
11049
|
+
tangentL = adjustTangentAngle(p0, p1, p0, p0_1);
|
|
11197
11050
|
}
|
|
11198
11051
|
|
|
11199
|
-
|
|
11200
|
-
|
|
11201
|
-
if (errChange > .9999 && errChange < 1.0001) {
|
|
11202
|
-
break;
|
|
11203
|
-
}
|
|
11052
|
+
if (!p2.isCorner) {
|
|
11053
|
+
tangentR = adjustTangentAngle(tangentR, p1, p2, p2_1);
|
|
11204
11054
|
}
|
|
11205
11055
|
|
|
11206
|
-
|
|
11207
|
-
|
|
11056
|
+
/*
|
|
11057
|
+
renderPoint(markers, p0, 'darkblue', '0.75%', '0.5')
|
|
11058
|
+
// renderPoint(markers, p0_1, 'blue', '0.5%')
|
|
11059
|
+
renderPoint(markers, tangentL, 'blue', '0.5%', '0.5')
|
|
11060
|
+
renderPoint(markers, tangentR, 'blue', '0.5%', '0.5')
|
|
11061
|
+
*/
|
|
11062
|
+
|
|
11208
11063
|
}
|
|
11209
|
-
}
|
|
11210
11064
|
|
|
11211
|
-
|
|
11212
|
-
|
|
11065
|
+
p1.tangentL = tangentL;
|
|
11066
|
+
p1.tangentR = tangentR;
|
|
11067
|
+
|
|
11068
|
+
/*
|
|
11069
|
+
debug = true
|
|
11070
|
+
if(debug){
|
|
11071
|
+
if (isCorner || isSemiExtreme || isDirChange || isExtreme) {
|
|
11072
|
+
renderPoint(markers, p1.tangentL, 'darkred', '0.5%')
|
|
11073
|
+
renderPoint(markers, p1.tangentR, 'darkblue', '0.5%')
|
|
11074
|
+
}
|
|
11075
|
+
}
|
|
11076
|
+
*/
|
|
11213
11077
|
|
|
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
11078
|
}
|
|
11220
11079
|
|
|
11221
|
-
|
|
11080
|
+
}
|
|
11222
11081
|
|
|
11223
|
-
|
|
11082
|
+
function getPolyCentroid(pts) {
|
|
11224
11083
|
|
|
11225
|
-
|
|
11084
|
+
let l = pts.length;
|
|
11085
|
+
let x = 0, y = 0;
|
|
11086
|
+
for (let i = 0; l && i < l; i++) {
|
|
11087
|
+
let pt = pts[i];
|
|
11088
|
+
x += pt.x;
|
|
11089
|
+
y += pt.y;
|
|
11090
|
+
}
|
|
11226
11091
|
|
|
11227
|
-
|
|
11228
|
-
|
|
11229
|
-
...fitCubic(pts.slice(splitPoint), fromCenterTangent, rightTangent, error, keepCorners)
|
|
11230
|
-
);
|
|
11092
|
+
let centroid = { x: x / l, y: y / l };
|
|
11093
|
+
return centroid
|
|
11231
11094
|
|
|
11232
|
-
return beziers;
|
|
11233
11095
|
}
|
|
11234
11096
|
|
|
11235
|
-
|
|
11236
|
-
|
|
11237
|
-
|
|
11238
|
-
function generateBezier(pts, parameters, leftTangent, rightTangent) {
|
|
11097
|
+
function detectRegularPolygon(pts, centroid = { x: 0, y: 0 }) {
|
|
11098
|
+
let rSq = getSquareDistance(pts[0], centroid);
|
|
11099
|
+
let isRegular = true;
|
|
11239
11100
|
|
|
11240
|
-
let
|
|
11101
|
+
for (let i = 1, l = pts.length; i < l; i++) {
|
|
11102
|
+
let pt1 = pts[i];
|
|
11103
|
+
let dist = getSquareDistance(pt1, centroid);
|
|
11241
11104
|
|
|
11242
|
-
|
|
11243
|
-
|
|
11244
|
-
let len = parameters.length;
|
|
11105
|
+
let diff = Math.abs(rSq - dist);
|
|
11106
|
+
let diffRel = diff / rSq;
|
|
11245
11107
|
|
|
11246
|
-
|
|
11247
|
-
|
|
11248
|
-
|
|
11249
|
-
a = A[i];
|
|
11108
|
+
if (diffRel > 0.05) {
|
|
11109
|
+
return false;
|
|
11110
|
+
}
|
|
11250
11111
|
|
|
11251
|
-
a[0] = mulItems(leftTangent, 3 * u * (ux * ux));
|
|
11252
|
-
a[1] = mulItems(rightTangent, 3 * ux * (u * u));
|
|
11253
11112
|
}
|
|
11113
|
+
return isRegular;
|
|
11114
|
+
}
|
|
11115
|
+
|
|
11116
|
+
function analyzePoly(pts, {
|
|
11117
|
+
x = 0,
|
|
11118
|
+
y = 0,
|
|
11119
|
+
width = 0,
|
|
11120
|
+
height = 0,
|
|
11121
|
+
debug = false
|
|
11122
|
+
} = {}) {
|
|
11254
11123
|
|
|
11255
|
-
let C = [[0, 0], [0, 0]];
|
|
11256
|
-
let X = [0, 0];
|
|
11257
11124
|
let l = pts.length;
|
|
11125
|
+
let left = x;
|
|
11126
|
+
let top = y;
|
|
11127
|
+
let right = x + width;
|
|
11128
|
+
let bottom = y + height;
|
|
11258
11129
|
|
|
11259
|
-
|
|
11260
|
-
|
|
11261
|
-
|
|
11130
|
+
if (!width || !height) {
|
|
11131
|
+
({ x, y, width, height, top, bottom, left, right } = getPolyBBox(pts));
|
|
11132
|
+
}
|
|
11262
11133
|
|
|
11263
|
-
|
|
11264
|
-
|
|
11265
|
-
C[1][0] += dot(a[0], a[1]);
|
|
11266
|
-
C[1][1] += dot(a[1], a[1]);
|
|
11134
|
+
// round
|
|
11135
|
+
[x, y, width, height, top, bottom, left, right] = [x, y, width, height, top, bottom, left, right].map(val => +val.toFixed(8));
|
|
11267
11136
|
|
|
11268
|
-
|
|
11137
|
+
// bounding box of this sub poly
|
|
11138
|
+
let bb0 = { x, y, top, left, width, height, right, bottom };
|
|
11269
11139
|
|
|
11270
|
-
|
|
11271
|
-
X[1] += dot(a[1], tmp);
|
|
11272
|
-
}
|
|
11140
|
+
let thresh = (width + height) * 0.01;
|
|
11273
11141
|
|
|
11274
|
-
|
|
11275
|
-
let
|
|
11276
|
-
let det_X_C1 = X[0] * C[1][1] - X[1] * C[0][1];
|
|
11142
|
+
// threshold for horizontal or vertical detection
|
|
11143
|
+
let thresh2 = thresh * 0.75;
|
|
11277
11144
|
|
|
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;
|
|
11145
|
+
let dims = [];
|
|
11282
11146
|
|
|
11283
|
-
|
|
11147
|
+
/*
|
|
11148
|
+
pts.forEach(pt=>{
|
|
11149
|
+
renderPoint(markers, pt, 'red', '2.5%')
|
|
11150
|
+
})
|
|
11151
|
+
*/
|
|
11152
|
+
|
|
11153
|
+
/**
|
|
11154
|
+
* 1st run:
|
|
11155
|
+
* collect more details
|
|
11156
|
+
* area for sign change detection
|
|
11157
|
+
* deltas and distances
|
|
11158
|
+
*/
|
|
11159
|
+
for (let i = 0; i < l; i++) {
|
|
11160
|
+
let p0 = i > 0 ? pts[i - 1] : pts[l - 1];
|
|
11161
|
+
let p1 = pts[i];
|
|
11162
|
+
let p2 = i < l - 1 ? pts[i + 1] : pts[l - 1];
|
|
11163
|
+
|
|
11164
|
+
let area = getPolygonArea([p0, p1, p2], false);
|
|
11165
|
+
let dx = i > 0 ? +(p1.x - p0.x).toFixed(7) : 0;
|
|
11166
|
+
let dy = i > 0 ? +(p1.y - p0.y).toFixed(7) : 0;
|
|
11167
|
+
|
|
11168
|
+
let dx2 = +(p2.x - p0.x).toFixed(7);
|
|
11169
|
+
let dy2 = +(p2.y - p0.y).toFixed(7);
|
|
11170
|
+
|
|
11171
|
+
p1.area = area;
|
|
11172
|
+
p1.dist = i > 0 ? getDistManhattan(p0, p1) : 0;
|
|
11173
|
+
// add dist for long/short segment detection
|
|
11174
|
+
dims.push(p1.dist);
|
|
11175
|
+
p1.idx = i;
|
|
11176
|
+
p1.dx = dx;
|
|
11177
|
+
p1.dy = dy;
|
|
11178
|
+
p1.dx2 = dx2;
|
|
11179
|
+
p1.dy2 = dy2;
|
|
11284
11180
|
|
|
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
11181
|
}
|
|
11292
11182
|
|
|
11293
|
-
|
|
11294
|
-
|
|
11183
|
+
/**
|
|
11184
|
+
* find average segment length
|
|
11185
|
+
* for long/short segment detection
|
|
11186
|
+
*/
|
|
11187
|
+
dims = dims.filter(Boolean).sort((a, b) => a - b);
|
|
11188
|
+
let lenD = dims.length;
|
|
11189
|
+
let dimMin = dims[0];
|
|
11190
|
+
dims[lenD - 1];
|
|
11295
11191
|
|
|
11296
|
-
|
|
11297
|
-
let
|
|
11192
|
+
let dimAv = dims.reduce((a, b) => a + b, 0) / lenD;
|
|
11193
|
+
let dimShort = (dimMin + dimAv) * 0.5;
|
|
11194
|
+
let dimLong = dimAv * 2;
|
|
11298
11195
|
|
|
11299
|
-
|
|
11300
|
-
|
|
11196
|
+
/*
|
|
11197
|
+
// round to adjust for minor deviations
|
|
11198
|
+
let idx_q = Math.ceil(lenD * 0.25);
|
|
11199
|
+
let dim_mid = dims[Math.floor(lenD * 0.5)]
|
|
11200
|
+
let dims_min = dims.slice(0, Math.floor(lenD * 0.25));
|
|
11201
|
+
let dim_min = ((dims_min.reduce((a, b) => a + b, 0) / idx_q) + dim_mid) * 0.5;
|
|
11301
11202
|
|
|
11302
|
-
|
|
11303
|
-
|
|
11203
|
+
let threshold = 75
|
|
11204
|
+
let decimalsAuto = dim_min > threshold * 1.5 ? 0 : Math.floor(threshold / dim_min).toString().length
|
|
11304
11205
|
|
|
11305
|
-
|
|
11306
|
-
|
|
11206
|
+
// clamp
|
|
11207
|
+
decimalsAuto = Math.min(Math.max(0, decimalsAuto), 8)
|
|
11307
11208
|
|
|
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
|
-
*/
|
|
11209
|
+
pts = roundPoly(pts, 2)
|
|
11210
|
+
console.log(pts);
|
|
11211
|
+
*/
|
|
11319
11212
|
|
|
11320
|
-
|
|
11321
|
-
|
|
11322
|
-
|
|
11323
|
-
|
|
11213
|
+
/**
|
|
11214
|
+
* analyze topology:
|
|
11215
|
+
* find significant commands:
|
|
11216
|
+
* extremes, inflections etc.
|
|
11217
|
+
*/
|
|
11218
|
+
for (let i = 0; i < l; i++) {
|
|
11324
11219
|
|
|
11325
|
-
|
|
11220
|
+
let p0 = i > 0 ? pts[i - 1] : pts[l - 1];
|
|
11221
|
+
let p1 = pts[i];
|
|
11222
|
+
let p2 = i < l - 1 ? pts[i + 1] : pts[l - 1];
|
|
11326
11223
|
|
|
11327
|
-
|
|
11224
|
+
// convert area to absolute for flatness checks
|
|
11225
|
+
let area1 = Math.abs(p1.area);
|
|
11226
|
+
let isCorner = false;
|
|
11227
|
+
let isSemiExtreme = false;
|
|
11228
|
+
let isShort = false;
|
|
11229
|
+
let isLong = false;
|
|
11328
11230
|
|
|
11329
|
-
|
|
11330
|
-
|
|
11231
|
+
/**
|
|
11232
|
+
* detect short or long
|
|
11233
|
+
*/
|
|
11234
|
+
if (p1.dist > dimLong) {
|
|
11235
|
+
isLong = true;
|
|
11236
|
+
}
|
|
11331
11237
|
|
|
11332
|
-
|
|
11333
|
-
|
|
11238
|
+
if (p1.dist < dimShort) {
|
|
11239
|
+
isShort = true;
|
|
11240
|
+
}
|
|
11334
11241
|
|
|
11335
|
-
|
|
11336
|
-
// This represents how much the error aligns with the tangent
|
|
11337
|
-
let numerator = dx * qp[0] + dy * qp[1];
|
|
11242
|
+
let flat = !p1.area || area1 < thresh;
|
|
11338
11243
|
|
|
11339
|
-
|
|
11340
|
-
|
|
11341
|
-
|
|
11244
|
+
/**
|
|
11245
|
+
* check extremes
|
|
11246
|
+
*/
|
|
11247
|
+
let isExtreme = false;
|
|
11342
11248
|
|
|
11343
|
-
|
|
11344
|
-
|
|
11249
|
+
// 1. total extreme
|
|
11250
|
+
let isTop = p1.y === bb0.top;
|
|
11251
|
+
let isBottom = p1.y === bb0.bottom;
|
|
11252
|
+
let isLeft = p1.x === bb0.left;
|
|
11253
|
+
let isRight = p1.x === bb0.right;
|
|
11345
11254
|
|
|
11346
|
-
|
|
11347
|
-
|
|
11255
|
+
if (isTop || isBottom || isLeft || isRight) {
|
|
11256
|
+
isExtreme = true;
|
|
11348
11257
|
|
|
11349
|
-
|
|
11258
|
+
}
|
|
11350
11259
|
|
|
11351
|
-
|
|
11352
|
-
|
|
11353
|
-
|
|
11260
|
+
// 1.2 horizontal or vertical
|
|
11261
|
+
/*
|
|
11262
|
+
let isHorizontal = isTop || isBottom || (p1.y === p0.y && p1.x !== p0.x);
|
|
11263
|
+
let isVertical = isLeft || isRight || (p1.x === p0.x && p1.y !== p0.y)
|
|
11354
11264
|
|
|
11355
|
-
|
|
11356
|
-
return u - numerator / denominator;
|
|
11357
|
-
}
|
|
11265
|
+
if ((isHorizontal || isVertical)) {
|
|
11358
11266
|
|
|
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;
|
|
11267
|
+
let diffX = Math.abs(p0.x - p1.x)
|
|
11268
|
+
let diffY = Math.abs(p0.y - p1.y)
|
|
11369
11269
|
|
|
11370
|
-
|
|
11371
|
-
|
|
11270
|
+
if (isLong) {
|
|
11271
|
+
}
|
|
11372
11272
|
|
|
11373
|
-
|
|
11374
|
-
|
|
11375
|
-
|
|
11273
|
+
if (isLong && (diffY < thresh2) && diffX > thresh) {
|
|
11274
|
+
p0.isExtreme = true;
|
|
11275
|
+
p0.isHorizontal = true;
|
|
11276
|
+
}
|
|
11277
|
+
else if (isLong && (diffX < thresh2) && diffY > thresh) {
|
|
11278
|
+
p0.isExtreme = true;
|
|
11279
|
+
p0.isVertical = true;
|
|
11280
|
+
}
|
|
11376
11281
|
|
|
11377
|
-
|
|
11378
|
-
|
|
11282
|
+
isExtreme = true
|
|
11283
|
+
}
|
|
11284
|
+
*/
|
|
11379
11285
|
|
|
11380
|
-
|
|
11381
|
-
|
|
11382
|
-
});
|
|
11286
|
+
let dx = Math.abs(p0.x - p1.x);
|
|
11287
|
+
let dy = Math.abs(p0.y - p1.y);
|
|
11383
11288
|
|
|
11384
|
-
|
|
11385
|
-
|
|
11386
|
-
|
|
11387
|
-
|
|
11388
|
-
*/
|
|
11389
|
-
function computeMaxError(pts, bez, parameters) {
|
|
11390
|
-
let dist,
|
|
11391
|
-
maxDist,
|
|
11392
|
-
splitPoint,
|
|
11393
|
-
i, point, t;
|
|
11289
|
+
let vh_thresh = thresh * 0.05;
|
|
11290
|
+
// vh_thresh = thresh * 0.25
|
|
11291
|
+
let isHorizontal = isTop || isBottom || (p1.y === p0.y && p1.x !== p0.x) || (dy <= vh_thresh);
|
|
11292
|
+
let isVertical = (isLeft || isRight || (p1.x === p0.x && p1.y !== p0.y) || (dx <= vh_thresh));
|
|
11394
11293
|
|
|
11395
|
-
|
|
11396
|
-
splitPoint = Math.floor(pts.length * 0.5);
|
|
11294
|
+
// renderPoint(markers, p1, 'red', '0.5%')
|
|
11397
11295
|
|
|
11398
|
-
|
|
11399
|
-
let l = pts.length;
|
|
11400
|
-
let ptOnPath = null;
|
|
11296
|
+
if (p1.y === p0.y) ;
|
|
11401
11297
|
|
|
11402
|
-
|
|
11403
|
-
point = pts[i];
|
|
11298
|
+
if ((isHorizontal || isVertical)) {
|
|
11404
11299
|
|
|
11405
|
-
|
|
11300
|
+
if (isLong && isHorizontal) {
|
|
11301
|
+
p0.isExtreme = true;
|
|
11302
|
+
p0.isHorizontal = true;
|
|
11406
11303
|
|
|
11407
|
-
|
|
11408
|
-
|
|
11304
|
+
}
|
|
11305
|
+
else if (isLong && isVertical) {
|
|
11306
|
+
p0.isExtreme = true;
|
|
11307
|
+
p0.isVertical = true;
|
|
11308
|
+
}
|
|
11409
11309
|
|
|
11410
|
-
|
|
11411
|
-
|
|
11412
|
-
v = subtract(pointAtT(bez, t), point);
|
|
11413
|
-
dist = v.x * v.x + v.y * v.y;
|
|
11414
|
-
*/
|
|
11310
|
+
isExtreme = true;
|
|
11311
|
+
}
|
|
11415
11312
|
|
|
11416
|
-
|
|
11313
|
+
// 1.3 is local or absolute extreme
|
|
11314
|
+
let bb = getPolyBBox([p0, p2]); // local bb
|
|
11315
|
+
let { left, right, top, bottom } = bb;
|
|
11316
|
+
|
|
11317
|
+
let extremeLocal = (p1.x < left || p1.x > right || p1.y < top || p1.y > bottom);
|
|
11318
|
+
if (!isExtreme && extremeLocal) {
|
|
11319
|
+
isExtreme = true;
|
|
11417
11320
|
|
|
11418
|
-
maxDist = dist;
|
|
11419
|
-
splitPoint = i;
|
|
11420
11321
|
}
|
|
11421
|
-
}
|
|
11422
11322
|
|
|
11423
|
-
|
|
11424
|
-
|
|
11323
|
+
/**
|
|
11324
|
+
* 2. sign changes
|
|
11325
|
+
*/
|
|
11326
|
+
let signChange = (p0.area < 0 && p1.area > 0) || (p0.area > 0 && p1.area < 0);
|
|
11327
|
+
let isDirChange = signChange && !flat && !p0.isDirChange && isLong;
|
|
11425
11328
|
|
|
11426
|
-
|
|
11427
|
-
|
|
11428
|
-
|
|
11429
|
-
let B_t_prev = bez[0];
|
|
11430
|
-
let sumLen = 0;
|
|
11329
|
+
/**
|
|
11330
|
+
* 3. corners
|
|
11331
|
+
*/
|
|
11431
11332
|
|
|
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
|
-
}
|
|
11333
|
+
if (isExtreme) {
|
|
11438
11334
|
|
|
11439
|
-
|
|
11440
|
-
|
|
11441
|
-
|
|
11442
|
-
return B_t_dist;
|
|
11443
|
-
}
|
|
11444
|
-
function find_t(param, t_distMap, B_parts) {
|
|
11335
|
+
let delta = getDeltaAngle(p1, p2, p0);
|
|
11336
|
+
let { deltaAngleDeg } = delta;
|
|
11337
|
+
deltaAngleDeg = Math.abs(deltaAngleDeg);
|
|
11445
11338
|
|
|
11446
|
-
|
|
11447
|
-
|
|
11448
|
-
}
|
|
11449
|
-
if (param > 1) {
|
|
11450
|
-
return 1;
|
|
11451
|
-
}
|
|
11339
|
+
let isCornerDelta = deltaAngleDeg > 10 && deltaAngleDeg < 160;
|
|
11340
|
+
if (isCornerDelta) {
|
|
11452
11341
|
|
|
11453
|
-
|
|
11342
|
+
isCorner = true;
|
|
11454
11343
|
|
|
11455
|
-
|
|
11344
|
+
}
|
|
11456
11345
|
|
|
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
11346
|
}
|
|
11465
|
-
}
|
|
11466
|
-
return t;
|
|
11467
|
-
}
|
|
11468
11347
|
|
|
11469
|
-
|
|
11348
|
+
if (isExtreme && !isCorner) {
|
|
11470
11349
|
|
|
11471
|
-
|
|
11472
|
-
|
|
11473
|
-
|
|
11350
|
+
if ((Math.abs(p1.dy2) < thresh2) && Math.abs(p1.dx2) > thresh) {
|
|
11351
|
+
isHorizontal = true;
|
|
11352
|
+
}
|
|
11353
|
+
else if (Math.abs(p1.dx2) < thresh2 && Math.abs(p1.dy2) > thresh) {
|
|
11354
|
+
isVertical = true;
|
|
11355
|
+
}
|
|
11356
|
+
}
|
|
11474
11357
|
|
|
11475
|
-
|
|
11476
|
-
|
|
11477
|
-
|
|
11358
|
+
/**
|
|
11359
|
+
* semi extremes
|
|
11360
|
+
* ~ 45deg tangent
|
|
11361
|
+
*/
|
|
11362
|
+
let diffX = Math.abs(p1.dx2);
|
|
11363
|
+
let diffY = Math.abs(p1.dy2);
|
|
11478
11364
|
|
|
11479
|
-
|
|
11480
|
-
|
|
11481
|
-
|
|
11482
|
-
|
|
11483
|
-
|
|
11365
|
+
let ratDelta = (diffX / diffY);
|
|
11366
|
+
|
|
11367
|
+
if (ratDelta > 0.8 && ratDelta <= 1.2) {
|
|
11368
|
+
isSemiExtreme = true;
|
|
11369
|
+
}
|
|
11484
11370
|
|
|
11485
|
-
|
|
11371
|
+
p1.isCorner = isCorner;
|
|
11372
|
+
p1.isExtreme = isExtreme;
|
|
11373
|
+
p1.isSemiExtreme = isSemiExtreme;
|
|
11374
|
+
p1.isLong = isLong;
|
|
11375
|
+
p1.isShort = isShort;
|
|
11486
11376
|
|
|
11487
|
-
|
|
11488
|
-
|
|
11377
|
+
p1.isHorizontal = isHorizontal;
|
|
11378
|
+
p1.isVertical = isVertical;
|
|
11379
|
+
p1.isDirChange = isDirChange;
|
|
11489
11380
|
|
|
11490
|
-
// flat line
|
|
11491
|
-
if (!polyArea && pts.length === 2) {
|
|
11492
|
-
polyArea = getSquareDistance(pts[0], pts[1]) * 0.01;
|
|
11493
11381
|
}
|
|
11494
11382
|
|
|
11495
|
-
|
|
11496
|
-
|
|
11383
|
+
// add tangents
|
|
11384
|
+
getTangents(pts, { x, y, width, height });
|
|
11497
11385
|
|
|
11498
|
-
|
|
11386
|
+
refineAdjacentPolyExtremes(pts);
|
|
11499
11387
|
|
|
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);
|
|
11388
|
+
// filter adjacent significant points
|
|
11389
|
+
cleanupPolyKeypoints(pts);
|
|
11508
11390
|
|
|
11509
|
-
|
|
11391
|
+
renderPolyTopology(pts);
|
|
11510
11392
|
|
|
11511
|
-
|
|
11393
|
+
return pts
|
|
11394
|
+
}
|
|
11512
11395
|
|
|
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
|
-
*/
|
|
11396
|
+
/*
|
|
11518
11397
|
|
|
11519
|
-
|
|
11520
|
-
bezierNew = bezCurve;
|
|
11521
|
-
}
|
|
11398
|
+
*/
|
|
11522
11399
|
|
|
11523
|
-
|
|
11524
|
-
|
|
11400
|
+
// just for visualization
|
|
11401
|
+
function renderPolyTopology(pts, showTangents = true) {
|
|
11525
11402
|
|
|
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);
|
|
11403
|
+
let l = pts.length;
|
|
11534
11404
|
|
|
11535
|
-
|
|
11536
|
-
|
|
11537
|
-
|
|
11405
|
+
// render
|
|
11406
|
+
for (let i = 0; i < l; i++) {
|
|
11407
|
+
i > 0 ? pts[i - 1] : pts[l - 1];
|
|
11408
|
+
let p1 = pts[i];
|
|
11409
|
+
i < l - 1 ? pts[i + 1] : pts[l - 1];
|
|
11538
11410
|
|
|
11539
|
-
|
|
11540
|
-
|
|
11541
|
-
|
|
11411
|
+
if (p1.isDirChange) {
|
|
11412
|
+
renderPoint(markers, p1, 'orange', '1%', '0.75');
|
|
11413
|
+
}
|
|
11542
11414
|
|
|
11543
|
-
|
|
11544
|
-
|
|
11545
|
-
|
|
11546
|
-
}
|
|
11415
|
+
if (p1.isSemiExtreme) {
|
|
11416
|
+
renderPoint(markers, p1, 'red', '1%', '0.5');
|
|
11417
|
+
}
|
|
11547
11418
|
|
|
11548
|
-
|
|
11549
|
-
|
|
11550
|
-
|
|
11419
|
+
/*
|
|
11420
|
+
if (p1.isLong && (p1.isDirChange || p1.isExtreme || p1.isCorner || p1.isSemiExtreme)) {
|
|
11421
|
+
renderPoint(markers, p1, 'green', '1.5%', '0.5')
|
|
11422
|
+
}
|
|
11423
|
+
*/
|
|
11551
11424
|
|
|
11552
|
-
|
|
11553
|
-
|
|
11554
|
-
|
|
11425
|
+
if (p1.isDirChange) {
|
|
11426
|
+
renderPoint(markers, p1, 'green', '1.5%', '0.5');
|
|
11427
|
+
}
|
|
11555
11428
|
|
|
11556
|
-
|
|
11557
|
-
|
|
11558
|
-
|
|
11559
|
-
}
|
|
11429
|
+
if (p1.isExtreme) {
|
|
11430
|
+
renderPoint(markers, p1, 'cyan', '1%', '0.5');
|
|
11431
|
+
}
|
|
11560
11432
|
|
|
11561
|
-
|
|
11562
|
-
|
|
11563
|
-
|
|
11433
|
+
if (p1.isHorizontal) {
|
|
11434
|
+
renderPoint(markers, p1, 'blue', '1.5%', '0.25');
|
|
11435
|
+
}
|
|
11436
|
+
|
|
11437
|
+
if (p1.isVertical) {
|
|
11438
|
+
renderPoint(markers, p1, 'purple', '1.5%', '0.25');
|
|
11439
|
+
}
|
|
11440
|
+
|
|
11441
|
+
if (p1.isCorner) {
|
|
11442
|
+
renderPoint(markers, p1, 'magenta', '1%', '1');
|
|
11443
|
+
}
|
|
11444
|
+
|
|
11445
|
+
if (showTangents && (p1.isCorner || p1.isSemiExtreme || p1.isDirChange || p1.isExtreme)) {
|
|
11446
|
+
renderPoint(markers, p1.tangentL, 'darkred', '0.5%');
|
|
11447
|
+
renderPoint(markers, p1.tangentR, 'darkblue', '0.5%');
|
|
11448
|
+
|
|
11449
|
+
/*
|
|
11450
|
+
if (p1.isDirChange) {
|
|
11451
|
+
renderPoint(markers, p1.tangentL, 'darkred', '1.5%')
|
|
11452
|
+
renderPoint(markers, p1.tangentR, 'darkblue', '1.5%')
|
|
11453
|
+
}
|
|
11454
|
+
*/
|
|
11455
|
+
|
|
11456
|
+
}
|
|
11564
11457
|
|
|
11565
|
-
function zeros_Xx2x2(x) {
|
|
11566
|
-
let zs = [];
|
|
11567
|
-
while (x--) {
|
|
11568
|
-
zs.push([0, 0]);
|
|
11569
11458
|
}
|
|
11570
|
-
|
|
11459
|
+
|
|
11571
11460
|
}
|
|
11572
11461
|
|
|
11573
|
-
|
|
11574
|
-
|
|
11575
|
-
|
|
11576
|
-
|
|
11577
|
-
|
|
11578
|
-
|
|
11579
|
-
|
|
11580
|
-
let
|
|
11462
|
+
function getPolyChunks(pts,
|
|
11463
|
+
{ closed = true,
|
|
11464
|
+
keepCorners = true,
|
|
11465
|
+
keepExtremes = true,
|
|
11466
|
+
keepInflections = false
|
|
11467
|
+
} = {}
|
|
11468
|
+
) {
|
|
11469
|
+
let chunks = [[pts[0]]];
|
|
11470
|
+
|
|
11471
|
+
let idx = 0;
|
|
11472
|
+
let lastChunk = chunks[idx];
|
|
11581
11473
|
|
|
11582
|
-
|
|
11583
|
-
dx = 6 * mt * (cp2.x - 2 * cp1.x + p0.x) +
|
|
11584
|
-
6 * t * (p1.x - 2 * cp2.x + cp1.x);
|
|
11474
|
+
let l = pts.length;
|
|
11585
11475
|
|
|
11586
|
-
|
|
11587
|
-
|
|
11476
|
+
// render
|
|
11477
|
+
for (let i = 1; i < l; i++) {
|
|
11478
|
+
i > 0 ? pts[i] : pts[l - 1];
|
|
11479
|
+
let p1 = pts[i];
|
|
11480
|
+
i < l - 1 ? pts[i + 1] : pts[l - 1];
|
|
11588
11481
|
|
|
11589
|
-
|
|
11590
|
-
|
|
11591
|
-
|
|
11592
|
-
|
|
11482
|
+
// start new chunk
|
|
11483
|
+
// keepInflections && p1.isDirChange
|
|
11484
|
+
if ((keepExtremes && p1.isExtreme || keepCorners && p1.isCorner ||
|
|
11485
|
+
(keepInflections && p1.isDirChange && !p1.isExtreme && !p1.isCorner )
|
|
11486
|
+
)) {
|
|
11487
|
+
idx++;
|
|
11488
|
+
chunks.push([]);
|
|
11489
|
+
}
|
|
11593
11490
|
|
|
11594
|
-
|
|
11595
|
-
|
|
11596
|
-
3 * t2 * (p1.y - cp2.y);
|
|
11491
|
+
lastChunk = chunks[idx];
|
|
11492
|
+
lastChunk.push(p1);
|
|
11597
11493
|
}
|
|
11598
11494
|
|
|
11599
|
-
|
|
11600
|
-
}
|
|
11495
|
+
// test render
|
|
11601
11496
|
|
|
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
|
|
11497
|
+
return chunks;
|
|
11608
11498
|
}
|
|
11609
11499
|
|
|
11610
|
-
|
|
11611
|
-
|
|
11612
|
-
|
|
11613
|
-
beziers.forEach(bez => {
|
|
11614
|
-
|
|
11615
|
-
let type = bez.length === 4 ? 'C' : (bez.length === 3 ? 'Q' : 'L');
|
|
11500
|
+
function removeCoincidingVertices(pts = []) {
|
|
11501
|
+
let l = pts.length;
|
|
11502
|
+
if (!l) return pts;
|
|
11616
11503
|
|
|
11617
|
-
|
|
11618
|
-
|
|
11619
|
-
let p = bez[bez.length - 1];
|
|
11504
|
+
let ptsN = [pts[0]];
|
|
11505
|
+
let pt1, pt2;
|
|
11620
11506
|
|
|
11621
|
-
|
|
11622
|
-
|
|
11623
|
-
|
|
11624
|
-
[cp1.x, cp1.y, p.x, p.y] :
|
|
11625
|
-
[p.x, p.y]
|
|
11626
|
-
);
|
|
11507
|
+
for (let i = 1; i < l; i++) {
|
|
11508
|
+
pt1 = pts[i - 1];
|
|
11509
|
+
pt2 = pts[i];
|
|
11627
11510
|
|
|
11628
|
-
|
|
11629
|
-
|
|
11630
|
-
|
|
11511
|
+
/**
|
|
11512
|
+
* 1. Skip zero-length segments
|
|
11513
|
+
*/
|
|
11514
|
+
if (pt1.x === pt2.x && pt1.y === pt2.y) {
|
|
11515
|
+
continue;
|
|
11516
|
+
}
|
|
11517
|
+
ptsN.push(pt2);
|
|
11518
|
+
}
|
|
11519
|
+
return ptsN
|
|
11631
11520
|
|
|
11632
|
-
return pathData
|
|
11633
11521
|
}
|
|
11634
11522
|
|
|
11635
|
-
function simplifyRC(pts, quality = 1, shiftStart = true) {
|
|
11636
|
-
|
|
11637
|
-
if (pts.length < 4) return pts;
|
|
11523
|
+
function simplifyRC(pts = [], quality = 1, shiftStart = true) {
|
|
11638
11524
|
|
|
11639
11525
|
let l = pts.length;
|
|
11526
|
+
if (l < 4) return pts;
|
|
11640
11527
|
|
|
11641
11528
|
// starting point
|
|
11642
11529
|
let M = pts[0];
|
|
@@ -11701,7 +11588,7 @@
|
|
|
11701
11588
|
let thresh = getSquareDistance(pt0, pt2) * 0.005;
|
|
11702
11589
|
|
|
11703
11590
|
// flat
|
|
11704
|
-
if (
|
|
11591
|
+
if (area <= thresh && i < l - 1) {
|
|
11705
11592
|
|
|
11706
11593
|
pt0 = pt1;
|
|
11707
11594
|
continue
|
|
@@ -11722,10 +11609,10 @@
|
|
|
11722
11609
|
}
|
|
11723
11610
|
|
|
11724
11611
|
// 1st and last are colinear
|
|
11725
|
-
let area0 = getPolygonArea([ptsSmp[1], M, ptsSmp[ptsSmp.length-1]], true);
|
|
11726
|
-
let thresh0 = getSquareDistance
|
|
11612
|
+
let area0 = getPolygonArea([ptsSmp[1], M, ptsSmp[ptsSmp.length - 1]], true);
|
|
11613
|
+
let thresh0 = getSquareDistance(ptsSmp[1], ptsSmp[ptsSmp.length - 1]) * 0.005;
|
|
11727
11614
|
// remove first point
|
|
11728
|
-
if(area0 < thresh0) ptsSmp.shift();
|
|
11615
|
+
if (area0 < thresh0) ptsSmp.shift();
|
|
11729
11616
|
|
|
11730
11617
|
return ptsSmp;
|
|
11731
11618
|
}
|
|
@@ -11744,6 +11631,7 @@
|
|
|
11744
11631
|
tolerance = 1,
|
|
11745
11632
|
simplifyRD = 1,
|
|
11746
11633
|
simplifyRDP = 1,
|
|
11634
|
+
isClosed = true,
|
|
11747
11635
|
} = {}) {
|
|
11748
11636
|
|
|
11749
11637
|
let polyPath = [];
|
|
@@ -11821,6 +11709,25 @@
|
|
|
11821
11709
|
|
|
11822
11710
|
// remove colinear
|
|
11823
11711
|
|
|
11712
|
+
keepExtremes = false;
|
|
11713
|
+
keepCorners = false;
|
|
11714
|
+
|
|
11715
|
+
keepExtremes = true;
|
|
11716
|
+
keepCorners = true;
|
|
11717
|
+
|
|
11718
|
+
// check if closed
|
|
11719
|
+
/*
|
|
11720
|
+
let bb = getPolyBBox(pts)
|
|
11721
|
+
let thresh = (bb.width+bb.height)*0.25
|
|
11722
|
+
let dist0 = getDistManhattan(pts[0], pts[pts.length-1])
|
|
11723
|
+
|
|
11724
|
+
*/
|
|
11725
|
+
|
|
11726
|
+
// copy 1st first to end
|
|
11727
|
+
if (isClosed) {
|
|
11728
|
+
pts.push(pts[0]);
|
|
11729
|
+
}
|
|
11730
|
+
|
|
11824
11731
|
// get topology of poly
|
|
11825
11732
|
let polyAnalyzed = !keepExtremes && !keepCorners ? pts : analyzePoly(pts, {
|
|
11826
11733
|
debug: false
|
|
@@ -11828,69 +11735,369 @@
|
|
|
11828
11735
|
});
|
|
11829
11736
|
|
|
11830
11737
|
// split into segment chunks
|
|
11831
|
-
|
|
11738
|
+
|
|
11739
|
+
let chunks = getPolyChunks(polyAnalyzed, { keepCorners, keepExtremes, keepInflections: true });
|
|
11832
11740
|
|
|
11833
11741
|
// Schneider curve fit
|
|
11834
11742
|
let threshold = width && height ? (width + height) / 2 * 0.004 * tolerance : 2.5;
|
|
11743
|
+
threshold = width && height ? (width + height) / 2 * 0.004 * tolerance : 2.5;
|
|
11835
11744
|
|
|
11836
|
-
|
|
11837
|
-
closed,
|
|
11838
|
-
tolerance: threshold,
|
|
11839
|
-
keepCorners,
|
|
11840
|
-
keepExtremes: true,
|
|
11841
|
-
});
|
|
11745
|
+
{
|
|
11842
11746
|
|
|
11843
|
-
|
|
11747
|
+
polyPath = simplifyPolyChunksTopology(chunks, {
|
|
11748
|
+
closed,
|
|
11749
|
+
tolerance: threshold,
|
|
11750
|
+
keepCorners,
|
|
11751
|
+
keepExtremes: true,
|
|
11752
|
+
});
|
|
11753
|
+
}
|
|
11844
11754
|
|
|
11845
11755
|
return polyPath;
|
|
11846
11756
|
}
|
|
11847
11757
|
|
|
11848
11758
|
/**
|
|
11849
|
-
*
|
|
11850
|
-
* to cubic beziers
|
|
11759
|
+
* topology based curve fit
|
|
11851
11760
|
*/
|
|
11852
|
-
|
|
11853
|
-
function simplifyPolyChunks(chunks = [], {
|
|
11761
|
+
function simplifyPolyChunksTopology(chunks = [], {
|
|
11854
11762
|
closed = true,
|
|
11855
11763
|
keepCorners = true,
|
|
11856
11764
|
tolerance = 1,
|
|
11857
11765
|
} = {}) {
|
|
11858
11766
|
|
|
11767
|
+
console.log(chunks);
|
|
11768
|
+
|
|
11859
11769
|
let l = chunks.length;
|
|
11860
11770
|
|
|
11861
11771
|
// new pathData
|
|
11772
|
+
|
|
11862
11773
|
let pathData = [{ type: 'M', values: [chunks[0][0].x, chunks[0][0].y] }];
|
|
11863
11774
|
|
|
11775
|
+
// loop chunks
|
|
11864
11776
|
for (let i = 0; i < l; i++) {
|
|
11865
11777
|
|
|
11778
|
+
let chunkPrev = i > 0 ? chunks[i - 1] : (closed ? chunks[l - 1] : null);
|
|
11866
11779
|
let chunk = chunks[i];
|
|
11867
|
-
let chunkN = chunks[i + 1] ? chunks[i + 1] : null;
|
|
11780
|
+
let chunkN = chunks[i + 1] ? chunks[i + 1] : (closed ? chunks[0] : null);
|
|
11868
11781
|
let segments = [];
|
|
11869
|
-
let chunklen = chunk.length;
|
|
11870
|
-
chunk[chunk.length - 1];
|
|
11871
11782
|
|
|
11872
11783
|
// add from next command
|
|
11873
11784
|
if (chunkN) {
|
|
11874
11785
|
chunk.push(chunkN[0]);
|
|
11875
11786
|
}
|
|
11876
11787
|
|
|
11877
|
-
|
|
11878
|
-
|
|
11879
|
-
|
|
11880
|
-
|
|
11788
|
+
let chunklen = chunk.length;
|
|
11789
|
+
let hasInflection = false;
|
|
11790
|
+
let segments_1 = [], segments_2 = [], segments_3 = [];
|
|
11791
|
+
let segsRequired = 3;
|
|
11792
|
+
|
|
11793
|
+
// 1st point
|
|
11794
|
+
let p1 = chunk[0];
|
|
11795
|
+
// last point in chunk
|
|
11796
|
+
let p2 = chunk[chunklen - 1];
|
|
11797
|
+
|
|
11798
|
+
// nothing to simplify - lineto
|
|
11799
|
+
|
|
11800
|
+
// if (chunklen < 2 || (chunklen === 2 && (chunk[1].isExtreme || i===l-1 && !closed) )) {
|
|
11801
|
+
|
|
11802
|
+
if (chunklen < 2 || (chunklen === 2 && (chunk[1].isExtreme))) {
|
|
11803
|
+
|
|
11804
|
+
if (chunklen === 2) {
|
|
11805
|
+
segsRequired = 2;
|
|
11806
|
+
segments_2 = [
|
|
11807
|
+
{
|
|
11808
|
+
type: 'L',
|
|
11809
|
+
values: [p1.x, p1.y],
|
|
11810
|
+
p0: chunkPrev,
|
|
11811
|
+
p: p1,
|
|
11812
|
+
},
|
|
11813
|
+
{
|
|
11814
|
+
type: 'L',
|
|
11815
|
+
values: [chunk[1].x, chunk[1].y],
|
|
11816
|
+
p0: p1,
|
|
11817
|
+
p: p2,
|
|
11818
|
+
}
|
|
11819
|
+
];
|
|
11820
|
+
} else {
|
|
11821
|
+
segsRequired = 1;
|
|
11822
|
+
segments_1 = [
|
|
11823
|
+
{
|
|
11824
|
+
type: 'L',
|
|
11825
|
+
values: [p1.x, p1.y],
|
|
11826
|
+
p0: chunkPrev,
|
|
11827
|
+
p: p2,
|
|
11828
|
+
},
|
|
11829
|
+
];
|
|
11830
|
+
}
|
|
11881
11831
|
|
|
11882
11832
|
} else {
|
|
11883
|
-
|
|
11884
|
-
|
|
11885
|
-
|
|
11833
|
+
|
|
11834
|
+
// point before inflection
|
|
11835
|
+
let p3 = chunk[chunklen - 2];
|
|
11836
|
+
|
|
11837
|
+
chunk.filter(pt => pt.isExtreme);
|
|
11838
|
+
let semiExtremes = chunk.filter(pt => pt.isSemiExtreme);
|
|
11839
|
+
chunk.filter(pt => pt.isCorner);
|
|
11840
|
+
let inflections = chunk.filter(pt => pt.isDirChange && !pt.isCorner && !pt.isExtreme);
|
|
11841
|
+
hasInflection = inflections.length && inflections[0] !== p1;
|
|
11842
|
+
|
|
11843
|
+
let idxMid = Math.floor(chunklen * 0.5);
|
|
11844
|
+
let pMid = semiExtremes.length ? semiExtremes[Math.floor(semiExtremes.length * 0.5)] : chunk[idxMid];
|
|
11845
|
+
|
|
11846
|
+
let dist0 = getDistManhattan(p1, p3);
|
|
11847
|
+
let dist1 = getDistManhattan(pMid, p3);
|
|
11848
|
+
let dist2 = getDistManhattan(pMid, p1);
|
|
11849
|
+
let thresh = dist0 * 0.25;
|
|
11850
|
+
let shortMidSegment = dist1 < thresh || dist2 < thresh;
|
|
11851
|
+
|
|
11852
|
+
/**
|
|
11853
|
+
* we have 3 modes
|
|
11854
|
+
* 1 segment: only 1 segment between extremes/corners
|
|
11855
|
+
* 2 segments: semiextreme/mid in between
|
|
11856
|
+
* 3 segments: inflection
|
|
11857
|
+
*/
|
|
11858
|
+
|
|
11859
|
+
segsRequired = shortMidSegment ? (!hasInflection ? 1 : 2) : (hasInflection ? 3 : 2);
|
|
11860
|
+
|
|
11861
|
+
let cp1_1 = p1.tangentR;
|
|
11862
|
+
let cp2_1 = pMid.tangentL;
|
|
11863
|
+
let p_1 = pMid;
|
|
11864
|
+
|
|
11865
|
+
// renderPoint(markers, pMid, 'orange', '2%')
|
|
11866
|
+
|
|
11867
|
+
let cp2_2 = p2.tangentL;
|
|
11868
|
+
let cp1_2 = pMid.tangentR;
|
|
11869
|
+
let p_2 = p2;
|
|
11870
|
+
|
|
11871
|
+
let cp1_3 = null;
|
|
11872
|
+
let cp2_3 = null;
|
|
11873
|
+
let p_3 = null;
|
|
11874
|
+
|
|
11875
|
+
// general extrapolation
|
|
11876
|
+
let t = 0.666;
|
|
11877
|
+
let ptI_1 = null, ptI_2 = null, ptI_3 = null;
|
|
11878
|
+
|
|
11879
|
+
// 1 segment
|
|
11880
|
+
ptI_1 = checkLineIntersection(p1, p1.tangentR, p2, p2.tangentL, false, true);
|
|
11881
|
+
if (ptI_1) {
|
|
11882
|
+
cp1_1 = interpolate(p1, ptI_1, t);
|
|
11883
|
+
cp2_1 = interpolate(p2, ptI_1, t);
|
|
11884
|
+
p_1 = p2;
|
|
11885
|
+
|
|
11886
|
+
segments_1 = [
|
|
11887
|
+
|
|
11888
|
+
{
|
|
11889
|
+
type: 'C',
|
|
11890
|
+
values: [cp1_1.x, cp1_1.y, cp2_1.x, cp2_1.y, p_1.x, p_1.y],
|
|
11891
|
+
p0: p1,
|
|
11892
|
+
cp1: cp1_1,
|
|
11893
|
+
cp2: cp2_1,
|
|
11894
|
+
p: p_1,
|
|
11895
|
+
}
|
|
11896
|
+
];
|
|
11897
|
+
|
|
11898
|
+
}
|
|
11899
|
+
|
|
11900
|
+
// 2 segments
|
|
11901
|
+
ptI_1 = checkLineIntersection(p1, p1.tangentR, pMid, pMid.tangentL, false, true);
|
|
11902
|
+
ptI_2 = checkLineIntersection(p2, p2.tangentL, pMid, pMid.tangentR, false, true);
|
|
11903
|
+
|
|
11904
|
+
if (ptI_1 && ptI_2) {
|
|
11905
|
+
cp1_1 = interpolate(p1, ptI_1, t);
|
|
11906
|
+
cp2_1 = interpolate(pMid, ptI_1, t);
|
|
11907
|
+
p_1 = pMid;
|
|
11908
|
+
|
|
11909
|
+
cp1_2 = interpolate(pMid, ptI_2, t);
|
|
11910
|
+
cp2_2 = interpolate(p2, ptI_2, t);
|
|
11911
|
+
p_2 = p2;
|
|
11912
|
+
|
|
11913
|
+
segments_2 = [
|
|
11914
|
+
|
|
11915
|
+
{
|
|
11916
|
+
type: 'C',
|
|
11917
|
+
values: [cp1_1.x, cp1_1.y, cp2_1.x, cp2_1.y, p_1.x, p_1.y],
|
|
11918
|
+
p0: p1,
|
|
11919
|
+
cp1: cp1_1,
|
|
11920
|
+
cp2: cp2_1,
|
|
11921
|
+
p: p_1,
|
|
11922
|
+
isExtreme: p_1.isExtreme
|
|
11923
|
+
},
|
|
11924
|
+
{
|
|
11925
|
+
type: 'C',
|
|
11926
|
+
values: [cp1_2.x, cp1_2.y, cp2_2.x, cp2_2.y, p_2.x, p_2.y],
|
|
11927
|
+
p0: p_1,
|
|
11928
|
+
cp1: cp1_2,
|
|
11929
|
+
cp2: cp2_2,
|
|
11930
|
+
p: p_2,
|
|
11931
|
+
isExtreme: p_2.isExtreme
|
|
11932
|
+
|
|
11933
|
+
},
|
|
11934
|
+
];
|
|
11935
|
+
|
|
11936
|
+
}
|
|
11937
|
+
|
|
11938
|
+
// 3 segments
|
|
11939
|
+
|
|
11940
|
+
if (hasInflection) {
|
|
11941
|
+
|
|
11942
|
+
// get pt between dir change and mid
|
|
11943
|
+
let idx_3_4 = Math.floor(chunklen * 0.75);
|
|
11944
|
+
p3 = chunk[idx_3_4];
|
|
11945
|
+
ptI_3 = checkLineIntersection(p3, p3.tangentR, p2, p2.tangentL, false, false);
|
|
11946
|
+
|
|
11947
|
+
if (ptI_3) {
|
|
11948
|
+
let tangentR_beforeDirChange = interpolate(p3, ptI_3, t);
|
|
11949
|
+
|
|
11950
|
+
// extend right tangent
|
|
11951
|
+
p3.tangentR.x = tangentR_beforeDirChange.x;
|
|
11952
|
+
p3.tangentR.y = tangentR_beforeDirChange.y;
|
|
11953
|
+
|
|
11954
|
+
// extend dir change tangent
|
|
11955
|
+
let tangentL_dirChange = interpolate(p2, ptI_3, t);
|
|
11956
|
+
p2.tangentL.x = tangentL_dirChange.x;
|
|
11957
|
+
p2.tangentL.y = tangentL_dirChange.y;
|
|
11958
|
+
} else {
|
|
11959
|
+
|
|
11960
|
+
if (p3 === p2) {
|
|
11961
|
+
|
|
11962
|
+
idx_3_4 = Math.floor(chunklen * 0.3);
|
|
11963
|
+
p3 = chunk[idx_3_4];
|
|
11964
|
+
|
|
11965
|
+
}
|
|
11966
|
+
checkLineIntersection(p3, p3.tangentR, p2, p2.tangentL, false, false);
|
|
11967
|
+
|
|
11968
|
+
cp1_1 = interpolate(p1, p1.tangentR, 1.333);
|
|
11969
|
+
cp2_1 = interpolate(p2, p2.tangentL, 1.333);
|
|
11970
|
+
|
|
11971
|
+
segments_3 = [
|
|
11972
|
+
{
|
|
11973
|
+
type: 'C',
|
|
11974
|
+
values: [cp1_1.x, cp1_1.y, cp2_1.x, cp2_1.y, p2.x, p2.y],
|
|
11975
|
+
p0: p1,
|
|
11976
|
+
cp1: cp1_1,
|
|
11977
|
+
cp2: cp2_1,
|
|
11978
|
+
p: p2,
|
|
11979
|
+
isExtreme: p2.isExtreme
|
|
11980
|
+
|
|
11981
|
+
},
|
|
11982
|
+
];
|
|
11983
|
+
|
|
11984
|
+
/*
|
|
11985
|
+
let tangentR_beforeDirChange = interpolate(p3, ptI_3, t)
|
|
11986
|
+
|
|
11987
|
+
// extend right tangent
|
|
11988
|
+
p3.tangentR.x = tangentR_beforeDirChange.x
|
|
11989
|
+
p3.tangentR.y = tangentR_beforeDirChange.y
|
|
11990
|
+
|
|
11991
|
+
let tangentL_dirChange = interpolate(p2, ptI_3, t)
|
|
11992
|
+
p2.tangentL.x = tangentL_dirChange.x
|
|
11993
|
+
p2.tangentL.y = tangentL_dirChange.y
|
|
11994
|
+
*/
|
|
11995
|
+
|
|
11996
|
+
pathDataToD([{ type: 'M', values: [p1.x, p1.y] }, ...segments_3]);
|
|
11997
|
+
|
|
11998
|
+
}
|
|
11999
|
+
|
|
12000
|
+
cp1_3 = p3.tangentR;
|
|
12001
|
+
cp2_3 = p2.tangentL;
|
|
12002
|
+
p_3 = p2;
|
|
12003
|
+
|
|
12004
|
+
ptI_1 = checkLineIntersection(p1, p1.tangentR, pMid, pMid.tangentL, false, true);
|
|
12005
|
+
ptI_2 = checkLineIntersection(pMid, pMid.tangentR, p3, p3.tangentL, false, true);
|
|
12006
|
+
|
|
12007
|
+
if (ptI_1 && ptI_2 && ptI_3) {
|
|
12008
|
+
|
|
12009
|
+
cp1_1 = interpolate(p1, ptI_1, t);
|
|
12010
|
+
cp2_1 = interpolate(pMid, ptI_1, t);
|
|
12011
|
+
p_1 = pMid;
|
|
12012
|
+
|
|
12013
|
+
cp1_2 = interpolate(pMid, ptI_2, t);
|
|
12014
|
+
cp2_2 = interpolate(p3, ptI_2, t);
|
|
12015
|
+
p_2 = p3;
|
|
12016
|
+
|
|
12017
|
+
segments_3 = [
|
|
12018
|
+
{
|
|
12019
|
+
type: 'C',
|
|
12020
|
+
values: [cp1_1.x, cp1_1.y, cp2_1.x, cp2_1.y, p_1.x, p_1.y],
|
|
12021
|
+
p0: p1,
|
|
12022
|
+
cp1: cp1_1,
|
|
12023
|
+
cp2: cp2_1,
|
|
12024
|
+
p: p_1,
|
|
12025
|
+
isExtreme: p_1.isExtreme
|
|
12026
|
+
|
|
12027
|
+
},
|
|
12028
|
+
{
|
|
12029
|
+
type: 'C',
|
|
12030
|
+
values: [cp1_2.x, cp1_2.y, cp2_2.x, cp2_2.y, p_2.x, p_2.y],
|
|
12031
|
+
p0: p_1,
|
|
12032
|
+
cp1: cp1_2,
|
|
12033
|
+
cp2: cp2_2,
|
|
12034
|
+
p: p_2,
|
|
12035
|
+
},
|
|
12036
|
+
{
|
|
12037
|
+
type: 'C',
|
|
12038
|
+
values: [cp1_3.x, cp1_3.y, cp2_3.x, cp2_3.y, p_3.x, p_3.y],
|
|
12039
|
+
p0: p_2,
|
|
12040
|
+
cp1: cp1_3,
|
|
12041
|
+
cp2: cp2_3,
|
|
12042
|
+
p: p_3,
|
|
12043
|
+
isExtreme: p_3.isExtreme
|
|
12044
|
+
|
|
12045
|
+
}
|
|
12046
|
+
];
|
|
12047
|
+
|
|
12048
|
+
pathDataToD([{ type: 'M', values: [p1.x, p1.y] }, ...segments_3]);
|
|
12049
|
+
|
|
12050
|
+
}
|
|
12051
|
+
|
|
12052
|
+
}
|
|
12053
|
+
|
|
12054
|
+
}
|
|
12055
|
+
|
|
12056
|
+
if (segsRequired === 1) {
|
|
12057
|
+
segments = segments_1;
|
|
12058
|
+
} else if (segsRequired === 2 && segments_2.length) {
|
|
12059
|
+
segments = segments_2;
|
|
12060
|
+
}
|
|
12061
|
+
else ;
|
|
12062
|
+
|
|
12063
|
+
segments = segments_3.length ? segments_3 : segments_2;
|
|
12064
|
+
/*
|
|
12065
|
+
if (simplify && !isLinetoSeg && segments.length > 1) {
|
|
12066
|
+
|
|
12067
|
+
let com1 = segments[0]
|
|
12068
|
+
let com2 = segments[1]
|
|
12069
|
+
|
|
12070
|
+
tolerance = 1.1
|
|
12071
|
+
let combined = combineCubicPairs(com1, com2, { tolerance })
|
|
12072
|
+
|
|
12073
|
+
let error = 0;
|
|
12074
|
+
let comsSimp =[]
|
|
12075
|
+
|
|
12076
|
+
console.log('!!!combined', segments.length, combined);
|
|
12077
|
+
|
|
12078
|
+
// success
|
|
12079
|
+
if (combined.length === 1) {
|
|
12080
|
+
|
|
12081
|
+
if(segments.length === 2){
|
|
12082
|
+
segments = combined
|
|
12083
|
+
}
|
|
12084
|
+
|
|
12085
|
+
let com = combined[0]
|
|
12086
|
+
}
|
|
12087
|
+
|
|
11886
12088
|
}
|
|
12089
|
+
*/
|
|
11887
12090
|
|
|
11888
12091
|
// remove first segment to connect to last segment
|
|
11889
12092
|
pathData.push(...segments);
|
|
11890
12093
|
|
|
11891
12094
|
}
|
|
11892
12095
|
|
|
11893
|
-
if (closed)
|
|
12096
|
+
if (closed) {
|
|
12097
|
+
pathData.push({ type: 'Z', values: [] });
|
|
12098
|
+
}
|
|
12099
|
+
|
|
12100
|
+
// refine extremes
|
|
11894
12101
|
return pathData
|
|
11895
12102
|
|
|
11896
12103
|
}
|
|
@@ -11901,13 +12108,18 @@
|
|
|
11901
12108
|
*/
|
|
11902
12109
|
function pathDataToPolygonOpt(pathData, {
|
|
11903
12110
|
precisionPoly = 1,
|
|
11904
|
-
autoAccuracy=false,
|
|
11905
|
-
polyFormat='
|
|
11906
|
-
decimals
|
|
11907
|
-
simplifyRD=1,
|
|
11908
|
-
|
|
12111
|
+
autoAccuracy = false,
|
|
12112
|
+
polyFormat = 'object',
|
|
12113
|
+
decimals = -1,
|
|
12114
|
+
simplifyRD = 1,
|
|
12115
|
+
simplifyRDP = 1,
|
|
11909
12116
|
} = {}) {
|
|
11910
12117
|
|
|
12118
|
+
pathData = convertPathData(pathData, {toAbsolute:true, toLonghands:true, arcToCubic:true});
|
|
12119
|
+
pathData = addExtremePoints(pathData);
|
|
12120
|
+
|
|
12121
|
+
pathData = getPathDataVerbose(pathData);
|
|
12122
|
+
|
|
11911
12123
|
let l = pathData.length;
|
|
11912
12124
|
let M = { x: pathData[0].values[0], y: pathData[0].values[1] };
|
|
11913
12125
|
let p0 = M;
|
|
@@ -11934,7 +12146,7 @@
|
|
|
11934
12146
|
let pts2 = [pts[0]];
|
|
11935
12147
|
|
|
11936
12148
|
// adjustments for very small or large paths
|
|
11937
|
-
dims = dims.filter(Boolean).sort();
|
|
12149
|
+
dims = dims.filter(Boolean).sort((a,b)=>a-b);
|
|
11938
12150
|
let dimMax = dims[dims.length - 1];
|
|
11939
12151
|
|
|
11940
12152
|
let scale = dimMax > 2 && dimMax < 25 ? 1 : (20 / dimMax);
|
|
@@ -11973,31 +12185,33 @@
|
|
|
11973
12185
|
}
|
|
11974
12186
|
|
|
11975
12187
|
// simplify polygon
|
|
11976
|
-
if(simplifyRD>0){
|
|
11977
|
-
pts2 = simplifyPolyRD(pts2, {quality:simplifyRD});
|
|
12188
|
+
if (simplifyRD > 0) {
|
|
12189
|
+
pts2 = simplifyPolyRD(pts2, { quality: simplifyRD });
|
|
11978
12190
|
}
|
|
11979
12191
|
|
|
11980
|
-
if(simplifyRDP>0){
|
|
11981
|
-
pts2 = simplifyPolyRDP(pts2, {quality:simplifyRDP});
|
|
12192
|
+
if (simplifyRDP > 0) {
|
|
12193
|
+
pts2 = simplifyPolyRDP(pts2, { quality: simplifyRDP });
|
|
11982
12194
|
}
|
|
11983
12195
|
|
|
11984
|
-
|
|
11985
|
-
pathData = pathDataPoly;
|
|
11986
|
-
|
|
11987
|
-
if(autoAccuracy){
|
|
12196
|
+
if (autoAccuracy) {
|
|
11988
12197
|
decimals = detectAccuracyPoly(pts);
|
|
11989
12198
|
}
|
|
11990
12199
|
|
|
11991
|
-
let poly = decimals
|
|
12200
|
+
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
12201
|
|
|
11993
|
-
|
|
12202
|
+
pathDataPoly = pathDataFromPoly(poly);
|
|
12203
|
+
pathData = pathDataPoly;
|
|
12204
|
+
|
|
12205
|
+
if (polyFormat === 'array') {
|
|
11994
12206
|
poly = poly.map(pt => { return [pt.x, pt.y] });
|
|
11995
12207
|
}
|
|
11996
|
-
else if(polyFormat==='string'){
|
|
12208
|
+
else if (polyFormat === 'string') {
|
|
11997
12209
|
poly = poly.map(pt => { return [pt.x, pt.y].join(',') }).flat().join(' ');
|
|
11998
12210
|
}
|
|
11999
12211
|
|
|
12000
|
-
|
|
12212
|
+
let d= pathDataToD(pathData);
|
|
12213
|
+
|
|
12214
|
+
return { pathData, poly, d }
|
|
12001
12215
|
|
|
12002
12216
|
}
|
|
12003
12217
|
|
|
@@ -12238,6 +12452,7 @@
|
|
|
12238
12452
|
// polygon
|
|
12239
12453
|
toPolygon: false,
|
|
12240
12454
|
smoothPoly: false,
|
|
12455
|
+
isClosed:true,
|
|
12241
12456
|
polyFormat: 'object',
|
|
12242
12457
|
precisionPoly: 1,
|
|
12243
12458
|
simplifyRD: 0,
|
|
@@ -12534,6 +12749,122 @@
|
|
|
12534
12749
|
}
|
|
12535
12750
|
*/
|
|
12536
12751
|
|
|
12752
|
+
function SlickVGObj(props = {}) {
|
|
12753
|
+
|
|
12754
|
+
Object.assign(this, props);
|
|
12755
|
+
}
|
|
12756
|
+
|
|
12757
|
+
SlickVGObj.prototype.getD = function () {
|
|
12758
|
+
let d = this.d;
|
|
12759
|
+
return d;
|
|
12760
|
+
};
|
|
12761
|
+
|
|
12762
|
+
SlickVGObj.prototype.getSvg = function () {
|
|
12763
|
+
let svg = this.svg;
|
|
12764
|
+
|
|
12765
|
+
if (!svg) {
|
|
12766
|
+
let xArr = [];
|
|
12767
|
+
let yArr = [];
|
|
12768
|
+
let d = this.d;
|
|
12769
|
+
let pathDataPlusArr = this.pathDataPlusArr || [];
|
|
12770
|
+
pathDataPlusArr.forEach(path => {
|
|
12771
|
+
path.forEach(sub => {
|
|
12772
|
+
let { pathData } = sub;
|
|
12773
|
+
let bb = getPathDataBBox(pathData);
|
|
12774
|
+
let { x, y, right, bottom } = bb;
|
|
12775
|
+
xArr.push(x, right);
|
|
12776
|
+
yArr.push(y, bottom);
|
|
12777
|
+
});
|
|
12778
|
+
});
|
|
12779
|
+
|
|
12780
|
+
let x = Math.min(...xArr);
|
|
12781
|
+
let right = Math.max(...xArr);
|
|
12782
|
+
let y = Math.min(...yArr);
|
|
12783
|
+
let bottom = Math.max(...yArr);
|
|
12784
|
+
let width = right - x;
|
|
12785
|
+
let height = bottom - y;
|
|
12786
|
+
|
|
12787
|
+
svg = `<svg xmlns="${svgNs}" viewBox="${[x, y, width, height].join(' ')}"><path d="${d}"/></svg>`;
|
|
12788
|
+
|
|
12789
|
+
}
|
|
12790
|
+
return svg;
|
|
12791
|
+
};
|
|
12792
|
+
|
|
12793
|
+
/**
|
|
12794
|
+
* retrieve poly
|
|
12795
|
+
* formats: points, array, string, pathData, d,
|
|
12796
|
+
*/
|
|
12797
|
+
SlickVGObj.prototype.getPoly = function (options = {}
|
|
12798
|
+
) {
|
|
12799
|
+
|
|
12800
|
+
options = {
|
|
12801
|
+
...{
|
|
12802
|
+
precisionPoly: 1,
|
|
12803
|
+
simplifyRDP: 0,
|
|
12804
|
+
simplifyRD: 0,
|
|
12805
|
+
autoAccuracy: true,
|
|
12806
|
+
decimals: 3,
|
|
12807
|
+
format: 'object'
|
|
12808
|
+
},
|
|
12809
|
+
...options
|
|
12810
|
+
};
|
|
12811
|
+
|
|
12812
|
+
let { precisionPoly, simplifyRDP, simplifyRD, autoAccuracy, decimals, format } = options;
|
|
12813
|
+
|
|
12814
|
+
let polyFormat = format;
|
|
12815
|
+
|
|
12816
|
+
let polys = this.polys;
|
|
12817
|
+
if (!polys.length) {
|
|
12818
|
+
let pathDataPlusArr = this.pathDataPlusArr || [];
|
|
12819
|
+
let poly = [];
|
|
12820
|
+
let polyPaths = [];
|
|
12821
|
+
let dPoly = '';
|
|
12822
|
+
pathDataPlusArr.forEach(path => {
|
|
12823
|
+
path.forEach(sub => {
|
|
12824
|
+
let { pathData } = sub;
|
|
12825
|
+
|
|
12826
|
+
let polyData = pathDataToPolygonOpt(pathData, {
|
|
12827
|
+
precisionPoly,
|
|
12828
|
+
autoAccuracy,
|
|
12829
|
+
decimals,
|
|
12830
|
+
simplifyRD,
|
|
12831
|
+
simplifyRDP,
|
|
12832
|
+
polyFormat
|
|
12833
|
+
});
|
|
12834
|
+
|
|
12835
|
+
dPoly += polyData.d;
|
|
12836
|
+
poly.push(polyData.poly);
|
|
12837
|
+
polyPaths.push(polyData.pathData);
|
|
12838
|
+
|
|
12839
|
+
});
|
|
12840
|
+
});
|
|
12841
|
+
|
|
12842
|
+
if (polyFormat === 'object' || polyFormat === 'array' || polyFormat === 'string') {
|
|
12843
|
+
polys = poly;
|
|
12844
|
+
}
|
|
12845
|
+
else if (polyFormat === 'pathData') {
|
|
12846
|
+
polys = polyPaths.flat();
|
|
12847
|
+
}
|
|
12848
|
+
|
|
12849
|
+
else if (polyFormat === 'd') {
|
|
12850
|
+
polys = dPoly;
|
|
12851
|
+
}
|
|
12852
|
+
|
|
12853
|
+
}
|
|
12854
|
+
return polys;
|
|
12855
|
+
};
|
|
12856
|
+
|
|
12857
|
+
/*
|
|
12858
|
+
export function PathLengthObject(props = {}) {
|
|
12859
|
+
Object.assign(this, props);
|
|
12860
|
+
}
|
|
12861
|
+
*/
|
|
12862
|
+
|
|
12863
|
+
function SlickVG(input = '', settings = {}) {
|
|
12864
|
+
settings.getObject = true;
|
|
12865
|
+
return svgPathSimplify(input, settings)
|
|
12866
|
+
}
|
|
12867
|
+
|
|
12537
12868
|
function svgPathSimplify(input = '', settings = {}) {
|
|
12538
12869
|
|
|
12539
12870
|
let preset = settings['preset'] !== undefined && settings['preset'] ? settings['preset'] : null;
|
|
@@ -12545,7 +12876,7 @@
|
|
|
12545
12876
|
...settings
|
|
12546
12877
|
};
|
|
12547
12878
|
|
|
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;
|
|
12879
|
+
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
12880
|
|
|
12550
12881
|
// clamp tolerance and scale
|
|
12551
12882
|
tolerance = Math.max(0.1, tolerance);
|
|
@@ -12556,6 +12887,10 @@
|
|
|
12556
12887
|
settings.convertTransforms = true;
|
|
12557
12888
|
}
|
|
12558
12889
|
|
|
12890
|
+
if (shapeConvert === 'toShapes' || shapeConvert === 'shapesToPaths') {
|
|
12891
|
+
keepSmaller = false;
|
|
12892
|
+
}
|
|
12893
|
+
|
|
12559
12894
|
/**
|
|
12560
12895
|
* intercept
|
|
12561
12896
|
* invalid inputs
|
|
@@ -12733,6 +13068,8 @@
|
|
|
12733
13068
|
toRelative,
|
|
12734
13069
|
toMixed,
|
|
12735
13070
|
toShorthands,
|
|
13071
|
+
// return true arc radii or minified/parametrized
|
|
13072
|
+
optimizeArcs: minifyD < 1,
|
|
12736
13073
|
decimals,
|
|
12737
13074
|
};
|
|
12738
13075
|
|
|
@@ -12744,7 +13081,6 @@
|
|
|
12744
13081
|
let pathDataPlusArr = [];
|
|
12745
13082
|
let path = paths[i];
|
|
12746
13083
|
let { d, el } = path;
|
|
12747
|
-
let isPoly = false;
|
|
12748
13084
|
|
|
12749
13085
|
// disable reordering for elements with stroke dash-array
|
|
12750
13086
|
if (el && (el.hasAttribute('stroke-dasharray') || el.hasAttribute('stroke-dashoffset'))) {
|
|
@@ -12755,6 +13091,7 @@
|
|
|
12755
13091
|
// if polygon we already heave absolute coordinates
|
|
12756
13092
|
|
|
12757
13093
|
let pathData = parsePathDataNormalized(d, { quadraticToCubic, arcToCubic });
|
|
13094
|
+
console.log('!!!pathData', pathData, arcToCubic);
|
|
12758
13095
|
|
|
12759
13096
|
// get polygon bbox
|
|
12760
13097
|
let bb_poly = smoothPoly || toPolygon ? getPolyBBox(getPathDataVertices(pathData)) : null;
|
|
@@ -12791,7 +13128,7 @@
|
|
|
12791
13128
|
// count commands for evaluation
|
|
12792
13129
|
comCount += pathData.length;
|
|
12793
13130
|
|
|
12794
|
-
if (
|
|
13131
|
+
if (removeOrphanSubpaths) pathData = removeOrphanedM(pathData);
|
|
12795
13132
|
|
|
12796
13133
|
/**
|
|
12797
13134
|
* get sub paths
|
|
@@ -12805,8 +13142,9 @@
|
|
|
12805
13142
|
let pathDataSub = subPathArr[i];
|
|
12806
13143
|
let poly = [];
|
|
12807
13144
|
let coms = Array.from(new Set(pathDataSub.map(com => com.type))).join('');
|
|
12808
|
-
isPoly = !(/[acqts]/gi).test(coms);
|
|
12809
|
-
|
|
13145
|
+
let isPoly = !(/[acqts]/gi).test(coms);
|
|
13146
|
+
|
|
13147
|
+
let closed = (/z/gi).test(coms);
|
|
12810
13148
|
|
|
12811
13149
|
if (isPoly && !mode) {
|
|
12812
13150
|
|
|
@@ -12823,7 +13161,6 @@
|
|
|
12823
13161
|
|
|
12824
13162
|
}
|
|
12825
13163
|
|
|
12826
|
-
toPolygon = false;
|
|
12827
13164
|
pathDataSub = pathDataFromPoly(poly, closed);
|
|
12828
13165
|
|
|
12829
13166
|
}
|
|
@@ -12832,26 +13169,56 @@
|
|
|
12832
13169
|
* convert curves to polygon
|
|
12833
13170
|
* flattening
|
|
12834
13171
|
*/
|
|
12835
|
-
|
|
13172
|
+
|
|
13173
|
+
if (toPolygon) {
|
|
12836
13174
|
simplifyBezier = false;
|
|
12837
13175
|
smoothPoly = false;
|
|
12838
13176
|
harmonizeCpts = false;
|
|
12839
13177
|
|
|
12840
|
-
|
|
13178
|
+
/**
|
|
13179
|
+
* if pathdata is already polygon- pass through
|
|
13180
|
+
* otherwise create precise polygon by curve splitting
|
|
13181
|
+
* */
|
|
12841
13182
|
|
|
12842
|
-
|
|
12843
|
-
|
|
12844
|
-
|
|
13183
|
+
if (!isPoly) {
|
|
13184
|
+
pathDataSub = getPathDataVerbose(pathDataSub);
|
|
13185
|
+
let polyData = pathDataToPolygonOpt(pathDataSub, {
|
|
13186
|
+
precisionPoly,
|
|
13187
|
+
autoAccuracy,
|
|
13188
|
+
polyFormat,
|
|
12845
13189
|
|
|
12846
|
-
|
|
12847
|
-
|
|
12848
|
-
|
|
13190
|
+
simplifyRD,
|
|
13191
|
+
simplifyRDP
|
|
13192
|
+
});
|
|
13193
|
+
|
|
13194
|
+
poly = polyData.poly;
|
|
13195
|
+
pathDataSub = polyData.pathData;
|
|
13196
|
+
isPoly = true;
|
|
13197
|
+
|
|
13198
|
+
}
|
|
13199
|
+
|
|
13200
|
+
polys.push(poly);
|
|
13201
|
+
|
|
13202
|
+
}
|
|
13203
|
+
|
|
13204
|
+
// harmonize cpts
|
|
13205
|
+
// if (harmonizeCpts) pathDataSub = harmonizeCubicCpts(pathDataSub)
|
|
12849
13206
|
|
|
12850
|
-
|
|
12851
|
-
isPoly = true;
|
|
13207
|
+
if (smoothPoly) {
|
|
12852
13208
|
|
|
13209
|
+
removeZeroLength=true;
|
|
13210
|
+
optimizeOrder=true;
|
|
12853
13211
|
}
|
|
12854
13212
|
|
|
13213
|
+
// remove zero length linetos
|
|
13214
|
+
if (removeColinear || removeZeroLength) pathDataSub = removeZeroLengthLinetos(pathDataSub);
|
|
13215
|
+
|
|
13216
|
+
// sort to top left
|
|
13217
|
+
if (optimizeOrder) pathDataSub = pathDataToTopLeft(pathDataSub);
|
|
13218
|
+
|
|
13219
|
+
// Preprocessing: remove colinear - ignore flat beziers (removed later)
|
|
13220
|
+
if (removeColinear) pathDataSub = pathDataRemoveColinear(pathDataSub, { tolerance, flatBezierToLinetos: false });
|
|
13221
|
+
|
|
12855
13222
|
/**
|
|
12856
13223
|
* poly to beziers via
|
|
12857
13224
|
* Philip J. Schneider's
|
|
@@ -12860,12 +13227,18 @@
|
|
|
12860
13227
|
if (smoothPoly) {
|
|
12861
13228
|
|
|
12862
13229
|
if (isPoly) {
|
|
12863
|
-
|
|
13230
|
+
/*
|
|
13231
|
+
pathDataSub = pathDataToTopLeft(pathDataSub)
|
|
13232
|
+
pathDataSub = removeZeroLengthLinetos(pathDataSub)
|
|
13233
|
+
pathDataSub = pathDataRemoveColinear(pathDataSub, { tolerance, flatBezierToLinetos: true });
|
|
13234
|
+
*/
|
|
13235
|
+
|
|
12864
13236
|
let poly = getPathDataVertices(pathDataSub);
|
|
12865
13237
|
|
|
12866
13238
|
// options for poly simplification
|
|
12867
13239
|
let optionsPoly = {
|
|
12868
|
-
|
|
13240
|
+
|
|
13241
|
+
denoise: 0,
|
|
12869
13242
|
tolerance,
|
|
12870
13243
|
width: bb_poly.width,
|
|
12871
13244
|
height: bb_poly.height,
|
|
@@ -12877,26 +13250,15 @@
|
|
|
12877
13250
|
closed,
|
|
12878
13251
|
simplifyRD,
|
|
12879
13252
|
simplifyRDP,
|
|
13253
|
+
isClosed,
|
|
12880
13254
|
};
|
|
12881
13255
|
|
|
12882
13256
|
pathDataSub = simplifyPolygonToPathData(poly, optionsPoly);
|
|
12883
13257
|
// flag as non poly as we're smoothing to curves
|
|
12884
|
-
|
|
13258
|
+
isPoly = false;
|
|
12885
13259
|
}
|
|
12886
13260
|
}
|
|
12887
13261
|
|
|
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
13262
|
let tMin = 0, tMax = 1;
|
|
12901
13263
|
if (addExtremes) pathDataSub = addExtremePoints(pathDataSub,
|
|
12902
13264
|
{ tMin, tMax, addExtremes, angles: [30] });
|
|
@@ -12919,11 +13281,13 @@
|
|
|
12919
13281
|
pathDataPlus.bb = getPolyBBox(getPathDataVertices(pathDataCubic));
|
|
12920
13282
|
}
|
|
12921
13283
|
pathDataPlus.dimA = pathDataPlus.bb.width + pathDataPlus.bb.height;
|
|
13284
|
+
|
|
12922
13285
|
pathDataPlus.pathData = getPathDataVerbose(pathDataSub, {
|
|
12923
13286
|
addSquareLength: false,
|
|
12924
13287
|
addArea: false,
|
|
12925
13288
|
addAverageDim: false
|
|
12926
13289
|
});
|
|
13290
|
+
|
|
12927
13291
|
}
|
|
12928
13292
|
|
|
12929
13293
|
// simplify beziers
|
|
@@ -12934,6 +13298,12 @@
|
|
|
12934
13298
|
|
|
12935
13299
|
if (refineClosing) pathData = refineClosingCommand(pathData, { threshold: dimA * 0.001 });
|
|
12936
13300
|
|
|
13301
|
+
// refine round segment sequences
|
|
13302
|
+
if (simplifyRound) {
|
|
13303
|
+
pathData = refineRoundSegments(pathData);
|
|
13304
|
+
pathData = simplifyAdjacentRound(pathData);
|
|
13305
|
+
}
|
|
13306
|
+
|
|
12937
13307
|
pathData = simplifyBezier ? simplifyPathDataCubic(pathData, { simplifyBezier, keepInflections, keepExtremes, keepCorners, revertToQuadratics, tolerance }) : pathData;
|
|
12938
13308
|
|
|
12939
13309
|
// refine extremes
|
|
@@ -12957,12 +13327,6 @@
|
|
|
12957
13327
|
pathData = refineRoundedCorners(pathData, { threshold, tolerance, simplifyQuadraticCorners });
|
|
12958
13328
|
}
|
|
12959
13329
|
|
|
12960
|
-
// refine round segment sequences
|
|
12961
|
-
if (simplifyRound) {
|
|
12962
|
-
pathData = refineRoundSegments(pathData);
|
|
12963
|
-
pathData = simplifyAdjacentRound(pathData);
|
|
12964
|
-
}
|
|
12965
|
-
|
|
12966
13330
|
// simplify to quadratics
|
|
12967
13331
|
if (revertToQuadratics) pathData = pathDataRevertCubicToQuadratic(pathData, tolerance);
|
|
12968
13332
|
|
|
@@ -12979,6 +13343,8 @@
|
|
|
12979
13343
|
|
|
12980
13344
|
}
|
|
12981
13345
|
|
|
13346
|
+
// offset path
|
|
13347
|
+
|
|
12982
13348
|
// update
|
|
12983
13349
|
pathDataPlusArr.push({ pathData, bb });
|
|
12984
13350
|
|
|
@@ -13038,17 +13404,18 @@
|
|
|
13038
13404
|
}
|
|
13039
13405
|
|
|
13040
13406
|
// add simplified poly - if not populated by toPoly conversion
|
|
13407
|
+
/*
|
|
13041
13408
|
if (isPoly) {
|
|
13042
13409
|
|
|
13043
13410
|
pathDataPlusArr.forEach(sub => {
|
|
13044
|
-
let poly = getPathDataVertices(sub.pathData, false, decimals)
|
|
13411
|
+
let poly = getPathDataVertices(sub.pathData, false, decimals)
|
|
13045
13412
|
if (polyFormat === 'array') {
|
|
13046
|
-
poly = polyPtsToArray(poly)
|
|
13413
|
+
poly = polyPtsToArray(poly)
|
|
13047
13414
|
}
|
|
13048
|
-
polys.push(poly)
|
|
13049
|
-
})
|
|
13050
|
-
|
|
13415
|
+
polys.push(poly)
|
|
13416
|
+
})
|
|
13051
13417
|
}
|
|
13418
|
+
*/
|
|
13052
13419
|
|
|
13053
13420
|
// split into sub paths - returns svg with multiple paths
|
|
13054
13421
|
if (splitCompound && !mode && pathDataPlusArr.length > 1) {
|
|
@@ -13147,7 +13514,6 @@
|
|
|
13147
13514
|
svg = stringifySVG(svg, { omitNamespace, removeComments, format: minifyD });
|
|
13148
13515
|
|
|
13149
13516
|
svgSizeOpt = svg.length;
|
|
13150
|
-
|
|
13151
13517
|
compression = +(100 / svgSize * (svgSizeOpt)).toFixed(2);
|
|
13152
13518
|
|
|
13153
13519
|
svgSize = +(svgSize / 1024).toFixed(3);
|
|
@@ -13173,15 +13539,52 @@
|
|
|
13173
13539
|
({ d, report } = paths[0]);
|
|
13174
13540
|
}
|
|
13175
13541
|
|
|
13176
|
-
|
|
13177
|
-
|
|
13178
|
-
|
|
13542
|
+
// sanitize poly output
|
|
13543
|
+
if (polys.length) {
|
|
13544
|
+
|
|
13545
|
+
// round point data
|
|
13546
|
+
polys.forEach((poly, i) => {
|
|
13547
|
+
|
|
13548
|
+
if (polyFormat === 'string') poly = normalizePoly(poly);
|
|
13549
|
+
|
|
13550
|
+
poly = roundPoly(poly, decimals);
|
|
13551
|
+
// remove coinciding points
|
|
13552
|
+
polys[i] = removeCoincidingVertices(poly);
|
|
13553
|
+
});
|
|
13554
|
+
|
|
13555
|
+
if (polys.length === 1) {
|
|
13556
|
+
polys = polys[0];
|
|
13557
|
+
}
|
|
13558
|
+
|
|
13559
|
+
if (polyFormat === 'string') {
|
|
13560
|
+
|
|
13561
|
+
polys = normalizePoly(polys, { toArray: true, flatten: true });
|
|
13562
|
+
|
|
13563
|
+
polys = polys.flat().join(' ');
|
|
13564
|
+
}
|
|
13179
13565
|
|
|
13180
|
-
if (polyFormat === 'string' && polys.length) {
|
|
13181
|
-
polys = polys.flat().map(pt => `${pt.x},${pt.y}`).join(' ');
|
|
13182
13566
|
}
|
|
13183
13567
|
|
|
13184
|
-
|
|
13568
|
+
// create object
|
|
13569
|
+
let svgObj = new SlickVGObj({ svg, d, polys, report, pathDataPlusArr: pathDataPlusArr_global, inputType, dOriginal });
|
|
13570
|
+
|
|
13571
|
+
/*
|
|
13572
|
+
let d2 = svgObj.getD()
|
|
13573
|
+
console.log('d2', d2);
|
|
13574
|
+
|
|
13575
|
+
let svg2 = svgObj.getSvg()
|
|
13576
|
+
console.log('svg2', svg2);
|
|
13577
|
+
|
|
13578
|
+
let format = 'd'
|
|
13579
|
+
format = 'pathData'
|
|
13580
|
+
format = 'd'
|
|
13581
|
+
format = 'points'
|
|
13582
|
+
format = 'array'
|
|
13583
|
+
let poly2 = svgObj.getPoly({ format })
|
|
13584
|
+
console.log('poly2', 'format', format, poly2);
|
|
13585
|
+
*/
|
|
13586
|
+
|
|
13587
|
+
return !getObject ? (d ? d : svg) : svgObj;
|
|
13185
13588
|
|
|
13186
13589
|
}
|
|
13187
13590
|
|
|
@@ -13190,6 +13593,7 @@
|
|
|
13190
13593
|
// IIFE
|
|
13191
13594
|
if (typeof window !== 'undefined') {
|
|
13192
13595
|
window.svgPathSimplify = svgPathSimplify;
|
|
13596
|
+
window.SlickVG = SlickVG;
|
|
13193
13597
|
window.getElementTransform = getElementTransform;
|
|
13194
13598
|
window.validateSVG = validateSVG;
|
|
13195
13599
|
window.detectInputType = detectInputType;
|
|
@@ -13199,6 +13603,7 @@
|
|
|
13199
13603
|
}
|
|
13200
13604
|
|
|
13201
13605
|
exports.PI = PI$1;
|
|
13606
|
+
exports.SlickVG = SlickVG;
|
|
13202
13607
|
exports.abs = abs$1;
|
|
13203
13608
|
exports.acos = acos$1;
|
|
13204
13609
|
exports.asin = asin$1;
|