svg-path-simplify 0.4.4 → 0.4.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/README.md +9 -1
- package/dist/svg-path-simplify.esm.js +1649 -1205
- package/dist/svg-path-simplify.esm.min.js +2 -2
- package/dist/svg-path-simplify.js +1649 -1204
- package/dist/svg-path-simplify.min.js +2 -2
- package/dist/svg-path-simplify.pathdata.esm.js +301 -637
- 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 +13 -8
- 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 +19 -21
- package/src/pathData_simplify_cubic_extrapolate.js +147 -8
- package/src/pathData_simplify_cubicsToArcs.js +65 -21
- package/src/pathSimplify-main.js +267 -64
- package/src/pathSimplify-presets.js +1 -1
- 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/svg-getAttributes.js +1 -1
- package/src/svgii/geometry.js +130 -27
- package/src/svgii/geometry_bbox.js +1 -1
- package/src/svgii/geometry_length.js +1 -1
- package/src/svgii/pathData_analyze.js +6 -28
- package/src/svgii/pathData_convert.js +63 -15
- package/src/svgii/pathData_remove_collinear.js +32 -36
- package/src/svgii/pathData_reorder.js +3 -2
- 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 +21 -8
- package/src/svgii/poly_to_pathdata.js +477 -8
- package/src/svgii/rounding.js +35 -14
- package/src/svgii/svg_cleanup.js +44 -51
- package/src/svgii/svg_cleanup_convertPathLength.js +5 -0
- package/src/svgii/svg_cleanup_normalize_transforms.js +2 -2
- package/src/svgii/visualize.js +33 -2
- package/v/0.4.5/index.html +1040 -0
- package/v/0.4.5/svg-path-simplify.js +13227 -0
|
@@ -380,7 +380,14 @@
|
|
|
380
380
|
|
|
381
381
|
// get angle helper
|
|
382
382
|
function getAngle(p1, p2, normalize = false) {
|
|
383
|
-
let angle = atan2(p2.y - p1.y, p2.x - p1.x);
|
|
383
|
+
let angle = Math.atan2(p2.y - p1.y, p2.x - p1.x);
|
|
384
|
+
// normalize negative angles
|
|
385
|
+
if (normalize && angle < 0) angle += Math.PI * 2;
|
|
386
|
+
return angle
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
function getAngleFromDelta(dx, dy, normalize = false) {
|
|
390
|
+
let angle = Math.atan2(dy, dx);
|
|
384
391
|
// normalize negative angles
|
|
385
392
|
if (normalize && angle < 0) angle += Math.PI * 2;
|
|
386
393
|
return angle
|
|
@@ -476,7 +483,9 @@
|
|
|
476
483
|
// if we cast these lines infinitely in both directions, they intersect here:
|
|
477
484
|
intersectionPoint = {
|
|
478
485
|
x: p1.x + (a * (p2.x - p1.x)),
|
|
479
|
-
y: p1.y + (a * (p2.y - p1.y))
|
|
486
|
+
y: p1.y + (a * (p2.y - p1.y)),
|
|
487
|
+
t1: a,
|
|
488
|
+
t2: b
|
|
480
489
|
};
|
|
481
490
|
|
|
482
491
|
let intersection = false;
|
|
@@ -578,13 +587,13 @@
|
|
|
578
587
|
if (t === 0 && !shortCp1) {
|
|
579
588
|
pt.x = p0.x;
|
|
580
589
|
pt.y = p0.y;
|
|
581
|
-
pt.angle = getAngle(p0, cp1);
|
|
590
|
+
if (getTangent) pt.angle = getAngle(p0, cp1);
|
|
582
591
|
}
|
|
583
592
|
|
|
584
593
|
else if (t === 1 && !shortCp2) {
|
|
585
594
|
pt.x = p.x;
|
|
586
595
|
pt.y = p.y;
|
|
587
|
-
pt.angle = getAngle(cp2, p);
|
|
596
|
+
if (getTangent) pt.angle = getAngle(cp2, p);
|
|
588
597
|
}
|
|
589
598
|
|
|
590
599
|
else {
|
|
@@ -601,18 +610,38 @@
|
|
|
601
610
|
pt = interpolate(m3, m4, t);
|
|
602
611
|
|
|
603
612
|
// add angles
|
|
604
|
-
pt.angle = getAngle(m3, m4);
|
|
613
|
+
if (getTangent) pt.angle = getAngle(m3, m4);
|
|
614
|
+
|
|
615
|
+
/*
|
|
616
|
+
renderPoint(markers, m0, 'cyan')
|
|
617
|
+
renderPoint(markers, m3, 'magenta')
|
|
618
|
+
renderPoint(markers, pt, 'green')
|
|
619
|
+
renderPoint(markers, m4, 'cyan')
|
|
620
|
+
renderPoint(markers, m2, 'magenta')
|
|
621
|
+
*/
|
|
605
622
|
|
|
606
623
|
// add control points
|
|
607
|
-
if (getCpts)
|
|
624
|
+
if (getCpts) {
|
|
625
|
+
pt.cpts = [m1, m2, m3, m4];
|
|
626
|
+
pt.segments = [
|
|
627
|
+
{ p0, cp1: m0, cp2: m3, p: pt },
|
|
628
|
+
{ p0: pt, cp1: m4, cp2: m2, p }
|
|
629
|
+
];
|
|
630
|
+
}
|
|
608
631
|
} else {
|
|
609
632
|
m1 = interpolate(p0, cp1, t);
|
|
610
633
|
m2 = interpolate(cp1, p, t);
|
|
611
634
|
pt = interpolate(m1, m2, t);
|
|
612
|
-
pt.angle = getAngle(m1, m2);
|
|
635
|
+
if (getTangent) pt.angle = getAngle(m1, m2);
|
|
613
636
|
|
|
614
637
|
// add control points
|
|
615
|
-
if (getCpts)
|
|
638
|
+
if (getCpts) {
|
|
639
|
+
pt.cpts = [m1, m2];
|
|
640
|
+
pt.segments = [
|
|
641
|
+
{ p0, cp1: m1, p: pt },
|
|
642
|
+
{ p0: p, cp1: m2, p }
|
|
643
|
+
];
|
|
644
|
+
}
|
|
616
645
|
}
|
|
617
646
|
}
|
|
618
647
|
|
|
@@ -696,7 +725,7 @@
|
|
|
696
725
|
* get vertices from path command final on-path points
|
|
697
726
|
*/
|
|
698
727
|
|
|
699
|
-
function getPathDataVertices(pathData=[], includeCpts = false, decimals = -1) {
|
|
728
|
+
function getPathDataVertices(pathData = [], includeCpts = false, decimals = -1) {
|
|
700
729
|
let polyPoints = [];
|
|
701
730
|
|
|
702
731
|
pathData.forEach((com) => {
|
|
@@ -783,7 +812,8 @@
|
|
|
783
812
|
|
|
784
813
|
if (rx == 0 || ry == 0) {
|
|
785
814
|
// invalid arguments
|
|
786
|
-
|
|
815
|
+
console.warn("rx and ry can not be 0");
|
|
816
|
+
return arcData
|
|
787
817
|
}
|
|
788
818
|
|
|
789
819
|
/**
|
|
@@ -796,10 +826,10 @@
|
|
|
796
826
|
let s_phi = !phi ? 0 : Math.sin(phi);
|
|
797
827
|
let c_phi = !phi ? 1 : Math.cos(phi);
|
|
798
828
|
|
|
799
|
-
let hd_x = (x1 - x2)
|
|
800
|
-
let hd_y = (y1 - y2)
|
|
801
|
-
let hs_x = (x1 + x2)
|
|
802
|
-
let hs_y = (y1 + y2)
|
|
829
|
+
let hd_x = (x1 - x2) * 0.5;
|
|
830
|
+
let hd_y = (y1 - y2) * 0.5;
|
|
831
|
+
let hs_x = (x1 + x2) * 0.5;
|
|
832
|
+
let hs_y = (y1 + y2) * 0.5;
|
|
803
833
|
|
|
804
834
|
// F6.5.1
|
|
805
835
|
let x1_ = !phi ? hd_x : c_phi * hd_x + s_phi * hd_y;
|
|
@@ -821,10 +851,11 @@
|
|
|
821
851
|
let rxry = rx * ry;
|
|
822
852
|
let rxy1_ = rx * y1_;
|
|
823
853
|
let ryx1_ = ry * x1_;
|
|
824
|
-
let sum_of_sq = rxy1_
|
|
854
|
+
let sum_of_sq = rxy1_ * rxy1_ + ryx1_ * ryx1_; // sum of square
|
|
825
855
|
if (!sum_of_sq) {
|
|
856
|
+
console.warn("start point can not be same as end point");
|
|
857
|
+
return arcData
|
|
826
858
|
|
|
827
|
-
throw Error("start point can not be same as end point");
|
|
828
859
|
}
|
|
829
860
|
let coe = Math.sqrt(Math.abs((rxry * rxry - sum_of_sq) / sum_of_sq));
|
|
830
861
|
if (largeArc === sweep) {
|
|
@@ -973,7 +1004,11 @@
|
|
|
973
1004
|
|
|
974
1005
|
function getBezierExtremeT(pts, { addExtremes = true, addSemiExtremes = false } = {}) {
|
|
975
1006
|
let tArr = pts.length === 4 ? cubicBezierExtremeT(pts[0], pts[1], pts[2], pts[3], { addExtremes, addSemiExtremes }) : quadraticBezierExtremeT(pts[0], pts[1], pts[2], { addExtremes, addSemiExtremes });
|
|
976
|
-
|
|
1007
|
+
if (tArr.length) {
|
|
1008
|
+
tArr = tArr.map(t => +t.toFixed(9)).sort();
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
return tArr
|
|
977
1012
|
}
|
|
978
1013
|
|
|
979
1014
|
function getArcExtemes(p0, values) {
|
|
@@ -1303,13 +1338,6 @@
|
|
|
1303
1338
|
return tArr;
|
|
1304
1339
|
}
|
|
1305
1340
|
|
|
1306
|
-
function isMultipleOf45(angleRad, epsilon = 0.001) {
|
|
1307
|
-
let rad90 = Math.PI / 2;
|
|
1308
|
-
let rad45 = rad90 / 2;
|
|
1309
|
-
let isRightAngle = Math.abs(angleRad / rad90 - Math.round(angleRad / rad90)) < epsilon;
|
|
1310
|
-
return !isRightAngle ? Math.abs(angleRad / rad45 - Math.round(angleRad / rad45)) < epsilon : false;
|
|
1311
|
-
}
|
|
1312
|
-
|
|
1313
1341
|
function quadraticBezierExtremeT(p0, cp1, p, { addExtremes = true, addSemiExtremes = false } = {}) {
|
|
1314
1342
|
|
|
1315
1343
|
// rotate cpts for semi extremes
|
|
@@ -1373,6 +1401,11 @@
|
|
|
1373
1401
|
let dx = isArray ? p2[0] - p1[0] : (p2.x - p1.x);
|
|
1374
1402
|
let dy = isArray ? p2[1] - p1[1] : (p2.y - p1.y);
|
|
1375
1403
|
|
|
1404
|
+
/*
|
|
1405
|
+
let sqrt2 = 1.4142135623730951
|
|
1406
|
+
return dx===dy ? Math.abs(dx) * sqrt2 : Math.sqrt(dx * dx + dy * dy);
|
|
1407
|
+
*/
|
|
1408
|
+
|
|
1376
1409
|
return Math.sqrt(dx * dx + dy * dy);
|
|
1377
1410
|
}
|
|
1378
1411
|
|
|
@@ -1388,6 +1421,7 @@
|
|
|
1388
1421
|
* sloppy but fast
|
|
1389
1422
|
*/
|
|
1390
1423
|
function getDistManhattan(pt1, pt2) {
|
|
1424
|
+
|
|
1391
1425
|
let dx = Math.abs(pt2.x - pt1.x);
|
|
1392
1426
|
let dy = Math.abs(pt2.y - pt1.y);
|
|
1393
1427
|
return dx + dy;
|
|
@@ -1855,7 +1889,7 @@
|
|
|
1855
1889
|
|
|
1856
1890
|
let len = pathData.length;
|
|
1857
1891
|
let decimals = decimalsGlobal;
|
|
1858
|
-
let decimalsArc = decimals < 3 ? decimals+
|
|
1892
|
+
let decimalsArc = decimals < 3 ? decimals + 1 : decimals;
|
|
1859
1893
|
|
|
1860
1894
|
for (let c = 0; c < len; c++) {
|
|
1861
1895
|
let com = pathData[c];
|
|
@@ -1890,7 +1924,7 @@
|
|
|
1890
1924
|
}
|
|
1891
1925
|
}
|
|
1892
1926
|
|
|
1893
|
-
let dim_min = dims.sort();
|
|
1927
|
+
let dim_min = dims.sort((a,b)=>a-b);
|
|
1894
1928
|
let sliceIdx = Math.ceil(dim_min.length / 8);
|
|
1895
1929
|
dim_min = dim_min.slice(0, sliceIdx);
|
|
1896
1930
|
let minVal = dim_min.reduce((a, b) => a + b, 0) / sliceIdx;
|
|
@@ -1910,31 +1944,50 @@
|
|
|
1910
1944
|
let com = pathData[i];
|
|
1911
1945
|
let { type, values, p0 = null, p = null, dimA = 0 } = com;
|
|
1912
1946
|
|
|
1913
|
-
// use existing
|
|
1947
|
+
// use existing average dimension value or calculate
|
|
1914
1948
|
if (values.length && p && p0) {
|
|
1915
|
-
|
|
1916
1949
|
dimA = dimA ? dimA : getDistManhattan(p0, p);
|
|
1917
1950
|
|
|
1918
|
-
if (
|
|
1951
|
+
if (type === 'A') dimA *= 0.5;
|
|
1919
1952
|
|
|
1953
|
+
if (dimA) dims.push(+dimA.toFixed(8));
|
|
1920
1954
|
}
|
|
1921
1955
|
|
|
1922
1956
|
}
|
|
1923
1957
|
|
|
1924
|
-
|
|
1958
|
+
dims = dims.sort((a,b)=>a-b);
|
|
1959
|
+
let len = dims.length;
|
|
1960
|
+
let dim_mid = dims[Math.floor(len * 0.5)];
|
|
1925
1961
|
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
let
|
|
1962
|
+
// smallest 25% of values
|
|
1963
|
+
let idx_q = Math.ceil(len * 0.25);
|
|
1964
|
+
let dims_min = dims.slice(0, idx_q);
|
|
1965
|
+
|
|
1966
|
+
// average smallest values with mid value
|
|
1967
|
+
let dim_min = ((dims_min.reduce((a, b) => a + b, 0) / idx_q) + dim_mid) * 0.5;
|
|
1929
1968
|
|
|
1930
1969
|
let threshold = 75;
|
|
1931
|
-
let decimalsAuto =
|
|
1970
|
+
let decimalsAuto = dim_min > threshold * 1.5 ? 0 : Math.floor(threshold / dim_min).toString().length;
|
|
1932
1971
|
|
|
1933
1972
|
// clamp
|
|
1934
1973
|
return Math.min(Math.max(0, decimalsAuto), 8)
|
|
1935
1974
|
|
|
1936
1975
|
}
|
|
1937
1976
|
|
|
1977
|
+
function roundPoly(poly = [], decimals = 3) {
|
|
1978
|
+
if (!poly.length || decimals < 0) return poly;
|
|
1979
|
+
poly = poly.map(pt => roundPoint(pt, decimals));
|
|
1980
|
+
return poly
|
|
1981
|
+
}
|
|
1982
|
+
|
|
1983
|
+
function roundPoint(pt = {}, decimals = 3) {
|
|
1984
|
+
if (pt.x === undefined || pt.y === undefined || decimals < 0) return pt;
|
|
1985
|
+
pt.x=roundTo(pt.x, decimals);
|
|
1986
|
+
pt.y=roundTo(pt.y, decimals);
|
|
1987
|
+
return pt
|
|
1988
|
+
|
|
1989
|
+
}
|
|
1990
|
+
|
|
1938
1991
|
/**
|
|
1939
1992
|
* rounding helper
|
|
1940
1993
|
* allows for quantized rounding
|
|
@@ -2497,7 +2550,7 @@
|
|
|
2497
2550
|
attributes.forEach(att=>{
|
|
2498
2551
|
|
|
2499
2552
|
let value = normalizeUnits(el.getAttribute(att), {x, y, width, height});
|
|
2500
|
-
atts[att
|
|
2553
|
+
atts[att] = value;
|
|
2501
2554
|
});
|
|
2502
2555
|
|
|
2503
2556
|
return atts
|
|
@@ -2709,7 +2762,7 @@
|
|
|
2709
2762
|
}
|
|
2710
2763
|
}
|
|
2711
2764
|
|
|
2712
|
-
let bbox = { x: xMin, y: yMin, width: xMax - xMin, height: yMax - yMin };
|
|
2765
|
+
let bbox = { x: xMin, y: yMin, right: xMax, width: xMax - xMin, bottom: yMax, height: yMax - yMin };
|
|
2713
2766
|
return bbox
|
|
2714
2767
|
}
|
|
2715
2768
|
|
|
@@ -2899,7 +2952,7 @@
|
|
|
2899
2952
|
* d attribute string
|
|
2900
2953
|
*/
|
|
2901
2954
|
|
|
2902
|
-
function pathDataToD(pathData, mode = 0) {
|
|
2955
|
+
function pathDataToD(pathData = [], mode = 0) {
|
|
2903
2956
|
|
|
2904
2957
|
mode = parseFloat(mode);
|
|
2905
2958
|
/*
|
|
@@ -2908,80 +2961,99 @@
|
|
|
2908
2961
|
1 = verbose
|
|
2909
2962
|
2 = beautify
|
|
2910
2963
|
*/
|
|
2964
|
+
|
|
2911
2965
|
let len = pathData.length;
|
|
2966
|
+
let d = '';
|
|
2912
2967
|
|
|
2913
|
-
|
|
2914
|
-
let
|
|
2915
|
-
|
|
2916
|
-
let separator_type = mode > 0.5 ? ' ' : '';
|
|
2968
|
+
// group same types
|
|
2969
|
+
let pathDataGrouped = mode >0.5 ? JSON.parse(JSON.stringify(pathData)) : [];
|
|
2970
|
+
let typePrev = 'M';
|
|
2917
2971
|
|
|
2918
|
-
|
|
2919
|
-
|
|
2972
|
+
if (mode < 1) {
|
|
2973
|
+
pathDataGrouped = [pathData[0]];
|
|
2920
2974
|
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
let
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2975
|
+
let idx = 0;
|
|
2976
|
+
|
|
2977
|
+
for (let i = 1; i < len; i++) {
|
|
2978
|
+
let com = pathData[i];
|
|
2979
|
+
let { type } = com;
|
|
2980
|
+
// decouple from object
|
|
2981
|
+
let values = [...com.values];
|
|
2982
|
+
|
|
2983
|
+
// new type
|
|
2984
|
+
if (type !== typePrev) {
|
|
2985
|
+
pathDataGrouped.push({type, values});
|
|
2986
|
+
idx++;
|
|
2987
|
+
} else {
|
|
2988
|
+
pathDataGrouped[idx].values.push(...values);
|
|
2989
|
+
}
|
|
2990
|
+
|
|
2991
|
+
// update type
|
|
2992
|
+
typePrev = type;
|
|
2934
2993
|
}
|
|
2994
|
+
}
|
|
2995
|
+
|
|
2996
|
+
// stringify grouped
|
|
2997
|
+
len = pathDataGrouped.length;
|
|
2998
|
+
let separator_type = mode < 1 ? '' : ' ';
|
|
2999
|
+
let separator_command = mode < 1 ? '' : (mode === 1 ? ' ' : `\n`);
|
|
2935
3000
|
|
|
2936
|
-
|
|
2937
|
-
type = ((mode < 1) && com0.type === com.type && com.type.toLowerCase() !== 'm')
|
|
2938
|
-
? " "
|
|
2939
|
-
: ((mode < 1) && com0.type === "M" && com.type === "L"
|
|
2940
|
-
? " "
|
|
2941
|
-
: com.type);
|
|
3001
|
+
typePrev = 'M';
|
|
2942
3002
|
|
|
2943
|
-
|
|
2944
|
-
|
|
3003
|
+
for (let i = 0; i < len; i++) {
|
|
3004
|
+
let com = pathDataGrouped[i];
|
|
3005
|
+
let { type, values } = com;
|
|
2945
3006
|
|
|
2946
|
-
|
|
3007
|
+
// we're always starting a path with absolute M!
|
|
3008
|
+
let omitType = mode < 1 && ((typePrev === 'M' && type === 'L') || (typePrev === 'm' && type === 'l'));
|
|
2947
3009
|
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
let valStr = val.toString();
|
|
2951
|
-
let isFloat = valStr.includes('.');
|
|
2952
|
-
let isSmallFloat = isFloat && Math.abs(val) < 1;
|
|
3010
|
+
// add type
|
|
3011
|
+
if (!omitType) d += type + separator_type;
|
|
2953
3012
|
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
}
|
|
3013
|
+
// add values
|
|
3014
|
+
let wasSmallFloat = false;
|
|
3015
|
+
let separatorVal = ' ';
|
|
2958
3016
|
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
3017
|
+
for (let v = 0, vlen = values.length; vlen && v < vlen; v++) {
|
|
3018
|
+
let val = values[v];
|
|
3019
|
+
let valAbs = Math.abs(val);
|
|
3020
|
+
let valStr = val.toString();
|
|
3021
|
+
let isNegative = val < 0;
|
|
3022
|
+
let sign = isNegative ? '-' : '';
|
|
3023
|
+
let isSmallFloat = mode > 0.5 ? false : (val && valAbs < 1);
|
|
3024
|
+
let idxSub = isSmallFloat ? (isNegative ? 2 : 1) : 0;
|
|
3025
|
+
|
|
3026
|
+
// we don't need whitespace for first value
|
|
3027
|
+
separatorVal = v === 0 || isNegative ? '' : ' ';
|
|
3028
|
+
|
|
3029
|
+
if (mode < 1) {
|
|
3030
|
+
// omit leading zero
|
|
3031
|
+
if (isSmallFloat) valStr = sign + valStr.substring(idxSub);
|
|
3032
|
+
|
|
3033
|
+
// omit whitespace for subsequent small floats
|
|
3034
|
+
separatorVal = (v === 0 && !omitType) || (wasSmallFloat && isSmallFloat) ?
|
|
3035
|
+
(!mode ? '' : (isNegative ? '' : ' '))
|
|
3036
|
+
: (isNegative ? '' : ' ');
|
|
2963
3037
|
|
|
2964
|
-
valsString += valStr;
|
|
2965
|
-
prevWasFloat = isSmallFloat;
|
|
2966
3038
|
}
|
|
2967
3039
|
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
3040
|
+
// omit separator between large Arc sweep and final x in minify mode
|
|
3041
|
+
if (!mode && (type === 'a' || type === 'A')) {
|
|
3042
|
+
let pos = (v % 7);
|
|
3043
|
+
if (pos > 3 && pos < 6) separatorVal = '';
|
|
3044
|
+
}
|
|
3045
|
+
|
|
3046
|
+
d += `${separatorVal}${valStr}`;
|
|
3047
|
+
wasSmallFloat = isSmallFloat;
|
|
3048
|
+
|
|
2972
3049
|
}
|
|
2973
3050
|
|
|
2974
|
-
|
|
2975
|
-
d +=
|
|
2976
|
-
|
|
3051
|
+
// add command separator
|
|
3052
|
+
if (mode) d += separator_command;
|
|
3053
|
+
|
|
3054
|
+
// update previous type
|
|
3055
|
+
typePrev = type;
|
|
2977
3056
|
|
|
2978
|
-
if (mode < 1) {
|
|
2979
|
-
d = d
|
|
2980
|
-
.replace(/[A-Za-z]0(?=\.)/g, m => m[0])
|
|
2981
|
-
.replace(/ 0\./g, " .") // Space before small decimals
|
|
2982
|
-
.replace(/ -/g, "-") // Remove space before negatives
|
|
2983
|
-
.replace(/-0\./g, "-.") // Remove leading zero from negative decimals
|
|
2984
|
-
.replace(/Z/g, "z"); // Convert uppercase 'Z' to lowercase
|
|
2985
3057
|
}
|
|
2986
3058
|
|
|
2987
3059
|
return d;
|
|
@@ -2989,39 +3061,26 @@
|
|
|
2989
3061
|
|
|
2990
3062
|
function getCombinedByDominant(com1, com2, maxDist = 0, tolerance = 1, debug = false) {
|
|
2991
3063
|
|
|
2992
|
-
// cubic Bézier derivative
|
|
2993
|
-
const cubicDerivative = (p0, p1, p2, p3, t) => {
|
|
2994
|
-
let mt = 1 - t;
|
|
2995
|
-
|
|
2996
|
-
return {
|
|
2997
|
-
x:
|
|
2998
|
-
3 * mt * mt * (p1.x - p0.x) +
|
|
2999
|
-
6 * mt * t * (p2.x - p1.x) +
|
|
3000
|
-
3 * t * t * (p3.x - p2.x),
|
|
3001
|
-
y:
|
|
3002
|
-
3 * mt * mt * (p1.y - p0.y) +
|
|
3003
|
-
6 * mt * t * (p2.y - p1.y) +
|
|
3004
|
-
3 * t * t * (p3.y - p2.y)
|
|
3005
|
-
};
|
|
3006
|
-
};
|
|
3007
|
-
|
|
3008
3064
|
// if combining fails return original commands
|
|
3009
3065
|
let commands = [com1, com2];
|
|
3010
3066
|
|
|
3011
3067
|
// detect dominant
|
|
3012
|
-
let dist1 =
|
|
3013
|
-
let dist2 =
|
|
3068
|
+
let dist1 = getDistManhattan(com1.p0, com1.p);
|
|
3069
|
+
let dist2 = getDistManhattan(com2.p0, com2.p);
|
|
3070
|
+
let thresh = (dist1 + dist2) * 0.5 * 0.075 * tolerance;
|
|
3014
3071
|
|
|
3015
|
-
|
|
3072
|
+
// take longer command
|
|
3073
|
+
let reverse = dist1 < dist2;
|
|
3016
3074
|
|
|
3017
3075
|
// backup original commands
|
|
3018
3076
|
let com1_o = JSON.parse(JSON.stringify(com1));
|
|
3019
3077
|
let com2_o = JSON.parse(JSON.stringify(com2));
|
|
3020
3078
|
|
|
3021
|
-
|
|
3079
|
+
// intersection of control tangents
|
|
3080
|
+
let ptI = checkLineIntersection(com1_o.p0, com1_o.cp1, com2_o.p, com2_o.cp2, false, true);
|
|
3022
3081
|
|
|
3082
|
+
// no intersection - we can't combine
|
|
3023
3083
|
if (!ptI) {
|
|
3024
|
-
|
|
3025
3084
|
return commands
|
|
3026
3085
|
}
|
|
3027
3086
|
|
|
@@ -3044,153 +3103,70 @@
|
|
|
3044
3103
|
com2 = com2_R;
|
|
3045
3104
|
}
|
|
3046
3105
|
|
|
3047
|
-
|
|
3048
|
-
let
|
|
3049
|
-
let
|
|
3050
|
-
let dot = (a, b) => a.x * b.x + a.y * b.y;
|
|
3051
|
-
|
|
3052
|
-
// estimate extrapolation parameter t0
|
|
3053
|
-
|
|
3054
|
-
let B0 = com2.p0;
|
|
3055
|
-
let D0 = cubicDerivative(
|
|
3056
|
-
com2.p0,
|
|
3057
|
-
com2.cp1,
|
|
3058
|
-
com2.cp2,
|
|
3059
|
-
com2.p,
|
|
3060
|
-
0
|
|
3061
|
-
);
|
|
3062
|
-
|
|
3063
|
-
let v = sub(com1.p0, B0);
|
|
3064
|
-
|
|
3065
|
-
// first-order projection onto tangent
|
|
3066
|
-
let t0 = dot(v, D0) / dot(D0, D0);
|
|
3067
|
-
|
|
3068
|
-
// refine with one Newton iteration (optional but cheap)
|
|
3069
|
-
let P = pointAtT([com2.p0, com2.cp1, com2.cp2, com2.p], t0);
|
|
3070
|
-
let dP = cubicDerivative(com2.p0, com2.cp1, com2.cp2, com2.p, t0);
|
|
3071
|
-
let r = sub(P, com1.p0);
|
|
3072
|
-
|
|
3073
|
-
t0 -= dot(r, dP) / dot(dP, dP);
|
|
3074
|
-
|
|
3075
|
-
// construct merged cubic over [t0, 1]
|
|
3076
|
-
let Q0 = pointAtT([com2.p0, com2.cp1, com2.cp2, com2.p], t0);
|
|
3077
|
-
let Q3 = com2.p;
|
|
3078
|
-
|
|
3079
|
-
let d0 = cubicDerivative(com2.p0, com2.cp1, com2.cp2, com2.p, t0);
|
|
3080
|
-
let d1 = cubicDerivative(com2.p0, com2.cp1, com2.cp2, com2.p, 1);
|
|
3081
|
-
|
|
3082
|
-
let scale = 1 - t0;
|
|
3083
|
-
|
|
3084
|
-
let Q1 = add(Q0, mul(d0, scale / 3));
|
|
3085
|
-
let Q2 = sub(Q3, mul(d1, scale / 3));
|
|
3086
|
-
|
|
3087
|
-
let result = {
|
|
3088
|
-
p0: Q0,
|
|
3089
|
-
cp1: Q1,
|
|
3090
|
-
cp2: Q2,
|
|
3091
|
-
p: Q3,
|
|
3092
|
-
t0
|
|
3093
|
-
};
|
|
3094
|
-
|
|
3095
|
-
if (reverse) {
|
|
3096
|
-
result = {
|
|
3097
|
-
p0: Q3,
|
|
3098
|
-
cp1: Q2,
|
|
3099
|
-
cp2: Q1,
|
|
3100
|
-
p: Q0,
|
|
3101
|
-
t0
|
|
3102
|
-
};
|
|
3103
|
-
}
|
|
3104
|
-
|
|
3105
|
-
let tMid = (1 - t0) * 0.5;
|
|
3106
|
+
// etsimate t for extrapolation
|
|
3107
|
+
let PtI = checkLineIntersection(com1.cp2, com1.p, com2.p, com2.cp2, false, true);
|
|
3108
|
+
let cp1_I = interpolate(com1.p, PtI, 0.666);
|
|
3106
3109
|
|
|
3107
|
-
let
|
|
3108
|
-
let
|
|
3110
|
+
let dist1_2 = getDistManhattan(com1.cp2, com1.p);
|
|
3111
|
+
let dist2_2 = getDistManhattan(com1.cp2, cp1_I);
|
|
3112
|
+
let t = dist2_2 / dist1_2;
|
|
3109
3113
|
|
|
3110
|
-
|
|
3111
|
-
let
|
|
3114
|
+
// extrapolate
|
|
3115
|
+
let segs = pointAtT([com1.p0, com1.cp1, com1.cp2, com1.p], t, false, true).segments;
|
|
3112
3116
|
|
|
3113
|
-
let
|
|
3114
|
-
let cp2_2 = interpolate(result.p, ptI_2, 1.333 );
|
|
3117
|
+
let seg = segs[0];
|
|
3115
3118
|
|
|
3116
|
-
//
|
|
3117
|
-
let
|
|
3118
|
-
if (cp_intersection) {
|
|
3119
|
+
// if it worked - points should be nearby
|
|
3120
|
+
let dist = getDistManhattan(seg.p, com2.p);
|
|
3119
3121
|
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
result.cp2 = cp2_2;
|
|
3125
|
-
|
|
3126
|
-
// check distances between original starting point and extrapolated
|
|
3127
|
-
let dist3 = getDistAv(com1_o.p0, result.p0);
|
|
3128
|
-
let dist4 = getDistAv(com2_o.p, result.p);
|
|
3129
|
-
let dist5 = (dist3 + dist4);
|
|
3130
|
-
|
|
3131
|
-
// use original points
|
|
3132
|
-
result.p0 = com1_o.p0;
|
|
3133
|
-
result.p = com2_o.p;
|
|
3134
|
-
result.extreme = com2_o.extreme;
|
|
3135
|
-
result.corner = com2_o.corner;
|
|
3136
|
-
result.dimA = com2_o.dimA;
|
|
3137
|
-
result.directionChange = com2_o.directionChange;
|
|
3138
|
-
result.type = 'C';
|
|
3139
|
-
result.values = [result.cp1.x, result.cp1.y, result.cp2.x, result.cp2.y, result.p.x, result.p.y];
|
|
3122
|
+
// if close enough adjust
|
|
3123
|
+
if (dist < thresh) {
|
|
3124
|
+
let angle = getAngle(seg.p, seg.cp2);
|
|
3125
|
+
let angle2 = getAngle(com2.p, com2.cp2);
|
|
3140
3126
|
|
|
3141
|
-
|
|
3142
|
-
if (dist5 < maxDist) {
|
|
3143
|
-
|
|
3144
|
-
/*
|
|
3145
|
-
let tTotal = 1 + Math.abs(t0);
|
|
3146
|
-
let tSplit = reverse ? 1 + t0 : Math.abs(t0);
|
|
3147
|
-
|
|
3148
|
-
let pO = pointAtT([com2_o.p0, com2_o.cp1, com2_o.cp2, com2_o.p], t0);
|
|
3149
|
-
*/
|
|
3127
|
+
let angleDiff = (angle2 - angle);
|
|
3150
3128
|
|
|
3151
|
-
//
|
|
3152
|
-
|
|
3129
|
+
// adjust cp angle
|
|
3130
|
+
seg.cp2 = rotatePoint(seg.cp2, seg.p.x, seg.p.y, angleDiff);
|
|
3131
|
+
let dist1 = getDistManhattan(seg.p, seg.cp2);
|
|
3153
3132
|
|
|
3154
|
-
|
|
3155
|
-
|
|
3133
|
+
// copy original final point coordinates
|
|
3134
|
+
seg.p = com2.p;
|
|
3156
3135
|
|
|
3157
|
-
|
|
3158
|
-
let
|
|
3136
|
+
// after rotation
|
|
3137
|
+
let dist2 = getDistManhattan(seg.p, seg.cp2);
|
|
3138
|
+
let scale = dist2 / dist1;
|
|
3159
3139
|
|
|
3160
|
-
//
|
|
3161
|
-
|
|
3140
|
+
// adjust tangent length
|
|
3141
|
+
seg.cp2 = interpolate(seg.p, seg.cp2, scale);
|
|
3162
3142
|
|
|
3163
|
-
|
|
3143
|
+
// reverse back
|
|
3144
|
+
if (reverse) {
|
|
3145
|
+
seg = {
|
|
3146
|
+
p0: seg.p,
|
|
3147
|
+
p: seg.p0,
|
|
3148
|
+
cp1: seg.cp2,
|
|
3149
|
+
cp2: seg.cp1,
|
|
3150
|
+
};
|
|
3164
3151
|
}
|
|
3165
3152
|
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3153
|
+
commands = [
|
|
3154
|
+
{
|
|
3155
|
+
type: 'C',
|
|
3156
|
+
values: [seg.cp1.x, seg.cp1.y, seg.cp2.x, seg.cp2.y, seg.p.x, seg.p.y],
|
|
3157
|
+
p0: seg.p0,
|
|
3158
|
+
cp1: seg.cp1,
|
|
3159
|
+
cp2: seg.cp2,
|
|
3160
|
+
p: seg.p,
|
|
3161
|
+
extreme: com2_o.extreme,
|
|
3162
|
+
corner: com2_o.corner,
|
|
3163
|
+
directionChange: com2_o.directionChange,
|
|
3164
|
+
dimA: getDistManhattan(seg.p0, seg.p),
|
|
3165
|
+
error: dist
|
|
3172
3166
|
|
|
3173
|
-
|
|
3174
|
-
let pathDataN = [
|
|
3175
|
-
{ type: 'M', values: [result.p0.x, result.p0.y] },
|
|
3176
|
-
{ type: 'C', values: [result.cp1.x, result.cp1.y, result.cp2.x, result.cp2.y, result.p.x, result.p.y] },
|
|
3167
|
+
}
|
|
3177
3168
|
];
|
|
3178
3169
|
|
|
3179
|
-
let areaN = getPathArea(pathDataN);
|
|
3180
|
-
let areaDiff = Math.abs(areaN / area0 - 1);
|
|
3181
|
-
|
|
3182
|
-
result.error = areaDiff * 5 * tolerance;
|
|
3183
|
-
|
|
3184
|
-
if (debug) {
|
|
3185
|
-
pathDataToD(pathDataN);
|
|
3186
|
-
|
|
3187
|
-
}
|
|
3188
|
-
|
|
3189
|
-
// success!!!
|
|
3190
|
-
if (areaDiff < 0.05 * tolerance) {
|
|
3191
|
-
commands = [result];
|
|
3192
|
-
|
|
3193
|
-
}
|
|
3194
3170
|
}
|
|
3195
3171
|
|
|
3196
3172
|
return commands
|
|
@@ -3242,7 +3218,7 @@
|
|
|
3242
3218
|
error += com.error;
|
|
3243
3219
|
|
|
3244
3220
|
// find next candidates
|
|
3245
|
-
for (let n = i +
|
|
3221
|
+
for (let n = i + offset; error < tolerance && n < l; n++) {
|
|
3246
3222
|
let comN = pathData[n];
|
|
3247
3223
|
|
|
3248
3224
|
if (comN.type !== 'C' ||
|
|
@@ -3252,6 +3228,7 @@
|
|
|
3252
3228
|
(keepExtremes && com.extreme)
|
|
3253
3229
|
)
|
|
3254
3230
|
) {
|
|
3231
|
+
|
|
3255
3232
|
break
|
|
3256
3233
|
}
|
|
3257
3234
|
|
|
@@ -3259,6 +3236,7 @@
|
|
|
3259
3236
|
|
|
3260
3237
|
// failure - could not be combined - exit loop
|
|
3261
3238
|
if (combined.length > 1) {
|
|
3239
|
+
|
|
3262
3240
|
break
|
|
3263
3241
|
}
|
|
3264
3242
|
|
|
@@ -3272,6 +3250,7 @@
|
|
|
3272
3250
|
|
|
3273
3251
|
// return combined
|
|
3274
3252
|
com = combined[0];
|
|
3253
|
+
|
|
3275
3254
|
}
|
|
3276
3255
|
|
|
3277
3256
|
pathDataN.push(com);
|
|
@@ -3310,11 +3289,12 @@
|
|
|
3310
3289
|
// quit if t is start
|
|
3311
3290
|
if (!t) return commands;
|
|
3312
3291
|
|
|
3292
|
+
// get averaged threshold
|
|
3313
3293
|
let distAv1 = getDistManhattan(com1.p0, com1.p);
|
|
3314
3294
|
let distAv2 = getDistManhattan(com2.p0, com2.p);
|
|
3315
3295
|
let distMin = Math.max(0, Math.min(distAv1, distAv2));
|
|
3316
3296
|
|
|
3317
|
-
let distScale = 0.
|
|
3297
|
+
let distScale = 0.075;
|
|
3318
3298
|
let maxDist = distMin * distScale * tolerance;
|
|
3319
3299
|
|
|
3320
3300
|
// get hypothetical combined command
|
|
@@ -3362,16 +3342,6 @@
|
|
|
3362
3342
|
|
|
3363
3343
|
if (error < maxDist) success = true;
|
|
3364
3344
|
|
|
3365
|
-
/*
|
|
3366
|
-
renderPoint(markers, ptM_seg1, 'cyan')
|
|
3367
|
-
renderPoint(markers, pt, 'orange', '1.5%', '1')
|
|
3368
|
-
renderPoint(markers, ptM_seg2, 'orange')
|
|
3369
|
-
|
|
3370
|
-
renderPoint(markers, com1.p, 'green')
|
|
3371
|
-
|
|
3372
|
-
renderPoint(markers, ptI_seg1, 'purple')
|
|
3373
|
-
*/
|
|
3374
|
-
|
|
3375
3345
|
}
|
|
3376
3346
|
|
|
3377
3347
|
} // end 1st try
|
|
@@ -3385,9 +3355,9 @@
|
|
|
3385
3355
|
|
|
3386
3356
|
comS.dimA = getDistManhattan(comS.p0, comS.p);
|
|
3387
3357
|
comS.type = 'C';
|
|
3358
|
+
|
|
3388
3359
|
comS.extreme = com2.extreme;
|
|
3389
3360
|
comS.directionChange = com2.directionChange;
|
|
3390
|
-
|
|
3391
3361
|
comS.corner = com2.corner;
|
|
3392
3362
|
|
|
3393
3363
|
comS.values = [comS.cp1.x, comS.cp1.y, comS.cp2.x, comS.cp2.y, comS.p.x, comS.p.y];
|
|
@@ -3448,7 +3418,9 @@
|
|
|
3448
3418
|
}
|
|
3449
3419
|
*/
|
|
3450
3420
|
|
|
3451
|
-
|
|
3421
|
+
let t = l1 / l3;
|
|
3422
|
+
|
|
3423
|
+
return t;
|
|
3452
3424
|
}
|
|
3453
3425
|
|
|
3454
3426
|
function commandIsFlat(points, {
|
|
@@ -3501,7 +3473,7 @@
|
|
|
3501
3473
|
detectDirection = true,
|
|
3502
3474
|
detectSemiExtremes = false,
|
|
3503
3475
|
debug = false,
|
|
3504
|
-
addSquareLength =
|
|
3476
|
+
addSquareLength = false,
|
|
3505
3477
|
addArea = true,
|
|
3506
3478
|
|
|
3507
3479
|
} = {}) {
|
|
@@ -3550,8 +3522,8 @@
|
|
|
3550
3522
|
let commandPts = (type === 'C' || type === 'Q') ?
|
|
3551
3523
|
(type === 'C' ? [p0, cp1, cp2, p] : [p0, cp1, p]) :
|
|
3552
3524
|
([p0, p]);
|
|
3553
|
-
|
|
3554
|
-
let threshold =
|
|
3525
|
+
|
|
3526
|
+
let threshold = dimA * 0.005;
|
|
3555
3527
|
|
|
3556
3528
|
// bezier types
|
|
3557
3529
|
let isBezier = type === 'Q' || type === 'C';
|
|
@@ -3564,7 +3536,7 @@
|
|
|
3564
3536
|
*/
|
|
3565
3537
|
let hasExtremes = false;
|
|
3566
3538
|
|
|
3567
|
-
if (isBezier) {
|
|
3539
|
+
if (detectExtremes && isBezier) {
|
|
3568
3540
|
|
|
3569
3541
|
let dx = type === 'C' ? Math.abs(com.cp2.x - com.p.x) : Math.abs(com.cp1.x - com.p.x);
|
|
3570
3542
|
let dy = type === 'C' ? Math.abs(com.cp2.y - com.p.y) : Math.abs(com.cp1.y - com.p.y);
|
|
@@ -3603,7 +3575,7 @@
|
|
|
3603
3575
|
}
|
|
3604
3576
|
|
|
3605
3577
|
// check extremes introduce by small arcs
|
|
3606
|
-
else if(isArc && comN && ((comPrev.type==='C' || comPrev.type==='Q') || (comN.type==='C' || comN.type==='Q')) ){
|
|
3578
|
+
else if(detectExtremes && isArc && comN && ((comPrev.type==='C' || comPrev.type==='Q') || (comN.type==='C' || comN.type==='Q')) ){
|
|
3607
3579
|
let distN = comN ? comN.dimA : 0;
|
|
3608
3580
|
let isShort = com.dimA < (comPrev.dimA + distN) * 0.1;
|
|
3609
3581
|
let smallRadius = com.values[0] === com.values[1] && (com.values[0] < 1);
|
|
@@ -3621,28 +3593,7 @@
|
|
|
3621
3593
|
if (hasExtremes) com.extreme = true;
|
|
3622
3594
|
|
|
3623
3595
|
// Corners and semi extremes
|
|
3624
|
-
if (isBezier && isBezierN) {
|
|
3625
|
-
|
|
3626
|
-
// semi extremes
|
|
3627
|
-
if (detectSemiExtremes && !com.extreme) {
|
|
3628
|
-
|
|
3629
|
-
let dx1 = Math.abs(p.x - cp2.x);
|
|
3630
|
-
let dy1 = Math.abs(p.y - cp2.y);
|
|
3631
|
-
let hasSemiExtreme = false;
|
|
3632
|
-
|
|
3633
|
-
// exclude extremes or small deltas
|
|
3634
|
-
if (dx1 && dy1 && dx1 > thresholdLength || dy1 > thresholdLength) {
|
|
3635
|
-
let ang1 = getAngle(cp2, p);
|
|
3636
|
-
let ang2 = getAngle(p, comN.cp1);
|
|
3637
|
-
|
|
3638
|
-
let ang3 = Math.abs(ang1 + ang2) / 2;
|
|
3639
|
-
hasSemiExtreme = isMultipleOf45(ang3);
|
|
3640
|
-
}
|
|
3641
|
-
|
|
3642
|
-
if (hasSemiExtreme) {
|
|
3643
|
-
com.semiExtreme = true;
|
|
3644
|
-
}
|
|
3645
|
-
}
|
|
3596
|
+
if (detectCorners && isBezier && isBezierN) {
|
|
3646
3597
|
|
|
3647
3598
|
/**
|
|
3648
3599
|
* Detect direction change points
|
|
@@ -4222,7 +4173,12 @@
|
|
|
4222
4173
|
if (hasShorthands && toLonghands) pathData = pathDataToLonghands(pathData);
|
|
4223
4174
|
|
|
4224
4175
|
// minify semicircle radii
|
|
4225
|
-
if (optimizeArcs)
|
|
4176
|
+
if (optimizeArcs) {
|
|
4177
|
+
pathData = optimizeArcPathData(pathData);
|
|
4178
|
+
} else {
|
|
4179
|
+
// get true absolute radii
|
|
4180
|
+
pathData = pathDataToTrueArcValues(pathData);
|
|
4181
|
+
}
|
|
4226
4182
|
|
|
4227
4183
|
if (toShorthands) pathData = pathDataToShorthands(pathData);
|
|
4228
4184
|
|
|
@@ -4312,9 +4268,42 @@
|
|
|
4312
4268
|
}
|
|
4313
4269
|
|
|
4314
4270
|
/**
|
|
4315
|
-
*
|
|
4316
|
-
*
|
|
4317
|
-
|
|
4271
|
+
* Converts minified arc
|
|
4272
|
+
* values to true rx and ry values
|
|
4273
|
+
*/
|
|
4274
|
+
function pathDataToTrueArcValues(pathData = []) {
|
|
4275
|
+
let l = pathData.length;
|
|
4276
|
+
let pathDataN = [pathData[0]];
|
|
4277
|
+
|
|
4278
|
+
for (let i = 1; i < l; i++) {
|
|
4279
|
+
let com = pathData[i];
|
|
4280
|
+
let comPrev = pathData[i-1];
|
|
4281
|
+
let { type, values } = com;
|
|
4282
|
+
|
|
4283
|
+
if (type === 'A') {
|
|
4284
|
+
|
|
4285
|
+
// previous commands final on-path point
|
|
4286
|
+
let [x1, y1] = comPrev.values.slice(-2);
|
|
4287
|
+
let [rx, ry, xAxisRotation, largeArc, sweep, x2, y2] = values;
|
|
4288
|
+
let arcData = svgArcToCenterParam(x1, y1, rx, ry, xAxisRotation, largeArc, sweep, x2, y2);
|
|
4289
|
+
|
|
4290
|
+
// set true arc values
|
|
4291
|
+
com.values[0] = arcData.rx;
|
|
4292
|
+
com.values[1] = arcData.ry;
|
|
4293
|
+
|
|
4294
|
+
}
|
|
4295
|
+
|
|
4296
|
+
pathDataN.push(com);
|
|
4297
|
+
|
|
4298
|
+
}
|
|
4299
|
+
return pathDataN;
|
|
4300
|
+
}
|
|
4301
|
+
|
|
4302
|
+
/**
|
|
4303
|
+
* Minify arc radii
|
|
4304
|
+
* for semi circles
|
|
4305
|
+
* returns smaller rx and ry
|
|
4306
|
+
* values
|
|
4318
4307
|
*/
|
|
4319
4308
|
|
|
4320
4309
|
function optimizeArcPathData(pathData = []) {
|
|
@@ -4371,7 +4360,7 @@
|
|
|
4371
4360
|
if (isHorizontal || isVertical) {
|
|
4372
4361
|
|
|
4373
4362
|
// check if semi circle
|
|
4374
|
-
let needsTrueR = isHorizontal ? rx*1.9 > diffX : ry*1.9 > diffY;
|
|
4363
|
+
let needsTrueR = isHorizontal ? rx * 1.9 > diffX : ry * 1.9 > diffY;
|
|
4375
4364
|
|
|
4376
4365
|
// is semicircle we can simplify rx
|
|
4377
4366
|
if (!needsTrueR) {
|
|
@@ -5067,8 +5056,8 @@
|
|
|
5067
5056
|
let phi = rotation ? rotation * TAU / 360 : 0;
|
|
5068
5057
|
let sinphi = phi ? Math.sin(phi) : 0;
|
|
5069
5058
|
let cosphi = phi ? Math.cos(phi) : 1;
|
|
5070
|
-
let pxp = cosphi * (p0.x - x)
|
|
5071
|
-
let pyp = -sinphi * (p0.x - x)
|
|
5059
|
+
let pxp = cosphi * (p0.x - x) *0.5 + sinphi * (p0.y - y) *0.5;
|
|
5060
|
+
let pyp = -sinphi * (p0.x - x) *0.5 + cosphi * (p0.y - y) *0.5;
|
|
5072
5061
|
|
|
5073
5062
|
if (pxp === 0 && pyp === 0) {
|
|
5074
5063
|
return []
|
|
@@ -5104,8 +5093,8 @@
|
|
|
5104
5093
|
|
|
5105
5094
|
let centerxp = radicant ? radicant * rx / ry * pyp : 0;
|
|
5106
5095
|
let centeryp = radicant ? radicant * -ry / rx * pxp : 0;
|
|
5107
|
-
let centerx = cosphi * centerxp - sinphi * centeryp + (p0.x + x)
|
|
5108
|
-
let centery = sinphi * centerxp + cosphi * centeryp + (p0.y + y)
|
|
5096
|
+
let centerx = cosphi * centerxp - sinphi * centeryp + (p0.x + x) * 0.5;
|
|
5097
|
+
let centery = sinphi * centerxp + cosphi * centeryp + (p0.y + y) * 0.5;
|
|
5109
5098
|
|
|
5110
5099
|
let vx1 = (pxp - centerxp) / rx;
|
|
5111
5100
|
let vy1 = (pyp - centeryp) / ry;
|
|
@@ -5127,15 +5116,17 @@
|
|
|
5127
5116
|
ang2 = vectorAngle(vx1, vy1, vx2, vy2);
|
|
5128
5117
|
|
|
5129
5118
|
if (sweepFlag === 0 && ang2 > 0) {
|
|
5130
|
-
|
|
5119
|
+
|
|
5120
|
+
ang2 -= TAU;
|
|
5131
5121
|
}
|
|
5132
5122
|
else if (sweepFlag === 1 && ang2 < 0) {
|
|
5133
|
-
|
|
5123
|
+
|
|
5124
|
+
ang2 += TAU;
|
|
5134
5125
|
}
|
|
5135
5126
|
|
|
5136
|
-
let ratio = +(Math.abs(ang2) / (TAU
|
|
5127
|
+
let ratio = +(Math.abs(ang2) / (TAU * 0.25)).toFixed(0) || 1;
|
|
5137
5128
|
|
|
5138
|
-
// increase segments for more
|
|
5129
|
+
// increase segments for more accurate length calculations
|
|
5139
5130
|
let segments = ratio * splitSegments;
|
|
5140
5131
|
ang2 /= segments;
|
|
5141
5132
|
let pathDataArc = [];
|
|
@@ -5147,7 +5138,8 @@
|
|
|
5147
5138
|
const k = 0.551785;
|
|
5148
5139
|
let a = ang2 === angle90 ? k :
|
|
5149
5140
|
(
|
|
5150
|
-
|
|
5141
|
+
|
|
5142
|
+
ang2 === -angle90 ? -k : 1.33333 * Math.tan(ang2 * 0.25)
|
|
5151
5143
|
);
|
|
5152
5144
|
|
|
5153
5145
|
let cos2 = ang2 ? Math.cos(ang2) : 1;
|
|
@@ -5194,7 +5186,7 @@
|
|
|
5194
5186
|
let comA = cubicCommandToArc(p0, cp1, cp2, p, areaThreshold);
|
|
5195
5187
|
let comAN = cubicCommandToArc(comN.p0, comN.cp1, comN.cp2, comN.p, areaThreshold);
|
|
5196
5188
|
|
|
5197
|
-
if (comA.isArc
|
|
5189
|
+
if (comA.isArc && comAN.isArc) {
|
|
5198
5190
|
|
|
5199
5191
|
let dist = getDistManhattan(p0, comN.p);
|
|
5200
5192
|
let maxDist = dist * 0.01;
|
|
@@ -5209,8 +5201,18 @@
|
|
|
5209
5201
|
let sweep = area < 0 ? 0 : 1;
|
|
5210
5202
|
|
|
5211
5203
|
if (vertical || horizontal) {
|
|
5204
|
+
|
|
5212
5205
|
rx = Math.min(rx, comAN.rx);
|
|
5213
5206
|
ry = Math.min(ry, comAN.ry);
|
|
5207
|
+
|
|
5208
|
+
let diffR = Math.abs(rx - ry) / rx;
|
|
5209
|
+
let isSemiCircle = diffR < 0.025;
|
|
5210
|
+
|
|
5211
|
+
if (isSemiCircle) {
|
|
5212
|
+
rx = rx > 1 ? 1 : Math.min(rx, ry);
|
|
5213
|
+
ry = rx;
|
|
5214
|
+
}
|
|
5215
|
+
|
|
5214
5216
|
pathData[c] = null;
|
|
5215
5217
|
pathData[c + 1].type = 'A';
|
|
5216
5218
|
pathData[c + 1].values = [rx, ry, 0, 0, sweep, comN.p.x, comN.p.y];
|
|
@@ -5233,33 +5235,61 @@
|
|
|
5233
5235
|
let arcSegArea = 0, isArc = false;
|
|
5234
5236
|
|
|
5235
5237
|
// check angles
|
|
5236
|
-
let
|
|
5237
|
-
let
|
|
5238
|
+
let dx1 = (cp1.x - p0.x);
|
|
5239
|
+
let dy1 = (cp1.y - p0.y);
|
|
5240
|
+
let dx2 = (cp2.x - p.x);
|
|
5241
|
+
let dy2 = (cp2.y - p.y);
|
|
5242
|
+
|
|
5243
|
+
let thresh = getDistManhattan(p0, p) * 0.001;
|
|
5244
|
+
|
|
5245
|
+
let isVertical1 = dx1 < thresh;
|
|
5246
|
+
let isVertical2 = dx2 < thresh;
|
|
5247
|
+
|
|
5248
|
+
let isHorizontal1 = dy1 < thresh;
|
|
5249
|
+
let isHorizontal2 = dy2 < thresh;
|
|
5250
|
+
|
|
5251
|
+
let isRightAngle = (isVertical1 || isVertical2) && (isHorizontal1 || isHorizontal2);
|
|
5252
|
+
|
|
5253
|
+
/*
|
|
5254
|
+
let angle1 = getAngleFromDelta(dx1, dy1, true);
|
|
5255
|
+
let angle2 = getAngleFromDelta(dx2, dy2, true);
|
|
5238
5256
|
let deltaAngle = Math.abs(angle1 - angle2) * 180 / Math.PI;
|
|
5239
5257
|
|
|
5240
5258
|
let angleDiff = Math.abs((deltaAngle % 180) - 90);
|
|
5241
5259
|
let isRightAngle = angleDiff < 3;
|
|
5260
|
+
*/
|
|
5242
5261
|
|
|
5243
5262
|
let rx = 0;
|
|
5244
5263
|
let ry = 0;
|
|
5245
|
-
let ptC;
|
|
5264
|
+
let ptC = p0;
|
|
5265
|
+
let r1 = 0, r2 = 0;
|
|
5246
5266
|
|
|
5247
5267
|
if (isRightAngle) {
|
|
5248
|
-
// point between cps
|
|
5249
5268
|
|
|
5250
|
-
|
|
5251
|
-
|
|
5252
|
-
|
|
5269
|
+
if (isHorizontal1 && isVertical2) {
|
|
5270
|
+
ptC = { x: p0.x, y: p.y };
|
|
5271
|
+
r1 = Math.abs(p.x-p0.x);
|
|
5272
|
+
r2 = Math.abs(p.y-p0.y);
|
|
5273
|
+
}
|
|
5274
|
+
else if (isHorizontal2 && isVertical1) {
|
|
5275
|
+
ptC = { x: p.x, y: p0.y };
|
|
5276
|
+
r2 = Math.abs(p0.x-p.x);
|
|
5277
|
+
r1 = Math.abs(p0.y-p.y);
|
|
5278
|
+
}
|
|
5279
|
+
|
|
5280
|
+
/*
|
|
5281
|
+
if (r1 && r2) {
|
|
5253
5282
|
|
|
5254
|
-
|
|
5255
|
-
ptC = checkLineIntersection(p0, cp1_r, p, cp2_r, false);
|
|
5283
|
+
}
|
|
5256
5284
|
|
|
5257
|
-
|
|
5285
|
+
*/
|
|
5258
5286
|
|
|
5259
|
-
if (
|
|
5287
|
+
if (r1 && r2) {
|
|
5260
5288
|
|
|
5289
|
+
/*
|
|
5261
5290
|
let r1 = getDistance(p0, pI);
|
|
5262
5291
|
let r2 = getDistance(p, pI);
|
|
5292
|
+
*/
|
|
5263
5293
|
|
|
5264
5294
|
let rMax = +Math.max(r1, r2).toFixed(8);
|
|
5265
5295
|
let rMin = +Math.min(r1, r2).toFixed(8);
|
|
@@ -5277,7 +5307,6 @@
|
|
|
5277
5307
|
let circular = (100 / rx * Math.abs(rx - ry)) < 5;
|
|
5278
5308
|
|
|
5279
5309
|
if (circular) {
|
|
5280
|
-
|
|
5281
5310
|
rx = rMax;
|
|
5282
5311
|
ry = rx;
|
|
5283
5312
|
}
|
|
@@ -5303,7 +5332,7 @@
|
|
|
5303
5332
|
arcSegArea = (Math.PI * (rx * ry)) / 4;
|
|
5304
5333
|
|
|
5305
5334
|
// subtract polygon between start, end and center point
|
|
5306
|
-
arcSegArea -= Math.abs(getPolygonArea([p0, p,
|
|
5335
|
+
arcSegArea -= Math.abs(getPolygonArea([p0, p, ptC]));
|
|
5307
5336
|
|
|
5308
5337
|
let areaDiff = getRelativeAreaDiff(comArea, arcSegArea);
|
|
5309
5338
|
|
|
@@ -6373,59 +6402,50 @@
|
|
|
6373
6402
|
pathData[pathData.length - 1].type.toLowerCase() === 'z';
|
|
6374
6403
|
|
|
6375
6404
|
for (let c = 1, l = pathData.length; c < l; c++) {
|
|
6376
|
-
|
|
6377
6405
|
let com = pathData[c];
|
|
6406
|
+
let { type, values } = com;
|
|
6378
6407
|
let comN = pathData[c + 1] || pathData[l - 1];
|
|
6379
|
-
|
|
6408
|
+
let valuesN = comN.values;
|
|
6380
6409
|
let p1 = comN.type.toLowerCase() === 'z' ? M : { x: comN.values[comN.values.length - 2], y: comN.values[comN.values.length - 1] };
|
|
6381
6410
|
|
|
6382
|
-
let { type, values } = com;
|
|
6383
6411
|
let valsL = values.slice(-2);
|
|
6384
6412
|
p = type !== 'Z' ? { x: valsL[0], y: valsL[1] } : M;
|
|
6385
6413
|
|
|
6414
|
+
let nextBezier = type!==comN.type && (comN.type==='C' || comN.type==='Q');
|
|
6415
|
+
|
|
6386
6416
|
let area = p1 ? getPolygonArea([p0, p, p1], true) : Infinity;
|
|
6387
6417
|
let distSquare = getSquareDistance(p0, p1);
|
|
6388
6418
|
let distMax = distSquare ? distSquare / 333 * tolerance : 0;
|
|
6389
6419
|
|
|
6390
6420
|
let isFlat = area < distMax;
|
|
6391
|
-
|
|
6392
6421
|
let isFlatBez = false;
|
|
6422
|
+
let cpts = [];
|
|
6393
6423
|
|
|
6394
|
-
|
|
6395
|
-
|
|
6396
|
-
|
|
6397
|
-
|
|
6398
|
-
|
|
6399
|
-
|
|
6400
|
-
|
|
6401
|
-
|
|
6402
|
-
let dx2 = Math.abs(p1.x - p.x)
|
|
6403
|
-
let dy2 = Math.abs(p1.y - p.y)
|
|
6404
|
-
|
|
6405
|
-
// zero length segments are flat
|
|
6406
|
-
let isZeroLength = (!dy1 && !dx1) || (!dy2 && !dx2)
|
|
6407
|
-
if (isZeroLength) isFlat = true;
|
|
6408
|
-
|
|
6409
|
-
// check cross products for colinearity
|
|
6410
|
-
if (!isFlat) {
|
|
6411
|
-
|
|
6412
|
-
let cross0 = Math.abs(dx0 * dy1 - dy0 * dx1);
|
|
6424
|
+
/**
|
|
6425
|
+
* type change
|
|
6426
|
+
* check flatness
|
|
6427
|
+
*/
|
|
6428
|
+
if (nextBezier) {
|
|
6429
|
+
cpts = comN.type === 'C' ?
|
|
6430
|
+
[{ x: valuesN[0], y: valuesN[1] }, { x: valuesN[2], y: valuesN[3] }] :
|
|
6431
|
+
(comN.type === 'Q' ? [{ x: valuesN[0], y: valuesN[1] }] : []);
|
|
6413
6432
|
|
|
6414
|
-
|
|
6433
|
+
isFlat = commandIsFlat([p0, ...cpts, p], { tolerance });
|
|
6415
6434
|
|
|
6416
|
-
|
|
6435
|
+
/*
|
|
6417
6436
|
|
|
6418
|
-
|
|
6437
|
+
if(!isFlatBez){
|
|
6438
|
+
pathDataN.push(com)
|
|
6439
|
+
continue
|
|
6419
6440
|
}
|
|
6420
|
-
|
|
6421
|
-
*/
|
|
6441
|
+
*/
|
|
6422
6442
|
|
|
6423
|
-
|
|
6443
|
+
}
|
|
6424
6444
|
|
|
6425
6445
|
// convert flat beziers to linetos
|
|
6426
6446
|
if (flatBezierToLinetos && (type === 'C' || type === 'Q')) {
|
|
6427
6447
|
|
|
6428
|
-
|
|
6448
|
+
cpts = type === 'C' ?
|
|
6429
6449
|
[{ x: values[0], y: values[1] }, { x: values[2], y: values[3] }] :
|
|
6430
6450
|
(type === 'Q' ? [{ x: values[0], y: values[1] }] : []);
|
|
6431
6451
|
|
|
@@ -6439,10 +6459,13 @@
|
|
|
6439
6459
|
}
|
|
6440
6460
|
}
|
|
6441
6461
|
|
|
6442
|
-
|
|
6462
|
+
/**
|
|
6463
|
+
* colinear = simplification success
|
|
6464
|
+
* exclude arcs (as always =)
|
|
6465
|
+
* as semicircles won't have an area
|
|
6466
|
+
*/
|
|
6443
6467
|
|
|
6444
6468
|
if (isFlat && c < l - 1 && comN.type !== 'A' && (type === 'L' || (flatBezierToLinetos && isFlatBez))) {
|
|
6445
|
-
|
|
6446
6469
|
continue;
|
|
6447
6470
|
}
|
|
6448
6471
|
|
|
@@ -6558,7 +6581,8 @@
|
|
|
6558
6581
|
let { type, values } = com;
|
|
6559
6582
|
let valsLen = values.length;
|
|
6560
6583
|
if (valsLen) {
|
|
6561
|
-
|
|
6584
|
+
// we need rounding otherwise sorting may crash due to e notation
|
|
6585
|
+
let p = { type: type, x: +values[valsLen - 2].toFixed(8), y: +values[valsLen - 1].toFixed(8), index: 0 };
|
|
6562
6586
|
p.index = i;
|
|
6563
6587
|
indices.push(p);
|
|
6564
6588
|
}
|
|
@@ -6566,7 +6590,7 @@
|
|
|
6566
6590
|
|
|
6567
6591
|
// reorder to top left most
|
|
6568
6592
|
|
|
6569
|
-
indices = indices.sort((a, b) =>
|
|
6593
|
+
indices = indices.sort((a, b) => a.y - b.y || a.x - b.x);
|
|
6570
6594
|
newIndex = indices[0].index;
|
|
6571
6595
|
|
|
6572
6596
|
return newIndex ? shiftSvgStartingPoint(pathData, newIndex) : pathData;
|
|
@@ -6925,9 +6949,7 @@
|
|
|
6925
6949
|
if (comEx.length === 1) {
|
|
6926
6950
|
|
|
6927
6951
|
comEx = comEx[0];
|
|
6928
|
-
|
|
6929
6952
|
pathData[i + 1] = null;
|
|
6930
|
-
|
|
6931
6953
|
pathData[i + 2].values = [comEx.cp1.x, comEx.cp1.y, comEx.cp2.x, comEx.cp2.y, comEx.p.x, comEx.p.y];
|
|
6932
6954
|
pathData[i + 2].cp1 = comEx.cp1;
|
|
6933
6955
|
pathData[i + 2].cp2 = comEx.cp2;
|
|
@@ -7102,18 +7124,28 @@
|
|
|
7102
7124
|
} = {}) {
|
|
7103
7125
|
|
|
7104
7126
|
// is stringified flat point attribute
|
|
7105
|
-
if(typeof pts === 'string' && !isNaN(pts[0])){
|
|
7127
|
+
if (typeof pts === 'string' && !isNaN(pts[0])) {
|
|
7106
7128
|
pts = toPointArray(pts.split(/,| /).filter(Boolean).map(Number));
|
|
7107
7129
|
return pts
|
|
7108
7130
|
}
|
|
7109
7131
|
|
|
7132
|
+
if (pts.length && typeof pts[0] === 'string') {
|
|
7133
|
+
pts = pts.map(pt => {
|
|
7134
|
+
return toPointArray(pt.split(/,| /).filter(Boolean).map(Number))
|
|
7135
|
+
});
|
|
7136
|
+
pts = pts.flat(2);
|
|
7137
|
+
|
|
7138
|
+
}
|
|
7139
|
+
|
|
7110
7140
|
if (flatten) pts = pts.flat(2);
|
|
7141
|
+
|
|
7111
7142
|
let poly = toArray ? polyPtsToArray(pts) : polyArrayToObject(pts);
|
|
7112
7143
|
return poly
|
|
7113
7144
|
}
|
|
7114
7145
|
|
|
7115
|
-
function polyArrayToObject(pts) {
|
|
7146
|
+
function polyArrayToObject(pts = []) {
|
|
7116
7147
|
|
|
7148
|
+
if (!pts.length) return [];
|
|
7117
7149
|
// is point object array
|
|
7118
7150
|
if (pts[0].x !== undefined && pts[0].y !== undefined) return pts
|
|
7119
7151
|
|
|
@@ -7131,7 +7163,7 @@
|
|
|
7131
7163
|
return poly
|
|
7132
7164
|
}
|
|
7133
7165
|
|
|
7134
|
-
else if(pts.length>3){
|
|
7166
|
+
else if (pts.length > 3) {
|
|
7135
7167
|
pts = toPointArray(pts);
|
|
7136
7168
|
return pts
|
|
7137
7169
|
}
|
|
@@ -7160,13 +7192,13 @@
|
|
|
7160
7192
|
function toPointArray(pts) {
|
|
7161
7193
|
let ptArr = [];
|
|
7162
7194
|
|
|
7163
|
-
if(pts[0].length===2){
|
|
7164
|
-
for (let i = 0, l = pts.length; i < l; i
|
|
7195
|
+
if (pts[0].length === 2) {
|
|
7196
|
+
for (let i = 0, l = pts.length; i < l; i++) {
|
|
7165
7197
|
let pt = pts[i];
|
|
7166
|
-
ptArr.push({ x: pt[0], y:pt[1] });
|
|
7198
|
+
ptArr.push({ x: pt[0], y: pt[1] });
|
|
7167
7199
|
}
|
|
7168
7200
|
|
|
7169
|
-
}else {
|
|
7201
|
+
} else {
|
|
7170
7202
|
for (let i = 1, l = pts.length; i < l; i += 2) {
|
|
7171
7203
|
ptArr.push({ x: pts[i - 1], y: pts[i] });
|
|
7172
7204
|
}
|
|
@@ -8008,7 +8040,6 @@
|
|
|
8008
8040
|
|
|
8009
8041
|
// LG weight/abscissae generator
|
|
8010
8042
|
function getLegendreGaussValues(n, x1 = -1, x2 = 1) {
|
|
8011
|
-
console.log('add new LG', n);
|
|
8012
8043
|
|
|
8013
8044
|
let waArr = [];
|
|
8014
8045
|
let z1, z, xm, xl, pp, p3, p2, p1;
|
|
@@ -8467,7 +8498,7 @@
|
|
|
8467
8498
|
styleProps.remove.push('transform');
|
|
8468
8499
|
|
|
8469
8500
|
// scale props like stroke width or dash-array
|
|
8470
|
-
styleProps = scaleProps(styleProps, { props: ['stroke-width', 'stroke-dasharray'], scale: scaleX });
|
|
8501
|
+
styleProps = scaleProps(styleProps, { props: ['stroke-width', 'stroke-dasharray', 'stroke-dashoffset'], scale: scaleX });
|
|
8471
8502
|
|
|
8472
8503
|
} else {
|
|
8473
8504
|
el.setAttribute('transform', transComponents.matrixAtt);
|
|
@@ -8485,7 +8516,7 @@
|
|
|
8485
8516
|
let prop = props[i];
|
|
8486
8517
|
|
|
8487
8518
|
if (styleProps[prop] !== undefined) {
|
|
8488
|
-
styleProps[prop] = styleProps[prop].map(val => round ? roundTo(val * scale,
|
|
8519
|
+
styleProps[prop] = styleProps[prop].map(val => round ? roundTo(val * scale, 3) : val * scale);
|
|
8489
8520
|
}
|
|
8490
8521
|
}
|
|
8491
8522
|
return styleProps
|
|
@@ -8506,6 +8537,7 @@
|
|
|
8506
8537
|
});
|
|
8507
8538
|
|
|
8508
8539
|
let scale = elLength / pathLength;
|
|
8540
|
+
|
|
8509
8541
|
styleProps = scaleProps(styleProps, { props: ['stroke-dasharray', 'stroke-dashoffset'], scale });
|
|
8510
8542
|
|
|
8511
8543
|
// set absolute
|
|
@@ -9013,6 +9045,38 @@
|
|
|
9013
9045
|
});
|
|
9014
9046
|
});
|
|
9015
9047
|
|
|
9048
|
+
/**
|
|
9049
|
+
* remove els and attributes
|
|
9050
|
+
*/
|
|
9051
|
+
|
|
9052
|
+
// remove meta
|
|
9053
|
+
if (!allowMeta) removeElements.push('meta', 'metadata', 'desc', 'title');
|
|
9054
|
+
|
|
9055
|
+
if (removeClassNames) {
|
|
9056
|
+
removeSVGAttributes.push('class');
|
|
9057
|
+
removeElAttributes.push('class');
|
|
9058
|
+
}
|
|
9059
|
+
|
|
9060
|
+
if (removeIds) {
|
|
9061
|
+
removeSVGAttributes.push('id');
|
|
9062
|
+
removeElAttributes.push('id');
|
|
9063
|
+
}
|
|
9064
|
+
|
|
9065
|
+
// remove hidden elements
|
|
9066
|
+
removeHiddenSvgEls(svg);
|
|
9067
|
+
|
|
9068
|
+
// remove SVG elements
|
|
9069
|
+
removeSvgEls(svg, { removeElements, removeNameSpaced });
|
|
9070
|
+
|
|
9071
|
+
// remove SVG attributes
|
|
9072
|
+
removeSvgAtts(svg, removeSVGAttributes);
|
|
9073
|
+
|
|
9074
|
+
// remove SVG child element attributes
|
|
9075
|
+
removeSvgChildAtts(svg, removeElAttributes);
|
|
9076
|
+
|
|
9077
|
+
// general cleanup
|
|
9078
|
+
if (cleanupSVGAtts) cleanupSVGAttributes(svg, { removeIds, removeClassNames, removeDimensions, stylesToAttributes, allowMeta, allowAriaAtts, allowDataAtts });
|
|
9079
|
+
|
|
9016
9080
|
// collect all elements' properties
|
|
9017
9081
|
let svgElProps = [];
|
|
9018
9082
|
let els = svg.querySelectorAll(`${renderedEls.join(', ')}`);
|
|
@@ -9033,7 +9097,12 @@
|
|
|
9033
9097
|
let styleProps = parseStylesProperties(el, propOptions);
|
|
9034
9098
|
let stylePropsFiltered = {};
|
|
9035
9099
|
|
|
9100
|
+
// reset remove array
|
|
9101
|
+
remove = [];
|
|
9102
|
+
|
|
9036
9103
|
// convert pathLength before transforming
|
|
9104
|
+
if (convertTransforms || attributesToGroup) convertPathLength = true;
|
|
9105
|
+
|
|
9037
9106
|
if (convertPathLength) {
|
|
9038
9107
|
styleProps = convertPathLengthAtt(el, { styleProps });
|
|
9039
9108
|
remove = [...new Set([...remove, ...styleProps.remove])];
|
|
@@ -9086,41 +9155,11 @@
|
|
|
9086
9155
|
|
|
9087
9156
|
remove = [...new Set([...remove, ...styleProps.remove])];
|
|
9088
9157
|
|
|
9089
|
-
|
|
9090
|
-
* remove els and attributes
|
|
9091
|
-
*/
|
|
9092
|
-
|
|
9093
|
-
// remove meta
|
|
9094
|
-
if (!allowMeta) removeElements.push('meta', 'metadata', 'desc', 'title');
|
|
9158
|
+
|
|
9095
9159
|
|
|
9096
|
-
|
|
9097
|
-
|
|
9098
|
-
|
|
9099
|
-
}
|
|
9100
|
-
|
|
9101
|
-
if (removeIds) {
|
|
9102
|
-
removeSVGAttributes.push('id');
|
|
9103
|
-
removeElAttributes.push('id');
|
|
9104
|
-
}
|
|
9105
|
-
|
|
9106
|
-
// remove hidden elements
|
|
9107
|
-
removeHiddenSvgEls(svg);
|
|
9108
|
-
|
|
9109
|
-
// remove SVG elements
|
|
9110
|
-
removeSvgEls(svg, { removeElements, removeNameSpaced });
|
|
9111
|
-
|
|
9112
|
-
// remove SVG attributes
|
|
9113
|
-
removeSvgAtts(svg, removeSVGAttributes);
|
|
9114
|
-
|
|
9115
|
-
// remove SVG child element attributes
|
|
9116
|
-
removeSvgChildAtts(svg, removeElAttributes);
|
|
9117
|
-
|
|
9118
|
-
// general cleanup
|
|
9119
|
-
if (cleanupSVGAtts) cleanupSVGAttributes(svg, { removeIds, removeClassNames, removeDimensions, stylesToAttributes, allowMeta, allowAriaAtts, allowDataAtts });
|
|
9120
|
-
|
|
9121
|
-
// all relative units to absolute
|
|
9122
|
-
if (toAbsoluteUnits) {
|
|
9123
|
-
normalizeTransforms = true;
|
|
9160
|
+
// all relative units to absolute
|
|
9161
|
+
if (toAbsoluteUnits) {
|
|
9162
|
+
normalizeTransforms = true;
|
|
9124
9163
|
|
|
9125
9164
|
/**
|
|
9126
9165
|
* apply consolidated
|
|
@@ -9161,7 +9200,6 @@
|
|
|
9161
9200
|
styleProps = setNormalizedTransformsToEl(el, { styleProps });
|
|
9162
9201
|
|
|
9163
9202
|
remove = [...new Set([...remove, ...styleProps.remove])];
|
|
9164
|
-
|
|
9165
9203
|
}
|
|
9166
9204
|
|
|
9167
9205
|
/**
|
|
@@ -9186,13 +9224,6 @@
|
|
|
9186
9224
|
*/
|
|
9187
9225
|
removeAtts(el, remove);
|
|
9188
9226
|
|
|
9189
|
-
/*
|
|
9190
|
-
for (let i = 0; i < remove.length; i++) {
|
|
9191
|
-
let att = remove[i];
|
|
9192
|
-
el.removeAttribute(att)
|
|
9193
|
-
}
|
|
9194
|
-
*/
|
|
9195
|
-
|
|
9196
9227
|
} // endof style processing
|
|
9197
9228
|
|
|
9198
9229
|
/**
|
|
@@ -9214,7 +9245,7 @@
|
|
|
9214
9245
|
|
|
9215
9246
|
// scale props like stroke width or dash-array before conversion
|
|
9216
9247
|
if (matrix && transComponents) {
|
|
9217
|
-
['stroke-width', 'stroke-dasharray'].forEach(att => {
|
|
9248
|
+
['stroke-width', 'stroke-dasharray', 'stroke-dashoffset'].forEach(att => {
|
|
9218
9249
|
let attVal = el.getAttribute(att);
|
|
9219
9250
|
let vals = attVal ? attVal.split(' ').filter(Boolean).map(Number).map(val => val * transComponents.scaleX) : [];
|
|
9220
9251
|
if (vals.length) el.setAttribute(att, vals.join(' '));
|
|
@@ -10483,71 +10514,6 @@
|
|
|
10483
10514
|
return pathData
|
|
10484
10515
|
}
|
|
10485
10516
|
|
|
10486
|
-
function fixIntersectingCpts(pathData = []) {
|
|
10487
|
-
|
|
10488
|
-
let l = pathData.length;
|
|
10489
|
-
let p0 = { x: pathData[0].values[0], y: pathData[0].values[1] };
|
|
10490
|
-
let p = p0;
|
|
10491
|
-
|
|
10492
|
-
for (let i = 1; i < l; i++) {
|
|
10493
|
-
let com = pathData[i];
|
|
10494
|
-
let comPrev = pathData[i - 1] || null;
|
|
10495
|
-
let { type, values } = com;
|
|
10496
|
-
pathData[i + 1] ? pathData[i + 1] : null;
|
|
10497
|
-
|
|
10498
|
-
if (type === 'C') {
|
|
10499
|
-
let tx = 1.2;
|
|
10500
|
-
let cp1 = { x: values[0], y: values[1] };
|
|
10501
|
-
p = { x: values[4], y: values[5] };
|
|
10502
|
-
let cp2 = { x: values[2], y: values[3] };
|
|
10503
|
-
|
|
10504
|
-
interpolate(p0, cp1, tx );
|
|
10505
|
-
let cp2_ex = interpolate( p, cp2, tx);
|
|
10506
|
-
|
|
10507
|
-
comPrev.values.slice(-2);
|
|
10508
|
-
|
|
10509
|
-
// extend tangents
|
|
10510
|
-
|
|
10511
|
-
let ptI = checkLineIntersection(p0, cp1, p, cp2_ex, true, true);
|
|
10512
|
-
|
|
10513
|
-
|
|
10514
|
-
/*
|
|
10515
|
-
renderPoly(markers, [p0, cp1], 'orange', '0.75')
|
|
10516
|
-
renderPoly(markers, [p, cp2], 'blue', '0.75')
|
|
10517
|
-
renderPoly(markers, [p, cp2_ex], 'magenta', '0.75')
|
|
10518
|
-
|
|
10519
|
-
renderPoint(markers, p0, 'orange', '0.75')
|
|
10520
|
-
renderPoint(markers, cp1, 'magenta', '0.75')
|
|
10521
|
-
renderPoint(markers, cp1_ex, 'blue', '0.5')
|
|
10522
|
-
|
|
10523
|
-
renderPoint(markers, cp2, 'cyan', '0.75')
|
|
10524
|
-
*/
|
|
10525
|
-
|
|
10526
|
-
|
|
10527
|
-
|
|
10528
|
-
/*
|
|
10529
|
-
renderPoint(markers, p0, 'orange')
|
|
10530
|
-
renderPoint(markers, p, 'magenta', '0.5')
|
|
10531
|
-
*/
|
|
10532
|
-
|
|
10533
|
-
if (ptI) {
|
|
10534
|
-
let t = 0.666;
|
|
10535
|
-
cp1 = interpolate(p0, ptI, t);
|
|
10536
|
-
cp2 = interpolate(p, ptI, t);
|
|
10537
|
-
com.values = [cp1.x, cp1.y, cp2.x, cp2.y, p.x, p.y];
|
|
10538
|
-
|
|
10539
|
-
}
|
|
10540
|
-
|
|
10541
|
-
}
|
|
10542
|
-
|
|
10543
|
-
if (values.length) {
|
|
10544
|
-
p0 = p;
|
|
10545
|
-
}
|
|
10546
|
-
}
|
|
10547
|
-
|
|
10548
|
-
return pathData;
|
|
10549
|
-
}
|
|
10550
|
-
|
|
10551
10517
|
function simplifyPolyRDP(pts, {quality = 0.9, width = 0, height = 0}={}) {
|
|
10552
10518
|
|
|
10553
10519
|
/**
|
|
@@ -10742,860 +10708,817 @@
|
|
|
10742
10708
|
|
|
10743
10709
|
}
|
|
10744
10710
|
|
|
10745
|
-
function
|
|
10711
|
+
function refineAdjacentPolyExtremes(pts = []) {
|
|
10746
10712
|
|
|
10747
10713
|
let l = pts.length;
|
|
10748
|
-
let x = 0, y = 0;
|
|
10749
|
-
for (let i = 0; l && i < l; i++) {
|
|
10750
|
-
let pt = pts[i];
|
|
10751
|
-
x += pt.x;
|
|
10752
|
-
y += pt.y;
|
|
10753
|
-
}
|
|
10754
|
-
|
|
10755
|
-
let centroid = {x: x/l, y:y/l};
|
|
10756
|
-
return centroid
|
|
10757
|
-
|
|
10758
|
-
}
|
|
10759
|
-
|
|
10760
|
-
function detectRegularPolygon(pts, centroid={x:0, y:0}) {
|
|
10761
|
-
let rSq = getSquareDistance(pts[0], centroid);
|
|
10762
|
-
let isRegular = true;
|
|
10763
|
-
|
|
10764
|
-
for (let i = 1, l = pts.length; i < l; i++) {
|
|
10765
|
-
let pt1 = pts[i];
|
|
10766
|
-
let dist = getSquareDistance(pt1, centroid);
|
|
10767
10714
|
|
|
10768
|
-
|
|
10769
|
-
|
|
10770
|
-
|
|
10771
|
-
if (diffRel > 0.05) {
|
|
10772
|
-
return false;
|
|
10773
|
-
}
|
|
10715
|
+
let { x, y, width, height, top, bottom, left, right } = getPolyBBox(pts);
|
|
10716
|
+
let threshShort = (width + height) * 0.05;
|
|
10717
|
+
let thresh = (width + height) * 0.001;
|
|
10774
10718
|
|
|
10775
|
-
|
|
10776
|
-
|
|
10777
|
-
}
|
|
10719
|
+
let pt0 = pts[0];
|
|
10720
|
+
let ptLast = pts[l - 1];
|
|
10778
10721
|
|
|
10779
|
-
|
|
10780
|
-
|
|
10781
|
-
|
|
10782
|
-
|
|
10783
|
-
|
|
10784
|
-
|
|
10785
|
-
} = {}) {
|
|
10722
|
+
/**
|
|
10723
|
+
* cleanup close path - almost vertical or horizontal
|
|
10724
|
+
* average start and end extremes
|
|
10725
|
+
*/
|
|
10726
|
+
let dx = Math.abs(ptLast.x - pt0.x);
|
|
10727
|
+
let dy = Math.abs(ptLast.y - pt0.y);
|
|
10786
10728
|
|
|
10787
|
-
|
|
10729
|
+
if (dy < threshShort || dx < threshShort) {
|
|
10788
10730
|
|
|
10789
|
-
|
|
10790
|
-
let bb0 = getPolyBBox(pts);
|
|
10731
|
+
if (pt0.isExtreme && !pt0.isCorner) {
|
|
10791
10732
|
|
|
10792
|
-
|
|
10793
|
-
|
|
10794
|
-
}
|
|
10733
|
+
let xAv = (pt0.x + ptLast.x) * 0.5;
|
|
10734
|
+
let yAv = (pt0.y + ptLast.y) * 0.5;
|
|
10795
10735
|
|
|
10796
|
-
|
|
10736
|
+
pt0.x = xAv;
|
|
10737
|
+
pt0.y = yAv;
|
|
10797
10738
|
|
|
10798
|
-
|
|
10799
|
-
|
|
10800
|
-
|
|
10801
|
-
let p1 = pts[i];
|
|
10802
|
-
let p2 = i < l - 1 ? pts[i + 1] : pts[l - 1];
|
|
10739
|
+
ptLast.x = xAv;
|
|
10740
|
+
ptLast.y = yAv;
|
|
10741
|
+
ptLast.isExtreme = true;
|
|
10803
10742
|
|
|
10804
|
-
|
|
10805
|
-
|
|
10806
|
-
|
|
10807
|
-
p1.idx = i;
|
|
10743
|
+
if (dy < thresh) {
|
|
10744
|
+
ptLast.tangentR.y = pt0.y;
|
|
10745
|
+
ptLast.tangentL.y = pt0.y;
|
|
10808
10746
|
|
|
10747
|
+
}
|
|
10748
|
+
if (dx < thresh) {
|
|
10749
|
+
ptLast.tangentR.x = pt0.x;
|
|
10750
|
+
ptLast.tangentL.x = pt0.x;
|
|
10751
|
+
}
|
|
10752
|
+
}
|
|
10809
10753
|
}
|
|
10810
10754
|
|
|
10811
|
-
for (let i =
|
|
10812
|
-
i >
|
|
10813
|
-
let p0 = i > 0 ? pts[i - 1] : pts[l - 1];
|
|
10755
|
+
for (let i = 1; i < l; i++) {
|
|
10756
|
+
i > 0 ? pts[i - 1] : pts[l - 1];
|
|
10814
10757
|
let p1 = pts[i];
|
|
10815
10758
|
let p2 = i < l - 1 ? pts[i + 1] : pts[l - 1];
|
|
10759
|
+
let dist = getDistManhattan(p1, p2);
|
|
10816
10760
|
|
|
10817
|
-
|
|
10818
|
-
|
|
10819
|
-
Math.abs(p0.area);
|
|
10820
|
-
let area1 = Math.abs(p1.area);
|
|
10821
|
-
Math.abs(p2.area);
|
|
10822
|
-
let isCorner = false;
|
|
10823
|
-
|
|
10824
|
-
let flat = !p1.area || area1 < thresh;
|
|
10825
|
-
|
|
10826
|
-
getDistManhattan(p1, p0);
|
|
10827
|
-
|
|
10828
|
-
/**
|
|
10829
|
-
* check extremes
|
|
10830
|
-
*/
|
|
10831
|
-
|
|
10832
|
-
let isExtreme = false;
|
|
10761
|
+
let { isHorizontal, isVertical, isCorner, isLong, isExtreme, isSemiExtreme, isDirChange } = p1;
|
|
10833
10762
|
|
|
10834
|
-
|
|
10835
|
-
if ((p1.x === bb0.left || p1.x === bb0.right || p1.y === bb0.top || p1.y === bb0.bottom)) {
|
|
10836
|
-
isExtreme = true;
|
|
10837
|
-
}
|
|
10763
|
+
let extremes = [];
|
|
10838
10764
|
|
|
10839
|
-
|
|
10840
|
-
|
|
10841
|
-
|
|
10765
|
+
/*
|
|
10766
|
+
if(isExtreme && p0.isCorner && !isLong && !isCorner){
|
|
10767
|
+
isExtreme= false
|
|
10768
|
+
p1.isExtreme = false
|
|
10769
|
+
p1.isHorizontal = false
|
|
10770
|
+
p1.isVertical = false
|
|
10842
10771
|
|
|
10843
|
-
|
|
10844
|
-
p0.isExtreme = true;
|
|
10845
|
-
isExtreme = true;
|
|
10772
|
+
continue;
|
|
10846
10773
|
}
|
|
10774
|
+
*/
|
|
10847
10775
|
|
|
10848
|
-
|
|
10849
|
-
|
|
10850
|
-
|
|
10851
|
-
|
|
10852
|
-
|
|
10853
|
-
|
|
10854
|
-
isExtreme = true;
|
|
10776
|
+
/*
|
|
10777
|
+
if(isExtreme && p2.isCorner && !isLong && !isCorner && dist<threshShort*0.5){
|
|
10778
|
+
isExtreme= false
|
|
10779
|
+
p1.isExtreme = false
|
|
10780
|
+
p1.isHorizontal = false
|
|
10781
|
+
p1.isVertical = false
|
|
10855
10782
|
|
|
10783
|
+
if(isVertical){
|
|
10784
|
+
p2.tangentL.x = p2.x
|
|
10785
|
+
}
|
|
10786
|
+
if(isHorizontal){
|
|
10787
|
+
p2.tangentL.y = p2.y
|
|
10788
|
+
}
|
|
10789
|
+
continue;
|
|
10856
10790
|
}
|
|
10791
|
+
*/
|
|
10857
10792
|
|
|
10858
|
-
|
|
10859
|
-
|
|
10860
|
-
*/
|
|
10861
|
-
let signChange = (p0.area < 0 && p1.area > 0) || (p0.area > 0 && p1.area < 0);
|
|
10862
|
-
let isDirChange = signChange && !flat && !p0.isDirChange;
|
|
10863
|
-
|
|
10864
|
-
/**
|
|
10865
|
-
* 3. corners
|
|
10866
|
-
*/
|
|
10867
|
-
|
|
10868
|
-
if (isExtreme) {
|
|
10869
|
-
|
|
10870
|
-
let delta = getDeltaAngle(p1, p2, p0);
|
|
10871
|
-
let { deltaAngleDeg } = delta;
|
|
10872
|
-
deltaAngleDeg = Math.abs(deltaAngleDeg);
|
|
10793
|
+
if (isExtreme && !isCorner && p2.isExtreme) {
|
|
10794
|
+
extremes.push(p1);
|
|
10873
10795
|
|
|
10874
|
-
let
|
|
10875
|
-
|
|
10796
|
+
for (let j = i + 1; j < l; j++) {
|
|
10797
|
+
let p2 = pts[j];
|
|
10798
|
+
dist = getDistManhattan(p1, p2);
|
|
10876
10799
|
|
|
10877
|
-
isCorner
|
|
10800
|
+
if (dist * 0.75 >= threshShort || p2.isCorner || p2.isDirChange) {
|
|
10801
|
+
break
|
|
10802
|
+
}
|
|
10803
|
+
if (p2.isExtreme && !p2.isDirChange && !p2.isCorner) {
|
|
10804
|
+
extremes.push(p2);
|
|
10805
|
+
}
|
|
10878
10806
|
}
|
|
10879
10807
|
|
|
10880
|
-
|
|
10808
|
+
if (extremes.length > 1) {
|
|
10881
10809
|
|
|
10882
|
-
|
|
10883
|
-
|
|
10884
|
-
if (debug) {
|
|
10810
|
+
// find best extreme according to angle
|
|
10811
|
+
let angleDiffMin = Infinity;
|
|
10885
10812
|
|
|
10886
|
-
|
|
10887
|
-
renderPoint(markers, p1, 'blue', '2%', '0.5')
|
|
10888
|
-
renderPoint(markers, p0, 'blue', '2%', '0.5')
|
|
10889
|
-
}
|
|
10813
|
+
let bestMatch = extremes[0];
|
|
10890
10814
|
|
|
10891
|
-
|
|
10892
|
-
|
|
10893
|
-
}
|
|
10815
|
+
|
|
10816
|
+
extremes.forEach(pt => {
|
|
10894
10817
|
|
|
10895
|
-
|
|
10896
|
-
|
|
10897
|
-
|
|
10818
|
+
let angle = Math.abs(getAngleFromDelta(pt.dx2, pt.dy2, false)) * rad2Deg;
|
|
10819
|
+
let angleDiff = angle > 160 ? Math.abs(180 - angle) : (angle > 60 ? Math.abs(90 - angle) : angle);
|
|
10820
|
+
pt.angle = angle;
|
|
10821
|
+
pt.angleDiff = angleDiff;
|
|
10898
10822
|
|
|
10899
|
-
|
|
10900
|
-
|
|
10901
|
-
|
|
10902
|
-
}
|
|
10903
|
-
*/
|
|
10823
|
+
if (angleDiff < angleDiffMin) {
|
|
10824
|
+
bestMatch = pt;
|
|
10825
|
+
angleDiffMin = angleDiff;
|
|
10904
10826
|
|
|
10905
|
-
|
|
10906
|
-
|
|
10907
|
-
p1.isHorizontal = isHorizontal;
|
|
10908
|
-
p1.isVertical = isVertical;
|
|
10909
|
-
p1.isDirChange = isDirChange;
|
|
10827
|
+
}
|
|
10828
|
+
});
|
|
10910
10829
|
|
|
10911
|
-
|
|
10830
|
+
let extremes2 = [];
|
|
10912
10831
|
|
|
10913
|
-
|
|
10914
|
-
let pts1 = [];
|
|
10915
|
-
let exclude = [];
|
|
10832
|
+
extremes.forEach((pt, i) => {
|
|
10916
10833
|
|
|
10917
|
-
|
|
10918
|
-
for (let i = 0; i < pts.length; i++) {
|
|
10919
|
-
let p = pts[i];
|
|
10920
|
-
let p1 = pts[i + 1] || null;
|
|
10921
|
-
let p2 = pts[i + 2] || null;
|
|
10834
|
+
let isBestMatch = pt === bestMatch;
|
|
10922
10835
|
|
|
10923
|
-
|
|
10836
|
+
if (isBestMatch) {
|
|
10924
10837
|
|
|
10925
|
-
|
|
10926
|
-
|
|
10927
|
-
|
|
10838
|
+
if (pt.isHorizontal) {
|
|
10839
|
+
pt.tangentL.y = pt.y;
|
|
10840
|
+
pt.tangentR.y = pt.y;
|
|
10841
|
+
}
|
|
10842
|
+
if (pt.isVertical) {
|
|
10843
|
+
pt.tangentL.x = pt.x;
|
|
10844
|
+
pt.tangentR.x = pt.x;
|
|
10845
|
+
}
|
|
10928
10846
|
|
|
10929
|
-
|
|
10930
|
-
extremes.push(p, p1);
|
|
10847
|
+
// renderPoint(markers, pt, 'green', '3%', '0.5')
|
|
10931
10848
|
|
|
10932
|
-
|
|
10933
|
-
|
|
10934
|
-
/*
|
|
10935
|
-
renderPoint(markers, p, 'green', '1%', '0.5')
|
|
10936
|
-
renderPoint(markers, p1, 'red', '1%', '0.5')
|
|
10937
|
-
renderPoint(markers, p2, 'blue', '1%', '0.5')
|
|
10938
|
-
*/
|
|
10939
|
-
}
|
|
10849
|
+
}
|
|
10850
|
+
else {
|
|
10940
10851
|
|
|
10941
|
-
|
|
10942
|
-
// average extreme
|
|
10852
|
+
if (bestMatch) {
|
|
10943
10853
|
|
|
10944
|
-
|
|
10945
|
-
|
|
10854
|
+
if (!isBestMatch && (pt.x === bestMatch.x || pt.y === bestMatch.y)) {
|
|
10855
|
+
extremes2.push(pt);
|
|
10856
|
+
}
|
|
10857
|
+
pt.isExtreme = false;
|
|
10858
|
+
pt.isHorizontal = false;
|
|
10859
|
+
pt.isVertical = false;
|
|
10860
|
+
}
|
|
10946
10861
|
|
|
10947
|
-
|
|
10948
|
-
p.y = y;
|
|
10862
|
+
}
|
|
10949
10863
|
|
|
10950
|
-
|
|
10951
|
-
}
|
|
10952
|
-
}
|
|
10864
|
+
});
|
|
10953
10865
|
|
|
10954
|
-
|
|
10955
|
-
|
|
10956
|
-
|
|
10866
|
+
// average coordinates
|
|
10867
|
+
if (extremes2.length) {
|
|
10868
|
+
bestMatch.x = (extremes2[0].x + bestMatch.x) * 0.5;
|
|
10869
|
+
bestMatch.y = (extremes2[0].y + bestMatch.y) * 0.5;
|
|
10957
10870
|
|
|
10958
|
-
|
|
10871
|
+
}
|
|
10959
10872
|
|
|
10960
|
-
|
|
10961
|
-
|
|
10962
|
-
let p0 = pts1[0];
|
|
10963
|
-
let pL = pts1[l2 - 1];
|
|
10964
|
-
let near0 = getDistManhattan(p0, pL) < thresh * 2;
|
|
10965
|
-
if (p0.isExtreme && pL.isExtreme && near0) {
|
|
10966
|
-
pL.x = p0.x;
|
|
10967
|
-
pL.y = p0.y;
|
|
10873
|
+
i += extremes.length;
|
|
10874
|
+
continue;
|
|
10968
10875
|
}
|
|
10969
10876
|
}
|
|
10970
10877
|
|
|
10971
|
-
pts = pts1;
|
|
10972
10878
|
}
|
|
10973
10879
|
|
|
10974
|
-
return pts
|
|
10975
10880
|
}
|
|
10976
10881
|
|
|
10977
|
-
function
|
|
10978
|
-
{ closed = true,
|
|
10979
|
-
keepCorners = true,
|
|
10980
|
-
keepExtremes = true,
|
|
10981
|
-
keepInflections = false
|
|
10982
|
-
} = {}
|
|
10983
|
-
) {
|
|
10984
|
-
let chunks = [[pts[0]]];
|
|
10985
|
-
|
|
10986
|
-
let idx = 0;
|
|
10987
|
-
let lastChunk = chunks[idx];
|
|
10882
|
+
function cleanupPolyKeypoints(pts = []) {
|
|
10988
10883
|
|
|
10989
10884
|
let l = pts.length;
|
|
10990
10885
|
|
|
10991
|
-
|
|
10886
|
+
getPolyBBox(pts);
|
|
10887
|
+
|
|
10888
|
+
pts[0];
|
|
10889
|
+
|
|
10992
10890
|
for (let i = 1; i < l; i++) {
|
|
10993
|
-
i > 0 ? pts[i] : pts[l - 1];
|
|
10891
|
+
i > 0 ? pts[i - 1] : pts[l - 1];
|
|
10994
10892
|
let p1 = pts[i];
|
|
10995
10893
|
i < l - 1 ? pts[i + 1] : pts[l - 1];
|
|
10996
10894
|
|
|
10997
|
-
|
|
10998
|
-
|
|
10999
|
-
if ((keepExtremes && p1.isExtreme || keepCorners && p1.isCorner )) {
|
|
11000
|
-
idx++;
|
|
11001
|
-
chunks.push([]);
|
|
11002
|
-
}
|
|
10895
|
+
let { isHorizontal, isVertical, isCorner, isLong, isExtreme, isSemiExtreme, isDirChange } = p1;
|
|
10896
|
+
let offset = 0;
|
|
11003
10897
|
|
|
11004
|
-
|
|
11005
|
-
lastChunk.push(p1);
|
|
11006
|
-
}
|
|
10898
|
+
if (!isSemiExtreme){
|
|
11007
10899
|
|
|
11008
|
-
|
|
10900
|
+
continue
|
|
10901
|
+
}
|
|
11009
10902
|
|
|
11010
|
-
|
|
11011
|
-
|
|
10903
|
+
if (isSemiExtreme || isExtreme) {
|
|
10904
|
+
let semiExtremes = isSemiExtreme ? [p1] : [];
|
|
11012
10905
|
|
|
11013
|
-
|
|
11014
|
-
|
|
11015
|
-
*
|
|
11016
|
-
*/
|
|
11017
|
-
function fitCurveSchneider(pts, {
|
|
11018
|
-
maxError = 0,
|
|
11019
|
-
adjustCpts = true,
|
|
11020
|
-
harmonize = true,
|
|
11021
|
-
keepCorners = true
|
|
11022
|
-
} = {}) {
|
|
10906
|
+
for (let j = i + 1; j < l; j++) {
|
|
10907
|
+
let p2 = pts[j];
|
|
11023
10908
|
|
|
11024
|
-
|
|
11025
|
-
|
|
11026
|
-
|
|
10909
|
+
if (!p2.isSemiExtreme || p2.isExtreme || p2.isCorner){
|
|
10910
|
+
break
|
|
10911
|
+
}
|
|
10912
|
+
semiExtremes.push(p2);
|
|
10913
|
+
}
|
|
11027
10914
|
|
|
11028
|
-
|
|
11029
|
-
if (pts.length === 2) {
|
|
11030
|
-
return [
|
|
11031
|
-
{ type: 'L', values: [pts[0].x, pts[0].y] },
|
|
11032
|
-
{ type: 'L', values: [pts[1].x, pts[1].y] }
|
|
11033
|
-
]
|
|
11034
|
-
}
|
|
10915
|
+
if (semiExtremes.length > 1) {
|
|
11035
10916
|
|
|
11036
|
-
|
|
10917
|
+
let semiExtremeMid = semiExtremes[Math.floor(semiExtremes.length*0.5)];
|
|
10918
|
+
let p1_1 = semiExtremes[0];
|
|
10919
|
+
let p2_1 = semiExtremes[semiExtremes.length - 1];
|
|
10920
|
+
let ptI = checkLineIntersection(p1_1, p1_1.tangentR, p2_1, p2_1.tangentL, false, true);
|
|
11037
10921
|
|
|
11038
|
-
|
|
11039
|
-
|
|
11040
|
-
|
|
11041
|
-
|
|
10922
|
+
semiExtremes.forEach(pt=>{
|
|
10923
|
+
pt.isSemiExtreme=false;
|
|
10924
|
+
});
|
|
10925
|
+
semiExtremeMid.isSemiExtreme=true;
|
|
11042
10926
|
|
|
11043
|
-
|
|
11044
|
-
|
|
11045
|
-
|
|
10927
|
+
// interpolate mid point
|
|
10928
|
+
if (ptI) {
|
|
10929
|
+
let pI_1 = interpolate(p1_1, ptI, 0.5);
|
|
10930
|
+
let pI_2 = interpolate(p2_1, ptI, 0.5);
|
|
10931
|
+
let pI_3 = interpolate(pI_2, pI_1, 0.5);
|
|
11046
10932
|
|
|
11047
|
-
|
|
10933
|
+
semiExtremeMid.x = pI_3.x;
|
|
10934
|
+
semiExtremeMid.y = pI_3.y;
|
|
10935
|
+
semiExtremeMid.tangentL = pI_1;
|
|
10936
|
+
semiExtremeMid.tangentR = pI_2;
|
|
11048
10937
|
|
|
11049
|
-
|
|
10938
|
+
i += offset;
|
|
10939
|
+
continue
|
|
10940
|
+
}
|
|
11050
10941
|
|
|
11051
|
-
|
|
10942
|
+
}
|
|
10943
|
+
}
|
|
10944
|
+
// find significant of same type
|
|
11052
10945
|
|
|
11053
|
-
|
|
11054
|
-
let com1 = pathData[0];
|
|
10946
|
+
}
|
|
11055
10947
|
|
|
11056
|
-
|
|
11057
|
-
|
|
10948
|
+
/*
|
|
10949
|
+
// update index
|
|
10950
|
+
ptsClean.forEach((pt, i) => {
|
|
10951
|
+
pt.idx = i
|
|
10952
|
+
})
|
|
10953
|
+
*/
|
|
11058
10954
|
|
|
11059
|
-
|
|
11060
|
-
let p1 = { x: pts[1].x, y: pts[1].y };
|
|
11061
|
-
let p2 = pts[2] ? { x: pts[2].x, y: pts[2].y } : null;
|
|
10955
|
+
return pts;
|
|
11062
10956
|
|
|
11063
|
-
|
|
11064
|
-
cp1 = { x: com1.values[0], y: com1.values[1] };
|
|
11065
|
-
cp1 = adjustTangentAngle(cp1, p0, p1, p2);
|
|
11066
|
-
com1.values[0] = cp1.x;
|
|
11067
|
-
com1.values[1] = cp1.y;
|
|
11068
|
-
}
|
|
10957
|
+
}
|
|
11069
10958
|
|
|
11070
|
-
|
|
11071
|
-
|
|
11072
|
-
|
|
10959
|
+
function adjustTangentAngle(cp, p0, p1, p2) {
|
|
10960
|
+
let ang1 = getAngle(p0, p1);
|
|
10961
|
+
let ang2 = getAngle(p0, p2);
|
|
10962
|
+
let angDiff = (ang2 - ang1);
|
|
11073
10963
|
|
|
11074
|
-
|
|
11075
|
-
|
|
11076
|
-
cp2 = adjustTangentAngle(cp2, pL, pL1, pL2);
|
|
11077
|
-
com2.values[2] = cp2.x;
|
|
11078
|
-
com2.values[3] = cp2.y;
|
|
11079
|
-
}
|
|
10964
|
+
let f = 0.666;
|
|
10965
|
+
f = 1;
|
|
11080
10966
|
|
|
11081
|
-
|
|
11082
|
-
|
|
11083
|
-
|
|
11084
|
-
harmonize = false;
|
|
11085
|
-
if (harmonize) {
|
|
11086
|
-
pathData = harmonizeCubicCptsThird([{ type: 'M', values: [pts[0].x, pts[0].y] },
|
|
11087
|
-
...pathData])
|
|
11088
|
-
pathData.shift()
|
|
11089
|
-
}
|
|
11090
|
-
*/
|
|
10967
|
+
cp = rotatePoint(cp, p0.x, p0.y, -angDiff * f);
|
|
10968
|
+
return cp
|
|
10969
|
+
}
|
|
11091
10970
|
|
|
11092
|
-
|
|
10971
|
+
function getTangents(pts = [], {
|
|
10972
|
+
x = 0,
|
|
10973
|
+
y = 0,
|
|
10974
|
+
width = 0,
|
|
10975
|
+
height = 0,
|
|
10976
|
+
debug = false,
|
|
10977
|
+
closed=false,
|
|
10978
|
+
} = {}) {
|
|
11093
10979
|
|
|
11094
|
-
|
|
11095
|
-
}
|
|
10980
|
+
let l = pts.length;
|
|
11096
10981
|
|
|
11097
|
-
|
|
11098
|
-
|
|
11099
|
-
|
|
11100
|
-
|
|
11101
|
-
*/
|
|
11102
|
-
function fitCubic(pts, leftTangent, rightTangent, error, keepCorners = false) {
|
|
10982
|
+
// bounding box of this sub poly
|
|
10983
|
+
if (!width || !height) {
|
|
10984
|
+
({ x, y, width, height } = getPolyBBox(pts));
|
|
10985
|
+
}
|
|
11103
10986
|
|
|
11104
|
-
|
|
11105
|
-
let bezCurve;
|
|
10987
|
+
// threshold for horizontal or vertical detection
|
|
11106
10988
|
|
|
11107
|
-
|
|
10989
|
+
for (let i = 0; i < l; i++) {
|
|
10990
|
+
let p0 = i > 0 ? pts[i - 1] : pts[l - 1];
|
|
10991
|
+
let p1 = pts[i];
|
|
10992
|
+
let p2 = i < l - 1 ? pts[i + 1] : pts[l - 1];
|
|
10993
|
+
let p3 = i < l - 1 ? pts[i + 2] : pts[l - 1];
|
|
11108
10994
|
|
|
11109
|
-
|
|
11110
|
-
let dist = getDistance(pts[0], pts[1], false) * 0.333;
|
|
11111
|
-
bezCurve = [pts[0], addArrays(pts[0], mulItems(leftTangent, dist)), addArrays(pts[1], mulItems(rightTangent, dist)), pts[1]];
|
|
11112
|
-
return [bezCurve];
|
|
11113
|
-
}
|
|
11114
|
-
*/
|
|
10995
|
+
let { isHorizontal, isVertical, isCorner, isLong, isExtreme, isSemiExtreme, isDirChange } = p1;
|
|
11115
10996
|
|
|
11116
|
-
|
|
11117
|
-
|
|
10997
|
+
// default
|
|
10998
|
+
let tangentL = { x: p1.x - p1.dx2 * 0.5, y: p1.y - p1.dy2 * 0.5 };
|
|
10999
|
+
let tangentR = { x: p1.x + p1.dx2 * 0.5, y: p1.y + p1.dy2 * 0.5 };
|
|
11118
11000
|
|
|
11119
|
-
|
|
11001
|
+
// average first tangent
|
|
11002
|
+
if(i===0){
|
|
11003
|
+
tangentR = adjustTangentAngle(p2, p1, p2, p3);
|
|
11004
|
+
}
|
|
11120
11005
|
|
|
11121
|
-
|
|
11122
|
-
|
|
11006
|
+
/**
|
|
11007
|
+
* add left and right tangents
|
|
11008
|
+
* for later curve fitting
|
|
11009
|
+
*/
|
|
11123
11010
|
|
|
11124
|
-
|
|
11125
|
-
|
|
11126
|
-
|
|
11127
|
-
|
|
11128
|
-
if (
|
|
11129
|
-
|
|
11130
|
-
|
|
11011
|
+
if (isHorizontal && !isCorner) {
|
|
11012
|
+
tangentL = { x: p1.x - p1.dx2*0.5, y: p1.y };
|
|
11013
|
+
tangentR = { x: p1.x + p1.dx2*0.5, y: p1.y };
|
|
11014
|
+
}
|
|
11015
|
+
else if (isVertical) {
|
|
11016
|
+
tangentL = { x: p1.x , y: p1.y - p1.dy2*0.5 };
|
|
11017
|
+
tangentR = { x: p1.x , y: p1.y + p1.dy2*0.5 };
|
|
11131
11018
|
}
|
|
11132
|
-
}
|
|
11133
11019
|
|
|
11134
|
-
|
|
11135
|
-
|
|
11136
|
-
|
|
11020
|
+
if (!isExtreme && p1.isLong) {
|
|
11021
|
+
tangentL = { x: p1.x - p1.dx*0.5, y: p1.y - p1.dy*0.5 };
|
|
11022
|
+
tangentR = { x: p1.x + p1.dx*0.5, y: p1.y + p1.dy*0.5 };
|
|
11023
|
+
}
|
|
11137
11024
|
|
|
11138
|
-
|
|
11025
|
+
/*
|
|
11139
11026
|
|
|
11140
|
-
|
|
11141
|
-
|
|
11142
|
-
|
|
11027
|
+
if (isDirChange && !isCorner && !isExtreme) {
|
|
11028
|
+
p1.tangentL = { x: p1.x-p1.dx2*0.5, y: p1.y-p1.dy2*0.5 }
|
|
11029
|
+
p1.tangentR = { x: p1.x+p1.dx2*0.5, y: p1.y+p1.dy2*0.5 }
|
|
11030
|
+
}
|
|
11031
|
+
*/
|
|
11143
11032
|
|
|
11144
|
-
|
|
11033
|
+
if (isCorner) {
|
|
11145
11034
|
|
|
11146
|
-
|
|
11035
|
+
tangentL = {x:p0.x, y:p0.y};
|
|
11036
|
+
tangentR = {x:p2.x, y:p2.y};
|
|
11147
11037
|
|
|
11148
|
-
let
|
|
11038
|
+
let p0_1 = pts[i - 2] ? pts[i - 2] : pts[l - 1];
|
|
11149
11039
|
|
|
11150
|
-
|
|
11151
|
-
maxError = _generateAndReport2[1];
|
|
11152
|
-
splitPoint = _generateAndReport2[2];
|
|
11040
|
+
let p2_1 = pts[i + 2] ? pts[i + 2] : pts[1];
|
|
11153
11041
|
|
|
11154
|
-
|
|
11155
|
-
|
|
11042
|
+
// adjust angle
|
|
11043
|
+
if (!p0.isCorner) {
|
|
11044
|
+
tangentL = adjustTangentAngle(p0, p1, p0, p0_1);
|
|
11156
11045
|
}
|
|
11157
11046
|
|
|
11158
|
-
|
|
11159
|
-
|
|
11160
|
-
if (errChange > .9999 && errChange < 1.0001) {
|
|
11161
|
-
break;
|
|
11162
|
-
}
|
|
11047
|
+
if (!p2.isCorner) {
|
|
11048
|
+
tangentR = adjustTangentAngle(tangentR, p1, p2, p2_1);
|
|
11163
11049
|
}
|
|
11164
11050
|
|
|
11165
|
-
|
|
11166
|
-
|
|
11051
|
+
/*
|
|
11052
|
+
renderPoint(markers, p0, 'darkblue', '0.75%', '0.5')
|
|
11053
|
+
// renderPoint(markers, p0_1, 'blue', '0.5%')
|
|
11054
|
+
renderPoint(markers, tangentL, 'blue', '0.5%', '0.5')
|
|
11055
|
+
renderPoint(markers, tangentR, 'blue', '0.5%', '0.5')
|
|
11056
|
+
*/
|
|
11057
|
+
|
|
11167
11058
|
}
|
|
11168
|
-
}
|
|
11169
11059
|
|
|
11170
|
-
|
|
11171
|
-
|
|
11060
|
+
p1.tangentL = tangentL;
|
|
11061
|
+
p1.tangentR = tangentR;
|
|
11062
|
+
|
|
11063
|
+
/*
|
|
11064
|
+
debug = true
|
|
11065
|
+
if(debug){
|
|
11066
|
+
if (isCorner || isSemiExtreme || isDirChange || isExtreme) {
|
|
11067
|
+
renderPoint(markers, p1.tangentL, 'darkred', '0.5%')
|
|
11068
|
+
renderPoint(markers, p1.tangentR, 'darkblue', '0.5%')
|
|
11069
|
+
}
|
|
11070
|
+
}
|
|
11071
|
+
*/
|
|
11172
11072
|
|
|
11173
|
-
if (centerVector.x === 0 && centerVector.y === 0) {
|
|
11174
|
-
centerVector = subtract(pts[splitPoint - 1], pts[splitPoint]);
|
|
11175
|
-
let _ref = { x: -centerVector.y, y: centerVector.x };
|
|
11176
|
-
centerVector.x = _ref.x;
|
|
11177
|
-
centerVector.y = _ref.y;
|
|
11178
11073
|
}
|
|
11179
11074
|
|
|
11180
|
-
|
|
11075
|
+
}
|
|
11181
11076
|
|
|
11182
|
-
|
|
11077
|
+
function getPolyCentroid(pts) {
|
|
11183
11078
|
|
|
11184
|
-
|
|
11079
|
+
let l = pts.length;
|
|
11080
|
+
let x = 0, y = 0;
|
|
11081
|
+
for (let i = 0; l && i < l; i++) {
|
|
11082
|
+
let pt = pts[i];
|
|
11083
|
+
x += pt.x;
|
|
11084
|
+
y += pt.y;
|
|
11085
|
+
}
|
|
11185
11086
|
|
|
11186
|
-
|
|
11187
|
-
|
|
11188
|
-
...fitCubic(pts.slice(splitPoint), fromCenterTangent, rightTangent, error, keepCorners)
|
|
11189
|
-
);
|
|
11087
|
+
let centroid = { x: x / l, y: y / l };
|
|
11088
|
+
return centroid
|
|
11190
11089
|
|
|
11191
|
-
return beziers;
|
|
11192
11090
|
}
|
|
11193
11091
|
|
|
11194
|
-
|
|
11195
|
-
|
|
11196
|
-
|
|
11197
|
-
function generateBezier(pts, parameters, leftTangent, rightTangent) {
|
|
11092
|
+
function detectRegularPolygon(pts, centroid = { x: 0, y: 0 }) {
|
|
11093
|
+
let rSq = getSquareDistance(pts[0], centroid);
|
|
11094
|
+
let isRegular = true;
|
|
11198
11095
|
|
|
11199
|
-
let
|
|
11096
|
+
for (let i = 1, l = pts.length; i < l; i++) {
|
|
11097
|
+
let pt1 = pts[i];
|
|
11098
|
+
let dist = getSquareDistance(pt1, centroid);
|
|
11200
11099
|
|
|
11201
|
-
|
|
11202
|
-
|
|
11203
|
-
let len = parameters.length;
|
|
11100
|
+
let diff = Math.abs(rSq - dist);
|
|
11101
|
+
let diffRel = diff / rSq;
|
|
11204
11102
|
|
|
11205
|
-
|
|
11206
|
-
|
|
11207
|
-
|
|
11208
|
-
a = A[i];
|
|
11103
|
+
if (diffRel > 0.05) {
|
|
11104
|
+
return false;
|
|
11105
|
+
}
|
|
11209
11106
|
|
|
11210
|
-
a[0] = mulItems(leftTangent, 3 * u * (ux * ux));
|
|
11211
|
-
a[1] = mulItems(rightTangent, 3 * ux * (u * u));
|
|
11212
11107
|
}
|
|
11108
|
+
return isRegular;
|
|
11109
|
+
}
|
|
11110
|
+
|
|
11111
|
+
function analyzePoly(pts, {
|
|
11112
|
+
x = 0,
|
|
11113
|
+
y = 0,
|
|
11114
|
+
width = 0,
|
|
11115
|
+
height = 0,
|
|
11116
|
+
debug = false
|
|
11117
|
+
} = {}) {
|
|
11213
11118
|
|
|
11214
|
-
let C = [[0, 0], [0, 0]];
|
|
11215
|
-
let X = [0, 0];
|
|
11216
11119
|
let l = pts.length;
|
|
11120
|
+
let left = x;
|
|
11121
|
+
let top = y;
|
|
11122
|
+
let right = x + width;
|
|
11123
|
+
let bottom = y + height;
|
|
11217
11124
|
|
|
11218
|
-
|
|
11219
|
-
|
|
11220
|
-
|
|
11125
|
+
if (!width || !height) {
|
|
11126
|
+
({ x, y, width, height, top, bottom, left, right } = getPolyBBox(pts));
|
|
11127
|
+
}
|
|
11128
|
+
|
|
11129
|
+
// round
|
|
11130
|
+
[x, y, width, height, top, bottom, left, right] = [x, y, width, height, top, bottom, left, right].map(val => +val.toFixed(8));
|
|
11221
11131
|
|
|
11222
|
-
|
|
11223
|
-
|
|
11224
|
-
C[1][0] += dot(a[0], a[1]);
|
|
11225
|
-
C[1][1] += dot(a[1], a[1]);
|
|
11132
|
+
// bounding box of this sub poly
|
|
11133
|
+
let bb0 = { x, y, top, left, width, height, right, bottom };
|
|
11226
11134
|
|
|
11227
|
-
|
|
11135
|
+
let thresh = (width + height) * 0.01;
|
|
11228
11136
|
|
|
11229
|
-
|
|
11230
|
-
|
|
11231
|
-
}
|
|
11137
|
+
// threshold for horizontal or vertical detection
|
|
11138
|
+
let thresh2 = thresh * 0.75;
|
|
11232
11139
|
|
|
11233
|
-
let
|
|
11234
|
-
let det_C0_X = C[0][0] * X[1] - C[1][0] * X[0];
|
|
11235
|
-
let det_X_C1 = X[0] * C[1][1] - X[1] * C[0][1];
|
|
11140
|
+
let dims = [];
|
|
11236
11141
|
|
|
11237
|
-
|
|
11238
|
-
|
|
11239
|
-
|
|
11240
|
-
|
|
11142
|
+
/*
|
|
11143
|
+
pts.forEach(pt=>{
|
|
11144
|
+
renderPoint(markers, pt, 'red', '2.5%')
|
|
11145
|
+
})
|
|
11146
|
+
*/
|
|
11241
11147
|
|
|
11242
|
-
|
|
11148
|
+
/**
|
|
11149
|
+
* 1st run:
|
|
11150
|
+
* collect more details
|
|
11151
|
+
* area for sign change detection
|
|
11152
|
+
* deltas and distances
|
|
11153
|
+
*/
|
|
11154
|
+
for (let i = 0; i < l; i++) {
|
|
11155
|
+
let p0 = i > 0 ? pts[i - 1] : pts[l - 1];
|
|
11156
|
+
let p1 = pts[i];
|
|
11157
|
+
let p2 = i < l - 1 ? pts[i + 1] : pts[l - 1];
|
|
11158
|
+
|
|
11159
|
+
let area = getPolygonArea([p0, p1, p2], false);
|
|
11160
|
+
let dx = i > 0 ? +(p1.x - p0.x).toFixed(7) : 0;
|
|
11161
|
+
let dy = i > 0 ? +(p1.y - p0.y).toFixed(7) : 0;
|
|
11162
|
+
|
|
11163
|
+
let dx2 = +(p2.x - p0.x).toFixed(7);
|
|
11164
|
+
let dy2 = +(p2.y - p0.y).toFixed(7);
|
|
11165
|
+
|
|
11166
|
+
p1.area = area;
|
|
11167
|
+
p1.dist = i > 0 ? getDistManhattan(p0, p1) : 0;
|
|
11168
|
+
// add dist for long/short segment detection
|
|
11169
|
+
dims.push(p1.dist);
|
|
11170
|
+
p1.idx = i;
|
|
11171
|
+
p1.dx = dx;
|
|
11172
|
+
p1.dy = dy;
|
|
11173
|
+
p1.dx2 = dx2;
|
|
11174
|
+
p1.dy2 = dy2;
|
|
11243
11175
|
|
|
11244
|
-
bezCurve[1] = addArrays(firstPoint, mulItems(leftTangent, segLength * 0.333));
|
|
11245
|
-
bezCurve[2] = addArrays(lastPoint, mulItems(rightTangent, segLength * 0.333));
|
|
11246
|
-
} else {
|
|
11247
|
-
// First and last control pts of the Bezier curve
|
|
11248
|
-
bezCurve[1] = addArrays(firstPoint, mulItems(leftTangent, alpha_l));
|
|
11249
|
-
bezCurve[2] = addArrays(lastPoint, mulItems(rightTangent, alpha_r));
|
|
11250
11176
|
}
|
|
11251
11177
|
|
|
11252
|
-
|
|
11253
|
-
|
|
11178
|
+
/**
|
|
11179
|
+
* find average segment length
|
|
11180
|
+
* for long/short segment detection
|
|
11181
|
+
*/
|
|
11182
|
+
dims = dims.filter(Boolean).sort((a, b) => a - b);
|
|
11183
|
+
let lenD = dims.length;
|
|
11184
|
+
let dimMin = dims[0];
|
|
11185
|
+
dims[lenD - 1];
|
|
11254
11186
|
|
|
11255
|
-
|
|
11256
|
-
let
|
|
11187
|
+
let dimAv = dims.reduce((a, b) => a + b, 0) / lenD;
|
|
11188
|
+
let dimShort = (dimMin + dimAv) * 0.5;
|
|
11189
|
+
let dimLong = dimAv * 2;
|
|
11257
11190
|
|
|
11258
|
-
|
|
11259
|
-
|
|
11191
|
+
/*
|
|
11192
|
+
// round to adjust for minor deviations
|
|
11193
|
+
let idx_q = Math.ceil(lenD * 0.25);
|
|
11194
|
+
let dim_mid = dims[Math.floor(lenD * 0.5)]
|
|
11195
|
+
let dims_min = dims.slice(0, Math.floor(lenD * 0.25));
|
|
11196
|
+
let dim_min = ((dims_min.reduce((a, b) => a + b, 0) / idx_q) + dim_mid) * 0.5;
|
|
11260
11197
|
|
|
11261
|
-
|
|
11262
|
-
|
|
11198
|
+
let threshold = 75
|
|
11199
|
+
let decimalsAuto = dim_min > threshold * 1.5 ? 0 : Math.floor(threshold / dim_min).toString().length
|
|
11263
11200
|
|
|
11264
|
-
|
|
11265
|
-
|
|
11201
|
+
// clamp
|
|
11202
|
+
decimalsAuto = Math.min(Math.max(0, decimalsAuto), 8)
|
|
11266
11203
|
|
|
11267
|
-
|
|
11268
|
-
|
|
11269
|
-
|
|
11270
|
-
function reparameterize(bezier, pts, parameters) {
|
|
11271
|
-
return parameters.map((p, i) => {
|
|
11272
|
-
return newtonRaphsonRootFind(bezier, pts[i], p);
|
|
11273
|
-
});
|
|
11274
|
-
}
|
|
11275
|
-
/**
|
|
11276
|
-
* Use Newton-Raphson iteration to find better root.
|
|
11277
|
-
*/
|
|
11204
|
+
pts = roundPoly(pts, 2)
|
|
11205
|
+
console.log(pts);
|
|
11206
|
+
*/
|
|
11278
11207
|
|
|
11279
|
-
|
|
11280
|
-
|
|
11281
|
-
|
|
11282
|
-
|
|
11208
|
+
/**
|
|
11209
|
+
* analyze topology:
|
|
11210
|
+
* find significant commands:
|
|
11211
|
+
* extremes, inflections etc.
|
|
11212
|
+
*/
|
|
11213
|
+
for (let i = 0; i < l; i++) {
|
|
11283
11214
|
|
|
11284
|
-
|
|
11215
|
+
let p0 = i > 0 ? pts[i - 1] : pts[l - 1];
|
|
11216
|
+
let p1 = pts[i];
|
|
11217
|
+
let p2 = i < l - 1 ? pts[i + 1] : pts[l - 1];
|
|
11285
11218
|
|
|
11286
|
-
|
|
11219
|
+
// convert area to absolute for flatness checks
|
|
11220
|
+
let area1 = Math.abs(p1.area);
|
|
11221
|
+
let isCorner = false;
|
|
11222
|
+
let isSemiExtreme = false;
|
|
11223
|
+
let isShort = false;
|
|
11224
|
+
let isLong = false;
|
|
11287
11225
|
|
|
11288
|
-
|
|
11289
|
-
|
|
11226
|
+
/**
|
|
11227
|
+
* detect short or long
|
|
11228
|
+
*/
|
|
11229
|
+
if (p1.dist > dimLong) {
|
|
11230
|
+
isLong = true;
|
|
11231
|
+
}
|
|
11290
11232
|
|
|
11291
|
-
|
|
11292
|
-
|
|
11233
|
+
if (p1.dist < dimShort) {
|
|
11234
|
+
isShort = true;
|
|
11235
|
+
}
|
|
11293
11236
|
|
|
11294
|
-
|
|
11295
|
-
// This represents how much the error aligns with the tangent
|
|
11296
|
-
let numerator = dx * qp[0] + dy * qp[1];
|
|
11237
|
+
let flat = !p1.area || area1 < thresh;
|
|
11297
11238
|
|
|
11298
|
-
|
|
11299
|
-
|
|
11300
|
-
|
|
11239
|
+
/**
|
|
11240
|
+
* check extremes
|
|
11241
|
+
*/
|
|
11242
|
+
let isExtreme = false;
|
|
11301
11243
|
|
|
11302
|
-
|
|
11303
|
-
|
|
11244
|
+
// 1. total extreme
|
|
11245
|
+
let isTop = p1.y === bb0.top;
|
|
11246
|
+
let isBottom = p1.y === bb0.bottom;
|
|
11247
|
+
let isLeft = p1.x === bb0.left;
|
|
11248
|
+
let isRight = p1.x === bb0.right;
|
|
11304
11249
|
|
|
11305
|
-
|
|
11306
|
-
|
|
11250
|
+
if (isTop || isBottom || isLeft || isRight) {
|
|
11251
|
+
isExtreme = true;
|
|
11307
11252
|
|
|
11308
|
-
|
|
11253
|
+
}
|
|
11309
11254
|
|
|
11310
|
-
|
|
11311
|
-
|
|
11312
|
-
|
|
11255
|
+
// 1.2 horizontal or vertical
|
|
11256
|
+
/*
|
|
11257
|
+
let isHorizontal = isTop || isBottom || (p1.y === p0.y && p1.x !== p0.x);
|
|
11258
|
+
let isVertical = isLeft || isRight || (p1.x === p0.x && p1.y !== p0.y)
|
|
11313
11259
|
|
|
11314
|
-
|
|
11315
|
-
return u - numerator / denominator;
|
|
11316
|
-
}
|
|
11260
|
+
if ((isHorizontal || isVertical)) {
|
|
11317
11261
|
|
|
11318
|
-
|
|
11319
|
-
|
|
11320
|
-
*/
|
|
11321
|
-
function chordLengthParameterize(pts) {
|
|
11322
|
-
let u = [];
|
|
11323
|
-
let l = pts.length;
|
|
11324
|
-
let p0 = pts[0];
|
|
11325
|
-
let p = pts[1];
|
|
11326
|
-
let currU = 0;
|
|
11327
|
-
let prevU = 0;
|
|
11262
|
+
let diffX = Math.abs(p0.x - p1.x)
|
|
11263
|
+
let diffY = Math.abs(p0.y - p1.y)
|
|
11328
11264
|
|
|
11329
|
-
|
|
11330
|
-
|
|
11265
|
+
if (isLong) {
|
|
11266
|
+
}
|
|
11331
11267
|
|
|
11332
|
-
|
|
11333
|
-
|
|
11334
|
-
|
|
11268
|
+
if (isLong && (diffY < thresh2) && diffX > thresh) {
|
|
11269
|
+
p0.isExtreme = true;
|
|
11270
|
+
p0.isHorizontal = true;
|
|
11271
|
+
}
|
|
11272
|
+
else if (isLong && (diffX < thresh2) && diffY > thresh) {
|
|
11273
|
+
p0.isExtreme = true;
|
|
11274
|
+
p0.isVertical = true;
|
|
11275
|
+
}
|
|
11335
11276
|
|
|
11336
|
-
|
|
11337
|
-
|
|
11277
|
+
isExtreme = true
|
|
11278
|
+
}
|
|
11279
|
+
*/
|
|
11338
11280
|
|
|
11339
|
-
|
|
11340
|
-
|
|
11341
|
-
});
|
|
11281
|
+
let dx = Math.abs(p0.x - p1.x);
|
|
11282
|
+
let dy = Math.abs(p0.y - p1.y);
|
|
11342
11283
|
|
|
11343
|
-
|
|
11344
|
-
|
|
11345
|
-
|
|
11346
|
-
|
|
11347
|
-
*/
|
|
11348
|
-
function computeMaxError(pts, bez, parameters) {
|
|
11349
|
-
let dist,
|
|
11350
|
-
maxDist,
|
|
11351
|
-
splitPoint,
|
|
11352
|
-
i, point, t;
|
|
11284
|
+
let vh_thresh = thresh * 0.05;
|
|
11285
|
+
// vh_thresh = thresh * 0.25
|
|
11286
|
+
let isHorizontal = isTop || isBottom || (p1.y === p0.y && p1.x !== p0.x) || (dy <= vh_thresh);
|
|
11287
|
+
let isVertical = (isLeft || isRight || (p1.x === p0.x && p1.y !== p0.y) || (dx <= vh_thresh));
|
|
11353
11288
|
|
|
11354
|
-
|
|
11355
|
-
splitPoint = Math.floor(pts.length * 0.5);
|
|
11289
|
+
// renderPoint(markers, p1, 'red', '0.5%')
|
|
11356
11290
|
|
|
11357
|
-
|
|
11358
|
-
let l = pts.length;
|
|
11359
|
-
let ptOnPath = null;
|
|
11291
|
+
if (p1.y === p0.y) ;
|
|
11360
11292
|
|
|
11361
|
-
|
|
11362
|
-
point = pts[i];
|
|
11293
|
+
if ((isHorizontal || isVertical)) {
|
|
11363
11294
|
|
|
11364
|
-
|
|
11295
|
+
if (isLong && isHorizontal) {
|
|
11296
|
+
p0.isExtreme = true;
|
|
11297
|
+
p0.isHorizontal = true;
|
|
11365
11298
|
|
|
11366
|
-
|
|
11367
|
-
|
|
11299
|
+
}
|
|
11300
|
+
else if (isLong && isVertical) {
|
|
11301
|
+
p0.isExtreme = true;
|
|
11302
|
+
p0.isVertical = true;
|
|
11303
|
+
}
|
|
11368
11304
|
|
|
11369
|
-
|
|
11370
|
-
|
|
11371
|
-
v = subtract(pointAtT(bez, t), point);
|
|
11372
|
-
dist = v.x * v.x + v.y * v.y;
|
|
11373
|
-
*/
|
|
11305
|
+
isExtreme = true;
|
|
11306
|
+
}
|
|
11374
11307
|
|
|
11375
|
-
|
|
11308
|
+
// 1.3 is local or absolute extreme
|
|
11309
|
+
let bb = getPolyBBox([p0, p2]); // local bb
|
|
11310
|
+
let { left, right, top, bottom } = bb;
|
|
11311
|
+
|
|
11312
|
+
let extremeLocal = (p1.x < left || p1.x > right || p1.y < top || p1.y > bottom);
|
|
11313
|
+
if (!isExtreme && extremeLocal) {
|
|
11314
|
+
isExtreme = true;
|
|
11376
11315
|
|
|
11377
|
-
maxDist = dist;
|
|
11378
|
-
splitPoint = i;
|
|
11379
11316
|
}
|
|
11380
|
-
}
|
|
11381
11317
|
|
|
11382
|
-
|
|
11383
|
-
|
|
11318
|
+
/**
|
|
11319
|
+
* 2. sign changes
|
|
11320
|
+
*/
|
|
11321
|
+
let signChange = (p0.area < 0 && p1.area > 0) || (p0.area > 0 && p1.area < 0);
|
|
11322
|
+
let isDirChange = signChange && !flat && !p0.isDirChange && isLong;
|
|
11384
11323
|
|
|
11385
|
-
|
|
11386
|
-
|
|
11387
|
-
|
|
11388
|
-
let B_t_prev = bez[0];
|
|
11389
|
-
let sumLen = 0;
|
|
11324
|
+
/**
|
|
11325
|
+
* 3. corners
|
|
11326
|
+
*/
|
|
11390
11327
|
|
|
11391
|
-
|
|
11392
|
-
B_t_curr = pointAtT(bez, i / B_parts);
|
|
11393
|
-
sumLen += getDistance(B_t_curr, B_t_prev);
|
|
11394
|
-
B_t_dist.push(sumLen);
|
|
11395
|
-
B_t_prev = B_t_curr;
|
|
11396
|
-
}
|
|
11328
|
+
if (isExtreme) {
|
|
11397
11329
|
|
|
11398
|
-
|
|
11399
|
-
|
|
11400
|
-
|
|
11401
|
-
return B_t_dist;
|
|
11402
|
-
}
|
|
11403
|
-
function find_t(param, t_distMap, B_parts) {
|
|
11330
|
+
let delta = getDeltaAngle(p1, p2, p0);
|
|
11331
|
+
let { deltaAngleDeg } = delta;
|
|
11332
|
+
deltaAngleDeg = Math.abs(deltaAngleDeg);
|
|
11404
11333
|
|
|
11405
|
-
|
|
11406
|
-
|
|
11407
|
-
}
|
|
11408
|
-
if (param > 1) {
|
|
11409
|
-
return 1;
|
|
11410
|
-
}
|
|
11334
|
+
let isCornerDelta = deltaAngleDeg > 10 && deltaAngleDeg < 160;
|
|
11335
|
+
if (isCornerDelta) {
|
|
11411
11336
|
|
|
11412
|
-
|
|
11337
|
+
isCorner = true;
|
|
11413
11338
|
|
|
11414
|
-
|
|
11339
|
+
}
|
|
11415
11340
|
|
|
11416
|
-
if (param <= t_distMap[i]) {
|
|
11417
|
-
tMin = (i - 1) / B_parts;
|
|
11418
|
-
tMax = i / B_parts;
|
|
11419
|
-
lenMin = t_distMap[i - 1];
|
|
11420
|
-
lenMax = t_distMap[i];
|
|
11421
|
-
t = (param - lenMin) / (lenMax - lenMin) * (tMax - tMin) + tMin;
|
|
11422
|
-
break;
|
|
11423
11341
|
}
|
|
11424
|
-
}
|
|
11425
|
-
return t;
|
|
11426
|
-
}
|
|
11427
11342
|
|
|
11428
|
-
|
|
11343
|
+
if (isExtreme && !isCorner) {
|
|
11429
11344
|
|
|
11430
|
-
|
|
11431
|
-
|
|
11432
|
-
|
|
11345
|
+
if ((Math.abs(p1.dy2) < thresh2) && Math.abs(p1.dx2) > thresh) {
|
|
11346
|
+
isHorizontal = true;
|
|
11347
|
+
}
|
|
11348
|
+
else if (Math.abs(p1.dx2) < thresh2 && Math.abs(p1.dy2) > thresh) {
|
|
11349
|
+
isVertical = true;
|
|
11350
|
+
}
|
|
11351
|
+
}
|
|
11433
11352
|
|
|
11434
|
-
|
|
11435
|
-
|
|
11436
|
-
|
|
11353
|
+
/**
|
|
11354
|
+
* semi extremes
|
|
11355
|
+
* ~ 45deg tangent
|
|
11356
|
+
*/
|
|
11357
|
+
let diffX = Math.abs(p1.dx2);
|
|
11358
|
+
let diffY = Math.abs(p1.dy2);
|
|
11437
11359
|
|
|
11438
|
-
|
|
11439
|
-
let t = step * i;
|
|
11440
|
-
pt = pointAtT(bezCurve, t);
|
|
11441
|
-
poly.push(pt);
|
|
11442
|
-
}
|
|
11360
|
+
let ratDelta = (diffX / diffY);
|
|
11443
11361
|
|
|
11444
|
-
|
|
11362
|
+
if (ratDelta > 0.8 && ratDelta <= 1.2) {
|
|
11363
|
+
isSemiExtreme = true;
|
|
11364
|
+
}
|
|
11445
11365
|
|
|
11446
|
-
|
|
11447
|
-
|
|
11366
|
+
p1.isCorner = isCorner;
|
|
11367
|
+
p1.isExtreme = isExtreme;
|
|
11368
|
+
p1.isSemiExtreme = isSemiExtreme;
|
|
11369
|
+
p1.isLong = isLong;
|
|
11370
|
+
p1.isShort = isShort;
|
|
11371
|
+
|
|
11372
|
+
p1.isHorizontal = isHorizontal;
|
|
11373
|
+
p1.isVertical = isVertical;
|
|
11374
|
+
p1.isDirChange = isDirChange;
|
|
11448
11375
|
|
|
11449
|
-
// flat line
|
|
11450
|
-
if (!polyArea && pts.length === 2) {
|
|
11451
|
-
polyArea = getSquareDistance(pts[0], pts[1]) * 0.01;
|
|
11452
11376
|
}
|
|
11453
11377
|
|
|
11454
|
-
|
|
11455
|
-
|
|
11378
|
+
// add tangents
|
|
11379
|
+
getTangents(pts, { x, y, width, height });
|
|
11456
11380
|
|
|
11457
|
-
|
|
11381
|
+
refineAdjacentPolyExtremes(pts);
|
|
11458
11382
|
|
|
11459
|
-
//
|
|
11460
|
-
|
|
11461
|
-
if (isBulged) {
|
|
11462
|
-
let ptMid = pts[Math.floor(l * 0.5)];
|
|
11463
|
-
let p = pts[l - 1];
|
|
11464
|
-
/*
|
|
11465
|
-
let cp1 = pointAtT([pts[0], ptMid], 0.666);
|
|
11466
|
-
let cp2 = pointAtT([p, ptMid], 0.666);
|
|
11383
|
+
// filter adjacent significant points
|
|
11384
|
+
cleanupPolyKeypoints(pts);
|
|
11467
11385
|
|
|
11468
|
-
|
|
11386
|
+
renderPolyTopology(pts);
|
|
11469
11387
|
|
|
11470
|
-
|
|
11388
|
+
return pts
|
|
11389
|
+
}
|
|
11471
11390
|
|
|
11472
|
-
|
|
11473
|
-
cp1 = pointAtT([bezCurve[0], bezCurve[3]], 0.333);
|
|
11474
|
-
cp2 = pointAtT([bezCurve[0], bezCurve[3]], 0.666);
|
|
11475
|
-
bezCurve = [bezCurve[0], cp1, cp2, bezCurve[3]]
|
|
11476
|
-
*/
|
|
11391
|
+
/*
|
|
11477
11392
|
|
|
11478
|
-
|
|
11479
|
-
bezierNew = bezCurve;
|
|
11480
|
-
}
|
|
11393
|
+
*/
|
|
11481
11394
|
|
|
11482
|
-
|
|
11483
|
-
|
|
11395
|
+
// just for visualization
|
|
11396
|
+
function renderPolyTopology(pts, showTangents = true) {
|
|
11484
11397
|
|
|
11485
|
-
|
|
11486
|
-
* Creates a vector of length 1 which shows the direction from B to A
|
|
11487
|
-
*/
|
|
11488
|
-
function createTangent(p1, p2) {
|
|
11489
|
-
// Returns unit vector pointing from B to A
|
|
11490
|
-
let dx = p1.x - p2.x;
|
|
11491
|
-
let dy = p1.y - p2.y;
|
|
11492
|
-
let length = Math.sqrt(dx * dx + dy * dy);
|
|
11398
|
+
let l = pts.length;
|
|
11493
11399
|
|
|
11494
|
-
|
|
11495
|
-
|
|
11496
|
-
|
|
11400
|
+
// render
|
|
11401
|
+
for (let i = 0; i < l; i++) {
|
|
11402
|
+
i > 0 ? pts[i - 1] : pts[l - 1];
|
|
11403
|
+
let p1 = pts[i];
|
|
11404
|
+
i < l - 1 ? pts[i + 1] : pts[l - 1];
|
|
11497
11405
|
|
|
11498
|
-
|
|
11499
|
-
|
|
11500
|
-
|
|
11406
|
+
if (p1.isDirChange) {
|
|
11407
|
+
renderPoint(markers, p1, 'orange', '1%', '0.75');
|
|
11408
|
+
}
|
|
11501
11409
|
|
|
11502
|
-
|
|
11503
|
-
|
|
11504
|
-
|
|
11505
|
-
}
|
|
11410
|
+
if (p1.isSemiExtreme) {
|
|
11411
|
+
renderPoint(markers, p1, 'red', '1%', '0.5');
|
|
11412
|
+
}
|
|
11506
11413
|
|
|
11507
|
-
|
|
11508
|
-
|
|
11509
|
-
|
|
11414
|
+
/*
|
|
11415
|
+
if (p1.isLong && (p1.isDirChange || p1.isExtreme || p1.isCorner || p1.isSemiExtreme)) {
|
|
11416
|
+
renderPoint(markers, p1, 'green', '1.5%', '0.5')
|
|
11417
|
+
}
|
|
11418
|
+
*/
|
|
11510
11419
|
|
|
11511
|
-
|
|
11512
|
-
|
|
11513
|
-
|
|
11420
|
+
if (p1.isDirChange) {
|
|
11421
|
+
renderPoint(markers, p1, 'green', '1.5%', '0.5');
|
|
11422
|
+
}
|
|
11514
11423
|
|
|
11515
|
-
|
|
11516
|
-
|
|
11517
|
-
|
|
11518
|
-
}
|
|
11424
|
+
if (p1.isExtreme) {
|
|
11425
|
+
renderPoint(markers, p1, 'cyan', '1%', '0.5');
|
|
11426
|
+
}
|
|
11519
11427
|
|
|
11520
|
-
|
|
11521
|
-
|
|
11522
|
-
|
|
11428
|
+
if (p1.isHorizontal) {
|
|
11429
|
+
renderPoint(markers, p1, 'blue', '1.5%', '0.25');
|
|
11430
|
+
}
|
|
11523
11431
|
|
|
11524
|
-
|
|
11525
|
-
|
|
11526
|
-
|
|
11527
|
-
zs.push([0, 0]);
|
|
11528
|
-
}
|
|
11529
|
-
return zs;
|
|
11530
|
-
}
|
|
11432
|
+
if (p1.isVertical) {
|
|
11433
|
+
renderPoint(markers, p1, 'purple', '1.5%', '0.25');
|
|
11434
|
+
}
|
|
11531
11435
|
|
|
11532
|
-
|
|
11533
|
-
|
|
11534
|
-
|
|
11535
|
-
let t = u;
|
|
11536
|
-
let mt = 1 - t;
|
|
11537
|
-
let mt2 = mt * mt;
|
|
11538
|
-
let t2 = t * t;
|
|
11539
|
-
let dx, dy;
|
|
11436
|
+
if (p1.isCorner) {
|
|
11437
|
+
renderPoint(markers, p1, 'magenta', '1%', '1');
|
|
11438
|
+
}
|
|
11540
11439
|
|
|
11541
|
-
|
|
11542
|
-
|
|
11543
|
-
|
|
11440
|
+
if (showTangents && (p1.isCorner || p1.isSemiExtreme || p1.isDirChange || p1.isExtreme)) {
|
|
11441
|
+
renderPoint(markers, p1.tangentL, 'darkred', '0.5%');
|
|
11442
|
+
renderPoint(markers, p1.tangentR, 'darkblue', '0.5%');
|
|
11544
11443
|
|
|
11545
|
-
|
|
11546
|
-
|
|
11444
|
+
/*
|
|
11445
|
+
if (p1.isDirChange) {
|
|
11446
|
+
renderPoint(markers, p1.tangentL, 'darkred', '1.5%')
|
|
11447
|
+
renderPoint(markers, p1.tangentR, 'darkblue', '1.5%')
|
|
11448
|
+
}
|
|
11449
|
+
*/
|
|
11547
11450
|
|
|
11548
|
-
|
|
11549
|
-
dx = 3 * mt2 * (cp1.x - p0.x) +
|
|
11550
|
-
6 * mt * t * (cp2.x - cp1.x) +
|
|
11551
|
-
3 * t2 * (p1.x - cp2.x);
|
|
11451
|
+
}
|
|
11552
11452
|
|
|
11553
|
-
dy = 3 * mt2 * (cp1.y - p0.y) +
|
|
11554
|
-
6 * mt * t * (cp2.y - cp1.y) +
|
|
11555
|
-
3 * t2 * (p1.y - cp2.y);
|
|
11556
11453
|
}
|
|
11557
11454
|
|
|
11558
|
-
return [dx, dy];
|
|
11559
11455
|
}
|
|
11560
11456
|
|
|
11561
|
-
function
|
|
11562
|
-
|
|
11563
|
-
|
|
11564
|
-
|
|
11565
|
-
|
|
11566
|
-
|
|
11567
|
-
|
|
11457
|
+
function getPolyChunks(pts,
|
|
11458
|
+
{ closed = true,
|
|
11459
|
+
keepCorners = true,
|
|
11460
|
+
keepExtremes = true,
|
|
11461
|
+
keepInflections = false
|
|
11462
|
+
} = {}
|
|
11463
|
+
) {
|
|
11464
|
+
let chunks = [[pts[0]]];
|
|
11568
11465
|
|
|
11569
|
-
|
|
11570
|
-
|
|
11571
|
-
let pathData = [];
|
|
11572
|
-
beziers.forEach(bez => {
|
|
11466
|
+
let idx = 0;
|
|
11467
|
+
let lastChunk = chunks[idx];
|
|
11573
11468
|
|
|
11574
|
-
|
|
11469
|
+
let l = pts.length;
|
|
11575
11470
|
|
|
11576
|
-
|
|
11577
|
-
|
|
11578
|
-
|
|
11471
|
+
// render
|
|
11472
|
+
for (let i = 1; i < l; i++) {
|
|
11473
|
+
i > 0 ? pts[i] : pts[l - 1];
|
|
11474
|
+
let p1 = pts[i];
|
|
11475
|
+
i < l - 1 ? pts[i + 1] : pts[l - 1];
|
|
11579
11476
|
|
|
11580
|
-
|
|
11581
|
-
|
|
11582
|
-
|
|
11583
|
-
|
|
11584
|
-
|
|
11585
|
-
|
|
11477
|
+
// start new chunk
|
|
11478
|
+
// keepInflections && p1.isDirChange
|
|
11479
|
+
if ((keepExtremes && p1.isExtreme || keepCorners && p1.isCorner ||
|
|
11480
|
+
(keepInflections && p1.isDirChange && !p1.isExtreme && !p1.isCorner )
|
|
11481
|
+
)) {
|
|
11482
|
+
idx++;
|
|
11483
|
+
chunks.push([]);
|
|
11484
|
+
}
|
|
11586
11485
|
|
|
11587
|
-
|
|
11588
|
-
|
|
11589
|
-
}
|
|
11486
|
+
lastChunk = chunks[idx];
|
|
11487
|
+
lastChunk.push(p1);
|
|
11488
|
+
}
|
|
11590
11489
|
|
|
11591
|
-
|
|
11490
|
+
// test render
|
|
11491
|
+
|
|
11492
|
+
return chunks;
|
|
11592
11493
|
}
|
|
11593
11494
|
|
|
11594
|
-
function
|
|
11495
|
+
function removeCoincidingVertices(pts = []) {
|
|
11496
|
+
let l = pts.length;
|
|
11497
|
+
if (!l) return pts;
|
|
11498
|
+
|
|
11499
|
+
let ptsN = [pts[0]];
|
|
11500
|
+
let pt1, pt2;
|
|
11501
|
+
|
|
11502
|
+
for (let i = 1; i < l; i++) {
|
|
11503
|
+
pt1 = pts[i - 1];
|
|
11504
|
+
pt2 = pts[i];
|
|
11505
|
+
|
|
11506
|
+
/**
|
|
11507
|
+
* 1. Skip zero-length segments
|
|
11508
|
+
*/
|
|
11509
|
+
if (pt1.x === pt2.x && pt1.y === pt2.y) {
|
|
11510
|
+
continue;
|
|
11511
|
+
}
|
|
11512
|
+
ptsN.push(pt2);
|
|
11513
|
+
}
|
|
11514
|
+
return ptsN
|
|
11515
|
+
|
|
11516
|
+
}
|
|
11595
11517
|
|
|
11596
|
-
|
|
11518
|
+
function simplifyRC(pts = [], quality = 1, shiftStart = true) {
|
|
11597
11519
|
|
|
11598
11520
|
let l = pts.length;
|
|
11521
|
+
if (l < 4) return pts;
|
|
11599
11522
|
|
|
11600
11523
|
// starting point
|
|
11601
11524
|
let M = pts[0];
|
|
@@ -11660,7 +11583,7 @@
|
|
|
11660
11583
|
let thresh = getSquareDistance(pt0, pt2) * 0.005;
|
|
11661
11584
|
|
|
11662
11585
|
// flat
|
|
11663
|
-
if (
|
|
11586
|
+
if (area <= thresh && i < l - 1) {
|
|
11664
11587
|
|
|
11665
11588
|
pt0 = pt1;
|
|
11666
11589
|
continue
|
|
@@ -11681,10 +11604,10 @@
|
|
|
11681
11604
|
}
|
|
11682
11605
|
|
|
11683
11606
|
// 1st and last are colinear
|
|
11684
|
-
let area0 = getPolygonArea([ptsSmp[1], M, ptsSmp[ptsSmp.length-1]], true);
|
|
11685
|
-
let thresh0 = getSquareDistance
|
|
11607
|
+
let area0 = getPolygonArea([ptsSmp[1], M, ptsSmp[ptsSmp.length - 1]], true);
|
|
11608
|
+
let thresh0 = getSquareDistance(ptsSmp[1], ptsSmp[ptsSmp.length - 1]) * 0.005;
|
|
11686
11609
|
// remove first point
|
|
11687
|
-
if(area0 < thresh0) ptsSmp.shift();
|
|
11610
|
+
if (area0 < thresh0) ptsSmp.shift();
|
|
11688
11611
|
|
|
11689
11612
|
return ptsSmp;
|
|
11690
11613
|
}
|
|
@@ -11703,6 +11626,7 @@
|
|
|
11703
11626
|
tolerance = 1,
|
|
11704
11627
|
simplifyRD = 1,
|
|
11705
11628
|
simplifyRDP = 1,
|
|
11629
|
+
isClosed = true,
|
|
11706
11630
|
} = {}) {
|
|
11707
11631
|
|
|
11708
11632
|
let polyPath = [];
|
|
@@ -11780,6 +11704,25 @@
|
|
|
11780
11704
|
|
|
11781
11705
|
// remove colinear
|
|
11782
11706
|
|
|
11707
|
+
keepExtremes = false;
|
|
11708
|
+
keepCorners = false;
|
|
11709
|
+
|
|
11710
|
+
keepExtremes = true;
|
|
11711
|
+
keepCorners = true;
|
|
11712
|
+
|
|
11713
|
+
// check if closed
|
|
11714
|
+
/*
|
|
11715
|
+
let bb = getPolyBBox(pts)
|
|
11716
|
+
let thresh = (bb.width+bb.height)*0.25
|
|
11717
|
+
let dist0 = getDistManhattan(pts[0], pts[pts.length-1])
|
|
11718
|
+
|
|
11719
|
+
*/
|
|
11720
|
+
|
|
11721
|
+
// copy 1st first to end
|
|
11722
|
+
if (isClosed) {
|
|
11723
|
+
pts.push(pts[0]);
|
|
11724
|
+
}
|
|
11725
|
+
|
|
11783
11726
|
// get topology of poly
|
|
11784
11727
|
let polyAnalyzed = !keepExtremes && !keepCorners ? pts : analyzePoly(pts, {
|
|
11785
11728
|
debug: false
|
|
@@ -11787,69 +11730,369 @@
|
|
|
11787
11730
|
});
|
|
11788
11731
|
|
|
11789
11732
|
// split into segment chunks
|
|
11790
|
-
|
|
11733
|
+
|
|
11734
|
+
let chunks = getPolyChunks(polyAnalyzed, { keepCorners, keepExtremes, keepInflections: true });
|
|
11791
11735
|
|
|
11792
11736
|
// Schneider curve fit
|
|
11793
11737
|
let threshold = width && height ? (width + height) / 2 * 0.004 * tolerance : 2.5;
|
|
11738
|
+
threshold = width && height ? (width + height) / 2 * 0.004 * tolerance : 2.5;
|
|
11794
11739
|
|
|
11795
|
-
|
|
11796
|
-
closed,
|
|
11797
|
-
tolerance: threshold,
|
|
11798
|
-
keepCorners,
|
|
11799
|
-
keepExtremes: true,
|
|
11800
|
-
});
|
|
11740
|
+
{
|
|
11801
11741
|
|
|
11802
|
-
|
|
11742
|
+
polyPath = simplifyPolyChunksTopology(chunks, {
|
|
11743
|
+
closed,
|
|
11744
|
+
tolerance: threshold,
|
|
11745
|
+
keepCorners,
|
|
11746
|
+
keepExtremes: true,
|
|
11747
|
+
});
|
|
11748
|
+
}
|
|
11803
11749
|
|
|
11804
11750
|
return polyPath;
|
|
11805
11751
|
}
|
|
11806
11752
|
|
|
11807
11753
|
/**
|
|
11808
|
-
*
|
|
11809
|
-
* to cubic beziers
|
|
11754
|
+
* topology based curve fit
|
|
11810
11755
|
*/
|
|
11811
|
-
|
|
11812
|
-
function simplifyPolyChunks(chunks = [], {
|
|
11756
|
+
function simplifyPolyChunksTopology(chunks = [], {
|
|
11813
11757
|
closed = true,
|
|
11814
11758
|
keepCorners = true,
|
|
11815
11759
|
tolerance = 1,
|
|
11816
11760
|
} = {}) {
|
|
11817
11761
|
|
|
11762
|
+
console.log(chunks);
|
|
11763
|
+
|
|
11818
11764
|
let l = chunks.length;
|
|
11819
11765
|
|
|
11820
11766
|
// new pathData
|
|
11767
|
+
|
|
11821
11768
|
let pathData = [{ type: 'M', values: [chunks[0][0].x, chunks[0][0].y] }];
|
|
11822
11769
|
|
|
11770
|
+
// loop chunks
|
|
11823
11771
|
for (let i = 0; i < l; i++) {
|
|
11824
11772
|
|
|
11773
|
+
let chunkPrev = i > 0 ? chunks[i - 1] : (closed ? chunks[l - 1] : null);
|
|
11825
11774
|
let chunk = chunks[i];
|
|
11826
|
-
let chunkN = chunks[i + 1] ? chunks[i + 1] : null;
|
|
11775
|
+
let chunkN = chunks[i + 1] ? chunks[i + 1] : (closed ? chunks[0] : null);
|
|
11827
11776
|
let segments = [];
|
|
11828
|
-
let chunklen = chunk.length;
|
|
11829
|
-
chunk[chunk.length - 1];
|
|
11830
11777
|
|
|
11831
11778
|
// add from next command
|
|
11832
11779
|
if (chunkN) {
|
|
11833
11780
|
chunk.push(chunkN[0]);
|
|
11834
11781
|
}
|
|
11835
11782
|
|
|
11836
|
-
|
|
11837
|
-
|
|
11838
|
-
|
|
11839
|
-
|
|
11783
|
+
let chunklen = chunk.length;
|
|
11784
|
+
let hasInflection = false;
|
|
11785
|
+
let segments_1 = [], segments_2 = [], segments_3 = [];
|
|
11786
|
+
let segsRequired = 3;
|
|
11787
|
+
|
|
11788
|
+
// 1st point
|
|
11789
|
+
let p1 = chunk[0];
|
|
11790
|
+
// last point in chunk
|
|
11791
|
+
let p2 = chunk[chunklen - 1];
|
|
11792
|
+
|
|
11793
|
+
// nothing to simplify - lineto
|
|
11794
|
+
|
|
11795
|
+
// if (chunklen < 2 || (chunklen === 2 && (chunk[1].isExtreme || i===l-1 && !closed) )) {
|
|
11796
|
+
|
|
11797
|
+
if (chunklen < 2 || (chunklen === 2 && (chunk[1].isExtreme))) {
|
|
11798
|
+
|
|
11799
|
+
if (chunklen === 2) {
|
|
11800
|
+
segsRequired = 2;
|
|
11801
|
+
segments_2 = [
|
|
11802
|
+
{
|
|
11803
|
+
type: 'L',
|
|
11804
|
+
values: [p1.x, p1.y],
|
|
11805
|
+
p0: chunkPrev,
|
|
11806
|
+
p: p1,
|
|
11807
|
+
},
|
|
11808
|
+
{
|
|
11809
|
+
type: 'L',
|
|
11810
|
+
values: [chunk[1].x, chunk[1].y],
|
|
11811
|
+
p0: p1,
|
|
11812
|
+
p: p2,
|
|
11813
|
+
}
|
|
11814
|
+
];
|
|
11815
|
+
} else {
|
|
11816
|
+
segsRequired = 1;
|
|
11817
|
+
segments_1 = [
|
|
11818
|
+
{
|
|
11819
|
+
type: 'L',
|
|
11820
|
+
values: [p1.x, p1.y],
|
|
11821
|
+
p0: chunkPrev,
|
|
11822
|
+
p: p2,
|
|
11823
|
+
},
|
|
11824
|
+
];
|
|
11825
|
+
}
|
|
11840
11826
|
|
|
11841
11827
|
} else {
|
|
11842
|
-
|
|
11843
|
-
|
|
11844
|
-
|
|
11828
|
+
|
|
11829
|
+
// point before inflection
|
|
11830
|
+
let p3 = chunk[chunklen - 2];
|
|
11831
|
+
|
|
11832
|
+
chunk.filter(pt => pt.isExtreme);
|
|
11833
|
+
let semiExtremes = chunk.filter(pt => pt.isSemiExtreme);
|
|
11834
|
+
chunk.filter(pt => pt.isCorner);
|
|
11835
|
+
let inflections = chunk.filter(pt => pt.isDirChange && !pt.isCorner && !pt.isExtreme);
|
|
11836
|
+
hasInflection = inflections.length && inflections[0] !== p1;
|
|
11837
|
+
|
|
11838
|
+
let idxMid = Math.floor(chunklen * 0.5);
|
|
11839
|
+
let pMid = semiExtremes.length ? semiExtremes[Math.floor(semiExtremes.length * 0.5)] : chunk[idxMid];
|
|
11840
|
+
|
|
11841
|
+
let dist0 = getDistManhattan(p1, p3);
|
|
11842
|
+
let dist1 = getDistManhattan(pMid, p3);
|
|
11843
|
+
let dist2 = getDistManhattan(pMid, p1);
|
|
11844
|
+
let thresh = dist0 * 0.25;
|
|
11845
|
+
let shortMidSegment = dist1 < thresh || dist2 < thresh;
|
|
11846
|
+
|
|
11847
|
+
/**
|
|
11848
|
+
* we have 3 modes
|
|
11849
|
+
* 1 segment: only 1 segment between extremes/corners
|
|
11850
|
+
* 2 segments: semiextreme/mid in between
|
|
11851
|
+
* 3 segments: inflection
|
|
11852
|
+
*/
|
|
11853
|
+
|
|
11854
|
+
segsRequired = shortMidSegment ? (!hasInflection ? 1 : 2) : (hasInflection ? 3 : 2);
|
|
11855
|
+
|
|
11856
|
+
let cp1_1 = p1.tangentR;
|
|
11857
|
+
let cp2_1 = pMid.tangentL;
|
|
11858
|
+
let p_1 = pMid;
|
|
11859
|
+
|
|
11860
|
+
// renderPoint(markers, pMid, 'orange', '2%')
|
|
11861
|
+
|
|
11862
|
+
let cp2_2 = p2.tangentL;
|
|
11863
|
+
let cp1_2 = pMid.tangentR;
|
|
11864
|
+
let p_2 = p2;
|
|
11865
|
+
|
|
11866
|
+
let cp1_3 = null;
|
|
11867
|
+
let cp2_3 = null;
|
|
11868
|
+
let p_3 = null;
|
|
11869
|
+
|
|
11870
|
+
// general extrapolation
|
|
11871
|
+
let t = 0.666;
|
|
11872
|
+
let ptI_1 = null, ptI_2 = null, ptI_3 = null;
|
|
11873
|
+
|
|
11874
|
+
// 1 segment
|
|
11875
|
+
ptI_1 = checkLineIntersection(p1, p1.tangentR, p2, p2.tangentL, false, true);
|
|
11876
|
+
if (ptI_1) {
|
|
11877
|
+
cp1_1 = interpolate(p1, ptI_1, t);
|
|
11878
|
+
cp2_1 = interpolate(p2, ptI_1, t);
|
|
11879
|
+
p_1 = p2;
|
|
11880
|
+
|
|
11881
|
+
segments_1 = [
|
|
11882
|
+
|
|
11883
|
+
{
|
|
11884
|
+
type: 'C',
|
|
11885
|
+
values: [cp1_1.x, cp1_1.y, cp2_1.x, cp2_1.y, p_1.x, p_1.y],
|
|
11886
|
+
p0: p1,
|
|
11887
|
+
cp1: cp1_1,
|
|
11888
|
+
cp2: cp2_1,
|
|
11889
|
+
p: p_1,
|
|
11890
|
+
}
|
|
11891
|
+
];
|
|
11892
|
+
|
|
11893
|
+
}
|
|
11894
|
+
|
|
11895
|
+
// 2 segments
|
|
11896
|
+
ptI_1 = checkLineIntersection(p1, p1.tangentR, pMid, pMid.tangentL, false, true);
|
|
11897
|
+
ptI_2 = checkLineIntersection(p2, p2.tangentL, pMid, pMid.tangentR, false, true);
|
|
11898
|
+
|
|
11899
|
+
if (ptI_1 && ptI_2) {
|
|
11900
|
+
cp1_1 = interpolate(p1, ptI_1, t);
|
|
11901
|
+
cp2_1 = interpolate(pMid, ptI_1, t);
|
|
11902
|
+
p_1 = pMid;
|
|
11903
|
+
|
|
11904
|
+
cp1_2 = interpolate(pMid, ptI_2, t);
|
|
11905
|
+
cp2_2 = interpolate(p2, ptI_2, t);
|
|
11906
|
+
p_2 = p2;
|
|
11907
|
+
|
|
11908
|
+
segments_2 = [
|
|
11909
|
+
|
|
11910
|
+
{
|
|
11911
|
+
type: 'C',
|
|
11912
|
+
values: [cp1_1.x, cp1_1.y, cp2_1.x, cp2_1.y, p_1.x, p_1.y],
|
|
11913
|
+
p0: p1,
|
|
11914
|
+
cp1: cp1_1,
|
|
11915
|
+
cp2: cp2_1,
|
|
11916
|
+
p: p_1,
|
|
11917
|
+
isExtreme: p_1.isExtreme
|
|
11918
|
+
},
|
|
11919
|
+
{
|
|
11920
|
+
type: 'C',
|
|
11921
|
+
values: [cp1_2.x, cp1_2.y, cp2_2.x, cp2_2.y, p_2.x, p_2.y],
|
|
11922
|
+
p0: p_1,
|
|
11923
|
+
cp1: cp1_2,
|
|
11924
|
+
cp2: cp2_2,
|
|
11925
|
+
p: p_2,
|
|
11926
|
+
isExtreme: p_2.isExtreme
|
|
11927
|
+
|
|
11928
|
+
},
|
|
11929
|
+
];
|
|
11930
|
+
|
|
11931
|
+
}
|
|
11932
|
+
|
|
11933
|
+
// 3 segments
|
|
11934
|
+
|
|
11935
|
+
if (hasInflection) {
|
|
11936
|
+
|
|
11937
|
+
// get pt between dir change and mid
|
|
11938
|
+
let idx_3_4 = Math.floor(chunklen * 0.75);
|
|
11939
|
+
p3 = chunk[idx_3_4];
|
|
11940
|
+
ptI_3 = checkLineIntersection(p3, p3.tangentR, p2, p2.tangentL, false, false);
|
|
11941
|
+
|
|
11942
|
+
if (ptI_3) {
|
|
11943
|
+
let tangentR_beforeDirChange = interpolate(p3, ptI_3, t);
|
|
11944
|
+
|
|
11945
|
+
// extend right tangent
|
|
11946
|
+
p3.tangentR.x = tangentR_beforeDirChange.x;
|
|
11947
|
+
p3.tangentR.y = tangentR_beforeDirChange.y;
|
|
11948
|
+
|
|
11949
|
+
// extend dir change tangent
|
|
11950
|
+
let tangentL_dirChange = interpolate(p2, ptI_3, t);
|
|
11951
|
+
p2.tangentL.x = tangentL_dirChange.x;
|
|
11952
|
+
p2.tangentL.y = tangentL_dirChange.y;
|
|
11953
|
+
} else {
|
|
11954
|
+
|
|
11955
|
+
if (p3 === p2) {
|
|
11956
|
+
|
|
11957
|
+
idx_3_4 = Math.floor(chunklen * 0.3);
|
|
11958
|
+
p3 = chunk[idx_3_4];
|
|
11959
|
+
|
|
11960
|
+
}
|
|
11961
|
+
checkLineIntersection(p3, p3.tangentR, p2, p2.tangentL, false, false);
|
|
11962
|
+
|
|
11963
|
+
cp1_1 = interpolate(p1, p1.tangentR, 1.333);
|
|
11964
|
+
cp2_1 = interpolate(p2, p2.tangentL, 1.333);
|
|
11965
|
+
|
|
11966
|
+
segments_3 = [
|
|
11967
|
+
{
|
|
11968
|
+
type: 'C',
|
|
11969
|
+
values: [cp1_1.x, cp1_1.y, cp2_1.x, cp2_1.y, p2.x, p2.y],
|
|
11970
|
+
p0: p1,
|
|
11971
|
+
cp1: cp1_1,
|
|
11972
|
+
cp2: cp2_1,
|
|
11973
|
+
p: p2,
|
|
11974
|
+
isExtreme: p2.isExtreme
|
|
11975
|
+
|
|
11976
|
+
},
|
|
11977
|
+
];
|
|
11978
|
+
|
|
11979
|
+
/*
|
|
11980
|
+
let tangentR_beforeDirChange = interpolate(p3, ptI_3, t)
|
|
11981
|
+
|
|
11982
|
+
// extend right tangent
|
|
11983
|
+
p3.tangentR.x = tangentR_beforeDirChange.x
|
|
11984
|
+
p3.tangentR.y = tangentR_beforeDirChange.y
|
|
11985
|
+
|
|
11986
|
+
let tangentL_dirChange = interpolate(p2, ptI_3, t)
|
|
11987
|
+
p2.tangentL.x = tangentL_dirChange.x
|
|
11988
|
+
p2.tangentL.y = tangentL_dirChange.y
|
|
11989
|
+
*/
|
|
11990
|
+
|
|
11991
|
+
pathDataToD([{ type: 'M', values: [p1.x, p1.y] }, ...segments_3]);
|
|
11992
|
+
|
|
11993
|
+
}
|
|
11994
|
+
|
|
11995
|
+
cp1_3 = p3.tangentR;
|
|
11996
|
+
cp2_3 = p2.tangentL;
|
|
11997
|
+
p_3 = p2;
|
|
11998
|
+
|
|
11999
|
+
ptI_1 = checkLineIntersection(p1, p1.tangentR, pMid, pMid.tangentL, false, true);
|
|
12000
|
+
ptI_2 = checkLineIntersection(pMid, pMid.tangentR, p3, p3.tangentL, false, true);
|
|
12001
|
+
|
|
12002
|
+
if (ptI_1 && ptI_2 && ptI_3) {
|
|
12003
|
+
|
|
12004
|
+
cp1_1 = interpolate(p1, ptI_1, t);
|
|
12005
|
+
cp2_1 = interpolate(pMid, ptI_1, t);
|
|
12006
|
+
p_1 = pMid;
|
|
12007
|
+
|
|
12008
|
+
cp1_2 = interpolate(pMid, ptI_2, t);
|
|
12009
|
+
cp2_2 = interpolate(p3, ptI_2, t);
|
|
12010
|
+
p_2 = p3;
|
|
12011
|
+
|
|
12012
|
+
segments_3 = [
|
|
12013
|
+
{
|
|
12014
|
+
type: 'C',
|
|
12015
|
+
values: [cp1_1.x, cp1_1.y, cp2_1.x, cp2_1.y, p_1.x, p_1.y],
|
|
12016
|
+
p0: p1,
|
|
12017
|
+
cp1: cp1_1,
|
|
12018
|
+
cp2: cp2_1,
|
|
12019
|
+
p: p_1,
|
|
12020
|
+
isExtreme: p_1.isExtreme
|
|
12021
|
+
|
|
12022
|
+
},
|
|
12023
|
+
{
|
|
12024
|
+
type: 'C',
|
|
12025
|
+
values: [cp1_2.x, cp1_2.y, cp2_2.x, cp2_2.y, p_2.x, p_2.y],
|
|
12026
|
+
p0: p_1,
|
|
12027
|
+
cp1: cp1_2,
|
|
12028
|
+
cp2: cp2_2,
|
|
12029
|
+
p: p_2,
|
|
12030
|
+
},
|
|
12031
|
+
{
|
|
12032
|
+
type: 'C',
|
|
12033
|
+
values: [cp1_3.x, cp1_3.y, cp2_3.x, cp2_3.y, p_3.x, p_3.y],
|
|
12034
|
+
p0: p_2,
|
|
12035
|
+
cp1: cp1_3,
|
|
12036
|
+
cp2: cp2_3,
|
|
12037
|
+
p: p_3,
|
|
12038
|
+
isExtreme: p_3.isExtreme
|
|
12039
|
+
|
|
12040
|
+
}
|
|
12041
|
+
];
|
|
12042
|
+
|
|
12043
|
+
pathDataToD([{ type: 'M', values: [p1.x, p1.y] }, ...segments_3]);
|
|
12044
|
+
|
|
12045
|
+
}
|
|
12046
|
+
|
|
12047
|
+
}
|
|
12048
|
+
|
|
12049
|
+
}
|
|
12050
|
+
|
|
12051
|
+
if (segsRequired === 1) {
|
|
12052
|
+
segments = segments_1;
|
|
12053
|
+
} else if (segsRequired === 2 && segments_2.length) {
|
|
12054
|
+
segments = segments_2;
|
|
12055
|
+
}
|
|
12056
|
+
else ;
|
|
12057
|
+
|
|
12058
|
+
segments = segments_3.length ? segments_3 : segments_2;
|
|
12059
|
+
/*
|
|
12060
|
+
if (simplify && !isLinetoSeg && segments.length > 1) {
|
|
12061
|
+
|
|
12062
|
+
let com1 = segments[0]
|
|
12063
|
+
let com2 = segments[1]
|
|
12064
|
+
|
|
12065
|
+
tolerance = 1.1
|
|
12066
|
+
let combined = combineCubicPairs(com1, com2, { tolerance })
|
|
12067
|
+
|
|
12068
|
+
let error = 0;
|
|
12069
|
+
let comsSimp =[]
|
|
12070
|
+
|
|
12071
|
+
console.log('!!!combined', segments.length, combined);
|
|
12072
|
+
|
|
12073
|
+
// success
|
|
12074
|
+
if (combined.length === 1) {
|
|
12075
|
+
|
|
12076
|
+
if(segments.length === 2){
|
|
12077
|
+
segments = combined
|
|
12078
|
+
}
|
|
12079
|
+
|
|
12080
|
+
let com = combined[0]
|
|
12081
|
+
}
|
|
12082
|
+
|
|
11845
12083
|
}
|
|
12084
|
+
*/
|
|
11846
12085
|
|
|
11847
12086
|
// remove first segment to connect to last segment
|
|
11848
12087
|
pathData.push(...segments);
|
|
11849
12088
|
|
|
11850
12089
|
}
|
|
11851
12090
|
|
|
11852
|
-
if (closed)
|
|
12091
|
+
if (closed) {
|
|
12092
|
+
pathData.push({ type: 'Z', values: [] });
|
|
12093
|
+
}
|
|
12094
|
+
|
|
12095
|
+
// refine extremes
|
|
11853
12096
|
return pathData
|
|
11854
12097
|
|
|
11855
12098
|
}
|
|
@@ -11860,13 +12103,18 @@
|
|
|
11860
12103
|
*/
|
|
11861
12104
|
function pathDataToPolygonOpt(pathData, {
|
|
11862
12105
|
precisionPoly = 1,
|
|
11863
|
-
autoAccuracy=false,
|
|
11864
|
-
polyFormat='
|
|
11865
|
-
decimals
|
|
11866
|
-
simplifyRD=1,
|
|
11867
|
-
|
|
12106
|
+
autoAccuracy = false,
|
|
12107
|
+
polyFormat = 'object',
|
|
12108
|
+
decimals = -1,
|
|
12109
|
+
simplifyRD = 1,
|
|
12110
|
+
simplifyRDP = 1,
|
|
11868
12111
|
} = {}) {
|
|
11869
12112
|
|
|
12113
|
+
pathData = convertPathData(pathData, {toAbsolute:true, toLonghands:true, arcToCubic:true});
|
|
12114
|
+
pathData = addExtremePoints(pathData);
|
|
12115
|
+
|
|
12116
|
+
pathData = getPathDataVerbose(pathData);
|
|
12117
|
+
|
|
11870
12118
|
let l = pathData.length;
|
|
11871
12119
|
let M = { x: pathData[0].values[0], y: pathData[0].values[1] };
|
|
11872
12120
|
let p0 = M;
|
|
@@ -11893,7 +12141,7 @@
|
|
|
11893
12141
|
let pts2 = [pts[0]];
|
|
11894
12142
|
|
|
11895
12143
|
// adjustments for very small or large paths
|
|
11896
|
-
dims = dims.filter(Boolean).sort();
|
|
12144
|
+
dims = dims.filter(Boolean).sort((a,b)=>a-b);
|
|
11897
12145
|
let dimMax = dims[dims.length - 1];
|
|
11898
12146
|
|
|
11899
12147
|
let scale = dimMax > 2 && dimMax < 25 ? 1 : (20 / dimMax);
|
|
@@ -11932,31 +12180,33 @@
|
|
|
11932
12180
|
}
|
|
11933
12181
|
|
|
11934
12182
|
// simplify polygon
|
|
11935
|
-
if(simplifyRD>0){
|
|
11936
|
-
pts2 = simplifyPolyRD(pts2, {quality:simplifyRD});
|
|
12183
|
+
if (simplifyRD > 0) {
|
|
12184
|
+
pts2 = simplifyPolyRD(pts2, { quality: simplifyRD });
|
|
11937
12185
|
}
|
|
11938
12186
|
|
|
11939
|
-
if(simplifyRDP>0){
|
|
11940
|
-
pts2 = simplifyPolyRDP(pts2, {quality:simplifyRDP});
|
|
12187
|
+
if (simplifyRDP > 0) {
|
|
12188
|
+
pts2 = simplifyPolyRDP(pts2, { quality: simplifyRDP });
|
|
11941
12189
|
}
|
|
11942
12190
|
|
|
11943
|
-
|
|
11944
|
-
pathData = pathDataPoly;
|
|
11945
|
-
|
|
11946
|
-
if(autoAccuracy){
|
|
12191
|
+
if (autoAccuracy) {
|
|
11947
12192
|
decimals = detectAccuracyPoly(pts);
|
|
11948
12193
|
}
|
|
11949
12194
|
|
|
11950
|
-
let poly = decimals
|
|
12195
|
+
let poly = decimals > -1 ? pts2.map(pt => { return { x: roundTo(pt.x, decimals), y: roundTo(pt.y, decimals) } }) : pts2.map(pt => { return { x: pt.x, y: pt.y } });
|
|
11951
12196
|
|
|
11952
|
-
|
|
12197
|
+
pathDataPoly = pathDataFromPoly(poly);
|
|
12198
|
+
pathData = pathDataPoly;
|
|
12199
|
+
|
|
12200
|
+
if (polyFormat === 'array') {
|
|
11953
12201
|
poly = poly.map(pt => { return [pt.x, pt.y] });
|
|
11954
12202
|
}
|
|
11955
|
-
else if(polyFormat==='string'){
|
|
12203
|
+
else if (polyFormat === 'string') {
|
|
11956
12204
|
poly = poly.map(pt => { return [pt.x, pt.y].join(',') }).flat().join(' ');
|
|
11957
12205
|
}
|
|
11958
12206
|
|
|
11959
|
-
|
|
12207
|
+
let d= pathDataToD(pathData);
|
|
12208
|
+
|
|
12209
|
+
return { pathData, poly, d }
|
|
11960
12210
|
|
|
11961
12211
|
}
|
|
11962
12212
|
|
|
@@ -12197,6 +12447,7 @@
|
|
|
12197
12447
|
// polygon
|
|
12198
12448
|
toPolygon: false,
|
|
12199
12449
|
smoothPoly: false,
|
|
12450
|
+
isClosed:true,
|
|
12200
12451
|
polyFormat: 'object',
|
|
12201
12452
|
precisionPoly: 1,
|
|
12202
12453
|
simplifyRD: 0,
|
|
@@ -12345,7 +12596,6 @@
|
|
|
12345
12596
|
addViewBox: true,
|
|
12346
12597
|
removeDimensions: true,
|
|
12347
12598
|
removeOffCanvas: true,
|
|
12348
|
-
|
|
12349
12599
|
/*
|
|
12350
12600
|
*/
|
|
12351
12601
|
}
|
|
@@ -12494,6 +12744,122 @@
|
|
|
12494
12744
|
}
|
|
12495
12745
|
*/
|
|
12496
12746
|
|
|
12747
|
+
function SlickVGObj(props = {}) {
|
|
12748
|
+
|
|
12749
|
+
Object.assign(this, props);
|
|
12750
|
+
}
|
|
12751
|
+
|
|
12752
|
+
SlickVGObj.prototype.getD = function () {
|
|
12753
|
+
let d = this.d;
|
|
12754
|
+
return d;
|
|
12755
|
+
};
|
|
12756
|
+
|
|
12757
|
+
SlickVGObj.prototype.getSvg = function () {
|
|
12758
|
+
let svg = this.svg;
|
|
12759
|
+
|
|
12760
|
+
if (!svg) {
|
|
12761
|
+
let xArr = [];
|
|
12762
|
+
let yArr = [];
|
|
12763
|
+
let d = this.d;
|
|
12764
|
+
let pathDataPlusArr = this.pathDataPlusArr || [];
|
|
12765
|
+
pathDataPlusArr.forEach(path => {
|
|
12766
|
+
path.forEach(sub => {
|
|
12767
|
+
let { pathData } = sub;
|
|
12768
|
+
let bb = getPathDataBBox(pathData);
|
|
12769
|
+
let { x, y, right, bottom } = bb;
|
|
12770
|
+
xArr.push(x, right);
|
|
12771
|
+
yArr.push(y, bottom);
|
|
12772
|
+
});
|
|
12773
|
+
});
|
|
12774
|
+
|
|
12775
|
+
let x = Math.min(...xArr);
|
|
12776
|
+
let right = Math.max(...xArr);
|
|
12777
|
+
let y = Math.min(...yArr);
|
|
12778
|
+
let bottom = Math.max(...yArr);
|
|
12779
|
+
let width = right - x;
|
|
12780
|
+
let height = bottom - y;
|
|
12781
|
+
|
|
12782
|
+
svg = `<svg xmlns="${svgNs}" viewBox="${[x, y, width, height].join(' ')}"><path d="${d}"/></svg>`;
|
|
12783
|
+
|
|
12784
|
+
}
|
|
12785
|
+
return svg;
|
|
12786
|
+
};
|
|
12787
|
+
|
|
12788
|
+
/**
|
|
12789
|
+
* retrieve poly
|
|
12790
|
+
* formats: points, array, string, pathData, d,
|
|
12791
|
+
*/
|
|
12792
|
+
SlickVGObj.prototype.getPoly = function (options = {}
|
|
12793
|
+
) {
|
|
12794
|
+
|
|
12795
|
+
options = {
|
|
12796
|
+
...{
|
|
12797
|
+
precisionPoly: 1,
|
|
12798
|
+
simplifyRDP: 0,
|
|
12799
|
+
simplifyRD: 0,
|
|
12800
|
+
autoAccuracy: true,
|
|
12801
|
+
decimals: 3,
|
|
12802
|
+
format: 'object'
|
|
12803
|
+
},
|
|
12804
|
+
...options
|
|
12805
|
+
};
|
|
12806
|
+
|
|
12807
|
+
let { precisionPoly, simplifyRDP, simplifyRD, autoAccuracy, decimals, format } = options;
|
|
12808
|
+
|
|
12809
|
+
let polyFormat = format;
|
|
12810
|
+
|
|
12811
|
+
let polys = this.polys;
|
|
12812
|
+
if (!polys.length) {
|
|
12813
|
+
let pathDataPlusArr = this.pathDataPlusArr || [];
|
|
12814
|
+
let poly = [];
|
|
12815
|
+
let polyPaths = [];
|
|
12816
|
+
let dPoly = '';
|
|
12817
|
+
pathDataPlusArr.forEach(path => {
|
|
12818
|
+
path.forEach(sub => {
|
|
12819
|
+
let { pathData } = sub;
|
|
12820
|
+
|
|
12821
|
+
let polyData = pathDataToPolygonOpt(pathData, {
|
|
12822
|
+
precisionPoly,
|
|
12823
|
+
autoAccuracy,
|
|
12824
|
+
decimals,
|
|
12825
|
+
simplifyRD,
|
|
12826
|
+
simplifyRDP,
|
|
12827
|
+
polyFormat
|
|
12828
|
+
});
|
|
12829
|
+
|
|
12830
|
+
dPoly += polyData.d;
|
|
12831
|
+
poly.push(polyData.poly);
|
|
12832
|
+
polyPaths.push(polyData.pathData);
|
|
12833
|
+
|
|
12834
|
+
});
|
|
12835
|
+
});
|
|
12836
|
+
|
|
12837
|
+
if (polyFormat === 'object' || polyFormat === 'array' || polyFormat === 'string') {
|
|
12838
|
+
polys = poly;
|
|
12839
|
+
}
|
|
12840
|
+
else if (polyFormat === 'pathData') {
|
|
12841
|
+
polys = polyPaths.flat();
|
|
12842
|
+
}
|
|
12843
|
+
|
|
12844
|
+
else if (polyFormat === 'd') {
|
|
12845
|
+
polys = dPoly;
|
|
12846
|
+
}
|
|
12847
|
+
|
|
12848
|
+
}
|
|
12849
|
+
return polys;
|
|
12850
|
+
};
|
|
12851
|
+
|
|
12852
|
+
/*
|
|
12853
|
+
export function PathLengthObject(props = {}) {
|
|
12854
|
+
Object.assign(this, props);
|
|
12855
|
+
}
|
|
12856
|
+
*/
|
|
12857
|
+
|
|
12858
|
+
function SlickVG(input = '', settings = {}) {
|
|
12859
|
+
settings.getObject = true;
|
|
12860
|
+
return svgPathSimplify(input, settings)
|
|
12861
|
+
}
|
|
12862
|
+
|
|
12497
12863
|
function svgPathSimplify(input = '', settings = {}) {
|
|
12498
12864
|
|
|
12499
12865
|
let preset = settings['preset'] !== undefined && settings['preset'] ? settings['preset'] : null;
|
|
@@ -12505,7 +12871,7 @@
|
|
|
12505
12871
|
...settings
|
|
12506
12872
|
};
|
|
12507
12873
|
|
|
12508
|
-
let { getObject = false, removeComments, removeOffCanvas, unGroup, mergePaths, removeElements, removeDimensions, removeIds, removeClassNames, omitNamespace, cleanUpStrokes, addViewBox, addDimensions, removePrologue, removeHidden, removeUnused, cleanupDefs, cleanupClip, cleanupSVGAtts, removeNameSpaced, removeNameSpacedAtts, attributesToGroup, minifyRgbColors, stylesToAttributes, fixHref, legacyHref, allowMeta, allowDataAtts, allowAriaAtts, removeSVGAttributes, removeElAttributes, shapesToPaths, shapeConvert, convertShapes, simplifyBezier, optimizeOrder, autoClose, removeZeroLength, refineClosing, removeColinear, flatBezierToLinetos, revertToQuadratics, refineExtremes, simplifyCorners, fixDirections, keepExtremes, keepCorners, keepInflections, addExtremes, reversePath, toAbsolute, toRelative, toMixed, toShorthands, toLonghands, quadraticToCubic, arcToCubic, cubicToArc, lineToCubic, decimals, autoAccuracy, minifyD, tolerance, toPolygon, smoothPoly, polyFormat, precisionPoly, simplifyRD, simplifyRDP, harmonizeCpts, removeOrphanSubpaths, simplifyRound, simplifyQuadraticCorners, scale, scaleTo, crop, alignToOrigin, convertTransforms, keepSmaller, splitCompound, convertPathLength, toAbsoluteUnits } = settings;
|
|
12874
|
+
let { getObject = false, removeComments, removeOffCanvas, unGroup, mergePaths, removeElements, removeDimensions, removeIds, removeClassNames, omitNamespace, cleanUpStrokes, addViewBox, addDimensions, removePrologue, removeHidden, removeUnused, cleanupDefs, cleanupClip, cleanupSVGAtts, removeNameSpaced, removeNameSpacedAtts, attributesToGroup, minifyRgbColors, stylesToAttributes, fixHref, legacyHref, allowMeta, allowDataAtts, allowAriaAtts, removeSVGAttributes, removeElAttributes, shapesToPaths, shapeConvert, convertShapes, simplifyBezier, optimizeOrder, autoClose, removeZeroLength, refineClosing, removeColinear, flatBezierToLinetos, revertToQuadratics, refineExtremes, simplifyCorners, fixDirections, keepExtremes, keepCorners, keepInflections, addExtremes, reversePath, toAbsolute, toRelative, toMixed, toShorthands, toLonghands, quadraticToCubic, arcToCubic, cubicToArc, lineToCubic, decimals, autoAccuracy, minifyD, tolerance, toPolygon, smoothPoly, polyFormat, isClosed, precisionPoly, simplifyRD, simplifyRDP, harmonizeCpts, removeOrphanSubpaths, simplifyRound, simplifyQuadraticCorners, scale, scaleTo, crop, alignToOrigin, convertTransforms, keepSmaller, splitCompound, convertPathLength, toAbsoluteUnits } = settings;
|
|
12509
12875
|
|
|
12510
12876
|
// clamp tolerance and scale
|
|
12511
12877
|
tolerance = Math.max(0.1, tolerance);
|
|
@@ -12516,6 +12882,10 @@
|
|
|
12516
12882
|
settings.convertTransforms = true;
|
|
12517
12883
|
}
|
|
12518
12884
|
|
|
12885
|
+
if (shapeConvert === 'toShapes' || shapeConvert === 'shapesToPaths') {
|
|
12886
|
+
keepSmaller = false;
|
|
12887
|
+
}
|
|
12888
|
+
|
|
12519
12889
|
/**
|
|
12520
12890
|
* intercept
|
|
12521
12891
|
* invalid inputs
|
|
@@ -12532,11 +12902,11 @@
|
|
|
12532
12902
|
original: 0,
|
|
12533
12903
|
new: 0,
|
|
12534
12904
|
saved: 0,
|
|
12535
|
-
svgSize:0,
|
|
12536
|
-
svgSizeOpt:0,
|
|
12537
|
-
compression:0,
|
|
12538
|
-
decimals:0,
|
|
12539
|
-
invalid:true
|
|
12905
|
+
svgSize: 0,
|
|
12906
|
+
svgSizeOpt: 0,
|
|
12907
|
+
compression: 0,
|
|
12908
|
+
decimals: 0,
|
|
12909
|
+
invalid: true
|
|
12540
12910
|
};
|
|
12541
12911
|
|
|
12542
12912
|
return { svg: dummySVG, d: '', polys: [], report, pathDataPlusArr: [], pathDataPlusArr_global: [], inputType: 'invalid', dOriginal: '' };
|
|
@@ -12693,6 +13063,8 @@
|
|
|
12693
13063
|
toRelative,
|
|
12694
13064
|
toMixed,
|
|
12695
13065
|
toShorthands,
|
|
13066
|
+
// return true arc radii or minified/parametrized
|
|
13067
|
+
optimizeArcs: minifyD < 1,
|
|
12696
13068
|
decimals,
|
|
12697
13069
|
};
|
|
12698
13070
|
|
|
@@ -12704,7 +13076,12 @@
|
|
|
12704
13076
|
let pathDataPlusArr = [];
|
|
12705
13077
|
let path = paths[i];
|
|
12706
13078
|
let { d, el } = path;
|
|
12707
|
-
|
|
13079
|
+
|
|
13080
|
+
// disable reordering for elements with stroke dash-array
|
|
13081
|
+
if (el && (el.hasAttribute('stroke-dasharray') || el.hasAttribute('stroke-dashoffset'))) {
|
|
13082
|
+
optimizeOrder = false;
|
|
13083
|
+
|
|
13084
|
+
}
|
|
12708
13085
|
|
|
12709
13086
|
// if polygon we already heave absolute coordinates
|
|
12710
13087
|
|
|
@@ -12745,7 +13122,7 @@
|
|
|
12745
13122
|
// count commands for evaluation
|
|
12746
13123
|
comCount += pathData.length;
|
|
12747
13124
|
|
|
12748
|
-
if (
|
|
13125
|
+
if (removeOrphanSubpaths) pathData = removeOrphanedM(pathData);
|
|
12749
13126
|
|
|
12750
13127
|
/**
|
|
12751
13128
|
* get sub paths
|
|
@@ -12759,8 +13136,9 @@
|
|
|
12759
13136
|
let pathDataSub = subPathArr[i];
|
|
12760
13137
|
let poly = [];
|
|
12761
13138
|
let coms = Array.from(new Set(pathDataSub.map(com => com.type))).join('');
|
|
12762
|
-
isPoly = !(/[acqts]/gi).test(coms);
|
|
12763
|
-
|
|
13139
|
+
let isPoly = !(/[acqts]/gi).test(coms);
|
|
13140
|
+
|
|
13141
|
+
let closed = (/z/gi).test(coms);
|
|
12764
13142
|
|
|
12765
13143
|
if (isPoly && !mode) {
|
|
12766
13144
|
|
|
@@ -12777,7 +13155,6 @@
|
|
|
12777
13155
|
|
|
12778
13156
|
}
|
|
12779
13157
|
|
|
12780
|
-
toPolygon = false;
|
|
12781
13158
|
pathDataSub = pathDataFromPoly(poly, closed);
|
|
12782
13159
|
|
|
12783
13160
|
}
|
|
@@ -12786,26 +13163,56 @@
|
|
|
12786
13163
|
* convert curves to polygon
|
|
12787
13164
|
* flattening
|
|
12788
13165
|
*/
|
|
12789
|
-
|
|
13166
|
+
|
|
13167
|
+
if (toPolygon) {
|
|
12790
13168
|
simplifyBezier = false;
|
|
12791
13169
|
smoothPoly = false;
|
|
12792
13170
|
harmonizeCpts = false;
|
|
12793
13171
|
|
|
12794
|
-
|
|
13172
|
+
/**
|
|
13173
|
+
* if pathdata is already polygon- pass through
|
|
13174
|
+
* otherwise create precise polygon by curve splitting
|
|
13175
|
+
* */
|
|
12795
13176
|
|
|
12796
|
-
|
|
12797
|
-
|
|
12798
|
-
|
|
13177
|
+
if (!isPoly) {
|
|
13178
|
+
pathDataSub = getPathDataVerbose(pathDataSub);
|
|
13179
|
+
let polyData = pathDataToPolygonOpt(pathDataSub, {
|
|
13180
|
+
precisionPoly,
|
|
13181
|
+
autoAccuracy,
|
|
13182
|
+
polyFormat,
|
|
12799
13183
|
|
|
12800
|
-
|
|
12801
|
-
|
|
12802
|
-
|
|
13184
|
+
simplifyRD,
|
|
13185
|
+
simplifyRDP
|
|
13186
|
+
});
|
|
13187
|
+
|
|
13188
|
+
poly = polyData.poly;
|
|
13189
|
+
pathDataSub = polyData.pathData;
|
|
13190
|
+
isPoly = true;
|
|
13191
|
+
|
|
13192
|
+
}
|
|
13193
|
+
|
|
13194
|
+
polys.push(poly);
|
|
13195
|
+
|
|
13196
|
+
}
|
|
13197
|
+
|
|
13198
|
+
// harmonize cpts
|
|
13199
|
+
// if (harmonizeCpts) pathDataSub = harmonizeCubicCpts(pathDataSub)
|
|
12803
13200
|
|
|
12804
|
-
|
|
12805
|
-
isPoly = true;
|
|
13201
|
+
if (smoothPoly) {
|
|
12806
13202
|
|
|
13203
|
+
removeZeroLength=true;
|
|
13204
|
+
optimizeOrder=true;
|
|
12807
13205
|
}
|
|
12808
13206
|
|
|
13207
|
+
// remove zero length linetos
|
|
13208
|
+
if (removeColinear || removeZeroLength) pathDataSub = removeZeroLengthLinetos(pathDataSub);
|
|
13209
|
+
|
|
13210
|
+
// sort to top left
|
|
13211
|
+
if (optimizeOrder) pathDataSub = pathDataToTopLeft(pathDataSub);
|
|
13212
|
+
|
|
13213
|
+
// Preprocessing: remove colinear - ignore flat beziers (removed later)
|
|
13214
|
+
if (removeColinear) pathDataSub = pathDataRemoveColinear(pathDataSub, { tolerance, flatBezierToLinetos: false });
|
|
13215
|
+
|
|
12809
13216
|
/**
|
|
12810
13217
|
* poly to beziers via
|
|
12811
13218
|
* Philip J. Schneider's
|
|
@@ -12814,12 +13221,18 @@
|
|
|
12814
13221
|
if (smoothPoly) {
|
|
12815
13222
|
|
|
12816
13223
|
if (isPoly) {
|
|
12817
|
-
|
|
13224
|
+
/*
|
|
13225
|
+
pathDataSub = pathDataToTopLeft(pathDataSub)
|
|
13226
|
+
pathDataSub = removeZeroLengthLinetos(pathDataSub)
|
|
13227
|
+
pathDataSub = pathDataRemoveColinear(pathDataSub, { tolerance, flatBezierToLinetos: true });
|
|
13228
|
+
*/
|
|
13229
|
+
|
|
12818
13230
|
let poly = getPathDataVertices(pathDataSub);
|
|
12819
13231
|
|
|
12820
13232
|
// options for poly simplification
|
|
12821
13233
|
let optionsPoly = {
|
|
12822
|
-
|
|
13234
|
+
|
|
13235
|
+
denoise: 0,
|
|
12823
13236
|
tolerance,
|
|
12824
13237
|
width: bb_poly.width,
|
|
12825
13238
|
height: bb_poly.height,
|
|
@@ -12831,26 +13244,15 @@
|
|
|
12831
13244
|
closed,
|
|
12832
13245
|
simplifyRD,
|
|
12833
13246
|
simplifyRDP,
|
|
13247
|
+
isClosed,
|
|
12834
13248
|
};
|
|
12835
13249
|
|
|
12836
13250
|
pathDataSub = simplifyPolygonToPathData(poly, optionsPoly);
|
|
12837
13251
|
// flag as non poly as we're smoothing to curves
|
|
12838
|
-
|
|
13252
|
+
isPoly = false;
|
|
12839
13253
|
}
|
|
12840
13254
|
}
|
|
12841
13255
|
|
|
12842
|
-
// harmonize cpts
|
|
12843
|
-
// if (harmonizeCpts) pathDataSub = harmonizeCubicCpts(pathDataSub)
|
|
12844
|
-
|
|
12845
|
-
// remove zero length linetos
|
|
12846
|
-
if (removeColinear || removeZeroLength) pathDataSub = removeZeroLengthLinetos(pathDataSub);
|
|
12847
|
-
|
|
12848
|
-
// sort to top left
|
|
12849
|
-
if (optimizeOrder) pathDataSub = pathDataToTopLeft(pathDataSub);
|
|
12850
|
-
|
|
12851
|
-
// Preprocessing: remove colinear - ignore flat beziers (removed later)
|
|
12852
|
-
if (removeColinear) pathDataSub = pathDataRemoveColinear(pathDataSub, { tolerance, flatBezierToLinetos: false });
|
|
12853
|
-
|
|
12854
13256
|
let tMin = 0, tMax = 1;
|
|
12855
13257
|
if (addExtremes) pathDataSub = addExtremePoints(pathDataSub,
|
|
12856
13258
|
{ tMin, tMax, addExtremes, angles: [30] });
|
|
@@ -12873,11 +13275,13 @@
|
|
|
12873
13275
|
pathDataPlus.bb = getPolyBBox(getPathDataVertices(pathDataCubic));
|
|
12874
13276
|
}
|
|
12875
13277
|
pathDataPlus.dimA = pathDataPlus.bb.width + pathDataPlus.bb.height;
|
|
13278
|
+
|
|
12876
13279
|
pathDataPlus.pathData = getPathDataVerbose(pathDataSub, {
|
|
12877
13280
|
addSquareLength: false,
|
|
12878
13281
|
addArea: false,
|
|
12879
13282
|
addAverageDim: false
|
|
12880
13283
|
});
|
|
13284
|
+
|
|
12881
13285
|
}
|
|
12882
13286
|
|
|
12883
13287
|
// simplify beziers
|
|
@@ -12888,6 +13292,12 @@
|
|
|
12888
13292
|
|
|
12889
13293
|
if (refineClosing) pathData = refineClosingCommand(pathData, { threshold: dimA * 0.001 });
|
|
12890
13294
|
|
|
13295
|
+
// refine round segment sequences
|
|
13296
|
+
if (simplifyRound) {
|
|
13297
|
+
pathData = refineRoundSegments(pathData);
|
|
13298
|
+
pathData = simplifyAdjacentRound(pathData);
|
|
13299
|
+
}
|
|
13300
|
+
|
|
12891
13301
|
pathData = simplifyBezier ? simplifyPathDataCubic(pathData, { simplifyBezier, keepInflections, keepExtremes, keepCorners, revertToQuadratics, tolerance }) : pathData;
|
|
12892
13302
|
|
|
12893
13303
|
// refine extremes
|
|
@@ -12911,12 +13321,6 @@
|
|
|
12911
13321
|
pathData = refineRoundedCorners(pathData, { threshold, tolerance, simplifyQuadraticCorners });
|
|
12912
13322
|
}
|
|
12913
13323
|
|
|
12914
|
-
// refine round segment sequences
|
|
12915
|
-
if (simplifyRound) {
|
|
12916
|
-
pathData = refineRoundSegments(pathData);
|
|
12917
|
-
pathData = simplifyAdjacentRound(pathData);
|
|
12918
|
-
}
|
|
12919
|
-
|
|
12920
13324
|
// simplify to quadratics
|
|
12921
13325
|
if (revertToQuadratics) pathData = pathDataRevertCubicToQuadratic(pathData, tolerance);
|
|
12922
13326
|
|
|
@@ -12933,6 +13337,8 @@
|
|
|
12933
13337
|
|
|
12934
13338
|
}
|
|
12935
13339
|
|
|
13340
|
+
// offset path
|
|
13341
|
+
|
|
12936
13342
|
// update
|
|
12937
13343
|
pathDataPlusArr.push({ pathData, bb });
|
|
12938
13344
|
|
|
@@ -12992,17 +13398,18 @@
|
|
|
12992
13398
|
}
|
|
12993
13399
|
|
|
12994
13400
|
// add simplified poly - if not populated by toPoly conversion
|
|
13401
|
+
/*
|
|
12995
13402
|
if (isPoly) {
|
|
12996
13403
|
|
|
12997
13404
|
pathDataPlusArr.forEach(sub => {
|
|
12998
|
-
let poly = getPathDataVertices(sub.pathData, false, decimals)
|
|
13405
|
+
let poly = getPathDataVertices(sub.pathData, false, decimals)
|
|
12999
13406
|
if (polyFormat === 'array') {
|
|
13000
|
-
poly = polyPtsToArray(poly)
|
|
13407
|
+
poly = polyPtsToArray(poly)
|
|
13001
13408
|
}
|
|
13002
|
-
polys.push(poly)
|
|
13003
|
-
})
|
|
13004
|
-
|
|
13409
|
+
polys.push(poly)
|
|
13410
|
+
})
|
|
13005
13411
|
}
|
|
13412
|
+
*/
|
|
13006
13413
|
|
|
13007
13414
|
// split into sub paths - returns svg with multiple paths
|
|
13008
13415
|
if (splitCompound && !mode && pathDataPlusArr.length > 1) {
|
|
@@ -13101,7 +13508,6 @@
|
|
|
13101
13508
|
svg = stringifySVG(svg, { omitNamespace, removeComments, format: minifyD });
|
|
13102
13509
|
|
|
13103
13510
|
svgSizeOpt = svg.length;
|
|
13104
|
-
|
|
13105
13511
|
compression = +(100 / svgSize * (svgSizeOpt)).toFixed(2);
|
|
13106
13512
|
|
|
13107
13513
|
svgSize = +(svgSize / 1024).toFixed(3);
|
|
@@ -13127,15 +13533,52 @@
|
|
|
13127
13533
|
({ d, report } = paths[0]);
|
|
13128
13534
|
}
|
|
13129
13535
|
|
|
13130
|
-
|
|
13131
|
-
|
|
13132
|
-
|
|
13536
|
+
// sanitize poly output
|
|
13537
|
+
if (polys.length) {
|
|
13538
|
+
|
|
13539
|
+
// round point data
|
|
13540
|
+
polys.forEach((poly, i) => {
|
|
13541
|
+
|
|
13542
|
+
if (polyFormat === 'string') poly = normalizePoly(poly);
|
|
13543
|
+
|
|
13544
|
+
poly = roundPoly(poly, decimals);
|
|
13545
|
+
// remove coinciding points
|
|
13546
|
+
polys[i] = removeCoincidingVertices(poly);
|
|
13547
|
+
});
|
|
13548
|
+
|
|
13549
|
+
if (polys.length === 1) {
|
|
13550
|
+
polys = polys[0];
|
|
13551
|
+
}
|
|
13552
|
+
|
|
13553
|
+
if (polyFormat === 'string') {
|
|
13554
|
+
|
|
13555
|
+
polys = normalizePoly(polys, { toArray: true, flatten: true });
|
|
13556
|
+
|
|
13557
|
+
polys = polys.flat().join(' ');
|
|
13558
|
+
}
|
|
13133
13559
|
|
|
13134
|
-
if (polyFormat === 'string' && polys.length) {
|
|
13135
|
-
polys = polys.flat().map(pt => `${pt.x},${pt.y}`).join(' ');
|
|
13136
13560
|
}
|
|
13137
13561
|
|
|
13138
|
-
|
|
13562
|
+
// create object
|
|
13563
|
+
let svgObj = new SlickVGObj({ svg, d, polys, report, pathDataPlusArr: pathDataPlusArr_global, inputType, dOriginal });
|
|
13564
|
+
|
|
13565
|
+
/*
|
|
13566
|
+
let d2 = svgObj.getD()
|
|
13567
|
+
console.log('d2', d2);
|
|
13568
|
+
|
|
13569
|
+
let svg2 = svgObj.getSvg()
|
|
13570
|
+
console.log('svg2', svg2);
|
|
13571
|
+
|
|
13572
|
+
let format = 'd'
|
|
13573
|
+
format = 'pathData'
|
|
13574
|
+
format = 'd'
|
|
13575
|
+
format = 'points'
|
|
13576
|
+
format = 'array'
|
|
13577
|
+
let poly2 = svgObj.getPoly({ format })
|
|
13578
|
+
console.log('poly2', 'format', format, poly2);
|
|
13579
|
+
*/
|
|
13580
|
+
|
|
13581
|
+
return !getObject ? (d ? d : svg) : svgObj;
|
|
13139
13582
|
|
|
13140
13583
|
}
|
|
13141
13584
|
|
|
@@ -13144,6 +13587,7 @@
|
|
|
13144
13587
|
// IIFE
|
|
13145
13588
|
if (typeof window !== 'undefined') {
|
|
13146
13589
|
window.svgPathSimplify = svgPathSimplify;
|
|
13590
|
+
window.SlickVG = SlickVG;
|
|
13147
13591
|
window.getElementTransform = getElementTransform;
|
|
13148
13592
|
window.validateSVG = validateSVG;
|
|
13149
13593
|
window.detectInputType = detectInputType;
|
|
@@ -13153,6 +13597,7 @@
|
|
|
13153
13597
|
}
|
|
13154
13598
|
|
|
13155
13599
|
exports.PI = PI$1;
|
|
13600
|
+
exports.SlickVG = SlickVG;
|
|
13156
13601
|
exports.abs = abs$1;
|
|
13157
13602
|
exports.acos = acos$1;
|
|
13158
13603
|
exports.asin = asin$1;
|