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