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